From: Stefan Eissing Date: Wed, 20 Jul 2016 13:04:57 +0000 (+0000) Subject: mod_http2: new H2CopyFiles directive X-Git-Tag: 2.5.0-alpha~1390 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0f02a22dd3ebf09b6e826567c76a1ee9ff5d7f16;p=apache mod_http2: new H2CopyFiles directive git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1753498 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 5b6de8c8b3..79d3959e5d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) 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_proxy_fcgi: avoid loops when ProxyErrorOverride is enabled and the error documents are proxied. PR 55415. [Luca Toscano] diff --git a/docs/manual/mod/mod_http2.xml b/docs/manual/mod/mod_http2.xml index 6eb829acda..34cefeb382 100644 --- a/docs/manual/mod/mod_http2.xml +++ b/docs/manual/mod/mod_http2.xml @@ -897,4 +897,41 @@ H2TLSCoolDownSecs 0 + + H2CopyFiles + Determine file handling in responses + H2CopyFiles on|off + H2CopyFiles off + + server config + virtual host + directory + .htaccess + + Available in version 2.4.24 and later. + + +

+ 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. +

+

+ When set to on, 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. +

+

+ An example for such a module is mod_wsgi 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 + mod_http2 is done with them. +

+
+
+ + diff --git a/modules/http2/h2_bucket_beam.c b/modules/http2/h2_bucket_beam.c index cf2cb84dad..6a1e74d3e7 100644 --- a/modules/http2/h2_bucket_beam.c +++ b/modules/http2/h2_bucket_beam.c @@ -1013,3 +1013,8 @@ apr_size_t h2_beam_get_files_beamed(h2_bucket_beam *beam) return n; } +int h2_beam_no_files(void *ctx, h2_bucket_beam *beam, apr_file_t *file) +{ + return 0; +} + diff --git a/modules/http2/h2_bucket_beam.h b/modules/http2/h2_bucket_beam.h index 5c5d65de2e..fcafb063dd 100644 --- a/modules/http2/h2_bucket_beam.h +++ b/modules/http2/h2_bucket_beam.h @@ -163,6 +163,12 @@ typedef struct { 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; diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c index 0c1e6c469b..251c3f05d2 100644 --- a/modules/http2/h2_config.c +++ b/modules/http2/h2_config.c @@ -61,7 +61,7 @@ static h2_config defconf = { 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) @@ -95,6 +95,7 @@ static void *h2_config_create(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; } @@ -141,6 +142,7 @@ void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) 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; } @@ -185,6 +187,8 @@ apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var) 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; } @@ -500,6 +504,23 @@ static const char *h2_conf_set_push_diary_size(cmd_parms *parms, 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[] = { @@ -539,6 +560,8 @@ 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 }; diff --git a/modules/http2/h2_config.h b/modules/http2/h2_config.h index 92b222f9f7..4dc5f6c9b9 100644 --- a/modules/http2/h2_config.h +++ b/modules/http2/h2_config.h @@ -40,6 +40,7 @@ typedef enum { 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; @@ -68,6 +69,7 @@ typedef struct h2_config { 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; diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c index 825cd77e78..fd5338406b 100644 --- a/modules/http2/h2_h2.c +++ b/modules/http2/h2_h2.c @@ -433,6 +433,7 @@ static int cipher_is_blacklisted(const char *cipher, const char **psource) 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 @@ -567,6 +568,7 @@ void h2_h2_register_hooks(void) * 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) @@ -682,7 +684,7 @@ static int h2_h2_post_read_req(request_rec *r) * 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"); @@ -712,3 +714,18 @@ static int h2_h2_post_read_req(request_rec *r) 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; +} + diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 001eb7f6d3..fd6bf6ba29 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -686,7 +686,9 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_response *response) 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); } diff --git a/modules/http2/h2_task.c b/modules/http2/h2_task.c index 381d0b1c06..1893b12fad 100644 --- a/modules/http2/h2_task.c +++ b/modules/http2/h2_task.c @@ -406,7 +406,12 @@ static apr_status_t output_write(h2_task *task, ap_filter_t* f, 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 */ diff --git a/modules/http2/h2_task.h b/modules/http2/h2_task.h index 010005a374..1086e053e9 100644 --- a/modules/http2/h2_task.h +++ b/modules/http2/h2_task.h @@ -71,6 +71,7 @@ struct h2_task { 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; diff --git a/modules/http2/mod_http2.c b/modules/http2/mod_http2.c index 480917a419..0641061293 100644 --- a/modules/http2/mod_http2.c +++ b/modules/http2/mod_http2.c @@ -47,8 +47,8 @@ static void h2_hooks(apr_pool_t *pool); 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 */