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)

Reply via email to