Changes with Apache 2.4.24
+ *) mod_http2: new H2CopyFiles directive that changes treatment of file
+ handles in responses. Necessary in order to fix broken lifetime handling
+ in modules such as mod_wsgi.
+
+ *) mod_http2: removing timeouts on master connection while requests are
+ being processed. Requests may timeout, but the master only times out when
+ no more requests are active. [Stefan Eissing]
+
*) mod_http2: fixes connection flush when answering SETTINGS without any
stream open. [Moto Ishizawa <@summerwind>, Stefan Eissing]
</usage>
</directivesynopsis>
+ <directivesynopsis>
+ <name>H2CopyFiles</name>
+ <description>Determine file handling in responses</description>
+ <syntax>H2CopyFiles on|off</syntax>
+ <default>H2CopyFiles off</default>
+ <contextlist>
+ <context>server config</context>
+ <context>virtual host</context>
+ <context>directory</context>
+ <context>.htaccess</context>
+ </contextlist>
+ <compatibility>Available in version 2.4.24 and later.</compatibility>
+
+ <usage>
+ <p>
+ This directive influences how file content is handled in
+ responses. When off, which is the default, file handles are
+ passed from the requestion processing down to the main
+ connection, using the usual Apache setaside handling for
+ manaaging the lifetime of the file.
+ </p>
+ <p>
+ When set to <code>on</code>, file content is copied while the
+ request is still being processed and the buffered data is passed
+ on to the main connection. This is better if a third party
+ module is injecting files with different lifetimes into the response.
+ </p>
+ <p>
+ An example for such a module is <code>mod_wsgi</code> that may place
+ Python file handles into the response. Those files get close down when
+ Python thinks processing has finished. That may be well before
+ <code>mod_http2</code> is done with them.
+ </p>
+ </usage>
+ </directivesynopsis>
+
+
</modulesynopsis>
return DECLINED;
}
- cfg = h2_config_rget(r);
+ cfg = h2_config_sget(r->server);
if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) {
const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used");
if (!alt_svc_used) {
return n;
}
+int h2_beam_no_files(void *ctx, h2_bucket_beam *beam, apr_file_t *file)
+{
+ return 0;
+}
+
typedef int h2_beam_can_beam_callback(void *ctx, h2_bucket_beam *beam,
apr_file_t *file);
+/**
+ * Will deny all transfer of apr_file_t across the beam and force
+ * a data copy instead.
+ */
+int h2_beam_no_files(void *ctx, h2_bucket_beam *beam, apr_file_t *file);
+
struct h2_bucket_beam {
int id;
const char *tag;
1, /* HTTP/2 server push enabled */
NULL, /* map of content-type to priorities */
256, /* push diary size */
-
+ 0, /* copy files across threads */
};
void h2_config_init(apr_pool_t *pool)
conf->h2_push = DEF_VAL;
conf->priorities = NULL;
conf->push_diary_size = DEF_VAL;
+ conf->copy_files = DEF_VAL;
return conf;
}
n->priorities = add->priorities? add->priorities : base->priorities;
}
n->push_diary_size = H2_CONFIG_GET(add, base, push_diary_size);
+ n->copy_files = H2_CONFIG_GET(add, base, copy_files);
return n;
}
return H2_CONFIG_GET(conf, &defconf, h2_push);
case H2_CONF_PUSH_DIARY_SIZE:
return H2_CONFIG_GET(conf, &defconf, push_diary_size);
+ case H2_CONF_COPY_FILES:
+ return H2_CONFIG_GET(conf, &defconf, copy_files);
default:
return DEF_VAL;
}
return NULL;
}
+static const char *h2_conf_set_copy_files(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = (h2_config *)arg;
+ if (!strcasecmp(value, "On")) {
+ cfg->copy_files = 1;
+ return NULL;
+ }
+ else if (!strcasecmp(value, "Off")) {
+ cfg->copy_files = 0;
+ return NULL;
+ }
+
+ (void)arg;
+ return "value must be On or Off";
+}
+
#define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
const command_rec h2_cmds[] = {
RSRC_CONF, "define priority of PUSHed resources per content type"),
AP_INIT_TAKE1("H2PushDiarySize", h2_conf_set_push_diary_size, NULL,
RSRC_CONF, "size of push diary"),
+ AP_INIT_TAKE1("H2CopyFiles", h2_conf_set_copy_files, NULL,
+ OR_ALL, "on to perform copy of file data"),
AP_END_CMD
};
H2_CONF_TLS_COOLDOWN_SECS,
H2_CONF_PUSH,
H2_CONF_PUSH_DIARY_SIZE,
+ H2_CONF_COPY_FILES,
} h2_config_var_t;
struct apr_hash_t;
struct apr_hash_t *priorities;/* map of content-type to h2_priority records */
int push_diary_size; /* # of entries in push diary */
+ int copy_files; /* if files shall be copied vs setaside on output */
} h2_config;
static int h2_h2_process_conn(conn_rec* c);
static int h2_h2_pre_close_conn(conn_rec* c);
static int h2_h2_post_read_req(request_rec *r);
+static int h2_h2_late_fixups(request_rec *r);
/*******************************************************************************
* Once per lifetime init, retrieve optional functions
* never see the response.
*/
ap_hook_post_read_request(h2_h2_post_read_req, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ ap_hook_fixups(h2_h2_late_fixups, NULL, NULL, APR_HOOK_LAST);
}
int h2_h2_process_conn(conn_rec* c)
* that we manipulate filters only once. */
if (task && !task->filters_set) {
ap_filter_t *f;
-
+
/* setup the correct output filters to process the response
* on the proper mod_http2 way. */
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "adding task output filter");
return DECLINED;
}
+static int h2_h2_late_fixups(request_rec *r)
+{
+ /* slave connection? */
+ if (r->connection->master) {
+ h2_ctx *ctx = h2_ctx_rget(r);
+ struct h2_task *task = h2_ctx_get_task(ctx);
+ if (task) {
+ /* check if we copy vs. setaside files in this location */
+ task->output.copy_files = h2_config_geti(h2_config_rget(r),
+ H2_CONF_COPY_FILES);
+ }
+ }
+ return DECLINED;
+}
+
h2_beam_timeout_set(task->output.beam, m->stream_timeout);
h2_beam_on_consumed(task->output.beam, stream_output_consumed, task);
m->tx_handles_reserved -= h2_beam_get_files_beamed(task->output.beam);
- h2_beam_on_file_beam(task->output.beam, can_beam_file, m);
+ if (!task->output.copy_files) {
+ h2_beam_on_file_beam(task->output.beam, can_beam_file, m);
+ }
h2_beam_mutex_set(task->output.beam, beam_enter, task->cond, m);
}
case H2_SESSION_ST_WAIT:
if (session->wait_us <= 0) {
session->wait_us = 10;
- session->start_wait = apr_time_now();
if (h2_conn_io_flush(&session->io) != APR_SUCCESS) {
dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
break;
}
}
- else if ((apr_time_now() - session->start_wait) >= session->s->timeout) {
- /* waited long enough */
- if (trace) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE3, APR_TIMEUP, c,
- "h2_session: wait for data");
- }
- dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
- break;
- }
else {
/* repeating, increase timer for graceful backoff */
session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
apr_size_t max_stream_count; /* max number of open streams */
apr_size_t max_stream_mem; /* max buffer memory for a single stream */
- apr_time_t start_wait; /* Time we started waiting for sth. to happen */
apr_time_t idle_until; /* Time we shut down due to sheer boredom */
apr_time_t keep_sync_until; /* Time we sync wait until passing to async mpm */
}
set_state(stream, H2_STREAM_ST_OPEN);
status = h2_request_rwrite(stream->request, stream->pool, r);
- stream->request->serialize = h2_config_geti(h2_config_rget(r),
+ stream->request->serialize = h2_config_geti(h2_config_sget(r->server),
H2_CONF_SER_HEADERS);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03058)
"h2_request(%d): rwrite %s host=%s://%s%s",
if (!task->output.beam) {
h2_beam_create(&task->output.beam, task->pool,
- task->stream_id, "output", 0);
+ task->stream_id, "output", 0);
+ if (task->output.copy_files) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
+ "h2_task(%s): copy_files on", task->id);
+ h2_beam_on_file_beam(task->output.beam, h2_beam_no_files, NULL);
+ }
}
/* Attempt to write saved brigade first */
struct h2_bucket_beam *beam;
struct h2_from_h1 *from_h1;
unsigned int response_open : 1;
+ unsigned int copy_files : 1;
apr_off_t written;
apr_bucket_brigade *bb;
} output;
* @macro
* Version number of the http2 module as c string
*/
-#define MOD_HTTP2_VERSION "1.5.12"
+#define MOD_HTTP2_VERSION "1.5.13"
/**
* @macro
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
-#define MOD_HTTP2_VERSION_NUM 0x01050c
+#define MOD_HTTP2_VERSION_NUM 0x01050d
#endif /* mod_h2_h2_version_h */
AP_DECLARE_MODULE(http2) = {
STANDARD20_MODULE_STUFF,
- NULL,
- NULL,
+ h2_config_create_dir, /* func to create per dir config */
+ h2_config_merge,
h2_config_create_svr, /* func to create per server config */
h2_config_merge, /* func to merge per server config */
h2_cmds, /* command handlers */