diff options
Diffstat (limited to '')
-rw-r--r-- | modules/http2/h2_config.c | 859 |
1 files changed, 620 insertions, 239 deletions
diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c index 8766355..22653d4 100644 --- a/modules/http2/h2_config.c +++ b/modules/http2/h2_config.c @@ -30,11 +30,10 @@ #include <apr_strings.h> #include "h2.h" -#include "h2_alt_svc.h" -#include "h2_ctx.h" -#include "h2_conn.h" +#include "h2_conn_ctx.h" +#include "h2_c1.h" #include "h2_config.h" -#include "h2_h2.h" +#include "h2_protocol.h" #include "h2_private.h" #define DEF_VAL (-1) @@ -42,17 +41,65 @@ #define H2_CONFIG_GET(a, b, n) \ (((a)->n == DEF_VAL)? (b) : (a))->n +#define H2_CONFIG_SET(a, n, v) \ + ((a)->n = v) + +#define CONFIG_CMD_SET(cmd,dir,var,val) \ + h2_config_seti(((cmd)->path? (dir) : NULL), h2_config_sget((cmd)->server), var, val) + +#define CONFIG_CMD_SET64(cmd,dir,var,val) \ + h2_config_seti64(((cmd)->path? (dir) : NULL), h2_config_sget((cmd)->server), var, val) + +/* Apache httpd module configuration for h2. */ +typedef struct h2_config { + const char *name; + int h2_max_streams; /* max concurrent # streams (http2) */ + int h2_window_size; /* stream window size (http2) */ + int min_workers; /* min # of worker threads/child */ + int max_workers; /* max # of worker threads/child */ + apr_interval_time_t idle_limit; /* max duration for idle workers */ + int stream_max_mem_size; /* max # bytes held in memory/stream */ + int h2_direct; /* if mod_h2 is active directly */ + int modern_tls_only; /* Accept only modern TLS in HTTP/2 connections */ + int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */ + 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 */ + + int push_diary_size; /* # of entries in push diary */ + int copy_files; /* if files shall be copied vs setaside on output */ + apr_array_header_t *push_list; /* list of h2_push_res configurations */ + apr_table_t *early_headers; /* HTTP headers for a 103 response */ + int early_hints; /* support status code 103 */ + int padding_bits; + int padding_always; + int output_buffered; + apr_interval_time_t stream_timeout;/* beam timeout */ + int max_data_frame_len; /* max # bytes in a single h2 DATA frame */ + int proxy_requests; /* act as forward proxy */ + int h2_websockets; /* if mod_h2 negotiating WebSockets */ +} h2_config; + +typedef struct h2_dir_config { + const char *name; + int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */ + int h2_push; /* if HTTP/2 server push is enabled */ + apr_array_header_t *push_list; /* list of h2_push_res configurations */ + apr_table_t *early_headers; /* HTTP headers for a 103 response */ + int early_hints; /* support status code 103 */ + apr_interval_time_t stream_timeout;/* beam timeout */ +} h2_dir_config; + + static h2_config defconf = { "default", 100, /* max_streams */ H2_INITIAL_WINDOW_SIZE, /* window_size */ -1, /* min workers */ -1, /* max workers */ - 10 * 60, /* max workers idle secs */ + apr_time_from_sec(10 * 60), /* workers idle limit */ 32 * 1024, /* stream max mem size */ - NULL, /* no alt-svcs */ - -1, /* alt-svc max age */ - 0, /* serialize headers */ -1, /* h2 direct mode */ 1, /* modern TLS only */ -1, /* HTTP/1 Upgrade support */ @@ -63,7 +110,25 @@ static h2_config defconf = { 256, /* push diary size */ 0, /* copy files across threads */ NULL, /* push list */ + NULL, /* early headers */ 0, /* early hints, http status 103 */ + 0, /* padding bits */ + 1, /* padding always */ + 1, /* stream output buffered */ + -1, /* beam timeout */ + 0, /* max DATA frame len, 0 == no extra limit */ + 0, /* forward proxy */ + 0, /* WebSockets negotiation, enabled */ +}; + +static h2_dir_config defdconf = { + "default", + -1, /* HTTP/1 Upgrade support */ + -1, /* HTTP/2 server push enabled */ + NULL, /* push list */ + NULL, /* early headers */ + -1, /* early hints, http status 103 */ + -1, /* beam timeout */ }; void h2_config_init(apr_pool_t *pool) @@ -71,22 +136,18 @@ void h2_config_init(apr_pool_t *pool) (void)pool; } -static void *h2_config_create(apr_pool_t *pool, - const char *prefix, const char *x) +void *h2_config_create_svr(apr_pool_t *pool, server_rec *s) { h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config)); - const char *s = x? x : "unknown"; - char *name = apr_pstrcat(pool, prefix, "[", s, "]", NULL); + char *name = apr_pstrcat(pool, "srv[", s->defn_name, "]", NULL); conf->name = name; conf->h2_max_streams = DEF_VAL; conf->h2_window_size = DEF_VAL; conf->min_workers = DEF_VAL; conf->max_workers = DEF_VAL; - conf->max_worker_idle_secs = DEF_VAL; + conf->idle_limit = DEF_VAL; conf->stream_max_mem_size = DEF_VAL; - conf->alt_svc_max_age = DEF_VAL; - conf->serialize_headers = DEF_VAL; conf->h2_direct = DEF_VAL; conf->modern_tls_only = DEF_VAL; conf->h2_upgrade = DEF_VAL; @@ -97,20 +158,18 @@ static void *h2_config_create(apr_pool_t *pool, conf->push_diary_size = DEF_VAL; conf->copy_files = DEF_VAL; conf->push_list = NULL; + conf->early_headers = NULL; conf->early_hints = DEF_VAL; + conf->padding_bits = DEF_VAL; + conf->padding_always = DEF_VAL; + conf->output_buffered = DEF_VAL; + conf->stream_timeout = DEF_VAL; + conf->max_data_frame_len = DEF_VAL; + conf->proxy_requests = DEF_VAL; + conf->h2_websockets = DEF_VAL; return conf; } -void *h2_config_create_svr(apr_pool_t *pool, server_rec *s) -{ - return h2_config_create(pool, "srv", s->defn_name); -} - -void *h2_config_create_dir(apr_pool_t *pool, char *x) -{ - return h2_config_create(pool, "dir", x); -} - static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) { h2_config *base = (h2_config *)basev; @@ -123,11 +182,8 @@ static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) n->h2_window_size = H2_CONFIG_GET(add, base, h2_window_size); n->min_workers = H2_CONFIG_GET(add, base, min_workers); n->max_workers = H2_CONFIG_GET(add, base, max_workers); - n->max_worker_idle_secs = H2_CONFIG_GET(add, base, max_worker_idle_secs); + n->idle_limit = H2_CONFIG_GET(add, base, idle_limit); n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size); - n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs; - n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age); - n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers); n->h2_direct = H2_CONFIG_GET(add, base, h2_direct); n->modern_tls_only = H2_CONFIG_GET(add, base, modern_tls_only); n->h2_upgrade = H2_CONFIG_GET(add, base, h2_upgrade); @@ -142,32 +198,75 @@ static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) } n->push_diary_size = H2_CONFIG_GET(add, base, push_diary_size); n->copy_files = H2_CONFIG_GET(add, base, copy_files); + n->output_buffered = H2_CONFIG_GET(add, base, output_buffered); if (add->push_list && base->push_list) { n->push_list = apr_array_append(pool, base->push_list, add->push_list); } else { n->push_list = add->push_list? add->push_list : base->push_list; } + if (add->early_headers && base->early_headers) { + n->early_headers = apr_table_overlay(pool, add->early_headers, base->early_headers); + } + else { + n->early_headers = add->early_headers? add->early_headers : base->early_headers; + } n->early_hints = H2_CONFIG_GET(add, base, early_hints); + n->padding_bits = H2_CONFIG_GET(add, base, padding_bits); + n->padding_always = H2_CONFIG_GET(add, base, padding_always); + n->stream_timeout = H2_CONFIG_GET(add, base, stream_timeout); + n->max_data_frame_len = H2_CONFIG_GET(add, base, max_data_frame_len); + n->proxy_requests = H2_CONFIG_GET(add, base, proxy_requests); + n->h2_websockets = H2_CONFIG_GET(add, base, h2_websockets); return n; } -void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv) +void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) { return h2_config_merge(pool, basev, addv); } -void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) +void *h2_config_create_dir(apr_pool_t *pool, char *x) { - return h2_config_merge(pool, basev, addv); + h2_dir_config *conf = (h2_dir_config *)apr_pcalloc(pool, sizeof(h2_dir_config)); + const char *s = x? x : "unknown"; + char *name = apr_pstrcat(pool, "dir[", s, "]", NULL); + + conf->name = name; + conf->h2_upgrade = DEF_VAL; + conf->h2_push = DEF_VAL; + conf->early_hints = DEF_VAL; + conf->stream_timeout = DEF_VAL; + return conf; } -int h2_config_geti(const h2_config *conf, h2_config_var_t var) +void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv) { - return (int)h2_config_geti64(conf, var); + h2_dir_config *base = (h2_dir_config *)basev; + h2_dir_config *add = (h2_dir_config *)addv; + h2_dir_config *n = (h2_dir_config *)apr_pcalloc(pool, sizeof(h2_dir_config)); + + n->name = apr_pstrcat(pool, "merged[", add->name, ", ", base->name, "]", NULL); + n->h2_upgrade = H2_CONFIG_GET(add, base, h2_upgrade); + n->h2_push = H2_CONFIG_GET(add, base, h2_push); + if (add->push_list && base->push_list) { + n->push_list = apr_array_append(pool, base->push_list, add->push_list); + } + else { + n->push_list = add->push_list? add->push_list : base->push_list; + } + if (add->early_headers && base->early_headers) { + n->early_headers = apr_table_overlay(pool, add->early_headers, base->early_headers); + } + else { + n->early_headers = add->early_headers? add->early_headers : base->early_headers; + } + n->early_hints = H2_CONFIG_GET(add, base, early_hints); + n->stream_timeout = H2_CONFIG_GET(add, base, stream_timeout); + return n; } -apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var) +static apr_int64_t h2_srv_config_geti64(const h2_config *conf, h2_config_var_t var) { switch(var) { case H2_CONF_MAX_STREAMS: @@ -178,14 +277,10 @@ apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var) return H2_CONFIG_GET(conf, &defconf, min_workers); case H2_CONF_MAX_WORKERS: return H2_CONFIG_GET(conf, &defconf, max_workers); - case H2_CONF_MAX_WORKER_IDLE_SECS: - return H2_CONFIG_GET(conf, &defconf, max_worker_idle_secs); + case H2_CONF_MAX_WORKER_IDLE_LIMIT: + return H2_CONFIG_GET(conf, &defconf, idle_limit); case H2_CONF_STREAM_MAX_MEM: return H2_CONFIG_GET(conf, &defconf, stream_max_mem_size); - case H2_CONF_ALT_SVC_MAX_AGE: - return H2_CONFIG_GET(conf, &defconf, alt_svc_max_age); - case H2_CONF_SER_HEADERS: - return H2_CONFIG_GET(conf, &defconf, serialize_headers); case H2_CONF_MODERN_TLS_ONLY: return H2_CONFIG_GET(conf, &defconf, modern_tls_only); case H2_CONF_UPGRADE: @@ -204,12 +299,112 @@ apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var) return H2_CONFIG_GET(conf, &defconf, copy_files); case H2_CONF_EARLY_HINTS: return H2_CONFIG_GET(conf, &defconf, early_hints); + case H2_CONF_PADDING_BITS: + return H2_CONFIG_GET(conf, &defconf, padding_bits); + case H2_CONF_PADDING_ALWAYS: + return H2_CONFIG_GET(conf, &defconf, padding_always); + case H2_CONF_OUTPUT_BUFFER: + return H2_CONFIG_GET(conf, &defconf, output_buffered); + case H2_CONF_STREAM_TIMEOUT: + return H2_CONFIG_GET(conf, &defconf, stream_timeout); + case H2_CONF_MAX_DATA_FRAME_LEN: + return H2_CONFIG_GET(conf, &defconf, max_data_frame_len); + case H2_CONF_PROXY_REQUESTS: + return H2_CONFIG_GET(conf, &defconf, proxy_requests); + case H2_CONF_WEBSOCKETS: + return H2_CONFIG_GET(conf, &defconf, h2_websockets); default: return DEF_VAL; } } -const h2_config *h2_config_sget(server_rec *s) +static void h2_srv_config_seti(h2_config *conf, h2_config_var_t var, int val) +{ + switch(var) { + case H2_CONF_MAX_STREAMS: + H2_CONFIG_SET(conf, h2_max_streams, val); + break; + case H2_CONF_WIN_SIZE: + H2_CONFIG_SET(conf, h2_window_size, val); + break; + case H2_CONF_MIN_WORKERS: + H2_CONFIG_SET(conf, min_workers, val); + break; + case H2_CONF_MAX_WORKERS: + H2_CONFIG_SET(conf, max_workers, val); + break; + case H2_CONF_STREAM_MAX_MEM: + H2_CONFIG_SET(conf, stream_max_mem_size, val); + break; + case H2_CONF_MODERN_TLS_ONLY: + H2_CONFIG_SET(conf, modern_tls_only, val); + break; + case H2_CONF_UPGRADE: + H2_CONFIG_SET(conf, h2_upgrade, val); + break; + case H2_CONF_DIRECT: + H2_CONFIG_SET(conf, h2_direct, val); + break; + case H2_CONF_TLS_WARMUP_SIZE: + H2_CONFIG_SET(conf, tls_warmup_size, val); + break; + case H2_CONF_TLS_COOLDOWN_SECS: + H2_CONFIG_SET(conf, tls_cooldown_secs, val); + break; + case H2_CONF_PUSH: + H2_CONFIG_SET(conf, h2_push, val); + break; + case H2_CONF_PUSH_DIARY_SIZE: + H2_CONFIG_SET(conf, push_diary_size, val); + break; + case H2_CONF_COPY_FILES: + H2_CONFIG_SET(conf, copy_files, val); + break; + case H2_CONF_EARLY_HINTS: + H2_CONFIG_SET(conf, early_hints, val); + break; + case H2_CONF_PADDING_BITS: + H2_CONFIG_SET(conf, padding_bits, val); + break; + case H2_CONF_PADDING_ALWAYS: + H2_CONFIG_SET(conf, padding_always, val); + break; + case H2_CONF_OUTPUT_BUFFER: + H2_CONFIG_SET(conf, output_buffered, val); + break; + case H2_CONF_MAX_DATA_FRAME_LEN: + H2_CONFIG_SET(conf, max_data_frame_len, val); + break; + case H2_CONF_PROXY_REQUESTS: + H2_CONFIG_SET(conf, proxy_requests, val); + break; + case H2_CONF_WEBSOCKETS: + H2_CONFIG_SET(conf, h2_websockets, val); + break; + default: + break; + } +} + +static void h2_srv_config_seti64(h2_config *conf, h2_config_var_t var, apr_int64_t val) +{ + switch(var) { + case H2_CONF_TLS_WARMUP_SIZE: + H2_CONFIG_SET(conf, tls_warmup_size, val); + break; + case H2_CONF_STREAM_TIMEOUT: + H2_CONFIG_SET(conf, stream_timeout, val); + break; + case H2_CONF_MAX_WORKER_IDLE_LIMIT: + H2_CONFIG_SET(conf, idle_limit, val); + break; + default: + h2_srv_config_seti(conf, var, (int)val); + break; + } +} + +static h2_config *h2_config_sget(server_rec *s) { h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, &http2_module); @@ -217,177 +412,310 @@ const 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) +static const h2_dir_config *h2_config_rget(request_rec *r) +{ + h2_dir_config *cfg = (h2_dir_config *)ap_get_module_config(r->per_dir_config, + &http2_module); + ap_assert(cfg); + return cfg; +} + +static apr_int64_t h2_dir_config_geti64(const h2_dir_config *conf, h2_config_var_t var) +{ + switch(var) { + case H2_CONF_UPGRADE: + return H2_CONFIG_GET(conf, &defdconf, h2_upgrade); + case H2_CONF_PUSH: + return H2_CONFIG_GET(conf, &defdconf, h2_push); + case H2_CONF_EARLY_HINTS: + return H2_CONFIG_GET(conf, &defdconf, early_hints); + case H2_CONF_STREAM_TIMEOUT: + return H2_CONFIG_GET(conf, &defdconf, stream_timeout); + + default: + return DEF_VAL; + } +} + +static void h2_config_seti(h2_dir_config *dconf, h2_config *conf, h2_config_var_t var, int val) +{ + int set_srv = !dconf; + if (dconf) { + switch(var) { + case H2_CONF_UPGRADE: + H2_CONFIG_SET(dconf, h2_upgrade, val); + break; + case H2_CONF_PUSH: + H2_CONFIG_SET(dconf, h2_push, val); + break; + case H2_CONF_EARLY_HINTS: + H2_CONFIG_SET(dconf, early_hints, val); + break; + default: + /* not handled in dir_conf */ + set_srv = 1; + break; + } + } + + if (set_srv) { + h2_srv_config_seti(conf, var, val); + } +} + +static void h2_config_seti64(h2_dir_config *dconf, h2_config *conf, h2_config_var_t var, apr_int64_t val) +{ + int set_srv = !dconf; + if (dconf) { + switch(var) { + case H2_CONF_STREAM_TIMEOUT: + H2_CONFIG_SET(dconf, stream_timeout, val); + break; + default: + /* not handled in dir_conf */ + set_srv = 1; + break; + } + } + + if (set_srv) { + h2_srv_config_seti64(conf, var, val); + } +} + +static const h2_config *h2_config_get(conn_rec *c) +{ + h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c); + + if (conn_ctx && conn_ctx->server) { + return h2_config_sget(conn_ctx->server); + } + return h2_config_sget(c->base_server); +} + +int h2_config_cgeti(conn_rec *c, h2_config_var_t var) { + return (int)h2_srv_config_geti64(h2_config_get(c), var); +} + +apr_int64_t h2_config_cgeti64(conn_rec *c, h2_config_var_t var) +{ + return h2_srv_config_geti64(h2_config_get(c), var); +} + +int h2_config_sgeti(server_rec *s, h2_config_var_t var) +{ + return (int)h2_srv_config_geti64(h2_config_sget(s), var); +} + +apr_int64_t h2_config_sgeti64(server_rec *s, h2_config_var_t var) +{ + return h2_srv_config_geti64(h2_config_sget(s), var); +} + +int h2_config_geti(request_rec *r, server_rec *s, h2_config_var_t var) +{ + return (int)h2_config_geti64(r, s, var); +} + +apr_int64_t h2_config_geti64(request_rec *r, server_rec *s, h2_config_var_t var) +{ + apr_int64_t mode = r? (int)h2_dir_config_geti64(h2_config_rget(r), var) : DEF_VAL; + return (mode != DEF_VAL)? mode : h2_config_sgeti64(s, var); +} + +int h2_config_rgeti(request_rec *r, h2_config_var_t var) +{ + return h2_config_geti(r, r->server, var); +} + +apr_int64_t h2_config_rgeti64(request_rec *r, h2_config_var_t var) +{ + return h2_config_geti64(r, r->server, var); +} + +apr_array_header_t *h2_config_push_list(request_rec *r) +{ + const h2_config *sconf; + const h2_dir_config *conf = h2_config_rget(r); + + if (conf && conf->push_list) { + return conf->push_list; + } + sconf = h2_config_sget(r->server); + return sconf? sconf->push_list : NULL; +} + +apr_table_t *h2_config_early_headers(request_rec *r) +{ + const h2_config *sconf; + const h2_dir_config *conf = h2_config_rget(r); + + if (conf && conf->early_headers) { + return conf->early_headers; + } + sconf = h2_config_sget(r->server); + return sconf? sconf->early_headers : NULL; +} + +const struct h2_priority *h2_cconfig_get_priority(conn_rec *c, const char *content_type) +{ + const h2_config *conf = h2_config_get(c); if (content_type && conf->priorities) { - size_t len = strcspn(content_type, "; \t"); + apr_ssize_t len = (apr_ssize_t)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) +static const char *h2_conf_set_max_streams(cmd_parms *cmd, + void *dirconf, const char *value) { - 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) { + apr_int64_t ival = (int)apr_atoi64(value); + if (ival < 1) { return "value must be > 0"; } + CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_MAX_STREAMS, ival); return NULL; } -static const char *h2_conf_set_window_size(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_window_size(cmd_parms *cmd, + void *dirconf, const char *value) { - 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) { + int val = (int)apr_atoi64(value); + if (val < 1024) { return "value must be >= 1024"; } + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_WIN_SIZE, val); return NULL; } -static const char *h2_conf_set_min_workers(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_min_workers(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - cfg->min_workers = (int)apr_atoi64(value); - (void)arg; - if (cfg->min_workers < 1) { + int val = (int)apr_atoi64(value); + if (val < 1) { return "value must be > 0"; } + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MIN_WORKERS, val); return NULL; } -static const char *h2_conf_set_max_workers(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_max_workers(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - cfg->max_workers = (int)apr_atoi64(value); - (void)arg; - if (cfg->max_workers < 1) { + int val = (int)apr_atoi64(value); + if (val < 1) { return "value must be > 0"; } + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_WORKERS, val); return NULL; } -static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_max_worker_idle_limit(cmd_parms *cmd, + void *dirconf, const char *value) { - 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) { - return "value must be > 0"; + apr_interval_time_t timeout; + apr_status_t rv = ap_timeout_parameter_parse(value, &timeout, "s"); + if (rv != APR_SUCCESS) { + return "Invalid idle limit value"; } + if (timeout <= 0) { + timeout = DEF_VAL; + } + CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_MAX_WORKER_IDLE_LIMIT, timeout); return NULL; } -static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_stream_max_mem_size(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - - - cfg->stream_max_mem_size = (int)apr_atoi64(value); - (void)arg; - if (cfg->stream_max_mem_size < 1024) { + int val = (int)apr_atoi64(value); + if (val < 1024) { return "value must be >= 1024"; } + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_STREAM_MAX_MEM, val); return NULL; } -static const char *h2_add_alt_svc(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_max_data_frame_len(cmd_parms *cmd, + void *dirconf, const char *value) { - if (value && *value) { - 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"; - } - if (!cfg->alt_svcs) { - cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*)); - } - APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as; + int val = (int)apr_atoi64(value); + if (val < 0) { + return "value must be 0 or larger"; } - (void)arg; - return NULL; -} - -static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms, - void *arg, const char *value) -{ - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - cfg->alt_svc_max_age = (int)apr_atoi64(value); - (void)arg; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_DATA_FRAME_LEN, val); return NULL; } -static const char *h2_conf_set_session_extra_files(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_session_extra_files(cmd_parms *cmd, + void *dirconf, const char *value) { /* deprecated, ignore */ - (void)arg; + (void)dirconf; (void)value; - ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, parms->pool, /* NO LOGNO */ + ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, cmd->pool, /* NO LOGNO */ "H2SessionExtraFiles is obsolete and will be ignored"); return NULL; } static const char *h2_conf_set_serialize_headers(cmd_parms *parms, - void *arg, const char *value) + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { - cfg->serialize_headers = 1; + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, parms->server, APLOGNO(10307) + "%s: this feature has been disabled and the directive " + "to enable it is ignored.", parms->cmd->name); + } + return NULL; +} + +static const char *h2_conf_set_direct(cmd_parms *cmd, + void *dirconf, const char *value) +{ + if (!strcasecmp(value, "On")) { + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_DIRECT, 1); return NULL; } else if (!strcasecmp(value, "Off")) { - cfg->serialize_headers = 0; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_DIRECT, 0); return NULL; } - - (void)arg; return "value must be On or Off"; } -static const char *h2_conf_set_direct(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_push(cmd_parms *cmd, void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { - cfg->h2_direct = 1; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH, 1); return NULL; } else if (!strcasecmp(value, "Off")) { - cfg->h2_direct = 0; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH, 0); return NULL; } - - (void)arg; return "value must be On or Off"; } -static const char *h2_conf_set_push(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_websockets(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { - cfg->h2_push = 1; +#if H2_USE_WEBSOCKETS + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_WEBSOCKETS, 1); return NULL; +#elif !H2_USE_PIPES + return "HTTP/2 WebSockets are not supported on this platform"; +#else + return "HTTP/2 WebSockets are not supported in this server version"; +#endif } else if (!strcasecmp(value, "Off")) { - cfg->h2_push = 0; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_WEBSOCKETS, 0); return NULL; } - - (void)arg; return "value must be On or Off"; } @@ -400,7 +728,8 @@ static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg, h2_dependency dependency; h2_priority *priority; int weight; - + + (void)_cfg; if (!*ctype) { return "1st argument must be a mime-type, like 'text/css' or '*'"; } @@ -419,7 +748,7 @@ static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg, else if (!strcasecmp("BEFORE", sdependency)) { dependency = H2_DEPENDANT_BEFORE; if (sweight) { - return "dependecy 'Before' does not allow a weight"; + return "dependency 'Before' does not allow a weight"; } } else if (!strcasecmp("INTERLEAVED", sdependency)) { @@ -443,104 +772,92 @@ static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg, if (!cfg->priorities) { cfg->priorities = apr_hash_make(cmd->pool); } - apr_hash_set(cfg->priorities, ctype, strlen(ctype), priority); + apr_hash_set(cfg->priorities, ctype, (apr_ssize_t)strlen(ctype), priority); return NULL; } -static const char *h2_conf_set_modern_tls_only(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_modern_tls_only(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { - cfg->modern_tls_only = 1; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MODERN_TLS_ONLY, 1); return NULL; } else if (!strcasecmp(value, "Off")) { - cfg->modern_tls_only = 0; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MODERN_TLS_ONLY, 0); return NULL; } - - (void)arg; return "value must be On or Off"; } -static const char *h2_conf_set_upgrade(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_upgrade(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { - cfg->h2_upgrade = 1; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_UPGRADE, 1); return NULL; } else if (!strcasecmp(value, "Off")) { - cfg->h2_upgrade = 0; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_UPGRADE, 0); return NULL; } - - (void)arg; return "value must be On or Off"; } -static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_tls_warmup_size(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - cfg->tls_warmup_size = apr_atoi64(value); - (void)arg; + apr_int64_t val = apr_atoi64(value); + CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_TLS_WARMUP_SIZE, val); return NULL; } -static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - cfg->tls_cooldown_secs = (int)apr_atoi64(value); - (void)arg; + apr_int64_t val = (int)apr_atoi64(value); + CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_TLS_COOLDOWN_SECS, val); return NULL; } -static const char *h2_conf_set_push_diary_size(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_push_diary_size(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - (void)arg; - cfg->push_diary_size = (int)apr_atoi64(value); - if (cfg->push_diary_size < 0) { + int val = (int)apr_atoi64(value); + if (val < 0) { return "value must be >= 0"; } - if (cfg->push_diary_size > 0 && (cfg->push_diary_size & (cfg->push_diary_size-1))) { + if (val > 0 && (val & (val-1))) { return "value must a power of 2"; } - if (cfg->push_diary_size > (1 << 15)) { + if (val > (1 << 15)) { return "value must <= 65536"; } + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH_DIARY_SIZE, val); return NULL; } -static const char *h2_conf_set_copy_files(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_copy_files(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)arg; if (!strcasecmp(value, "On")) { - cfg->copy_files = 1; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_COPY_FILES, 1); return NULL; } else if (!strcasecmp(value, "Off")) { - cfg->copy_files = 0; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_COPY_FILES, 0); return NULL; } - - (void)arg; return "value must be On or Off"; } -static void add_push(apr_pool_t *pool, h2_config *conf, h2_push_res *push) +static void add_push(apr_array_header_t **plist, apr_pool_t *pool, h2_push_res *push) { h2_push_res *new; - if (!conf->push_list) { - conf->push_list = apr_array_make(pool, 10, sizeof(*push)); + if (!*plist) { + *plist = apr_array_make(pool, 10, sizeof(*push)); } - new = apr_array_push(conf->push_list); + new = apr_array_push(*plist); new->uri_ref = push->uri_ref; new->critical = push->critical; } @@ -549,8 +866,6 @@ static const char *h2_conf_add_push_res(cmd_parms *cmd, void *dirconf, const char *arg1, const char *arg2, const char *arg3) { - h2_config *dconf = (h2_config*)dirconf ; - h2_config *sconf = (h2_config*)h2_config_sget(cmd->server); h2_push_res push; const char *last = arg3; @@ -575,57 +890,136 @@ static const char *h2_conf_add_push_res(cmd_parms *cmd, void *dirconf, } } - /* server command? set both */ - if (cmd->path == NULL) { - add_push(cmd->pool, sconf, &push); - add_push(cmd->pool, dconf, &push); + if (cmd->path) { + add_push(&(((h2_dir_config*)dirconf)->push_list), cmd->pool, &push); } else { - add_push(cmd->pool, dconf, &push); + add_push(&(h2_config_sget(cmd->server)->push_list), cmd->pool, &push); } + return NULL; +} + +static const char *h2_conf_add_early_hint(cmd_parms *cmd, void *dirconf, + const char *name, const char *value) +{ + apr_table_t *hds, **phds; + + if(!name || !*name) + return "Early Hint header name must not be empty"; + if(!value) + return "Early Hint header value must not be empty"; + while (apr_isspace(*value)) + ++value; + if(!*value) + return "Early Hint header value must not be empty/only space"; + if (*ap_scan_http_field_content(value)) + return "Early Hint header value contains invalid characters"; + + if (cmd->path) { + phds = &((h2_dir_config*)dirconf)->early_headers; + } + else { + phds = &(h2_config_sget(cmd->server))->early_headers; + } + hds = *phds; + if (!hds) { + *phds = hds = apr_table_make(cmd->pool, 10); + } + apr_table_add(hds, name, value); return NULL; } -static const char *h2_conf_set_early_hints(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_early_hints(cmd_parms *cmd, + void *dirconf, const char *value) +{ + int val; + + if (!strcasecmp(value, "On")) val = 1; + else if (!strcasecmp(value, "Off")) val = 0; + else return "value must be On or Off"; + + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_EARLY_HINTS, val); + if (cmd->path) { + ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, cmd->pool, + "H2EarlyHints = %d on path %s", val, cmd->path); + } + return NULL; +} + +static const char *h2_conf_set_padding(cmd_parms *cmd, void *dirconf, const char *value) +{ + int val; + + val = (int)apr_atoi64(value); + if (val < 0) { + return "number of bits must be >= 0"; + } + if (val > 8) { + return "number of bits must be <= 8"; + } + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PADDING_BITS, val); + return NULL; +} + +static const char *h2_conf_set_output_buffer(cmd_parms *cmd, + void *dirconf, const char *value) { - h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { - cfg->early_hints = 1; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_OUTPUT_BUFFER, 1); return NULL; } else if (!strcasecmp(value, "Off")) { - cfg->early_hints = 0; + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_OUTPUT_BUFFER, 0); return NULL; } - - (void)arg; return "value must be On or Off"; } -void h2_get_num_workers(server_rec *s, int *minw, int *maxw) +static const char *h2_conf_set_stream_timeout(cmd_parms *cmd, + void *dirconf, const char *value) +{ + apr_status_t rv; + apr_interval_time_t timeout; + + rv = ap_timeout_parameter_parse(value, &timeout, "s"); + if (rv != APR_SUCCESS) { + return "Invalid timeout value"; + } + CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_STREAM_TIMEOUT, timeout); + return NULL; +} + +static const char *h2_conf_set_proxy_requests(cmd_parms *cmd, + void *dirconf, const char *value) +{ + if (!strcasecmp(value, "On")) { + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PROXY_REQUESTS, 1); + return NULL; + } + else if (!strcasecmp(value, "Off")) { + CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PROXY_REQUESTS, 0); + return NULL; + } + return "value must be On or Off"; +} + +void h2_get_workers_config(server_rec *s, int *pminw, int *pmaxw, + apr_time_t *pidle_limit) { int threads_per_child = 0; - const h2_config *config = h2_config_sget(s); - *minw = h2_config_geti(config, H2_CONF_MIN_WORKERS); - *maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS); - ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child); + *pminw = h2_config_sgeti(s, H2_CONF_MIN_WORKERS); + *pmaxw = h2_config_sgeti(s, H2_CONF_MAX_WORKERS); - if (*minw <= 0) { - *minw = threads_per_child; - } - if (*maxw <= 0) { - /* As a default, this seems to work quite well under mpm_event. - * For people enabling http2 under mpm_prefork, start 4 threads unless - * configured otherwise. People get unhappy if their http2 requests are - * blocking each other. */ - *maxw = 3 * (*minw) / 2; - if (*maxw < 4) { - *maxw = 4; - } + ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child); + if (*pminw <= 0) { + *pminw = threads_per_child; + } + if (*pmaxw <= 0) { + *pmaxw = H2MAX(4, 3 * (*pminw) / 2); } + *pidle_limit = h2_config_sgeti64(s, H2_CONF_MAX_WORKER_IDLE_LIMIT); } #define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) @@ -639,20 +1033,16 @@ const command_rec h2_cmds[] = { RSRC_CONF, "minimum number of worker threads per child"), AP_INIT_TAKE1("H2MaxWorkers", h2_conf_set_max_workers, NULL, RSRC_CONF, "maximum number of worker threads per child"), - AP_INIT_TAKE1("H2MaxWorkerIdleSeconds", h2_conf_set_max_worker_idle_secs, NULL, + AP_INIT_TAKE1("H2MaxWorkerIdleSeconds", h2_conf_set_max_worker_idle_limit, NULL, RSRC_CONF, "maximum number of idle seconds before a worker shuts down"), AP_INIT_TAKE1("H2StreamMaxMemSize", h2_conf_set_stream_max_mem_size, NULL, RSRC_CONF, "maximum number of bytes buffered in memory for a stream"), - AP_INIT_TAKE1("H2AltSvc", h2_add_alt_svc, NULL, - RSRC_CONF, "adds an Alt-Svc for this server"), - AP_INIT_TAKE1("H2AltSvcMaxAge", h2_conf_set_alt_svc_max_age, NULL, - RSRC_CONF, "set the maximum age (in seconds) that client can rely on alt-svc information"), AP_INIT_TAKE1("H2SerializeHeaders", h2_conf_set_serialize_headers, NULL, - RSRC_CONF, "on to enable header serialization for compatibility"), + RSRC_CONF, "disabled, this directive has no longer an effect."), AP_INIT_TAKE1("H2ModernTLSOnly", h2_conf_set_modern_tls_only, NULL, RSRC_CONF, "off to not impose RFC 7540 restrictions on TLS"), AP_INIT_TAKE1("H2Upgrade", h2_conf_set_upgrade, NULL, - RSRC_CONF, "on to allow HTTP/1 Upgrades to h2/h2c"), + RSRC_CONF|OR_AUTHCFG, "on to allow HTTP/1 Upgrades to h2/h2c"), AP_INIT_TAKE1("H2Direct", h2_conf_set_direct, NULL, RSRC_CONF, "on to enable direct HTTP/2 mode"), AP_INIT_TAKE1("H2SessionExtraFiles", h2_conf_set_session_extra_files, NULL, @@ -662,7 +1052,7 @@ const command_rec h2_cmds[] = { AP_INIT_TAKE1("H2TLSCoolDownSecs", h2_conf_set_tls_cooldown_secs, NULL, 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"), + RSRC_CONF|OR_AUTHCFG, "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_INIT_TAKE1("H2PushDiarySize", h2_conf_set_push_diary_size, NULL, @@ -670,33 +1060,24 @@ const command_rec h2_cmds[] = { AP_INIT_TAKE1("H2CopyFiles", h2_conf_set_copy_files, NULL, OR_FILEINFO, "on to perform copy of file data"), AP_INIT_TAKE123("H2PushResource", h2_conf_add_push_res, NULL, - OR_FILEINFO, "add a resource to be pushed in this location/on this server."), + OR_FILEINFO|OR_AUTHCFG, "add a resource to be pushed in this location/on this server."), AP_INIT_TAKE1("H2EarlyHints", h2_conf_set_early_hints, NULL, RSRC_CONF, "on to enable interim status 103 responses"), + AP_INIT_TAKE1("H2Padding", h2_conf_set_padding, NULL, + RSRC_CONF, "set payload padding"), + AP_INIT_TAKE1("H2OutputBuffering", h2_conf_set_output_buffer, NULL, + RSRC_CONF, "set stream output buffer on/off"), + AP_INIT_TAKE1("H2StreamTimeout", h2_conf_set_stream_timeout, NULL, + RSRC_CONF, "set stream timeout"), + AP_INIT_TAKE1("H2MaxDataFrameLen", h2_conf_set_max_data_frame_len, NULL, + RSRC_CONF, "maximum number of bytes in a single HTTP/2 DATA frame"), + AP_INIT_TAKE2("H2EarlyHint", h2_conf_add_early_hint, NULL, + OR_FILEINFO|OR_AUTHCFG, "add a a 'Link:' header for a 103 Early Hints response."), + AP_INIT_TAKE1("H2ProxyRequests", h2_conf_set_proxy_requests, NULL, + OR_FILEINFO, "Enables forward proxy requests via HTTP/2"), + AP_INIT_TAKE1("H2WebSockets", h2_conf_set_websockets, NULL, + RSRC_CONF, "off to disable WebSockets over HTTP/2"), AP_END_CMD }; -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); -} - -const h2_config *h2_config_get(conn_rec *c) -{ - h2_ctx *ctx = h2_ctx_get(c, 0); - - if (ctx) { - if (ctx->config) { - return ctx->config; - } - else if (ctx->server) { - ctx->config = h2_config_sget(ctx->server); - return ctx->config; - } - } - - return h2_config_sget(c->base_server); -} |