Le 08/06/2021 à 08:25, Yadd a écrit : > Le 08/06/2021 à 07:58, Yadd a écrit : >> Le 07/06/2021 à 17:34, Salvatore Bonaccorso a écrit : >>> Source: apache2 >>> Version: 2.4.47-1 >>> Severity: grave >>> Tags: security upstream >>> Justification: user security hole >>> X-Debbugs-Cc: car...@debian.org, Debian Security Team >>> <t...@security.debian.org> >>> >>> Hi, >>> >>> The following vulnerability was published for apache2. >>> >>> CVE-2021-31618[0]: >>> | httpd: NULL pointer dereference on specially crafted HTTP/2 request >>> >>> If you fix the vulnerability please also make sure to include the >>> CVE (Common Vulnerabilities & Exposures) id in your changelog entry. >>> >>> For further information see: >>> >>> [0] https://security-tracker.debian.org/tracker/CVE-2021-31618 >>> https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-31618 >>> [1] >>> https://github.com/apache/httpd/commit/a4fba223668c554e06bc78d6e3a88f33d4238ae4 >>> [2] https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2021-31618 >>> >>> Please adjust the affected versions in the BTS as needed. >>> >>> Regards, >>> Salvatore >> >> Hi all, >> >> I can't import the whole patch for Bullseye since it is written for >> 2.4.47. I think the best solution is to import the whole http2 module in >> Bullseye. This gives the attached patch >> >> Cheers, >> Yadd > > We can also fix this for Buster using the same way (we did it previously > for 2.4.46). Here is the debdiff
Update for Buster
diff --git a/debian/changelog b/debian/changelog index b6096f7d..2cb587ef 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +apache2 (2.4.38-3+deb10u5) buster-security; urgency=medium + + * Import the whole HTTP2 module from 2.4.48 (Closes: #989562, CVE-2021-31618) + + -- Yadd <y...@debian.org> Tue, 08 Jun 2021 08:20:24 +0200 + apache2 (2.4.38-3+deb10u4) buster-security; urgency=high * Import http2 modules from 2.4.46 (Closes: CVE-2020-9490, CVE-2020-11993) diff --git a/debian/patches/import-http2-module-from-2.4.46.patch b/debian/patches/import-http2-module-from-2.4.46.patch index cdca37d0..6742686b 100644 --- a/debian/patches/import-http2-module-from-2.4.46.patch +++ b/debian/patches/import-http2-module-from-2.4.46.patch @@ -1,15 +1,16 @@ -Description: import http2 module from 2.4.41 +Description: import http2 module from 2.4.48 There are too many changes in http2 module to distiguish CVE-2019-9517, - CVE-2019-10082 and CVE-2019-10081 changes. + CVE-2019-10082, CVE-2019-10081 and CVE-2021-31618 changes. Author: Apache authors Bug: https://security-tracker.debian.org/tracker/CVE-2019-9517 https://security-tracker.debian.org/tracker/CVE-2019-10082 https://security-tracker.debian.org/tracker/CVE-2019-10081 https://security-tracker.debian.org/tracker/CVE-2020-9490 https://security-tracker.debian.org/tracker/CVE-2020-11993 + https://security-tracker.debian.org/tracker/CVE-2021-31618 Forwarded: not-needed Reviewed-By: Xavier Guimard <y...@debian.org> -Last-Update: 2020-08-25 +Last-Update: 2021-06-08 --- a/modules/http2/config2.m4 +++ b/modules/http2/config2.m4 @@ -39,7 +40,7 @@ Last-Update: 2020-08-25 /* Maximum number of padding bytes in a frame, rfc7540 */ #define H2_MAX_PADLEN 256 /* Initial default window size, RFC 7540 ch. 6.5.2 */ -@@ -138,7 +138,7 @@ +@@ -138,11 +138,22 @@ apr_table_t *headers; apr_time_t request_time; @@ -47,8 +48,23 @@ Last-Update: 2020-08-25 + unsigned int chunked : 1; /* iff request body needs to be forwarded as chunked */ unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */ apr_off_t raw_bytes; /* RAW network bytes that generated this request - if known. */ ++ int http_status; /* Store a possible HTTP status code that gets ++ * defined before creating the dummy HTTP/1.1 ++ * request e.g. due to an error already ++ * detected. ++ */ }; -@@ -162,5 +162,6 @@ + ++/* ++ * A possible HTTP status code is not defined yet. See the http_status field ++ * in struct h2_request above for further explanation. ++ */ ++#define H2_HTTP_STATUS_UNSET (0) ++ + typedef struct h2_headers h2_headers; + + struct h2_headers { +@@ -162,5 +173,6 @@ #define H2_FILTER_DEBUG_NOTE "http2-debug" #define H2_HDR_CONFORMANCE "http2-hdr-conformance" #define H2_HDR_CONFORMANCE_UNSAFE "unsafe" @@ -57,7 +73,15 @@ Last-Update: 2020-08-25 #endif /* defined(__mod_h2__h2__) */ --- a/modules/http2/h2_alt_svc.c +++ b/modules/http2/h2_alt_svc.c -@@ -75,7 +75,7 @@ +@@ -19,6 +19,7 @@ + #include <http_core.h> + #include <http_connection.h> + #include <http_protocol.h> ++#include <http_ssl.h> + #include <http_log.h> + + #include "h2_private.h" +@@ -75,7 +76,7 @@ static int h2_alt_svc_handler(request_rec *r) { @@ -66,7 +90,7 @@ Last-Update: 2020-08-25 int i; if (r->connection->keepalives > 0) { -@@ -87,8 +87,8 @@ +@@ -87,8 +88,8 @@ return DECLINED; } @@ -77,16 +101,18 @@ Last-Update: 2020-08-25 const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used"); if (!alt_svc_used) { /* We have alt-svcs defined and client is not already using -@@ -99,7 +99,7 @@ +@@ -98,8 +99,8 @@ + */ const char *alt_svc = ""; const char *svc_ma = ""; - int secure = h2_h2_is_tls(r->connection); +- int secure = h2_h2_is_tls(r->connection); - int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE); ++ int secure = ap_ssl_conn_is_ssl(r->connection); + int ma = h2_config_rgeti(r, H2_CONF_ALT_SVC_MAX_AGE); if (ma >= 0) { svc_ma = apr_psprintf(r->pool, "; ma=%d", ma); } -@@ -107,8 +107,8 @@ +@@ -107,8 +108,8 @@ "h2_alt_svc: announce %s for %s:%d", (secure? "secure" : "insecure"), r->hostname, (int)r->server->port); @@ -117,6 +143,43 @@ Last-Update: 2020-08-25 } } +@@ -945,7 +945,8 @@ + apr_status_t h2_beam_receive(h2_bucket_beam *beam, + apr_bucket_brigade *bb, + apr_read_type_e block, +- apr_off_t readbytes) ++ apr_off_t readbytes, ++ int *pclosed) + { + h2_beam_lock bl; + apr_bucket *bsender, *brecv, *ng; +@@ -953,7 +954,7 @@ + apr_status_t status = APR_SUCCESS; + apr_off_t remain; + int transferred_buckets = 0; +- ++ + /* Called from the receiver thread to take buckets from the beam */ + if (enter_yellow(beam, &bl) == APR_SUCCESS) { + if (readbytes <= 0) { +@@ -1039,6 +1040,7 @@ + H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender); + + remain -= bsender->length; ++ beam->received_bytes += bsender->length; + ++transferred; + ++transferred_buckets; + continue; +@@ -1126,7 +1128,8 @@ + } + goto transfer; + } +-leave: ++leave: ++ if (pclosed) *pclosed = beam->closed? 1 : 0; + leave_yellow(beam, &bl); + } + return status; --- a/modules/http2/h2_bucket_beam.h +++ b/modules/http2/h2_bucket_beam.h @@ -126,12 +126,11 @@ @@ -133,9 +196,26 @@ Last-Update: 2020-08-25 } h2_beam_lock; typedef struct h2_bucket_beam h2_bucket_beam; +@@ -259,11 +258,15 @@ + * if no data is available. + * + * Call from the receiver side only. ++ * @param pclosed on return != 0 iff the beam has been closed by the sender. It ++ * may still hold untransfered data. Maybe NULL if the caller is ++ * not interested in this. + */ + apr_status_t h2_beam_receive(h2_bucket_beam *beam, + apr_bucket_brigade *green_buckets, + apr_read_type_e block, +- apr_off_t readbytes); ++ apr_off_t readbytes, ++ int *pclosed); + + /** + * Determine if beam is empty. --- a/modules/http2/h2_config.c +++ b/modules/http2/h2_config.c -@@ -42,6 +42,55 @@ +@@ -42,6 +42,56 @@ #define H2_CONFIG_GET(a, b, n) \ (((a)->n == DEF_VAL)? (b) : (a))->n @@ -175,6 +255,7 @@ Last-Update: 2020-08-25 + int early_hints; /* support status code 103 */ + int padding_bits; + int padding_always; ++ int output_buffered; +} h2_config; + +typedef struct h2_dir_config { @@ -191,12 +272,13 @@ Last-Update: 2020-08-25 static h2_config defconf = { "default", 100, /* max_streams */ -@@ -64,6 +113,18 @@ +@@ -64,6 +114,19 @@ 0, /* copy files across threads */ NULL, /* push list */ 0, /* early hints, http status 103 */ + 0, /* padding bits */ + 1, /* padding always */ ++ 1, /* strean output buffered */ +}; + +static h2_dir_config defdconf = { @@ -210,7 +292,7 @@ Last-Update: 2020-08-25 }; void h2_config_init(apr_pool_t *pool) -@@ -71,12 +132,10 @@ +@@ -71,12 +134,10 @@ (void)pool; } @@ -225,12 +307,13 @@ Last-Update: 2020-08-25 conf->name = name; conf->h2_max_streams = DEF_VAL; -@@ -98,19 +157,11 @@ +@@ -98,19 +159,12 @@ conf->copy_files = DEF_VAL; conf->push_list = NULL; conf->early_hints = DEF_VAL; + conf->padding_bits = DEF_VAL; + conf->padding_always = DEF_VAL; ++ conf->output_buffered = DEF_VAL; return conf; } @@ -247,7 +330,15 @@ Last-Update: 2020-08-25 static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) { h2_config *base = (h2_config *)basev; -@@ -149,25 +200,52 @@ +@@ -142,6 +196,7 @@ + } + 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); + } +@@ -149,25 +204,52 @@ n->push_list = add->push_list? add->push_list : base->push_list; } n->early_hints = H2_CONFIG_GET(add, base, early_hints); @@ -306,7 +397,7 @@ Last-Update: 2020-08-25 { switch(var) { case H2_CONF_MAX_STREAMS: -@@ -204,12 +282,93 @@ +@@ -204,12 +286,98 @@ return H2_CONFIG_GET(conf, &defconf, copy_files); case H2_CONF_EARLY_HINTS: return H2_CONFIG_GET(conf, &defconf, early_hints); @@ -314,6 +405,8 @@ Last-Update: 2020-08-25 + 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); default: return DEF_VAL; } @@ -380,6 +473,9 @@ Last-Update: 2020-08-25 + 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; + default: + break; + } @@ -401,7 +497,7 @@ Last-Update: 2020-08-25 { h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, &http2_module); -@@ -217,9 +376,162 @@ +@@ -217,9 +385,162 @@ return cfg; } @@ -462,7 +558,7 @@ Last-Update: 2020-08-25 +} + +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) { @@ -561,12 +657,12 @@ Last-Update: 2020-08-25 +} + +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"); h2_priority *prio = apr_hash_get(conf->priorities, content_type, len); -@@ -228,166 +540,156 @@ +@@ -228,166 +549,156 @@ return NULL; } @@ -799,7 +895,7 @@ Last-Update: 2020-08-25 return "value must be On or Off"; } -@@ -419,7 +721,7 @@ +@@ -419,7 +730,7 @@ else if (!strcasecmp("BEFORE", sdependency)) { dependency = H2_DEPENDANT_BEFORE; if (sweight) { @@ -808,7 +904,7 @@ Last-Update: 2020-08-25 } } else if (!strcasecmp("INTERLEAVED", sdependency)) { -@@ -447,100 +749,88 @@ +@@ -447,100 +758,88 @@ return NULL; } @@ -940,7 +1036,7 @@ Last-Update: 2020-08-25 new->uri_ref = push->uri_ref; new->critical = push->critical; } -@@ -549,8 +839,6 @@ +@@ -549,8 +848,6 @@ const char *arg1, const char *arg2, const char *arg3) { @@ -949,7 +1045,7 @@ Last-Update: 2020-08-25 h2_push_res push; const char *last = arg3; -@@ -575,42 +863,54 @@ +@@ -575,42 +872,67 @@ } } @@ -987,31 +1083,39 @@ Last-Update: 2020-08-25 -static const char *h2_conf_set_early_hints(cmd_parms *parms, - void *arg, const char *value) +static const char *h2_conf_set_padding(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; -- return NULL; ++{ + int val; + + val = (int)apr_atoi64(value); + if (val < 0) { + return "number of bits must be >= 0"; - } -- else if (!strcasecmp(value, "Off")) { -- cfg->early_hints = 0; -- return NULL; ++ } + 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"; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PADDING_BITS, val); -+ return NULL; + return "value must be On or Off"; } -+ void h2_get_num_workers(server_rec *s, int *minw, int *maxw) { int threads_per_child = 0; @@ -1024,7 +1128,7 @@ Last-Update: 2020-08-25 ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child); if (*minw <= 0) { -@@ -652,7 +952,7 @@ +@@ -652,7 +974,7 @@ 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, @@ -1033,7 +1137,7 @@ Last-Update: 2020-08-25 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 +962,7 @@ +@@ -662,7 +984,7 @@ 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, @@ -1042,7 +1146,7 @@ Last-Update: 2020-08-25 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 +970,12 @@ +@@ -670,33 +992,14 @@ 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, @@ -1052,6 +1156,8 @@ Last-Update: 2020-08-25 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_END_CMD }; @@ -1081,16 +1187,17 @@ Last-Update: 2020-08-25 -} --- a/modules/http2/h2_config.h +++ b/modules/http2/h2_config.h -@@ -42,6 +42,8 @@ +@@ -42,6 +42,9 @@ H2_CONF_PUSH_DIARY_SIZE, H2_CONF_COPY_FILES, H2_CONF_EARLY_HINTS, + H2_CONF_PADDING_BITS, + H2_CONF_PADDING_ALWAYS, ++ H2_CONF_OUTPUT_BUFFER, } h2_config_var_t; struct apr_hash_t; -@@ -53,33 +55,6 @@ +@@ -53,33 +56,6 @@ int critical; } h2_push_res; @@ -1124,7 +1231,7 @@ Last-Update: 2020-08-25 void *h2_config_create_dir(apr_pool_t *pool, char *x); void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv); -@@ -88,19 +63,37 @@ +@@ -88,19 +64,37 @@ extern const command_rec h2_cmds[]; @@ -1465,7 +1572,17 @@ Last-Update: 2020-08-25 #endif /* defined(__mod_h2__h2_conn__) */ --- a/modules/http2/h2_conn_io.c +++ b/modules/http2/h2_conn_io.c -@@ -40,12 +40,17 @@ +@@ -22,7 +22,9 @@ + #include <http_core.h> + #include <http_log.h> + #include <http_connection.h> ++#include <http_protocol.h> + #include <http_request.h> ++#include <http_ssl.h> + + #include "h2_private.h" + #include "h2_bucket_eos.h" +@@ -40,12 +42,17 @@ * ~= 1300 bytes */ #define WRITE_SIZE_INITIAL 1300 @@ -1488,7 +1605,7 @@ Last-Update: 2020-08-25 static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level, -@@ -123,21 +128,20 @@ +@@ -123,21 +130,20 @@ } @@ -1498,7 +1615,8 @@ Last-Update: 2020-08-25 { io->c = c; io->output = apr_brigade_create(c->pool, c->bucket_alloc); - io->is_tls = h2_h2_is_tls(c); +- io->is_tls = h2_h2_is_tls(c); ++ io->is_tls = ap_ssl_conn_is_ssl(c); io->buffer_output = io->is_tls; - io->flush_threshold = (apr_size_t)h2_config_geti64(cfg, H2_CONF_STREAM_MAX_MEM); + io->flush_threshold = (apr_size_t)h2_config_sgeti64(s, H2_CONF_STREAM_MAX_MEM); @@ -1903,15 +2021,60 @@ Last-Update: 2020-08-25 } --- a/modules/http2/h2_h2.c +++ b/modules/http2/h2_h2.c -@@ -463,19 +463,18 @@ - return opt_ssl_is_https && opt_ssl_is_https(c); +@@ -26,10 +26,9 @@ + #include <http_connection.h> + #include <http_protocol.h> + #include <http_request.h> ++#include <http_ssl.h> + #include <http_log.h> + +-#include "mod_ssl.h" +- + #include "mod_http2.h" + #include "h2_private.h" + +@@ -58,13 +57,6 @@ + const char *H2_MAGIC_TOKEN = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + + /******************************************************************************* +- * The optional mod_ssl functions we need. +- */ +-static APR_OPTIONAL_FN_TYPE(ssl_is_https) *opt_ssl_is_https; +-static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *opt_ssl_var_lookup; +- +- +-/******************************************************************************* + * HTTP/2 error stuff + */ + static const char *h2_err_descr[] = { +@@ -445,45 +437,26 @@ + { + (void)pool; + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "h2_h2, child_init"); +- opt_ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); +- opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); +- +- if (!opt_ssl_is_https || !opt_ssl_var_lookup) { +- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, +- APLOGNO(02951) "mod_ssl does not seem to be enabled"); +- } +- + cipher_init(pool); + + return APR_SUCCESS; } +-int h2_h2_is_tls(conn_rec *c) +-{ +- return opt_ssl_is_https && opt_ssl_is_https(c); +-} +- -int h2_is_acceptable_connection(conn_rec *c, int require_all) +int h2_is_acceptable_connection(conn_rec *c, request_rec *r, int require_all) { - int is_tls = h2_h2_is_tls(c); +- int is_tls = h2_h2_is_tls(c); - const h2_config *cfg = h2_config_get(c); ++ int is_tls = ap_ssl_conn_is_ssl(c); - if (is_tls && h2_config_geti(cfg, H2_CONF_MODERN_TLS_ONLY) > 0) { + if (is_tls && h2_config_cgeti(c, H2_CONF_MODERN_TLS_ONLY) > 0) { @@ -1920,13 +2083,32 @@ Last-Update: 2020-08-25 */ apr_pool_t *pool = c->pool; server_rec *s = c->base_server; - char *val; +- char *val; - +- if (!opt_ssl_var_lookup) { +- /* unable to check */ +- return 0; +- } +- ++ const char *val; + - if (!opt_ssl_var_lookup) { - /* unable to check */ - return 0; -@@ -521,33 +520,29 @@ + /* Need Tlsv1.2 or higher, rfc 7540, ch. 9.2 + */ +- val = opt_ssl_var_lookup(pool, s, c, NULL, (char*)"SSL_PROTOCOL"); ++ val = ap_ssl_var_lookup(pool, s, c, NULL, "SSL_PROTOCOL"); + if (val && *val) { + if (strncmp("TLS", val, 3) + || !strcmp("TLSv1", val) +@@ -502,7 +475,7 @@ + + /* Check TLS cipher blacklist + */ +- val = opt_ssl_var_lookup(pool, s, c, NULL, (char*)"SSL_CIPHER"); ++ val = ap_ssl_var_lookup(pool, s, c, NULL, "SSL_CIPHER"); + if (val && *val) { + const char *source; + if (cipher_is_blacklisted(val, &source)) { +@@ -521,33 +494,29 @@ return 1; } @@ -1934,7 +2116,8 @@ Last-Update: 2020-08-25 +static int h2_allows_h2_direct(conn_rec *c) { - const h2_config *cfg = h2_config_get(c); - int is_tls = h2_h2_is_tls(c); +- int is_tls = h2_h2_is_tls(c); ++ int is_tls = ap_ssl_conn_is_ssl(c); const char *needed_protocol = is_tls? "h2" : "h2c"; - int h2_direct = h2_config_geti(cfg, H2_CONF_DIRECT); + int h2_direct = h2_config_cgeti(c, H2_CONF_DIRECT); @@ -1955,7 +2138,7 @@ Last-Update: 2020-08-25 - - return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(c)); + int h2_upgrade = h2_config_rgeti(r, H2_CONF_UPGRADE); -+ return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(r->connection)); ++ return h2_upgrade > 0 || (h2_upgrade < 0 && !ap_ssl_conn_is_ssl(r->connection)); } /******************************************************************************* @@ -1967,7 +2150,7 @@ Last-Update: 2020-08-25 void h2_h2_register_hooks(void) { -@@ -558,7 +553,7 @@ +@@ -558,7 +527,7 @@ * a chance to take over before it. */ ap_hook_process_connection(h2_h2_process_conn, @@ -1976,7 +2159,7 @@ Last-Update: 2020-08-25 /* One last chance to properly say goodbye if we have not done so * already. */ -@@ -581,14 +576,17 @@ +@@ -581,14 +550,17 @@ { apr_status_t status; h2_ctx *ctx; @@ -1995,7 +2178,7 @@ Last-Update: 2020-08-25 /* our stream pseudo connection */ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, task, declined"); return DECLINED; -@@ -601,19 +599,19 @@ +@@ -601,19 +573,19 @@ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn, " "new connection using protocol '%s', direct=%d, " "tls acceptable=%d", proto, h2_allows_h2_direct(c), @@ -2019,7 +2202,7 @@ Last-Update: 2020-08-25 temp = apr_brigade_create(c->pool, c->bucket_alloc); status = ap_get_brigade(c->input_filters, temp, -@@ -626,8 +624,8 @@ +@@ -626,19 +598,19 @@ return DECLINED; } @@ -2030,7 +2213,11 @@ Last-Update: 2020-08-25 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, direct mode detected"); if (!ctx) { -@@ -638,7 +636,7 @@ + ctx = h2_ctx_get(c, 1); + } +- h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c"); ++ h2_ctx_protocol_set(ctx, ap_ssl_conn_is_ssl(c)? "h2" : "h2c"); + } else if (APLOGctrace2(c)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, not detected in %d bytes(base64): %s", @@ -2039,7 +2226,7 @@ Last-Update: 2020-08-25 } apr_brigade_destroy(temp); -@@ -647,15 +645,16 @@ +@@ -647,15 +619,16 @@ if (ctx) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "process_conn"); @@ -2059,7 +2246,7 @@ Last-Update: 2020-08-25 return OK; } -@@ -667,7 +666,7 @@ +@@ -667,7 +640,7 @@ { h2_ctx *ctx; @@ -2068,7 +2255,7 @@ Last-Update: 2020-08-25 if (c->master) { return DECLINED; } -@@ -684,16 +683,17 @@ +@@ -684,16 +657,17 @@ static void check_push(request_rec *r, const char *tag) { @@ -2092,7 +2279,7 @@ Last-Update: 2020-08-25 apr_table_add(r->headers_out, "Link", apr_psprintf(r->pool, "<%s>; rel=preload%s", push->uri_ref, push->critical? "; critical" : "")); -@@ -710,10 +710,9 @@ +@@ -710,10 +684,9 @@ static int h2_h2_post_read_req(request_rec *r) { @@ -2105,7 +2292,7 @@ Last-Update: 2020-08-25 /* This hook will get called twice on internal redirects. Take care * that we manipulate filters only once. */ if (task && !task->filters_set) { -@@ -730,7 +729,7 @@ +@@ -730,7 +703,7 @@ ap_add_output_filter("H2_RESPONSE", task, r, r->connection); for (f = r->input_filters; f; f = f->next) { @@ -2114,7 +2301,7 @@ Last-Update: 2020-08-25 f->r = r; break; } -@@ -744,17 +743,15 @@ +@@ -744,17 +717,16 @@ static int h2_h2_late_fixups(request_rec *r) { @@ -2129,6 +2316,7 @@ Last-Update: 2020-08-25 - task->output.copy_files = h2_config_geti(h2_config_rget(r), - H2_CONF_COPY_FILES); + task->output.copy_files = h2_config_rgeti(r, H2_CONF_COPY_FILES); ++ task->output.buffered = h2_config_rgeti(r, H2_CONF_OUTPUT_BUFFER); if (task->output.copy_files) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, - "h2_slave_out(%s): copy_files on", task->id); @@ -2138,7 +2326,18 @@ Last-Update: 2020-08-25 check_push(r, "late_fixup"); --- a/modules/http2/h2_h2.h +++ b/modules/http2/h2_h2.h -@@ -57,23 +57,15 @@ +@@ -41,10 +41,6 @@ + */ + apr_status_t h2_h2_init(apr_pool_t *pool, server_rec *s); + +-/* Is the connection a TLS connection? +- */ +-int h2_h2_is_tls(conn_rec *c); +- + /* Register apache hooks for h2 protocol + */ + void h2_h2_register_hooks(void); +@@ -57,23 +53,15 @@ * the handshake is still ongoing. * @return != 0 iff connection requirements are met */ @@ -2176,7 +2375,15 @@ Last-Update: 2020-08-25 #include "h2_util.h" #include "h2_request.h" #include "h2_headers.h" -@@ -101,8 +102,9 @@ +@@ -63,6 +64,7 @@ + + b = apr_bucket_shared_make(b, br, 0, 0); + b->type = &h2_bucket_type_headers; ++ b->length = h2_headers_length(r); + + return b; + } +@@ -101,8 +103,9 @@ const apr_bucket *src) { if (H2_BUCKET_IS_HEADERS(src)) { @@ -2188,7 +2395,26 @@ Last-Update: 2020-08-25 APR_BRIGADE_INSERT_TAIL(dest, b); return b; } -@@ -128,28 +130,41 @@ +@@ -123,33 +126,60 @@ + return headers; + } + ++static int add_header_lengths(void *ctx, const char *name, const char *value) ++{ ++ apr_size_t *plen = ctx; ++ *plen += strlen(name) + strlen(value); ++ return 1; ++} ++ ++apr_size_t h2_headers_length(h2_headers *headers) ++{ ++ apr_size_t len = 0; ++ apr_table_do(add_header_lengths, &len, headers->headers, NULL); ++ return len; ++} ++ + h2_headers *h2_headers_rcreate(request_rec *r, int status, + apr_table_t *header, apr_pool_t *pool) { h2_headers *headers = h2_headers_create(status, header, r->notes, 0, pool); if (headers->status == HTTP_FORBIDDEN) { @@ -2265,6 +2491,16 @@ Last-Update: 2020-08-25 * Create the headers for the given error. * @param stream_id id of the stream to create the headers for * @param type the error code +@@ -76,4 +82,9 @@ + + int h2_headers_are_response(h2_headers *headers); + ++/** ++ * Give the number of bytes of all contained header strings. ++ */ ++apr_size_t h2_headers_length(h2_headers *headers); ++ + #endif /* defined(__mod_h2__h2_headers__) */ --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -40,7 +40,6 @@ @@ -2298,7 +2534,7 @@ Last-Update: 2020-08-25 { return APR_SUCCESS; } -@@ -72,46 +83,40 @@ +@@ -72,46 +83,36 @@ #define H2_MPLX_ENTER_ALWAYS(m) \ apr_thread_mutex_lock(m->lock) @@ -2317,16 +2553,15 @@ Last-Update: 2020-08-25 -static void stream_output_consumed(void *ctx, - h2_bucket_beam *beam, apr_off_t length) -+static void mst_stream_output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) - { +-{ - h2_stream *stream = ctx; - h2_task *task = stream->task; - - if (length > 0 && task && task->assigned) { - h2_req_engine_out_consumed(task->assigned, task->c, length); - } - } - +-} +- -static void stream_input_ev(void *ctx, h2_bucket_beam *beam) +static void mst_stream_input_ev(void *ctx, h2_bucket_beam *beam) { @@ -2357,7 +2592,7 @@ Last-Update: 2020-08-25 { ap_assert(stream->state == H2_SS_CLEANUP); -@@ -128,15 +133,16 @@ +@@ -128,15 +129,16 @@ h2_ihash_remove(m->streams, stream->id); h2_iq_remove(m->q, stream->id); @@ -2381,7 +2616,7 @@ Last-Update: 2020-08-25 } } -@@ -151,29 +157,23 @@ +@@ -151,29 +153,23 @@ * their HTTP/1 cousins, the separate allocator seems to work better * than protecting a shared h2_session one with an own lock. */ @@ -2416,7 +2651,7 @@ Last-Update: 2020-08-25 * can work in another thread. Also, the new allocator needs its own * mutex to synchronize sub-pools. */ -@@ -204,17 +204,10 @@ +@@ -204,17 +200,10 @@ return NULL; } @@ -2436,7 +2671,7 @@ Last-Update: 2020-08-25 m->shold = h2_ihash_create(m->pool, offsetof(h2_stream,id)); m->spurge = h2_ihash_create(m->pool, offsetof(h2_stream,id)); m->q = h2_iq_create(m->pool, m->max_streams); -@@ -228,19 +221,15 @@ +@@ -228,19 +217,15 @@ m->workers = workers; m->max_active = workers->max_workers; m->limit_active = 6; /* the original h1 max parallel connections */ @@ -2460,7 +2695,7 @@ Last-Update: 2020-08-25 { int max_stream_started = 0; -@@ -254,7 +243,7 @@ +@@ -254,7 +239,7 @@ return max_stream_started; } @@ -2469,7 +2704,7 @@ Last-Update: 2020-08-25 { if (stream->input) { return h2_beam_report_consumption(stream->input); -@@ -262,12 +251,12 @@ +@@ -262,12 +247,12 @@ return 0; } @@ -2484,7 +2719,7 @@ Last-Update: 2020-08-25 if (stream->state == H2_SS_CLOSED_L && (!stream->task || stream->task->worker_done)) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, -@@ -278,7 +267,7 @@ +@@ -278,7 +263,7 @@ return 1; } @@ -2493,7 +2728,7 @@ Last-Update: 2020-08-25 { if (task->output.beam) { return h2_beam_report_consumption(task->output.beam); -@@ -286,7 +275,7 @@ +@@ -286,7 +271,7 @@ return 0; } @@ -2502,7 +2737,7 @@ Last-Update: 2020-08-25 { h2_mplx *m = ctx; h2_stream *stream = val; -@@ -296,7 +285,7 @@ +@@ -296,7 +281,7 @@ if (stream->input) { /* Process outstanding events before destruction */ @@ -2511,7 +2746,7 @@ Last-Update: 2020-08-25 h2_beam_log(stream->input, m->c, APLOG_TRACE2, "stream_destroy"); h2_beam_destroy(stream->input); stream->input = NULL; -@@ -304,12 +293,12 @@ +@@ -304,40 +289,27 @@ if (stream->task) { h2_task *task = stream->task; @@ -2523,23 +2758,23 @@ Last-Update: 2020-08-25 stream->task = NULL; - slave = task->c; - if (slave) { -+ secondary = task->c; -+ if (secondary) { - /* On non-serialized requests, the IO logging has not accounted for any - * meta data send over the network: response headers and h2 frame headers. we - * counted this on the stream and need to add this now. -@@ -318,26 +307,25 @@ - if (task->request && !task->request->serialize && h2_task_logio_add_bytes_out) { - apr_off_t unaccounted = stream->out_frame_octets - stream->out_data_octets; - if (unaccounted > 0) { +- /* On non-serialized requests, the IO logging has not accounted for any +- * meta data send over the network: response headers and h2 frame headers. we +- * counted this on the stream and need to add this now. +- * This is supposed to happen before the EOR bucket triggers the +- * logging of the transaction. *fingers crossed* */ +- if (task->request && !task->request->serialize && h2_task_logio_add_bytes_out) { +- apr_off_t unaccounted = stream->out_frame_octets - stream->out_data_octets; +- if (unaccounted > 0) { - h2_task_logio_add_bytes_out(slave, unaccounted); -+ h2_task_logio_add_bytes_out(secondary, unaccounted); - } - } - +- } +- } +- - if (m->s->keep_alive_max == 0 || slave->keepalives < m->s->keep_alive_max) { - reuse_slave = ((m->spare_slaves->nelts < (m->limit_active * 3 / 2)) - && !task->rst_error); ++ secondary = task->c; ++ if (secondary) { + if (m->s->keep_alive_max == 0 || secondary->keepalives < m->s->keep_alive_max) { + reuse_secondary = ((m->spare_secondary->nelts < (m->limit_active * 3 / 2)) + && !task->rst_error); @@ -2564,7 +2799,7 @@ Last-Update: 2020-08-25 } } } -@@ -345,11 +333,11 @@ +@@ -345,11 +317,11 @@ return 0; } @@ -2578,7 +2813,7 @@ Last-Update: 2020-08-25 /* repeat until empty */ } H2_MPLX_LEAVE_MAYBE(m, lock); -@@ -361,13 +349,13 @@ +@@ -361,13 +333,13 @@ void *ctx; } stream_iter_ctx_t; @@ -2594,7 +2829,7 @@ Last-Update: 2020-08-25 { stream_iter_ctx_t x; -@@ -375,13 +363,13 @@ +@@ -375,13 +347,13 @@ x.cb = cb; x.ctx = ctx; @@ -2610,7 +2845,7 @@ Last-Update: 2020-08-25 h2_mplx *m = ctx; h2_stream *stream = val; h2_task *task = stream->task; -@@ -394,10 +382,10 @@ +@@ -394,10 +366,10 @@ if (task) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */ H2_STRM_MSG(stream, "->03198: %s %s %s" @@ -2623,7 +2858,7 @@ Last-Update: 2020-08-25 } else { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */ -@@ -406,7 +394,7 @@ +@@ -406,7 +378,7 @@ return 1; } @@ -2632,7 +2867,7 @@ Last-Update: 2020-08-25 h2_mplx *m = ctx; h2_stream *stream = val; ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, /* NO APLOGNO */ -@@ -415,7 +403,7 @@ +@@ -415,7 +387,7 @@ return 1; } @@ -2641,7 +2876,7 @@ Last-Update: 2020-08-25 h2_mplx *m = ctx; h2_stream *stream = val; -@@ -429,14 +417,14 @@ +@@ -429,14 +401,14 @@ h2_stream_rst(stream, H2_ERR_NO_ERROR); /* All connection data has been sent, simulate cleanup */ h2_stream_dispatch(stream, H2_SEV_EOS_SENT); @@ -2659,7 +2894,7 @@ Last-Update: 2020-08-25 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, "h2_mplx(%ld): start release", m->id); -@@ -447,15 +435,23 @@ +@@ -447,15 +419,23 @@ H2_MPLX_ENTER_ALWAYS(m); @@ -2687,7 +2922,7 @@ Last-Update: 2020-08-25 ap_assert(h2_ihash_empty(m->streams)); ap_assert(h2_iq_empty(m->q)); -@@ -473,65 +469,60 @@ +@@ -473,65 +453,60 @@ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, APLOGNO(03198) "h2_mplx(%ld): waited %d sec for %d tasks", m->id, i*wait_secs, (int)h2_ihash_count(m->shold)); @@ -2766,7 +3001,7 @@ Last-Update: 2020-08-25 h2_stream *stream = h2_ihash_get(m->streams, stream_id); if (!stream || !stream->task || m->aborted) { -@@ -542,26 +533,26 @@ +@@ -542,26 +517,25 @@ stream->output = beam; if (APLOGctrace2(m->c)) { @@ -2781,7 +3016,6 @@ Last-Update: 2020-08-25 - h2_beam_on_consumed(stream->output, NULL, stream_output_consumed, stream); - h2_beam_on_produced(stream->output, output_produced, stream); -+ h2_beam_on_consumed(stream->output, NULL, mst_stream_output_consumed, stream); + h2_beam_on_produced(stream->output, mst_output_produced, stream); if (stream->task->output.copy_files) { h2_beam_on_file_beam(stream->output, h2_beam_no_files, NULL); @@ -2800,7 +3034,7 @@ Last-Update: 2020-08-25 { apr_status_t status; -@@ -571,14 +562,14 @@ +@@ -571,14 +545,14 @@ status = APR_ECONNABORTED; } else { @@ -2817,7 +3051,7 @@ Last-Update: 2020-08-25 { apr_status_t status = APR_SUCCESS; h2_stream *stream; -@@ -595,17 +586,17 @@ +@@ -595,17 +569,17 @@ return APR_ECONNABORTED; } @@ -2841,7 +3075,7 @@ Last-Update: 2020-08-25 { apr_status_t status; -@@ -614,12 +605,12 @@ +@@ -614,12 +588,12 @@ if (m->aborted) { status = APR_ECONNABORTED; } @@ -2857,7 +3091,7 @@ Last-Update: 2020-08-25 m->added_output = iowait; status = apr_thread_cond_timedwait(m->added_output, m->lock, timeout); if (APLOGctrace2(m->c)) { -@@ -634,19 +625,27 @@ +@@ -634,19 +608,27 @@ return status; } @@ -2889,7 +3123,7 @@ Last-Update: 2020-08-25 { apr_status_t status; -@@ -666,22 +665,22 @@ +@@ -666,22 +648,22 @@ return status; } @@ -2916,7 +3150,7 @@ Last-Update: 2020-08-25 { apr_status_t status; -@@ -695,13 +694,13 @@ +@@ -695,13 +677,13 @@ h2_ihash_add(m->streams, stream); if (h2_stream_is_ready(stream)) { /* already have a response */ @@ -2932,7 +3166,7 @@ Last-Update: 2020-08-25 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, H2_STRM_MSG(stream, "process, added to q")); } -@@ -711,7 +710,7 @@ +@@ -711,7 +693,7 @@ return status; } @@ -2941,7 +3175,7 @@ Last-Update: 2020-08-25 { h2_stream *stream; int sid; -@@ -720,40 +719,39 @@ +@@ -720,40 +702,39 @@ stream = h2_ihash_get(m->streams, sid); if (stream) { @@ -2993,7 +3227,7 @@ Last-Update: 2020-08-25 ++m->tasks_active; return stream->task; } -@@ -761,7 +759,7 @@ +@@ -761,7 +742,7 @@ return NULL; } @@ -3002,7 +3236,7 @@ Last-Update: 2020-08-25 { apr_status_t rv = APR_EOF; -@@ -777,7 +775,7 @@ +@@ -777,7 +758,7 @@ rv = APR_EOF; } else { @@ -3011,7 +3245,7 @@ Last-Update: 2020-08-25 rv = (*ptask != NULL && !h2_iq_empty(m->q))? APR_EAGAIN : APR_SUCCESS; } if (APR_EAGAIN != rv) { -@@ -787,127 +785,87 @@ +@@ -787,127 +768,87 @@ return rv; } @@ -3162,7 +3396,7 @@ Last-Update: 2020-08-25 H2_MPLX_LEAVE(m); } -@@ -916,94 +874,161 @@ +@@ -916,94 +857,161 @@ * h2_mplx DoS protection ******************************************************************************/ @@ -3369,7 +3603,7 @@ Last-Update: 2020-08-25 apr_size_t scount; H2_MPLX_ENTER(m); -@@ -1023,31 +1048,7 @@ +@@ -1023,31 +1031,7 @@ * of busy workers we allow for this connection until it * well behaves. */ @@ -3402,7 +3636,7 @@ Last-Update: 2020-08-25 } else if (!h2_iq_empty(m->q)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -@@ -1077,167 +1078,30 @@ +@@ -1077,167 +1061,30 @@ h2_beam_is_closed(stream->output), (long)h2_beam_get_buffered(stream->output)); h2_ihash_add(m->streams, stream); @@ -3415,13 +3649,12 @@ Last-Update: 2020-08-25 } } - register_if_needed(m); -+ ms_register_if_needed(m, 1); - - H2_MPLX_LEAVE(m); - return status; - } - - /******************************************************************************* +- +- H2_MPLX_LEAVE(m); +- return status; +-} +- +-/******************************************************************************* - * HTTP/2 request engines - ******************************************************************************/ - @@ -3519,10 +3752,11 @@ Last-Update: 2020-08-25 - status = h2_ngn_shed_pull_request(shed, ngn, capacity, - want_shutdown, pr); - } -- -- H2_MPLX_LEAVE(m); -- return status; --} ++ ms_register_if_needed(m, 1); + + H2_MPLX_LEAVE(m); + return status; + } - -void h2_mplx_req_engine_done(h2_req_engine *ngn, conn_rec *r_conn, - apr_status_t status) @@ -3556,8 +3790,8 @@ Last-Update: 2020-08-25 - H2_MPLX_LEAVE(m); - } -} -- --/******************************************************************************* + + /******************************************************************************* * mplx master events dispatching ******************************************************************************/ @@ -3575,7 +3809,7 @@ Last-Update: 2020-08-25 { h2_stream *stream; int n, id; -@@ -1247,8 +1111,8 @@ +@@ -1247,8 +1094,8 @@ apr_atomic_set32(&m->event_pending, 0); /* update input windows for streams */ @@ -3586,7 +3820,7 @@ Last-Update: 2020-08-25 n = h2_ififo_count(m->readyq); while (n > 0 -@@ -1263,13 +1127,13 @@ +@@ -1263,13 +1110,13 @@ return APR_SUCCESS; } @@ -3603,7 +3837,7 @@ Last-Update: 2020-08-25 { int waiting = 1; -@@ -1278,11 +1142,24 @@ +@@ -1278,11 +1125,24 @@ if (h2_ihash_empty(m->streams)) { waiting = 0; } @@ -4900,9 +5134,21 @@ Last-Update: 2020-08-25 } apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool, -@@ -84,8 +85,7 @@ - req->path = path; - req->headers = apr_table_make(pool, 10); +@@ -78,14 +79,14 @@ + } + + req = apr_pcalloc(pool, sizeof(*req)); +- req->method = apr_pstrdup(pool, r->method); +- req->scheme = scheme; +- req->authority = authority; +- req->path = path; +- req->headers = apr_table_make(pool, 10); ++ req->method = apr_pstrdup(pool, r->method); ++ req->scheme = scheme; ++ req->authority = authority; ++ req->path = path; ++ req->headers = apr_table_make(pool, 10); ++ req->http_status = H2_HTTP_STATUS_UNSET; if (r->server) { - req->serialize = h2_config_geti(h2_config_sget(r->server), - H2_CONF_SER_HEADERS); @@ -4910,7 +5156,7 @@ Last-Update: 2020-08-25 } x.pool = pool; -@@ -99,10 +99,12 @@ +@@ -99,10 +100,12 @@ apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, const char *name, size_t nlen, @@ -4924,7 +5170,7 @@ Last-Update: 2020-08-25 if (nlen <= 0) { return status; } -@@ -143,8 +145,9 @@ +@@ -143,8 +146,9 @@ } } else { @@ -4936,7 +5182,7 @@ Last-Update: 2020-08-25 } return status; -@@ -156,7 +159,7 @@ +@@ -156,7 +160,7 @@ /* rfc7540, ch. 8.1.2.3: * - if we have :authority, it overrides any Host header @@ -4945,7 +5191,7 @@ Last-Update: 2020-08-25 * might get a stream without, but then Host needs to be there */ if (!req->authority) { const char *host = apr_table_get(req->headers, "Host"); -@@ -206,13 +209,11 @@ +@@ -206,13 +210,11 @@ return dst; } @@ -4961,7 +5207,7 @@ Last-Update: 2020-08-25 apr_pool_create(&p, c->pool); apr_pool_tag(p, "request"); -@@ -226,8 +227,8 @@ +@@ -226,8 +228,8 @@ r->ap_auth_type = NULL; r->allowed_methods = ap_make_method_list(p, 2); @@ -4972,7 +5218,7 @@ Last-Update: 2020-08-25 r->trailers_in = apr_table_make(r->pool, 5); r->subprocess_env = apr_table_make(r->pool, 25); r->headers_out = apr_table_make(r->pool, 12); -@@ -262,6 +263,24 @@ +@@ -262,45 +264,102 @@ r->useragent_addr = c->client_addr; r->useragent_ip = c->client_ip; @@ -4982,9 +5228,7 @@ Last-Update: 2020-08-25 + +request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) +{ -+ int access_status = HTTP_OK; -+ const char *rpath; -+ const char *s; ++ int access_status; + +#if AP_MODULE_MAGIC_AT_LEAST(20150222, 13) + request_rec *r = ap_create_request(c); @@ -4992,33 +5236,176 @@ Last-Update: 2020-08-25 + request_rec *r = my_ap_create_request(c); +#endif + -+ r->headers_in = apr_table_clone(r->pool, req->headers); -+ ++#if AP_MODULE_MAGIC_AT_LEAST(20200331, 3) ap_run_pre_read_request(r, c); - +- ++ /* Time to populate r with the data we have. */ -@@ -272,6 +291,9 @@ - if (r->method_number == M_GET && r->method[0] == 'H') { - r->header_only = 1; - } -+ r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", + r->request_time = req->request_time; +- r->method = apr_pstrdup(r->pool, req->method); +- /* Provide quick information about the request method as soon as known */ +- r->method_number = ap_method_number_of(r->method); +- if (r->method_number == M_GET && r->method[0] == 'H') { +- r->header_only = 1; +- } +- +- rpath = (req->path ? req->path : ""); +- ap_parse_uri(r, rpath); +- r->protocol = (char*)"HTTP/2.0"; +- r->proto_num = HTTP_VERSION(2, 0); +- +- r->the_request = apr_psprintf(r->pool, "%s %s %s", +- r->method, rpath, r->protocol); +- +- /* update what we think the virtual host is based on the headers we've +- * now read. may update status. +- * Leave r->hostname empty, vhost will parse if form our Host: header, +- * otherwise we get complains about port numbers. ++ r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", + req->method, req->path ? req->path : ""); + r->headers_in = apr_table_clone(r->pool, req->headers); - - rpath = (req->path ? req->path : ""); - ap_parse_uri(r, rpath); -@@ -288,7 +310,9 @@ ++ ++ /* Start with r->hostname = NULL, ap_check_request_header() will get it ++ * form Host: header, otherwise we get complains about port numbers. */ r->hostname = NULL; - ap_update_vhost_from_headers(r); +- ap_update_vhost_from_headers(r); +- +- /* we may have switched to another server */ +- r->per_dir_config = r->server->lookup_defaults; - -+ r->protocol = "HTTP/2.0"; -+ r->proto_num = HTTP_VERSION(2, 0); +- s = apr_table_get(r->headers_in, "Expect"); +- if (s && s[0]) { +- if (ap_cstr_casecmp(s, "100-continue") == 0) { +- r->expecting_100 = 1; + - /* we may have switched to another server */ - r->per_dir_config = r->server->lookup_defaults; - -@@ -337,3 +361,4 @@ ++ /* Validate HTTP/1 request and select vhost. */ ++ if (!ap_parse_request_line(r) || !ap_check_request_header(r)) { ++ /* we may have switched to another server still */ ++ r->per_dir_config = r->server->lookup_defaults; ++ if (req->http_status != H2_HTTP_STATUS_UNSET) { ++ access_status = req->http_status; ++ /* Be safe and close the connection */ ++ c->keepalive = AP_CONN_CLOSE; + } + else { +- r->status = HTTP_EXPECTATION_FAILED; +- ap_send_error_response(r, 0); ++ access_status = r->status; + } ++ r->status = HTTP_OK; ++ goto die; ++ } ++#else ++ { ++ const char *s; ++ ++ r->headers_in = apr_table_clone(r->pool, req->headers); ++ ap_run_pre_read_request(r, c); ++ ++ /* Time to populate r with the data we have. */ ++ r->request_time = req->request_time; ++ r->method = apr_pstrdup(r->pool, req->method); ++ /* Provide quick information about the request method as soon as known */ ++ r->method_number = ap_method_number_of(r->method); ++ if (r->method_number == M_GET && r->method[0] == 'H') { ++ r->header_only = 1; ++ } ++ ap_parse_uri(r, req->path ? req->path : ""); ++ r->protocol = (char*)"HTTP/2.0"; ++ r->proto_num = HTTP_VERSION(2, 0); ++ r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", ++ r->method, req->path ? req->path : ""); ++ ++ /* Start with r->hostname = NULL, ap_check_request_header() will get it ++ * form Host: header, otherwise we get complains about port numbers. ++ */ ++ r->hostname = NULL; ++ ap_update_vhost_from_headers(r); ++ ++ /* we may have switched to another server */ ++ r->per_dir_config = r->server->lookup_defaults; ++ ++ s = apr_table_get(r->headers_in, "Expect"); ++ if (s && s[0]) { ++ if (ap_cstr_casecmp(s, "100-continue") == 0) { ++ r->expecting_100 = 1; ++ } ++ else { ++ r->status = HTTP_EXPECTATION_FAILED; ++ access_status = r->status; ++ goto die; ++ } ++ } ++ } ++#endif ++ ++ /* we may have switched to another server */ ++ r->per_dir_config = r->server->lookup_defaults; ++ ++ if (req->http_status != H2_HTTP_STATUS_UNSET) { ++ access_status = req->http_status; ++ r->status = HTTP_OK; ++ /* Be safe and close the connection */ ++ c->keepalive = AP_CONN_CLOSE; ++ goto die; + } + + /* +@@ -312,28 +371,48 @@ + ap_add_input_filter_handle(ap_http_input_filter_handle, + NULL, r, r->connection); + +- if (access_status != HTTP_OK +- || (access_status = ap_run_post_read_request(r))) { ++ if ((access_status = ap_run_post_read_request(r))) { + /* Request check post hooks failed. An example of this would be a + * request for a vhost where h2 is disabled --> 421. + */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03367) + "h2_request: access_status=%d, request_create failed", + access_status); +- ap_die(access_status, r); +- ap_update_child_status(c->sbh, SERVER_BUSY_LOG, r); +- ap_run_log_transaction(r); +- r = NULL; +- goto traceout; ++ goto die; + } + + AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, + (char *)r->uri, (char *)r->server->defn_name, + r->status); + return r; +-traceout: ++ ++die: ++ ap_die(access_status, r); ++ ++ /* ap_die() sent the response through the output filters, we must now ++ * end the request with an EOR bucket for stream/pipeline accounting. ++ */ ++ { ++ apr_bucket_brigade *eor_bb; ++#if AP_MODULE_MAGIC_AT_LEAST(20180905, 1) ++ eor_bb = ap_acquire_brigade(c); ++ APR_BRIGADE_INSERT_TAIL(eor_bb, ++ ap_bucket_eor_create(c->bucket_alloc, r)); ++ ap_pass_brigade(c->output_filters, eor_bb); ++ ap_release_brigade(c, eor_bb); ++#else ++ eor_bb = apr_brigade_create(c->pool, c->bucket_alloc); ++ APR_BRIGADE_INSERT_TAIL(eor_bb, ++ ap_bucket_eor_create(c->bucket_alloc, r)); ++ ap_pass_brigade(c->output_filters, eor_bb); ++ apr_brigade_destroy(eor_bb); ++#endif ++ } ++ ++ r = NULL; + AP_READ_REQUEST_FAILURE((uintptr_t)r); +- return r; ++ return NULL; } @@ -5046,7 +5433,18 @@ Last-Update: 2020-08-25 } static h2_stream *h2_session_open_stream(h2_session *session, int stream_id, -@@ -385,14 +385,19 @@ +@@ -311,7 +311,9 @@ + + status = h2_stream_add_header(stream, (const char *)name, namelen, + (const char *)value, valuelen); +- if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) { ++ if (status != APR_SUCCESS ++ && (!stream->rtmp ++ || stream->rtmp->http_status == H2_HTTP_STATUS_UNSET)) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + return 0; +@@ -385,14 +387,19 @@ break; case NGHTTP2_RST_STREAM: ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03067) @@ -5067,7 +5465,7 @@ Last-Update: 2020-08-25 ++session->streams_reset; } break; -@@ -462,7 +467,7 @@ +@@ -462,7 +469,7 @@ } static int h2_session_continue_data(h2_session *session) { @@ -5076,7 +5474,7 @@ Last-Update: 2020-08-25 return 0; } if (h2_conn_io_needs_flush(&session->io)) { -@@ -495,9 +500,7 @@ +@@ -495,9 +502,7 @@ return NGHTTP2_ERR_WOULDBLOCK; } @@ -5087,7 +5485,7 @@ Last-Update: 2020-08-25 padlen = (unsigned char)frame->data.padlen; stream = h2_session_stream_get(session, stream_id); -@@ -513,8 +516,9 @@ +@@ -513,8 +518,9 @@ H2_STRM_MSG(stream, "send_data_cb for %ld bytes"), (long)length); @@ -5098,7 +5496,7 @@ Last-Update: 2020-08-25 status = h2_conn_io_write(&session->io, (const char *)&padlen, 1); } -@@ -622,6 +626,39 @@ +@@ -622,6 +628,39 @@ } #endif @@ -5138,7 +5536,7 @@ Last-Update: 2020-08-25 #define NGH2_SET_CALLBACK(callbacks, name, fn)\ nghttp2_session_callbacks_set_##name##_callback(callbacks, fn) -@@ -647,6 +684,7 @@ +@@ -647,6 +686,7 @@ #ifdef H2_NG2_INVALID_HEADER_CB NGH2_SET_CALLBACK(*pcb, on_invalid_header, on_invalid_header_cb); #endif @@ -5146,7 +5544,7 @@ Last-Update: 2020-08-25 return APR_SUCCESS; } -@@ -691,7 +729,7 @@ +@@ -691,7 +731,7 @@ * Remove all streams greater than this number without submitting * a RST_STREAM frame, since that should be clear from the GOAWAY * we send. */ @@ -5155,7 +5553,7 @@ Last-Update: 2020-08-25 session->local.error = error; } else { -@@ -741,7 +779,7 @@ +@@ -741,7 +781,7 @@ } transit(session, trigger, H2_SESSION_ST_CLEANUP); @@ -5164,7 +5562,7 @@ Last-Update: 2020-08-25 session->mplx = NULL; ap_assert(session->ngh2); -@@ -757,13 +795,12 @@ +@@ -757,13 +797,12 @@ { conn_rec *c = data; h2_session *session; @@ -5180,7 +5578,7 @@ Last-Update: 2020-08-25 * data which has, at this time, already been freed. An example * is mod_ssl that uses request hooks. */ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, -@@ -775,11 +812,8 @@ +@@ -775,11 +814,8 @@ return APR_SUCCESS; } @@ -5194,7 +5592,7 @@ Last-Update: 2020-08-25 { nghttp2_session_callbacks *callbacks = NULL; nghttp2_option *options = NULL; -@@ -820,19 +854,16 @@ +@@ -820,19 +856,16 @@ session->id = c->id; session->c = c; session->r = r; @@ -5217,7 +5615,7 @@ Last-Update: 2020-08-25 status = apr_thread_cond_create(&session->iowait, session->pool); if (status != APR_SUCCESS) { -@@ -862,14 +893,18 @@ +@@ -862,14 +895,18 @@ session->monitor->on_state_event = on_stream_state_event; session->monitor->on_event = on_stream_event; @@ -5239,7 +5637,7 @@ Last-Update: 2020-08-25 session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc); status = init_callbacks(c, &callbacks); -@@ -888,8 +923,7 @@ +@@ -888,8 +925,7 @@ apr_pool_destroy(pool); return status; } @@ -5249,7 +5647,7 @@ Last-Update: 2020-08-25 /* We need to handle window updates ourself, otherwise we * get flooded by nghttp2. */ nghttp2_option_set_no_auto_window_update(options, 1); -@@ -907,7 +941,7 @@ +@@ -907,7 +943,7 @@ return APR_ENOMEM; } @@ -5258,7 +5656,7 @@ Last-Update: 2020-08-25 session->push_diary = h2_push_diary_create(session->pool, n); if (APLOGcdebug(c)) { -@@ -924,22 +958,11 @@ +@@ -924,22 +960,11 @@ (int)session->push_diary->N); } @@ -5283,7 +5681,7 @@ Last-Update: 2020-08-25 static apr_status_t h2_session_start(h2_session *session, int *rv) { apr_status_t status = APR_SUCCESS; -@@ -1004,7 +1027,7 @@ +@@ -1004,7 +1029,7 @@ settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; settings[slen].value = (uint32_t)session->max_stream_count; ++slen; @@ -5292,7 +5690,7 @@ Last-Update: 2020-08-25 if (win_size != H2_INITIAL_WINDOW_SIZE) { settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; settings[slen].value = win_size; -@@ -1156,7 +1179,7 @@ +@@ -1156,7 +1181,7 @@ stream = h2_session_open_stream(session, nid, is->id); if (!stream) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, @@ -5301,7 +5699,7 @@ Last-Update: 2020-08-25 "failed to create stream obj %d"), nid); /* kill the push_promise */ nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE, nid, -@@ -1262,7 +1285,7 @@ +@@ -1262,7 +1287,7 @@ rv = nghttp2_session_change_stream_priority(session->ngh2, stream->id, &ps); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, @@ -5310,7 +5708,7 @@ Last-Update: 2020-08-25 "PUSH %s, weight=%d, depends=%d, returned=%d"), ptype, ps.weight, ps.stream_id, rv); status = (rv < 0)? APR_EGENERAL : APR_SUCCESS; -@@ -1280,7 +1303,7 @@ +@@ -1280,7 +1305,7 @@ { /* iff we can and they can and want */ return (session->remote.accepting /* remote GOAWAY received */ @@ -5319,7 +5717,7 @@ Last-Update: 2020-08-25 && nghttp2_session_get_remote_settings(session->ngh2, NGHTTP2_SETTINGS_ENABLE_PUSH)); } -@@ -1324,6 +1347,7 @@ +@@ -1324,6 +1349,7 @@ int eos) { apr_status_t status = APR_SUCCESS; @@ -5327,7 +5725,7 @@ Last-Update: 2020-08-25 int rv = 0; ap_assert(session); -@@ -1391,8 +1415,12 @@ +@@ -1391,8 +1417,12 @@ && (headers->status < 400) && (headers->status != 304) && h2_session_push_enabled(session)) { @@ -5342,7 +5740,7 @@ Last-Update: 2020-08-25 } if (!stream->pref_priority) { -@@ -1414,7 +1442,7 @@ +@@ -1414,7 +1444,7 @@ } if (headers->status == 103 @@ -5351,7 +5749,7 @@ Last-Update: 2020-08-25 /* suppress sending this to the client, it might have triggered * pushes and served its purpose nevertheless */ rv = 0; -@@ -1524,7 +1552,7 @@ +@@ -1524,7 +1554,7 @@ if (stream) { ap_assert(!stream->scheduled); if (h2_stream_prep_processing(stream) == APR_SUCCESS) { @@ -5360,7 +5758,7 @@ Last-Update: 2020-08-25 } else { h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR); -@@ -1680,7 +1708,7 @@ +@@ -1680,7 +1710,7 @@ * that already served requests - not fair. */ session->idle_sync_until = apr_time_now() + apr_time_from_sec(1); s = "timeout"; @@ -5369,7 +5767,7 @@ Last-Update: 2020-08-25 update_child_status(session, SERVER_BUSY_READ, "idle"); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, H2_SSSN_LOG("", session, "enter idle, timeout = %d sec"), -@@ -1688,8 +1716,8 @@ +@@ -1688,8 +1718,8 @@ } else if (session->open_streams) { s = "timeout"; @@ -5380,7 +5778,7 @@ Last-Update: 2020-08-25 } else { /* normal keepalive setup */ -@@ -1796,7 +1824,7 @@ +@@ -1796,7 +1826,7 @@ session->open_streams); h2_conn_io_flush(&session->io); if (session->open_streams > 0) { @@ -5389,7 +5787,7 @@ Last-Update: 2020-08-25 /* waiting for at least one stream to produce data */ transit(session, "no io", H2_SESSION_ST_WAIT); } -@@ -1954,7 +1982,8 @@ +@@ -1954,7 +1984,8 @@ ev_stream_closed(session, stream); break; case H2_SS_CLEANUP: @@ -5399,7 +5797,7 @@ Last-Update: 2020-08-25 break; default: break; -@@ -2044,7 +2073,7 @@ +@@ -2044,7 +2075,7 @@ static apr_status_t dispatch_master(h2_session *session) { apr_status_t status; @@ -5408,7 +5806,7 @@ Last-Update: 2020-08-25 on_stream_resume, session); if (status == APR_EAGAIN) { ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, session->c, -@@ -2089,7 +2118,7 @@ +@@ -2089,7 +2120,7 @@ switch (session->state) { case H2_SESSION_ST_INIT: ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c); @@ -5417,7 +5815,7 @@ Last-Update: 2020-08-25 update_child_status(session, SERVER_BUSY_READ, "inadequate security"); h2_session_shutdown(session, -@@ -2112,7 +2141,7 @@ +@@ -2112,7 +2143,7 @@ break; case H2_SESSION_ST_IDLE: @@ -5426,7 +5824,7 @@ Last-Update: 2020-08-25 ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c, H2_SSSN_MSG(session, "idle, timeout reached, closing")); if (session->idle_delay) { -@@ -2146,6 +2175,14 @@ +@@ -2146,6 +2177,14 @@ session->have_read = 1; } else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { @@ -5441,7 +5839,7 @@ Last-Update: 2020-08-25 status = APR_EAGAIN; goto out; } -@@ -2168,7 +2205,7 @@ +@@ -2168,7 +2207,7 @@ /* We wait in smaller increments, using a 1 second timeout. * That gives us the chance to check for MPMQ_STOPPING often. */ @@ -5450,7 +5848,7 @@ Last-Update: 2020-08-25 if (status == APR_EAGAIN) { break; } -@@ -2282,7 +2319,7 @@ +@@ -2282,7 +2321,7 @@ "h2_session: wait for data, %ld micros", (long)session->wait_us); } @@ -5459,7 +5857,7 @@ Last-Update: 2020-08-25 session->iowait); if (status == APR_SUCCESS) { session->wait_us = 0; -@@ -2319,7 +2356,7 @@ +@@ -2319,7 +2358,7 @@ dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL); } if (session->reprioritize) { @@ -5592,7 +5990,25 @@ Last-Update: 2020-08-25 return status; } -@@ -663,11 +654,14 @@ +@@ -647,27 +638,21 @@ + + static void set_error_response(h2_stream *stream, int http_status) + { +- if (!h2_stream_is_ready(stream)) { +- conn_rec *c = stream->session->c; +- apr_bucket *b; +- h2_headers *response; +- +- response = h2_headers_die(http_status, stream->request, stream->pool); +- prep_output(stream); +- b = apr_bucket_eos_create(c->bucket_alloc); +- APR_BRIGADE_INSERT_HEAD(stream->out_buffer, b); +- b = h2_bucket_headers_create(c->bucket_alloc, response); +- APR_BRIGADE_INSERT_HEAD(stream->out_buffer, b); ++ if (!h2_stream_is_ready(stream) && stream->rtmp) { ++ stream->rtmp->http_status = http_status; + } + } static apr_status_t add_trailer(h2_stream *stream, const char *name, size_t nlen, @@ -5608,7 +6024,7 @@ Last-Update: 2020-08-25 if (nlen == 0 || name[0] == ':') { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, c, H2_STRM_LOG(APLOGNO(03060), stream, -@@ -681,9 +675,18 @@ +@@ -681,9 +666,18 @@ stream->trailers = apr_table_make(stream->pool, 5); } hname = apr_pstrndup(stream->pool, name, nlen); @@ -5628,7 +6044,7 @@ Last-Update: 2020-08-25 return APR_SUCCESS; } -@@ -693,44 +696,31 @@ +@@ -693,44 +687,31 @@ const char *value, size_t vlen) { h2_session *session = stream->session; @@ -5663,7 +6079,7 @@ Last-Update: 2020-08-25 - H2_STRM_MSG(stream, "header %s too long"), name); - error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; - } -- + - if (stream->request_headers_added > session->s->limit_req_fields + 4) { - /* too many header lines, include 4 pseudo headers */ - if (stream->request_headers_added @@ -5676,7 +6092,7 @@ Last-Update: 2020-08-25 - H2_STRM_MSG(stream, "too many header lines")); - error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; - } - +- - if (error) { - set_error_response(stream, error); - return APR_EINVAL; @@ -5687,7 +6103,7 @@ Last-Update: 2020-08-25 } else if (H2_SS_IDLE == stream->state) { if (!stream->rtmp) { -@@ -738,16 +728,55 @@ +@@ -738,16 +719,55 @@ NULL, NULL, NULL, NULL, NULL, 0); } status = h2_request_add_header(stream->rtmp, stream->pool, @@ -5705,8 +6121,9 @@ Last-Update: 2020-08-25 else { status = APR_EINVAL; + goto cleanup; -+ } -+ + } + +- if (status != APR_SUCCESS) { + if (APR_EINVAL == status) { + /* header too long */ + if (!h2_stream_is_ready(stream)) { @@ -5734,9 +6151,8 @@ Last-Update: 2020-08-25 + } + error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; + goto cleanup; - } - -- if (status != APR_SUCCESS) { ++ } ++ +cleanup: + if (error) { + set_error_response(stream, error); @@ -5746,7 +6162,7 @@ Last-Update: 2020-08-25 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, H2_STRM_MSG(stream, "header %s not accepted"), name); h2_stream_dispatch(stream, H2_SEV_CANCELLED); -@@ -755,6 +784,49 @@ +@@ -755,6 +775,49 @@ return status; } @@ -5796,7 +6212,16 @@ Last-Update: 2020-08-25 static apr_bucket *get_first_headers_bucket(apr_bucket_brigade *bb) { if (bb) { -@@ -855,7 +927,7 @@ +@@ -838,7 +901,7 @@ + apr_status_t status = APR_SUCCESS; + apr_off_t requested, missing, max_chunk = H2_DATA_CHUNK_SIZE; + conn_rec *c; +- int complete; ++ int complete, was_closed = 0; + + ap_assert(stream); + +@@ -855,7 +918,7 @@ * is requested. But we can reduce the size in case the master * connection operates in smaller chunks. (TSL warmup) */ if (stream->session->io.write_size > 0) { @@ -5805,7 +6230,7 @@ Last-Update: 2020-08-25 } requested = (*plen > 0)? H2MIN(*plen, max_chunk) : max_chunk; -@@ -864,7 +936,7 @@ +@@ -864,7 +927,7 @@ if (status == APR_EAGAIN) { /* TODO: ugly, someone needs to retrieve the response first */ @@ -5814,7 +6239,30 @@ Last-Update: 2020-08-25 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, H2_STRM_MSG(stream, "prep, response eagain")); return status; -@@ -987,7 +1059,7 @@ +@@ -887,9 +950,11 @@ + + if (stream->output) { + H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "pre"); +- rv = h2_beam_receive(stream->output, stream->out_buffer, +- APR_NONBLOCK_READ, stream->max_mem - *plen); ++ h2_beam_log(stream->output, c, APLOG_TRACE2, "pre read output"); ++ rv = h2_beam_receive(stream->output, stream->out_buffer, ++ APR_NONBLOCK_READ, stream->max_mem - *plen, &was_closed); + H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "post"); ++ h2_beam_log(stream->output, c, APLOG_TRACE2, "post read output"); + } + + if (rv == APR_SUCCESS) { +@@ -919,7 +984,7 @@ + (long)*plen, *peos); + } + else { +- status = (stream->output && h2_beam_is_closed(stream->output))? APR_EOF : APR_EAGAIN; ++ status = was_closed? APR_EOF : APR_EAGAIN; + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, + H2_STRM_MSG(stream, "prepare, no data")); + } +@@ -987,7 +1052,7 @@ const char *ctype = apr_table_get(response->headers, "content-type"); if (ctype) { /* FIXME: Not good enough, config needs to come from request->server */ @@ -5825,7 +6273,17 @@ Last-Update: 2020-08-25 return NULL; --- a/modules/http2/h2_stream.h +++ b/modules/http2/h2_stream.h -@@ -198,6 +198,10 @@ +@@ -92,7 +92,8 @@ + unsigned int input_eof : 1; /* no more request data coming */ + unsigned int out_checked : 1; /* output eof was double checked */ + unsigned int push_policy; /* which push policy to use for this request */ +- ++ unsigned int input_buffering : 1; /* buffer request bodies for efficiency */ ++ + struct h2_task *task; /* assigned task to fullfill request */ + + const h2_priority *pref_priority; /* preferred priority for this stream */ +@@ -198,6 +199,10 @@ apr_status_t h2_stream_add_header(h2_stream *stream, const char *name, size_t nlen, const char *value, size_t vlen); @@ -5838,15 +6296,27 @@ Last-Update: 2020-08-25 apr_status_t h2_stream_recv_frame(h2_stream *stream, int frame_type, int flags, size_t frame_len); --- a/modules/http2/h2_switch.c +++ b/modules/http2/h2_switch.c -@@ -55,7 +55,6 @@ - int is_tls = h2_h2_is_tls(c); +@@ -25,6 +25,7 @@ + #include <http_config.h> + #include <http_connection.h> + #include <http_protocol.h> ++#include <http_ssl.h> + #include <http_log.h> + + #include "h2_private.h" +@@ -52,10 +53,9 @@ + apr_array_header_t *proposals) + { + int proposed = 0; +- int is_tls = h2_h2_is_tls(c); ++ int is_tls = ap_ssl_conn_is_ssl(c); const char **protos = is_tls? h2_tls_protos : h2_clear_protos; - (void)s; if (!h2_mpm_supported()) { return DECLINED; } -@@ -68,7 +67,7 @@ +@@ -68,7 +68,7 @@ return DECLINED; } @@ -5855,7 +6325,7 @@ Last-Update: 2020-08-25 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03084) "protocol propose: connection requirements not met"); return DECLINED; -@@ -81,7 +80,7 @@ +@@ -81,7 +81,7 @@ */ const char *p; @@ -5864,7 +6334,16 @@ Last-Update: 2020-08-25 return DECLINED; } -@@ -150,7 +149,7 @@ +@@ -128,7 +128,7 @@ + const char *protocol) + { + int found = 0; +- const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos; ++ const char **protos = ap_ssl_conn_is_ssl(c)? h2_tls_protos : h2_clear_protos; + const char **p = protos; + + (void)s; +@@ -150,7 +150,7 @@ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "switching protocol to '%s'", protocol); h2_ctx_protocol_set(ctx, protocol); @@ -5873,7 +6352,7 @@ Last-Update: 2020-08-25 if (r != NULL) { apr_status_t status; -@@ -160,12 +159,11 @@ +@@ -160,12 +160,11 @@ * right away. */ ap_remove_input_filter_byhandle(r->input_filters, "http_in"); @@ -5888,7 +6367,7 @@ Last-Update: 2020-08-25 if (status != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03088) "session setup"); -@@ -173,7 +171,7 @@ +@@ -173,7 +172,7 @@ return !OK; } @@ -5899,16 +6378,24 @@ Last-Update: 2020-08-25 } --- a/modules/http2/h2_task.c +++ b/modules/http2/h2_task.c -@@ -86,7 +86,7 @@ +@@ -86,7 +86,15 @@ task->request->authority, task->request->path); task->output.opened = 1; - return h2_mplx_out_open(task->mplx, task->stream_id, task->output.beam); + return h2_mplx_t_out_open(task->mplx, task->stream_id, task->output.beam); ++} ++ ++static void output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) ++{ ++ h2_task *task = ctx; ++ if (task && h2_task_logio_add_bytes_out) { ++ h2_task_logio_add_bytes_out(task->c, length); ++ } } static apr_status_t send_out(h2_task *task, apr_bucket_brigade* bb, int block) -@@ -97,7 +97,7 @@ +@@ -97,7 +105,7 @@ apr_brigade_length(bb, 0, &written); H2_TASK_OUT_LOG(APLOG_TRACE2, task, bb, "h2_task send_out"); h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "send_out(before)"); @@ -5917,7 +6404,17 @@ Last-Update: 2020-08-25 status = h2_beam_send(task->output.beam, bb, block? APR_BLOCK_READ : APR_NONBLOCK_READ); h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "send_out(after)"); -@@ -126,33 +126,16 @@ +@@ -108,9 +116,6 @@ + status = APR_SUCCESS; + } + if (status == APR_SUCCESS) { +- if (h2_task_logio_add_bytes_out) { +- h2_task_logio_add_bytes_out(task->c, written); +- } + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, + "h2_task(%s): send_out done", task->id); + } +@@ -126,33 +131,16 @@ * request_rec out filter chain) into the h2_mplx for further sending * on the master connection. */ @@ -5955,7 +6452,7 @@ Last-Update: 2020-08-25 for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { -@@ -192,7 +175,7 @@ +@@ -192,7 +180,7 @@ if (APR_SUCCESS == rv) { /* could not write all, buffer the rest */ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, task->c, APLOGNO(03405) @@ -5964,7 +6461,16 @@ Last-Update: 2020-08-25 ap_assert(NULL); rv = ap_save_brigade(f, &task->output.bb, &bb, task->pool); flush = 1; -@@ -206,7 +189,7 @@ +@@ -200,13 +188,15 @@ + } + } + +- if (APR_SUCCESS == rv && !task->output.opened && flush) { ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, ++ "h2_secondary_out(%s): buffered=%d", task->id, task->output.buffered); ++ if (APR_SUCCESS == rv && !task->output.opened && (flush || !task->output.buffered)) { + /* got a flush or could not write all, time to tell someone to read */ + rv = open_output(task); } out: ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, task->c, @@ -5973,7 +6479,7 @@ Last-Update: 2020-08-25 return rv; } -@@ -219,14 +202,14 @@ +@@ -219,14 +209,14 @@ } /******************************************************************************* @@ -5994,7 +6500,7 @@ Last-Update: 2020-08-25 { h2_task *task; apr_status_t status = APR_SUCCESS; -@@ -236,12 +219,12 @@ +@@ -236,12 +226,12 @@ apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)? (apr_size_t)readbytes : APR_SIZE_MAX); @@ -6009,7 +6515,7 @@ Last-Update: 2020-08-25 task->id, mode, block, (long)readbytes); } -@@ -271,7 +254,7 @@ +@@ -271,12 +261,12 @@ /* Get more input data for our request. */ if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, @@ -6018,7 +6524,13 @@ Last-Update: 2020-08-25 "readbytes=%ld", task->id, block, (long)readbytes); } if (task->input.beam) { -@@ -284,7 +267,7 @@ + status = h2_beam_receive(task->input.beam, task->input.bb, block, +- 128*1024); ++ 128*1024, NULL); + } + else { + status = APR_EOF; +@@ -284,7 +274,7 @@ if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, @@ -6027,7 +6539,7 @@ Last-Update: 2020-08-25 } if (APR_STATUS_IS_EAGAIN(status) && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) { -@@ -310,11 +293,9 @@ +@@ -310,11 +300,9 @@ } } @@ -6041,7 +6553,7 @@ Last-Update: 2020-08-25 } if (trace1) { -@@ -325,7 +306,7 @@ +@@ -325,7 +313,7 @@ if (APR_BRIGADE_EMPTY(task->input.bb)) { if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, @@ -6050,7 +6562,7 @@ Last-Update: 2020-08-25 } return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF; } -@@ -353,7 +334,7 @@ +@@ -353,7 +341,7 @@ buffer[len] = 0; if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, @@ -6059,7 +6571,7 @@ Last-Update: 2020-08-25 task->id, buffer); } } -@@ -363,7 +344,7 @@ +@@ -363,7 +351,7 @@ * to support it. Seems to work. */ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c, APLOGNO(03472) @@ -6068,7 +6580,7 @@ Last-Update: 2020-08-25 task->id, mode); status = APR_ENOTIMPL; } -@@ -371,19 +352,19 @@ +@@ -371,19 +359,19 @@ if (trace1) { apr_brigade_length(bb, 0, &bblen); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, @@ -6093,7 +6605,7 @@ Last-Update: 2020-08-25 if (status != APR_SUCCESS) { h2_task_rst(task, H2_ERR_INTERNAL_ERROR); } -@@ -392,14 +373,14 @@ +@@ -392,14 +380,14 @@ static apr_status_t h2_filter_parse_h1(ap_filter_t* f, apr_bucket_brigade* bb) { @@ -6110,7 +6622,7 @@ Last-Update: 2020-08-25 status = h2_from_h1_parse_response(task, f, bb); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, "h2_task(%s): parsed response", task->id); -@@ -425,8 +406,15 @@ +@@ -425,8 +413,15 @@ || !strcmp("OPTIONS", task->request->method)); } @@ -6126,7 +6638,7 @@ Last-Update: 2020-08-25 task->rst_error = 0; } -@@ -468,9 +456,9 @@ +@@ -468,9 +463,9 @@ ap_hook_process_connection(h2_task_process_conn, NULL, NULL, APR_HOOK_FIRST); @@ -6138,7 +6650,7 @@ Last-Update: 2020-08-25 NULL, AP_FTYPE_NETWORK); ap_register_output_filter("H2_PARSE_H1", h2_filter_parse_h1, NULL, AP_FTYPE_NETWORK); -@@ -502,17 +490,17 @@ +@@ -502,17 +497,17 @@ ctx = h2_ctx_get(c, 0); (void)arg; @@ -6161,7 +6673,7 @@ Last-Update: 2020-08-25 const h2_request *req, h2_mplx *m, h2_bucket_beam *input, apr_interval_time_t timeout, -@@ -521,17 +509,18 @@ +@@ -521,17 +516,18 @@ apr_pool_t *pool; h2_task *task; @@ -6183,7 +6695,7 @@ Last-Update: 2020-08-25 task->mplx = m; task->pool = pool; task->request = req; -@@ -564,41 +553,40 @@ +@@ -564,41 +560,40 @@ ap_assert(task); c = task->c; task->worker_started = 1; @@ -6252,7 +6764,13 @@ Last-Update: 2020-08-25 } h2_beam_create(&task->output.beam, c->pool, task->stream_id, "output", -@@ -613,7 +601,7 @@ +@@ -609,11 +604,12 @@ + + h2_beam_buffer_size_set(task->output.beam, task->output.max_buffer); + h2_beam_send_from(task->output.beam, task->pool); +- ++ h2_beam_on_consumed(task->output.beam, NULL, output_consumed, task); ++ h2_ctx_create_for(c, task); apr_table_setn(c->notes, H2_TASK_ID_NOTE, task->id); @@ -6261,7 +6779,7 @@ Last-Update: 2020-08-25 task->input.bb = apr_brigade_create(task->pool, c->bucket_alloc); if (task->request->serialize) { -@@ -633,18 +621,9 @@ +@@ -633,18 +629,9 @@ task->c->current_thread = thread; ap_run_process_connection(c); @@ -6283,7 +6801,7 @@ Last-Update: 2020-08-25 } static apr_status_t h2_task_process_request(h2_task *task, conn_rec *c) -@@ -682,14 +661,8 @@ +@@ -682,14 +669,8 @@ ap_process_request(r); @@ -6300,7 +6818,7 @@ Last-Update: 2020-08-25 /* After the call to ap_process_request, the * request pool may have been deleted. We set -@@ -724,7 +697,7 @@ +@@ -724,7 +705,7 @@ } ctx = h2_ctx_get(c, 0); @@ -6309,7 +6827,7 @@ Last-Update: 2020-08-25 if (!ctx->task->request->serialize) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, processing request directly"); -@@ -736,33 +709,8 @@ +@@ -736,33 +717,8 @@ } else { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, @@ -6362,7 +6880,15 @@ Last-Update: 2020-08-25 struct h2_request; struct h2_response_parser; struct h2_stream; -@@ -80,20 +79,18 @@ +@@ -72,6 +71,7 @@ + unsigned int opened : 1; + unsigned int sent_response : 1; + unsigned int copy_files : 1; ++ unsigned int buffered : 1; + struct h2_response_parser *rparser; + apr_bucket_brigade *bb; + apr_size_t max_buffer; +@@ -80,20 +80,18 @@ struct h2_mplx *mplx; unsigned int filters_set : 1; @@ -6388,7 +6914,7 @@ Last-Update: 2020-08-25 const h2_request *req, struct h2_mplx *m, struct h2_bucket_beam *input, apr_interval_time_t timeout, -@@ -105,6 +102,7 @@ +@@ -105,6 +103,7 @@ void h2_task_redo(h2_task *task); int h2_task_can_redo(h2_task *task); @@ -6396,7 +6922,7 @@ Last-Update: 2020-08-25 /** * Reset the task with the given error code, resets all input/output. -@@ -120,8 +118,4 @@ +@@ -120,8 +119,4 @@ extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_task_logio_add_bytes_in; extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_task_logio_add_bytes_out; @@ -6407,6 +6933,15 @@ Last-Update: 2020-08-25 #endif /* defined(__mod_h2__h2_task__) */ --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c +@@ -113,7 +113,7 @@ + } + } + +-/* base64 url encoding ****************************************************************************/ ++/* base64 url encoding */ + + #define N6 (unsigned int)-1 + @@ -638,15 +638,6 @@ apr_status_t rv; if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { @@ -6632,6 +7167,25 @@ Last-Update: 2020-08-25 "last_stream=%d]", frame->goaway.error_code, --- a/modules/http2/h2_util.h +++ b/modules/http2/h2_util.h +@@ -96,8 +96,8 @@ + + /** + * Allocate a new queue from the pool and initialize. +- * @param id the identifier of the queue + * @param pool the memory pool ++ * @param capacity the initial capacity of the queue + */ + h2_iqueue *h2_iq_create(apr_pool_t *pool, int capacity); + +@@ -179,7 +179,7 @@ + /** + * Determine if int is in the queue already + * +- * @parm q the queue ++ * @param q the queue + * @param sid the integer id to check for + * @return != 0 iff sid is already in the queue + */ @@ -209,7 +209,6 @@ apr_status_t h2_fifo_set_create(h2_fifo **pfifo, apr_pool_t *pool, int capacity); @@ -6680,22 +7234,107 @@ Last-Update: 2020-08-25 * Version number of the http2 module as c string */ -#define MOD_HTTP2_VERSION "1.11.4" -+#define MOD_HTTP2_VERSION "1.15.14" ++#define MOD_HTTP2_VERSION "1.15.18" /** * @macro -@@ -35,7 +35,6 @@ +@@ -35,7 +35,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 0x010b04 -- -+#define MOD_HTTP2_VERSION_NUM 0x010f0e ++#define MOD_HTTP2_VERSION_NUM 0x010f12 + #endif /* mod_h2_h2_version_h */ --- a/modules/http2/h2_workers.c +++ b/modules/http2/h2_workers.c -@@ -155,7 +155,7 @@ +@@ -34,17 +34,16 @@ + typedef struct h2_slot h2_slot; + struct h2_slot { + int id; ++ int sticks; + h2_slot *next; + h2_workers *workers; +- int aborted; +- int sticks; + h2_task *task; + apr_thread_t *thread; + apr_thread_mutex_t *lock; + apr_thread_cond_t *not_idle; + }; + +-static h2_slot *pop_slot(h2_slot **phead) ++static h2_slot *pop_slot(h2_slot *volatile *phead) + { + /* Atomically pop a slot from the list */ + for (;;) { +@@ -59,7 +58,7 @@ + } + } + +-static void push_slot(h2_slot **phead, h2_slot *slot) ++static void push_slot(h2_slot *volatile *phead, h2_slot *slot) + { + /* Atomically push a slot to the list */ + ap_assert(!slot->next); +@@ -78,7 +77,6 @@ + apr_status_t status; + + slot->workers = workers; +- slot->aborted = 0; + slot->task = NULL; + + if (!slot->lock) { +@@ -101,16 +99,18 @@ + + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, workers->s, + "h2_workers: new thread for slot %d", slot->id); ++ + /* thread will either immediately start work or add itself + * to the idle queue */ +- apr_thread_create(&slot->thread, workers->thread_attr, slot_run, slot, +- workers->pool); +- if (!slot->thread) { ++ apr_atomic_inc32(&workers->worker_count); ++ status = apr_thread_create(&slot->thread, workers->thread_attr, ++ slot_run, slot, workers->pool); ++ if (status != APR_SUCCESS) { ++ apr_atomic_dec32(&workers->worker_count); + push_slot(&workers->free, slot); +- return APR_ENOMEM; ++ return status; + } + +- apr_atomic_inc32(&workers->worker_count); + return APR_SUCCESS; + } + +@@ -136,17 +136,15 @@ + } + } + +-static void cleanup_zombies(h2_workers *workers) ++static void join_zombies(h2_workers *workers) + { + h2_slot *slot; + while ((slot = pop_slot(&workers->zombies))) { +- if (slot->thread) { +- apr_status_t status; +- apr_thread_join(&status, slot->thread); +- slot->thread = NULL; +- } +- apr_atomic_dec32(&workers->worker_count); +- slot->next = NULL; ++ apr_status_t status; ++ ap_assert(slot->thread != NULL); ++ apr_thread_join(&status, slot->thread); ++ slot->thread = NULL; ++ + push_slot(&workers->free, slot); + } + } +@@ -155,7 +153,7 @@ { apr_status_t rv; @@ -6704,11 +7343,95 @@ Last-Update: 2020-08-25 if (slot->task) { /* Ok, we got something to give back to the worker for execution. * If we still have idle workers, we let the worker be sticky, -@@ -234,10 +234,10 @@ +@@ -184,37 +182,49 @@ + * Get the next task for the given worker. Will block until a task arrives + * or the max_wait timer expires and more than min workers exist. + */ +-static apr_status_t get_next(h2_slot *slot) ++static int get_next(h2_slot *slot) + { + h2_workers *workers = slot->workers; +- apr_status_t status; +- +- slot->task = NULL; +- while (!slot->aborted) { +- if (!slot->task) { +- status = h2_fifo_try_peek(workers->mplxs, mplx_peek, slot); +- if (status == APR_EOF) { +- return status; +- } ++ ++ while (!workers->aborted) { ++ ap_assert(slot->task == NULL); ++ if (h2_fifo_try_peek(workers->mplxs, mplx_peek, slot) == APR_EOF) { ++ /* The queue is terminated with the MPM child being cleaned up, ++ * just leave. ++ */ ++ break; + } +- + if (slot->task) { +- return APR_SUCCESS; ++ return 1; + } + +- cleanup_zombies(workers); ++ join_zombies(workers); + + apr_thread_mutex_lock(slot->lock); +- push_slot(&workers->idle, slot); +- apr_thread_cond_wait(slot->not_idle, slot->lock); ++ if (!workers->aborted) { ++ push_slot(&workers->idle, slot); ++ apr_thread_cond_wait(slot->not_idle, slot->lock); ++ } + apr_thread_mutex_unlock(slot->lock); + } +- return APR_EOF; ++ ++ return 0; + } + + static void slot_done(h2_slot *slot) + { +- push_slot(&(slot->workers->zombies), slot); ++ h2_workers *workers = slot->workers; ++ ++ push_slot(&workers->zombies, slot); ++ ++ /* If this worker is the last one exiting and the MPM child is stopping, ++ * unblock workers_pool_cleanup(). ++ */ ++ if (!apr_atomic_dec32(&workers->worker_count) && workers->aborted) { ++ apr_thread_mutex_lock(workers->lock); ++ apr_thread_cond_signal(workers->all_done); ++ apr_thread_mutex_unlock(workers->lock); ++ } + } + + +@@ -222,28 +232,28 @@ + { + h2_slot *slot = wctx; + +- while (!slot->aborted) { +- +- /* Get a h2_task from the mplxs queue. */ +- get_next(slot); +- while (slot->task) { +- ++ /* Get the h2_task(s) from the ->mplxs queue. */ ++ while (get_next(slot)) { ++ ap_assert(slot->task != NULL); ++ do { + h2_task_do(slot->task, thread, slot->id); + + /* Report the task as done. If stickyness is left, offer the * mplx the opportunity to give us back a new task right away. */ - if (!slot->aborted && (--slot->sticks > 0)) { +- if (!slot->aborted && (--slot->sticks > 0)) { - h2_mplx_task_done(slot->task->mplx, slot->task, &slot->task); ++ if (!slot->workers->aborted && --slot->sticks > 0) { + h2_mplx_s_task_done(slot->task->mplx, slot->task, &slot->task); } else { @@ -6716,15 +7439,124 @@ Last-Update: 2020-08-25 + h2_mplx_s_task_done(slot->task->mplx, slot->task, NULL); slot->task = NULL; } - } -@@ -269,7 +269,6 @@ - } +- } ++ } while (slot->task); + } + + slot_done(slot); ++ ++ apr_thread_exit(thread, APR_SUCCESS); + return NULL; + } - h2_fifo_term(workers->mplxs); +@@ -252,31 +262,28 @@ + h2_workers *workers = data; + h2_slot *slot; + +- if (!workers->aborted) { +- workers->aborted = 1; +- /* abort all idle slots */ +- for (;;) { +- slot = pop_slot(&workers->idle); +- if (slot) { +- apr_thread_mutex_lock(slot->lock); +- slot->aborted = 1; +- apr_thread_cond_signal(slot->not_idle); +- apr_thread_mutex_unlock(slot->lock); +- } +- else { +- break; +- } +- } ++ workers->aborted = 1; ++ h2_fifo_term(workers->mplxs); + +- h2_fifo_term(workers->mplxs); - h2_fifo_interrupt(workers->mplxs); ++ /* abort all idle slots */ ++ while ((slot = pop_slot(&workers->idle))) { ++ apr_thread_mutex_lock(slot->lock); ++ apr_thread_cond_signal(slot->not_idle); ++ apr_thread_mutex_unlock(slot->lock); ++ } + +- cleanup_zombies(workers); ++ /* wait for all the workers to become zombies and join them */ ++ apr_thread_mutex_lock(workers->lock); ++ if (apr_atomic_read32(&workers->worker_count)) { ++ apr_thread_cond_wait(workers->all_done, workers->lock); + } ++ apr_thread_mutex_unlock(workers->lock); ++ join_zombies(workers); ++ + return APR_SUCCESS; + } + +-h2_workers *h2_workers_create(server_rec *s, apr_pool_t *server_pool, ++h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild, + int min_workers, int max_workers, + int idle_secs) + { +@@ -286,14 +293,14 @@ + int i, n; + + ap_assert(s); +- ap_assert(server_pool); ++ ap_assert(pchild); - cleanup_zombies(workers); + /* let's have our own pool that will be parent to all h2_worker + * instances we create. This happens in various threads, but always + * guarded by our lock. Without this pool, all subpool creations would + * happen on the pool handed to us, which we do not guard. + */ +- apr_pool_create(&pool, server_pool); ++ apr_pool_create(&pool, pchild); + apr_pool_tag(pool, "h2_workers"); + workers = apr_pcalloc(pool, sizeof(h2_workers)); + if (!workers) { +@@ -339,6 +346,9 @@ + APR_THREAD_MUTEX_DEFAULT, + workers->pool); + if (status == APR_SUCCESS) { ++ status = apr_thread_cond_create(&workers->all_done, workers->pool); ++ } ++ if (status == APR_SUCCESS) { + n = workers->nslots = workers->max_workers; + workers->slots = apr_pcalloc(workers->pool, n * sizeof(h2_slot)); + if (workers->slots == NULL) { +@@ -364,7 +374,12 @@ + workers->dynamic = (workers->worker_count < workers->max_workers); + } + if (status == APR_SUCCESS) { +- apr_pool_pre_cleanup_register(pool, workers, workers_pool_cleanup); ++ /* Stop/join the workers threads when the MPM child exits (pchild is ++ * destroyed), and as a pre_cleanup of pchild thus before the threads ++ * pools (children of workers->pool) so that they are not destroyed ++ * before/under us. ++ */ ++ apr_pool_pre_cleanup_register(pchild, workers, workers_pool_cleanup); + return workers; } + return NULL; +--- a/modules/http2/h2_workers.h ++++ b/modules/http2/h2_workers.h +@@ -42,7 +42,7 @@ + int max_workers; + int max_idle_secs; + +- int aborted; ++ volatile int aborted; + int dynamic; + + apr_threadattr_t *thread_attr; +@@ -58,6 +58,7 @@ + struct h2_fifo *mplxs; + + struct apr_thread_mutex_t *lock; ++ struct apr_thread_cond_t *all_done; + }; + + --- a/modules/http2/mod_http2.c +++ b/modules/http2/mod_http2.c @@ -172,27 +172,6 @@ @@ -6755,7 +7587,44 @@ Last-Update: 2020-08-25 static void http2_get_num_workers(server_rec *s, int *minw, int *maxw) { h2_get_num_workers(s, minw, maxw); -@@ -220,9 +199,6 @@ +@@ -201,15 +180,33 @@ + /* Runs once per created child process. Perform any process + * related initionalization here. + */ +-static void h2_child_init(apr_pool_t *pool, server_rec *s) ++static void h2_child_init(apr_pool_t *pchild, server_rec *s) + { ++ apr_allocator_t *allocator; ++ apr_thread_mutex_t *mutex; ++ apr_status_t status; ++ ++ /* The allocator of pchild has no mutex with MPM prefork, but we need one ++ * for h2 workers threads synchronization. Even though mod_http2 shouldn't ++ * be used with prefork, better be safe than sorry, so forcibly set the ++ * mutex here. For MPM event/worker, pchild has no allocator so pconf's ++ * is used, with its mutex. ++ */ ++ allocator = apr_pool_allocator_get(pchild); ++ if (allocator) { ++ mutex = apr_allocator_mutex_get(allocator); ++ if (!mutex) { ++ apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, pchild); ++ apr_allocator_mutex_set(allocator, mutex); ++ } ++ } ++ + /* Set up our connection processing */ +- apr_status_t status = h2_conn_child_init(pool, s); ++ status = h2_conn_child_init(pchild, s); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, s, + APLOGNO(02949) "initializing connection handling"); + } +- + } + + /* Install this module into the apache2 infrastructure. +@@ -220,9 +217,6 @@ APR_REGISTER_OPTIONAL_FN(http2_is_h2); APR_REGISTER_OPTIONAL_FN(http2_var_lookup); @@ -6765,7 +7634,7 @@ Last-Update: 2020-08-25 APR_REGISTER_OPTIONAL_FN(http2_get_num_workers); ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks"); -@@ -260,9 +236,8 @@ +@@ -260,9 +254,8 @@ { if (ctx) { if (r) { @@ -6777,7 +7646,7 @@ Last-Update: 2020-08-25 if (stream && stream->push_policy != H2_PUSH_NONE) { return "on"; } -@@ -273,8 +248,7 @@ +@@ -273,8 +266,7 @@ } } else if (s) { @@ -6787,7 +7656,7 @@ Last-Update: 2020-08-25 return "on"; } } -@@ -285,8 +259,7 @@ +@@ -285,8 +277,7 @@ conn_rec *c, request_rec *r, h2_ctx *ctx) { if (ctx) { @@ -6797,7 +7666,7 @@ Last-Update: 2020-08-25 return "PUSHED"; } } -@@ -297,9 +270,8 @@ +@@ -297,9 +288,8 @@ conn_rec *c, request_rec *r, h2_ctx *ctx) { if (ctx) { @@ -6809,7 +7678,7 @@ Last-Update: 2020-08-25 if (stream) { return apr_itoa(p, stream->initiated_on); } -@@ -312,9 +284,8 @@ +@@ -312,9 +302,8 @@ conn_rec *c, request_rec *r, h2_ctx *ctx) { if (ctx) { @@ -6821,7 +7690,7 @@ Last-Update: 2020-08-25 } } return ""; -@@ -366,7 +337,7 @@ +@@ -366,7 +355,7 @@ for (i = 0; i < H2_ALEN(H2_VARS); ++i) { h2_var_def *vdef = &H2_VARS[i]; if (!strcmp(vdef->name, name)) { @@ -6830,7 +7699,7 @@ Last-Update: 2020-08-25 h2_ctx_get(c->master? c->master : c, 0)); return (char *)vdef->lookup(p, s, c, r, ctx); } -@@ -377,7 +348,7 @@ +@@ -377,7 +366,7 @@ static int h2_h2_fixups(request_rec *r) { if (r->connection->master) { @@ -7554,7 +8423,7 @@ Last-Update: 2020-08-25 } ++reconnects; - if (reconnects < 5 && !ctx->owner->aborted) { -+ if (reconnects < 5) { ++ if (reconnects < 2) { goto run_connect; } ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(10023)