diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/http2/h2_alt_svc.c | 2 | ||||
-rw-r--r-- | modules/http2/h2_config.c | 121 | ||||
-rw-r--r-- | modules/http2/h2_config.h | 19 | ||||
-rw-r--r-- | modules/http2/h2_conn.c | 13 | ||||
-rw-r--r-- | modules/http2/h2_conn.h | 5 | ||||
-rw-r--r-- | modules/http2/h2_conn_io.c | 6 | ||||
-rw-r--r-- | modules/http2/h2_conn_io.h | 6 | ||||
-rw-r--r-- | modules/http2/h2_ctx.h | 12 | ||||
-rw-r--r-- | modules/http2/h2_h2.c | 8 | ||||
-rw-r--r-- | modules/http2/h2_mplx.c | 5 | ||||
-rw-r--r-- | modules/http2/h2_mplx.h | 1 | ||||
-rw-r--r-- | modules/http2/h2_push.c | 12 | ||||
-rw-r--r-- | modules/http2/h2_push.h | 2 | ||||
-rw-r--r-- | modules/http2/h2_request.c | 10 | ||||
-rw-r--r-- | modules/http2/h2_request.h | 7 | ||||
-rw-r--r-- | modules/http2/h2_session.c | 110 | ||||
-rw-r--r-- | modules/http2/h2_session.h | 7 | ||||
-rw-r--r-- | modules/http2/h2_stream.c | 20 | ||||
-rw-r--r-- | modules/http2/h2_stream.h | 11 | ||||
-rw-r--r-- | modules/http2/h2_switch.c | 2 | ||||
-rw-r--r-- | modules/http2/h2_task.c | 13 | ||||
-rw-r--r-- | modules/http2/h2_task.h | 3 | ||||
-rw-r--r-- | modules/http2/h2_version.h | 4 |
23 files changed, 260 insertions, 139 deletions
diff --git a/modules/http2/h2_alt_svc.c b/modules/http2/h2_alt_svc.c index d18ae5f20d..6bc3cd0dff 100644 --- a/modules/http2/h2_alt_svc.c +++ b/modules/http2/h2_alt_svc.c @@ -74,7 +74,7 @@ h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool) { static int h2_alt_svc_handler(request_rec *r) { h2_ctx *ctx; - h2_config *cfg; + const h2_config *cfg; int i; if (r->connection->keepalives > 0) { diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c index 7dc0b20d20..11f9b0a609 100644 --- a/modules/http2/h2_config.c +++ b/modules/http2/h2_config.c @@ -15,6 +15,9 @@ #include <assert.h> +#include <apr_hash.h> +#include <apr_lib.h> + #include <httpd.h> #include <http_core.h> #include <http_config.h> @@ -43,7 +46,7 @@ static h2_config defconf = { H2_INITIAL_WINDOW_SIZE, /* window_size */ -1, /* min workers */ -1, /* max workers */ - 10, /* max workers idle secs */ + 10 * 60, /* max workers idle secs */ 64 * 1024, /* stream max mem size */ NULL, /* no alt-svcs */ -1, /* alt-svc max age */ @@ -55,6 +58,7 @@ static h2_config defconf = { 1024*1024, /* TLS warmup size */ 1, /* TLS cooldown secs */ 1, /* HTTP/2 server push enabled */ + NULL, /* map of content-type to priorities */ }; static int files_per_session = 0; @@ -111,6 +115,7 @@ static void *h2_config_create(apr_pool_t *pool, conf->tls_warmup_size = DEF_VAL; conf->tls_cooldown_secs = DEF_VAL; conf->h2_push = DEF_VAL; + conf->priorities = NULL; return conf; } @@ -155,16 +160,22 @@ void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) n->tls_warmup_size = H2_CONFIG_GET(add, base, tls_warmup_size); n->tls_cooldown_secs = H2_CONFIG_GET(add, base, tls_cooldown_secs); n->h2_push = H2_CONFIG_GET(add, base, h2_push); + if (add->priorities && base->priorities) { + n->priorities = apr_hash_overlay(pool, add->priorities, base->priorities); + } + else { + n->priorities = add->priorities? add->priorities : base->priorities; + } return n; } -int h2_config_geti(h2_config *conf, h2_config_var_t var) +int h2_config_geti(const h2_config *conf, h2_config_var_t var) { return (int)h2_config_geti64(conf, var); } -apr_int64_t h2_config_geti64(h2_config *conf, h2_config_var_t var) +apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var) { int n; switch(var) { @@ -207,7 +218,7 @@ apr_int64_t h2_config_geti64(h2_config *conf, h2_config_var_t var) } } -h2_config *h2_config_sget(server_rec *s) +const h2_config *h2_config_sget(server_rec *s) { h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, &http2_module); @@ -215,11 +226,21 @@ h2_config *h2_config_sget(server_rec *s) return cfg; } +const struct h2_priority *h2_config_get_priority(const h2_config *conf, + const char *content_type) +{ + if (content_type && conf->priorities) { + size_t len = strcspn(content_type, "; \t"); + h2_priority *prio = apr_hash_get(conf->priorities, content_type, len); + return prio? prio : apr_hash_get(conf->priorities, "*", 1); + } + return NULL; +} static const char *h2_conf_set_max_streams(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->h2_max_streams = (int)apr_atoi64(value); (void)arg; if (cfg->h2_max_streams < 1) { @@ -231,7 +252,7 @@ static const char *h2_conf_set_max_streams(cmd_parms *parms, static const char *h2_conf_set_window_size(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->h2_window_size = (int)apr_atoi64(value); (void)arg; if (cfg->h2_window_size < 1024) { @@ -243,7 +264,7 @@ static const char *h2_conf_set_window_size(cmd_parms *parms, static const char *h2_conf_set_min_workers(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->min_workers = (int)apr_atoi64(value); (void)arg; if (cfg->min_workers < 1) { @@ -255,7 +276,7 @@ static const char *h2_conf_set_min_workers(cmd_parms *parms, static const char *h2_conf_set_max_workers(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->max_workers = (int)apr_atoi64(value); (void)arg; if (cfg->max_workers < 1) { @@ -267,7 +288,7 @@ static const char *h2_conf_set_max_workers(cmd_parms *parms, static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->max_worker_idle_secs = (int)apr_atoi64(value); (void)arg; if (cfg->max_worker_idle_secs < 1) { @@ -279,7 +300,7 @@ static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms, static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->stream_max_mem_size = (int)apr_atoi64(value); @@ -294,7 +315,7 @@ static const char *h2_add_alt_svc(cmd_parms *parms, void *arg, const char *value) { if (value && strlen(value)) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool); if (!as) { return "unable to parse alt-svc specifier"; @@ -311,7 +332,7 @@ static const char *h2_add_alt_svc(cmd_parms *parms, static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->alt_svc_max_age = (int)apr_atoi64(value); (void)arg; return NULL; @@ -320,7 +341,7 @@ static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms, static const char *h2_conf_set_session_extra_files(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); apr_int64_t max = (int)apr_atoi64(value); if (max < 0) { return "value must be a non-negative number"; @@ -333,7 +354,7 @@ static const char *h2_conf_set_session_extra_files(cmd_parms *parms, static const char *h2_conf_set_serialize_headers(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { cfg->serialize_headers = 1; return NULL; @@ -350,7 +371,7 @@ static const char *h2_conf_set_serialize_headers(cmd_parms *parms, static const char *h2_conf_set_direct(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { cfg->h2_direct = 1; return NULL; @@ -367,7 +388,7 @@ static const char *h2_conf_set_direct(cmd_parms *parms, static const char *h2_conf_set_push(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { cfg->h2_push = 1; return NULL; @@ -381,10 +402,64 @@ static const char *h2_conf_set_push(cmd_parms *parms, return "value must be On or Off"; } +static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg, + const char *ctype, const char *sdependency, + const char *sweight) +{ + h2_config *cfg = (h2_config *)h2_config_sget(cmd->server); + const char *sdefweight = "16"; /* default AFTER weight */ + h2_dependency dependency; + h2_priority *priority; + int weight; + + if (!strlen(ctype)) { + return "1st argument must be a mime-type, like 'text/css' or '*'"; + } + + if (!sweight) { + /* 2 args only, but which one? */ + if (apr_isdigit(sdependency[0])) { + sweight = sdependency; + sdependency = "AFTER"; /* default dependency */ + } + } + + if (!strcasecmp("AFTER", sdependency)) { + dependency = H2_DEPENDANT_AFTER; + } + else if (!strcasecmp("BEFORE", sdependency)) { + dependency = H2_DEPENDANT_BEFORE; + sdefweight = "256"; /* default BEFORE weight */ + } + else if (!strcasecmp("INTERLEAVED", sdependency)) { + dependency = H2_DEPENDANT_INTERLEAVED; + sdefweight = "256"; /* default INTERLEAVED weight */ + } + else { + return "dependency must be one of 'After', 'Before' or 'Interleaved'"; + } + + weight = (int)apr_atoi64(sweight? sweight : sdefweight); + if (weight < NGHTTP2_MIN_WEIGHT) { + return apr_psprintf(cmd->pool, "weight must be a number >= %d", + NGHTTP2_MIN_WEIGHT); + } + + priority = apr_pcalloc(cmd->pool, sizeof(*priority)); + priority->dependency = dependency; + priority->weight = weight; + + if (!cfg->priorities) { + cfg->priorities = apr_hash_make(cmd->pool); + } + apr_hash_set(cfg->priorities, ctype, strlen(ctype), priority); + return NULL; +} + static const char *h2_conf_set_modern_tls_only(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { cfg->modern_tls_only = 1; return NULL; @@ -401,7 +476,7 @@ static const char *h2_conf_set_modern_tls_only(cmd_parms *parms, static const char *h2_conf_set_upgrade(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { cfg->h2_upgrade = 1; return NULL; @@ -418,7 +493,7 @@ static const char *h2_conf_set_upgrade(cmd_parms *parms, static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->tls_warmup_size = apr_atoi64(value); (void)arg; return NULL; @@ -427,7 +502,7 @@ static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms, static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->tls_cooldown_secs = (int)apr_atoi64(value); (void)arg; return NULL; @@ -469,18 +544,20 @@ const command_rec h2_cmds[] = { RSRC_CONF, "seconds of idle time on TLS before shrinking writes"), AP_INIT_TAKE1("H2Push", h2_conf_set_push, NULL, RSRC_CONF, "off to disable HTTP/2 server push"), + AP_INIT_TAKE23("H2PushPriority", h2_conf_add_push_priority, NULL, + RSRC_CONF, "define priority of PUSHed resources per content type"), AP_END_CMD }; -h2_config *h2_config_rget(request_rec *r) +const h2_config *h2_config_rget(request_rec *r) { h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, &http2_module); return cfg? cfg : h2_config_sget(r->server); } -h2_config *h2_config_get(conn_rec *c) +const h2_config *h2_config_get(conn_rec *c) { h2_ctx *ctx = h2_ctx_get(c); diff --git a/modules/http2/h2_config.h b/modules/http2/h2_config.h index 7d2ed0fa28..3d85ec247d 100644 --- a/modules/http2/h2_config.h +++ b/modules/http2/h2_config.h @@ -41,6 +41,9 @@ typedef enum { H2_CONF_PUSH, } h2_config_var_t; +struct apr_hash_t; +struct h2_priority; + /* Apache httpd module configuration for h2. */ typedef struct h2_config { const char *name; @@ -61,6 +64,7 @@ typedef struct h2_config { apr_int64_t tls_warmup_size; /* Amount of TLS data to send before going full write size */ int tls_cooldown_secs; /* Seconds of idle time before going back to small TLS records */ int h2_push; /* if HTTP/2 server push is enabled */ + struct apr_hash_t *priorities;/* map of content-type to h2_priority records */ } h2_config; @@ -68,18 +72,21 @@ void *h2_config_create_dir(apr_pool_t *pool, char *x); void *h2_config_create_svr(apr_pool_t *pool, server_rec *s); void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv); -apr_status_t h2_config_apply_header(h2_config *config, request_rec *r); +apr_status_t h2_config_apply_header(const h2_config *config, request_rec *r); extern const command_rec h2_cmds[]; -h2_config *h2_config_get(conn_rec *c); -h2_config *h2_config_sget(server_rec *s); -h2_config *h2_config_rget(request_rec *r); +const h2_config *h2_config_get(conn_rec *c); +const h2_config *h2_config_sget(server_rec *s); +const h2_config *h2_config_rget(request_rec *r); -int h2_config_geti(h2_config *conf, h2_config_var_t var); -apr_int64_t h2_config_geti64(h2_config *conf, h2_config_var_t var); +int h2_config_geti(const h2_config *conf, h2_config_var_t var); +apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var); void h2_config_init(apr_pool_t *pool); +const struct h2_priority *h2_config_get_priority(const h2_config *conf, + const char *content_type); + #endif /* __mod_h2__h2_config_h__ */ diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c index 6fec75ea9a..cf44fd7c1f 100644 --- a/modules/http2/h2_conn.c +++ b/modules/http2/h2_conn.c @@ -69,7 +69,7 @@ static void check_modules(void) apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s) { - h2_config *config = h2_config_sget(s); + const h2_config *config = h2_config_sget(s); apr_status_t status = APR_SUCCESS; int minw = h2_config_geti(config, H2_CONF_MIN_WORKERS); int maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS); @@ -131,11 +131,11 @@ static module *h2_conn_mpm_module(void) { return mpm_module; } -apr_status_t h2_conn_process(conn_rec *c, request_rec *r) +apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s) { apr_status_t status; h2_session *session; - h2_config *config; + const h2_config *config; int rv; if (!workers) { @@ -146,12 +146,15 @@ apr_status_t h2_conn_process(conn_rec *c, request_rec *r) ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_process start"); + if (!s && r) { + s = r->server; + } + + config = s? h2_config_sget(s) : h2_config_get(c); if (r) { - config = h2_config_rget(r); session = h2_session_rcreate(r, config, workers); } else { - config = h2_config_get(c); session = h2_session_create(c, config, workers); } diff --git a/modules/http2/h2_conn.h b/modules/http2/h2_conn.h index 84cf8d83c4..917a57e039 100644 --- a/modules/http2/h2_conn.h +++ b/modules/http2/h2_conn.h @@ -24,9 +24,10 @@ struct h2_task; * and the connection will close. * * @param c the connection HTTP/2 is starting on - * @param r the upgrad requestion that still awaits an answer, optional + * @param r the upgrade request that still awaits an answer, optional + * @param s the server selected by request or, if NULL, connection */ -apr_status_t h2_conn_process(conn_rec *c, request_rec *r); +apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s); /* Initialize this child process for h2 connection work, * to be called once during child init before multi processing diff --git a/modules/http2/h2_conn_io.c b/modules/http2/h2_conn_io.c index 485a8bd47e..a67d025fa8 100644 --- a/modules/http2/h2_conn_io.c +++ b/modules/http2/h2_conn_io.c @@ -45,10 +45,10 @@ #define WRITE_BUFFER_SIZE (8*WRITE_SIZE_MAX) -apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, apr_pool_t *pool) +apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, + const h2_config *cfg, + apr_pool_t *pool) { - h2_config *cfg = h2_config_get(c); - io->connection = c; io->input = apr_brigade_create(pool, c->bucket_alloc); io->output = apr_brigade_create(pool, c->bucket_alloc); diff --git a/modules/http2/h2_conn_io.h b/modules/http2/h2_conn_io.h index a0dd0d0e5c..f051c6c3d9 100644 --- a/modules/http2/h2_conn_io.h +++ b/modules/http2/h2_conn_io.h @@ -16,6 +16,8 @@ #ifndef __mod_h2__h2_conn_io__ #define __mod_h2__h2_conn_io__ +struct h2_config; + /* h2_io is the basic handler of a httpd connection. It keeps two brigades, * one for input, one for output and works with the installed connection * filters. @@ -42,7 +44,9 @@ typedef struct { int unflushed; } h2_conn_io; -apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, apr_pool_t *pool); +apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, + const struct h2_config *cfg, + apr_pool_t *pool); int h2_conn_io_is_buffered(h2_conn_io *io); diff --git a/modules/http2/h2_ctx.h b/modules/http2/h2_ctx.h index e4bc7506ae..7f8f2b5949 100644 --- a/modules/http2/h2_ctx.h +++ b/modules/http2/h2_ctx.h @@ -28,12 +28,12 @@ struct h2_config; * - those created by ourself to perform work on HTTP/2 streams */ typedef struct h2_ctx { - int is_h2; /* h2 engine is used */ - const char *protocol; /* the protocol negotiated */ - struct h2_task *task; /* the h2_task executing or NULL */ - const char *hostname; /* hostname negotiated via SNI, optional */ - server_rec *server; /* httpd server config selected. */ - struct h2_config *config; /* effective config in this context */ + int is_h2; /* h2 engine is used */ + const char *protocol; /* the protocol negotiated */ + struct h2_task *task; /* the h2_task executing or NULL */ + const char *hostname; /* hostname negotiated via SNI, optional */ + server_rec *server; /* httpd server config selected. */ + const struct h2_config *config; /* effective config in this context */ } h2_ctx; h2_ctx *h2_ctx_get(const conn_rec *c); diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c index e48e64e8a4..3b62dbf1c8 100644 --- a/modules/http2/h2_h2.c +++ b/modules/http2/h2_h2.c @@ -469,7 +469,7 @@ int h2_h2_is_tls(conn_rec *c) int h2_is_acceptable_connection(conn_rec *c, int require_all) { int is_tls = h2_h2_is_tls(c); - h2_config *cfg = h2_config_get(c); + const h2_config *cfg = h2_config_get(c); if (is_tls && h2_config_geti(cfg, H2_CONF_MODERN_TLS_ONLY) > 0) { /* Check TLS connection for modern TLS parameters, as defined in @@ -526,7 +526,7 @@ int h2_is_acceptable_connection(conn_rec *c, int require_all) int h2_allows_h2_direct(conn_rec *c) { - h2_config *cfg = h2_config_get(c); + const h2_config *cfg = h2_config_get(c); int h2_direct = h2_config_geti(cfg, H2_CONF_DIRECT); if (h2_direct < 0) { @@ -544,7 +544,7 @@ int h2_allows_h2_direct(conn_rec *c) int h2_allows_h2_upgrade(conn_rec *c) { - h2_config *cfg = h2_config_get(c); + const h2_config *cfg = h2_config_get(c); int h2_upgrade = h2_config_geti(cfg, H2_CONF_UPGRADE); return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(c)); @@ -651,7 +651,7 @@ int h2_h2_process_conn(conn_rec* c) ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, connection, h2 active"); - return h2_conn_process(c, NULL); + return h2_conn_process(c, NULL, ctx->server); } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, declined"); diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 6f2a512465..c4efed6276 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -102,10 +102,11 @@ static void h2_mplx_destroy(h2_mplx *m) * their HTTP/1 cousins, the separate allocator seems to work better * than protecting a shared h2_session one with an own lock. */ -h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, h2_workers *workers) +h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, + const h2_config *conf, + h2_workers *workers) { apr_status_t status = APR_SUCCESS; - h2_config *conf = h2_config_get(c); apr_allocator_t *allocator = NULL; h2_mplx *m; AP_DEBUG_ASSERT(conf); diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index c570e91fd4..f145428ff3 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -88,6 +88,7 @@ struct h2_mplx { * Implicitly has reference count 1. */ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *master, + const struct h2_config *conf, struct h2_workers *workers); /** diff --git a/modules/http2/h2_push.c b/modules/http2/h2_push.c index 65b2b7b625..b80ffb9081 100644 --- a/modules/http2/h2_push.c +++ b/modules/http2/h2_push.c @@ -282,7 +282,6 @@ static int add_push(link_ctx *ctx) path = apr_uri_unparse(ctx->pool, &uri, APR_URI_UNP_OMITSITEPART); push = apr_pcalloc(ctx->pool, sizeof(*push)); - push->initiating_id = ctx->req->id; headers = apr_table_make(ctx->pool, 5); apr_table_do(set_header, headers, ctx->req->headers, @@ -290,20 +289,13 @@ static int add_push(link_ctx *ctx) "Cache-Control", "Accept-Language", NULL); - /* TODO: which headers do we add here? - */ - - req = h2_request_createn(0, ctx->pool, - ctx->req->method, - ctx->req->scheme, + req = h2_request_createn(0, ctx->pool, ctx->req->config, + "GET", ctx->req->scheme, ctx->req->authority, path, headers); h2_request_end_headers(req, ctx->pool, 1); push->req = req; - push->prio.dependency = H2_DEPENDANT_AFTER; - push->prio.weight = NGHTTP2_DEFAULT_WEIGHT; - if (!ctx->pushes) { ctx->pushes = apr_array_make(ctx->pool, 5, sizeof(h2_push*)); } diff --git a/modules/http2/h2_push.h b/modules/http2/h2_push.h index b98a2f73e1..871548cee3 100644 --- a/modules/http2/h2_push.h +++ b/modules/http2/h2_push.h @@ -20,9 +20,7 @@ struct h2_response; struct h2_ngheader; typedef struct h2_push { - int initiating_id; const struct h2_request *req; - h2_priority prio; } h2_push; diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c index e1a371f6a4..bce58593c1 100644 --- a/modules/http2/h2_request.c +++ b/modules/http2/h2_request.c @@ -30,18 +30,22 @@ #include <scoreboard.h> #include "h2_private.h" +#include "h2_config.h" #include "h2_mplx.h" #include "h2_request.h" #include "h2_task.h" #include "h2_util.h" -h2_request *h2_request_create(int id, apr_pool_t *pool) +h2_request *h2_request_create(int id, apr_pool_t *pool, + const struct h2_config *config) { - return h2_request_createn(id, pool, NULL, NULL, NULL, NULL, NULL); + return h2_request_createn(id, pool, config, + NULL, NULL, NULL, NULL, NULL); } h2_request *h2_request_createn(int id, apr_pool_t *pool, + const struct h2_config *config, const char *method, const char *scheme, const char *authority, const char *path, apr_table_t *header) @@ -49,6 +53,7 @@ h2_request *h2_request_createn(int id, apr_pool_t *pool, h2_request *req = apr_pcalloc(pool, sizeof(h2_request)); req->id = id; + req->config = config; req->method = method; req->scheme = scheme; req->authority = authority; @@ -137,6 +142,7 @@ apr_status_t h2_request_rwrite(h2_request *req, request_rec *r) { apr_status_t status; + req->config = h2_config_rget(r); req->method = r->method; req->scheme = (r->parsed_uri.scheme? r->parsed_uri.scheme : ap_http_scheme(r)); diff --git a/modules/http2/h2_request.h b/modules/http2/h2_request.h index 69d24f38a2..9e74492cd9 100644 --- a/modules/http2/h2_request.h +++ b/modules/http2/h2_request.h @@ -20,6 +20,7 @@ * format that will be fed to various httpd input filters to finally * become a request_rec to be handled by soemone. */ +struct h2_config; struct h2_to_h1; struct h2_mplx; struct h2_task; @@ -42,11 +43,15 @@ struct h2_request { apr_off_t content_length; int chunked; int eoh; + + const struct h2_config *config; }; -h2_request *h2_request_create(int id, apr_pool_t *pool); +h2_request *h2_request_create(int id, apr_pool_t *pool, + const struct h2_config *config); h2_request *h2_request_createn(int id, apr_pool_t *pool, + const struct h2_config *config, const char *method, const char *scheme, const char *authority, const char *path, apr_table_t *headers); diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index 91a3d9465c..ca1d87824d 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -550,6 +550,22 @@ static int on_send_data_cb(nghttp2_session *ngh2, return h2_session_status_from_apr_status(status); } +static int on_frame_send_cb(nghttp2_session *ngh2, + const nghttp2_frame *frame, + void *user_data) +{ + h2_session *session = user_data; + if (APLOGctrace1(session->c)) { + char buffer[256]; + + frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, + "h2_session(%ld): frame_send %s", + session->id, buffer); + } + return 0; +} + #define NGH2_SET_CALLBACK(callbacks, name, fn)\ nghttp2_session_callbacks_set_##name##_callback(callbacks, fn) @@ -571,7 +587,8 @@ static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb) NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb); NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb); NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb); - + NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb); + return APR_SUCCESS; } @@ -625,7 +642,7 @@ static void *session_realloc(void *p, size_t size, void *ctx) static h2_session *h2_session_create_int(conn_rec *c, request_rec *r, - h2_config *config, + const h2_config *config, h2_workers *workers) { nghttp2_session_callbacks *callbacks = NULL; @@ -646,6 +663,7 @@ static h2_session *h2_session_create_int(conn_rec *c, session->id = c->id; session->c = c; session->r = r; + session->config = config; session->pool = pool; apr_pool_pre_cleanup_register(pool, session, session_pool_cleanup); @@ -661,9 +679,9 @@ static h2_session *h2_session_create_int(conn_rec *c, session->streams = h2_stream_set_create(session->pool, session->max_stream_count); session->workers = workers; - session->mplx = h2_mplx_create(c, session->pool, workers); + session->mplx = h2_mplx_create(c, session->pool, config, workers); - h2_conn_io_init(&session->io, c, session->pool); + h2_conn_io_init(&session->io, c, config, session->pool); session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc); status = init_callbacks(c, &callbacks); @@ -718,13 +736,13 @@ static h2_session *h2_session_create_int(conn_rec *c, return session; } -h2_session *h2_session_create(conn_rec *c, h2_config *config, +h2_session *h2_session_create(conn_rec *c, const h2_config *config, h2_workers *workers) { return h2_session_create_int(c, NULL, config, workers); } -h2_session *h2_session_rcreate(request_rec *r, h2_config *config, +h2_session *h2_session_rcreate(request_rec *r, const h2_config *config, h2_workers *workers) { return h2_session_create_int(r->connection, r, config, workers); @@ -851,7 +869,6 @@ apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv) apr_status_t h2_session_start(h2_session *session, int *rv) { apr_status_t status = APR_SUCCESS; - h2_config *config; nghttp2_settings_entry settings[3]; size_t slen; int i; @@ -859,15 +876,11 @@ apr_status_t h2_session_start(h2_session *session, int *rv) AP_DEBUG_ASSERT(session); /* Start the conversation by submitting our SETTINGS frame */ *rv = 0; - config = h2_config_get(session->c); if (session->r) { const char *s, *cs; apr_size_t dlen; h2_stream * stream; - /* better for vhost matching */ - config = h2_config_rget(session->r); - /* 'h2c' mode: we should have a 'HTTP2-Settings' header with * base64 encoded client settings. */ s = apr_table_get(session->r->headers_in, "HTTP2-Settings"); @@ -921,7 +934,7 @@ apr_status_t h2_session_start(h2_session *session, int *rv) settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; settings[slen].value = (uint32_t)session->max_stream_count; ++slen; - i = h2_config_geti(config, H2_CONF_WIN_SIZE); + i = h2_config_geti(session->config, H2_CONF_WIN_SIZE); if (i != H2_INITIAL_WINDOW_SIZE) { settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; settings[slen].value = i; @@ -1167,7 +1180,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream) nghttp2_data_provider provider; h2_response *response = stream->response; h2_ngheader *ngh; - h2_priority *prio; + const h2_priority *prio; memset(&provider, 0, sizeof(provider)); provider.source.fd = stream->id; @@ -1203,7 +1216,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream) */ if (!rv && !stream->initiated_on - && h2_config_geti(h2_config_get(session->c), H2_CONF_PUSH) + && h2_config_geti(session->config, H2_CONF_PUSH) && H2_HTTP_2XX(response->http_status) && h2_session_push_enabled(session)) { @@ -1243,26 +1256,24 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is, int nid; ngh = h2_util_ngheader_make_req(is->pool, push->req); - nid = nghttp2_submit_push_promise(session->ngh2, 0, push->initiating_id, + nid = nghttp2_submit_push_promise(session->ngh2, 0, is->id, ngh->nv, ngh->nvlen, NULL); if (nid <= 0) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): submitting push promise fail: %s", - session->id, push->initiating_id, - nghttp2_strerror(nid)); + session->id, is->id, nghttp2_strerror(nid)); return NULL; } ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_stream(%ld-%d): promised new stream %d for %s %s", - session->id, push->initiating_id, nid, - push->req->method, push->req->path); + "h2_stream(%ld-%d): promised new stream %d for %s %s on %d", + session->id, is->id, nid, + push->req->method, push->req->path, is->id); stream = h2_session_open_stream(session, nid); if (stream) { h2_stream_set_h2_request(stream, is->id, push->req); - h2_stream_set_priority(stream, &push->prio); status = stream_schedule(session, stream, 1); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, @@ -1275,7 +1286,7 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is, else { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): failed to create stream obj %d", - session->id, push->initiating_id, nid); + session->id, is->id, nid); } if (!stream) { @@ -1295,7 +1306,7 @@ static int valid_weight(float f) } apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, - h2_priority *prio) + const h2_priority *prio) { apr_status_t status = APR_SUCCESS; #ifdef H2_NG2_CHANGE_PRIO @@ -1336,7 +1347,7 @@ apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, */ ptype = "INTERLEAVED"; w_parent = nghttp2_stream_get_weight(s_parent); - w = valid_weight(w_parent * ((float)NGHTTP2_MAX_WEIGHT / prio->weight)); + w = valid_weight(w_parent * ((float)prio->weight / NGHTTP2_MAX_WEIGHT)); nghttp2_priority_spec_init(&ps, id_grandpa, w, 0); break; @@ -1347,7 +1358,8 @@ apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, * stream as child with MAX_WEIGHT. */ ptype = "BEFORE"; - nghttp2_priority_spec_init(&ps, stream->id, NGHTTP2_MAX_WEIGHT, 0); + w_parent = nghttp2_stream_get_weight(s_parent); + nghttp2_priority_spec_init(&ps, stream->id, w_parent, 0); rv = nghttp2_session_change_stream_priority(session->ngh2, id_parent, &ps); if (rv < 0) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, @@ -1357,8 +1369,8 @@ apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, return APR_EGENERAL; } id_grandpa = nghttp2_stream_get_stream_id(s_grandpa); - w_parent = nghttp2_stream_get_weight(s_parent); - nghttp2_priority_spec_init(&ps, id_grandpa, valid_weight(w_parent), 0); + w = valid_weight(w_parent * ((float)prio->weight / NGHTTP2_MAX_WEIGHT)); + nghttp2_priority_spec_init(&ps, id_grandpa, w, 0); break; case H2_DEPENDANT_AFTER: @@ -1508,12 +1520,43 @@ apr_status_t h2_session_process(h2_session *session) apr_interval_time_t wait_micros = 0; static const int MAX_WAIT_MICROS = 200 * 1000; int got_streams = 0; + h2_stream *stream; while (!session->aborted && (nghttp2_session_want_read(session->ngh2) || nghttp2_session_want_write(session->ngh2))) { int have_written = 0; int have_read = 0; + got_streams = !h2_stream_set_is_empty(session->streams); + if (got_streams) { + h2_session_resume_streams_with_data(session); + + if (h2_stream_set_has_unsubmitted(session->streams)) { + /* If we have responses ready, submit them now. */ + while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) { + status = submit_response(session, stream); + if (status == APR_SUCCESS + && nghttp2_session_want_write(session->ngh2)) { + int rv; + + rv = nghttp2_session_send(session->ngh2); + if (rv != 0) { + ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_session: send: %s", nghttp2_strerror(rv)); + if (nghttp2_is_fatal(rv)) { + h2_session_abort(session, status, rv); + goto end_process; + } + } + else { + have_written = 1; + wait_micros = 0; + } + } + } + } + } + /* Send data as long as we have it and window sizes allow. We are * a server after all. */ @@ -1629,9 +1672,7 @@ apr_status_t h2_session_process(h2_session *session) } got_streams = !h2_stream_set_is_empty(session->streams); - if (got_streams) { - h2_stream *stream; - + if (got_streams) { if (session->reprioritize) { h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session); session->reprioritize = 0; @@ -1657,15 +1698,6 @@ apr_status_t h2_session_process(h2_session *session) h2_conn_io_flush(&session->io); } } - - h2_session_resume_streams_with_data(session); - - if (h2_stream_set_has_unsubmitted(session->streams)) { - /* If we have responses ready, submit them now. */ - while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) { - status = submit_response(session, stream); - } - } } if (have_written) { diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h index 16767fb785..00347c93e4 100644 --- a/modules/http2/h2_session.h +++ b/modules/http2/h2_session.h @@ -59,6 +59,7 @@ struct h2_session { conn_rec *c; /* the connection this session serves */ request_rec *r; /* the request that started this in case * of 'h2c', NULL otherwise */ + const struct h2_config *config; /* Relevant config for this session */ int aborted; /* this session is being aborted */ int reprioritize; /* scheduled streams priority needs to * be re-evaluated */ @@ -94,7 +95,7 @@ struct h2_session { * @param workers the worker pool to use * @return the created session */ -h2_session *h2_session_create(conn_rec *c, struct h2_config *cfg, +h2_session *h2_session_create(conn_rec *c, const struct h2_config *cfg, struct h2_workers *workers); /** @@ -105,7 +106,7 @@ h2_session *h2_session_create(conn_rec *c, struct h2_config *cfg, * @param workers the worker pool to use * @return the created session */ -h2_session *h2_session_rcreate(request_rec *r, struct h2_config *cfg, +h2_session *h2_session_rcreate(request_rec *r, const struct h2_config *cfg, struct h2_workers *workers); /** @@ -198,7 +199,7 @@ struct h2_stream *h2_session_push(h2_session *session, apr_status_t h2_session_set_prio(h2_session *session, struct h2_stream *stream, - struct h2_priority *prio); + const struct h2_priority *prio); #endif /* defined(__mod_h2__h2_session__) */ diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c index c9f88a27b6..58f722bc64 100644 --- a/modules/http2/h2_stream.c +++ b/modules/http2/h2_stream.c @@ -25,6 +25,7 @@ #include "h2_private.h" #include "h2_conn.h" +#include "h2_config.h" #include "h2_h2.h" #include "h2_mplx.h" #include "h2_push.h" @@ -159,7 +160,7 @@ h2_stream *h2_stream_open(int id, apr_pool_t *pool, h2_session *session) { h2_stream *stream = h2_stream_create(id, pool, session); set_state(stream, H2_STREAM_ST_OPEN); - stream->request = h2_request_create(id, pool); + stream->request = h2_request_create(id, pool, session->config); stream->bbout = apr_brigade_create(stream->pool, stream->session->c->bucket_alloc); @@ -669,14 +670,15 @@ apr_table_t *h2_stream_get_trailers(h2_stream *stream) return stream->response? stream->response->trailers : NULL; } -void h2_stream_set_priority(h2_stream *stream, h2_priority *prio) +const h2_priority *h2_stream_get_priority(h2_stream *stream) { - stream->prio = apr_pcalloc(stream->pool, sizeof(*prio)); - memcpy(stream->prio, prio, sizeof(*prio)); -} - -h2_priority *h2_stream_get_priority(h2_stream *stream) -{ - return stream->prio; + if (stream->initiated_on && stream->response) { + const char *ctype = apr_table_get(stream->response->headers, "content-type"); + if (ctype) { + /* FIXME: Not good enough, config needs to come from request->server */ + return h2_config_get_priority(stream->session->config, ctype); + } + } + return NULL; } diff --git a/modules/http2/h2_stream.h b/modules/http2/h2_stream.h index 8de3ecbacf..7b3eb3e7bc 100644 --- a/modules/http2/h2_stream.h +++ b/modules/http2/h2_stream.h @@ -70,8 +70,6 @@ struct h2_stream { apr_bucket_brigade *bbout; /* output DATA */ apr_off_t data_frames_sent; /* # of DATA frames sent out for this stream */ - - struct h2_priority *prio; /* priority information to set before submit */ }; @@ -306,13 +304,6 @@ apr_table_t *h2_stream_get_trailers(h2_stream *stream); /** * Get priority information set for this stream. */ -struct h2_priority *h2_stream_get_priority(h2_stream *stream); - -/** - * Set the priority information to use on the submit of the stream. - * @param stream the stream to set priority on - * @param prio the priority information - */ -void h2_stream_set_priority(h2_stream *stream, struct h2_priority *prio); +const struct h2_priority *h2_stream_get_priority(h2_stream *stream); #endif /* defined(__mod_h2__h2_stream__) */ diff --git a/modules/http2/h2_switch.c b/modules/http2/h2_switch.c index c107db8e73..49e5440624 100644 --- a/modules/http2/h2_switch.c +++ b/modules/http2/h2_switch.c @@ -154,7 +154,7 @@ static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s, ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout"); /* Ok, start an h2_conn on this one. */ - status = h2_conn_process(r->connection, r); + status = h2_conn_process(r->connection, r, r->server); if (status != DONE) { /* Nothing really to do about this. */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, diff --git a/modules/http2/h2_task.c b/modules/http2/h2_task.c index fee406e1bb..55bcec7293 100644 --- a/modules/http2/h2_task.c +++ b/modules/http2/h2_task.c @@ -147,13 +147,13 @@ h2_task *h2_task_create(long session_id, const h2_request *req, return NULL; } - task->id = apr_psprintf(pool, "%ld-%d", session_id, req->id); + task->id = apr_psprintf(pool, "%ld-%d", session_id, req->id); task->stream_id = req->id; - task->pool = pool; - task->mplx = mplx; - task->c = h2_conn_create(mplx->c, task->pool); + task->pool = pool; + task->mplx = mplx; + task->c = h2_conn_create(mplx->c, task->pool); - task->request = req; + task->request = req; task->input_eos = eos; return task; @@ -168,11 +168,10 @@ apr_status_t h2_task_destroy(h2_task *task) apr_status_t h2_task_do(h2_task *task, h2_worker *worker) { apr_status_t status = APR_SUCCESS; - h2_config *cfg = h2_config_get(task->mplx->c); AP_DEBUG_ASSERT(task); - task->serialize_headers = h2_config_geti(cfg, H2_CONF_SER_HEADERS); + task->serialize_headers = h2_config_geti(task->request->config, H2_CONF_SER_HEADERS); status = h2_worker_setup_task(worker, task); diff --git a/modules/http2/h2_task.h b/modules/http2/h2_task.h index 7cf0f20de2..1cc32d647c 100644 --- a/modules/http2/h2_task.h +++ b/modules/http2/h2_task.h @@ -66,7 +66,8 @@ struct h2_task { }; h2_task *h2_task_create(long session_id, const struct h2_request *req, - apr_pool_t *pool, struct h2_mplx *mplx, int eos); + apr_pool_t *pool, struct h2_mplx *mplx, + int eos); apr_status_t h2_task_destroy(h2_task *task); diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index 950e43ff4a..cf05c0dc35 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -20,7 +20,7 @@ * @macro * Version number of the h2 module as c string */ -#define MOD_HTTP2_VERSION "1.0.7" +#define MOD_HTTP2_VERSION "1.0.8" /** * @macro @@ -28,7 +28,7 @@ * 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 0x010007 +#define MOD_HTTP2_VERSION_NUM 0x010008 #endif /* mod_h2_h2_version_h */ |