]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Merge of /httpd/httpd/trunk:r1924267,1927235,1927647,1927792,1929517,1929527
authorStefan Eissing <icing@apache.org>
Thu, 20 Nov 2025 13:13:18 +0000 (13:13 +0000)
committerStefan Eissing <icing@apache.org>
Thu, 20 Nov 2025 13:13:18 +0000 (13:13 +0000)
All accpeted backport proposals for mod_http2

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1929885 13f79535-47bb-0310-9956-ffa450edef68

19 files changed:
STATUS
changes-entries/h2_v2.0.35.txt [new file with mode: 0644]
changes-entries/pr69580.txt [new file with mode: 0644]
changes-entries/pr69741.txt [new file with mode: 0644]
changes-entries/pr69771.txt [new file with mode: 0644]
docs/manual/mod/mod_http2.xml
modules/http2/h2_config.c
modules/http2/h2_config.h
modules/http2/h2_mplx.c
modules/http2/h2_proxy_session.c
modules/http2/h2_proxy_util.c
modules/http2/h2_proxy_util.h
modules/http2/h2_session.c
modules/http2/h2_session.h
modules/http2/h2_stream.c
modules/http2/h2_util.c
modules/http2/h2_util.h
modules/http2/h2_version.h
modules/http2/mod_proxy_http2.c

diff --git a/STATUS b/STATUS
index f3b65d598c2d9e429890282e33c33e70e6d5dd61..c82abbd091f1b4f37c91f67a31889c547adcf3f5 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -160,38 +160,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
 
-  *) mod_http2/mod_proxy_http2: Fix bug in log2() calculation
-     trunk patch: http://svn.apache.org/r1927235
-     2.4.x patch: svn merge -c 1927235 ^/httpd/httpd/trunk .
-     +1: icing, rpluem, jorton
-
-  *) mod_proxy_http2: add support for ProxyErrorOverride. PR69771
-     trunk patch: http://svn.apache.org/r1927647
-     2.4.x patch: svn merge -c 1927647 ^/httpd/httpd/trunk .
-     +1: icing, rpluem, jorton
-
-  *) mod_http2: new directive H2MaxStreamErrors.
-     trunk patch: http://svn.apache.org/r1927792
-     2.4.x patch: svn merge -c 1927792 ^/httpd/httpd/trunk .
-     +1: icing, rpluem, jorton
-
-  *) mod_http2: Fix handling of 304 responses from mod_cache. PR 69580.
-     Trunk version of patch:
-        https://svn.apache.org/r1924267
-     Backport version for 2.4.x of patch:
-      Trunk version of patch works
-      svn merge -c 1924267 ^/httpd/httpd/trunk .
-     +1: rpluem, jorton, covener 
-
-  *) mod_http2: use nghttp2 supplied lengths when checking trailers.
-     Trunk version of patch:
-        https://svn.apache.org/r1929517
-        https://svn.apache.org/r1929527
-     Backport version for 2.4.x of patch:
-      Trunk version of patch works
-      svn merge -c 1929517,1929527 ^/httpd/httpd/trunk .
-     +1: icing, covener, rpluem
-
   *) mod_ssl: fix strict mode handling in SSLVHostSNIPolicy
      Trunk version of patch:
         https://svn.apache.org/r1929631
diff --git a/changes-entries/h2_v2.0.35.txt b/changes-entries/h2_v2.0.35.txt
new file mode 100644 (file)
index 0000000..288f6e1
--- /dev/null
@@ -0,0 +1,4 @@
+  *) mod_http2: update to version 2.0.35
+     New directive `H2MaxStreamErrors` to control how much bad behaviour
+     by clients is tolerated before the connection is closed.
+     [Stefan Eissing]
diff --git a/changes-entries/pr69580.txt b/changes-entries/pr69580.txt
new file mode 100644 (file)
index 0000000..08647ea
--- /dev/null
@@ -0,0 +1,2 @@
+  *) mod_http2: Fix handling of 304 responses from mod_cache. PR 69580.
+     [Stefan Eissing]
diff --git a/changes-entries/pr69741.txt b/changes-entries/pr69741.txt
new file mode 100644 (file)
index 0000000..2b1d876
--- /dev/null
@@ -0,0 +1,3 @@
+  * mod_http2/mod_proxy_http2: fix a bug in calculating the log2 value of
+    integers, used in push diaries and proxy window size calculations.
+    PR69741 [Benjamin P. Kallus]
diff --git a/changes-entries/pr69771.txt b/changes-entries/pr69771.txt
new file mode 100644 (file)
index 0000000..8c8402e
--- /dev/null
@@ -0,0 +1 @@
+  * mod_proxy_http2: add support for ProxyErrorOverride directive. PR69771
index 0d48ae45f83690abfb44fc825ba6e10510e17370..635ec9017fa58012900eeaf4a577fb3687d8efa3 100644 (file)
@@ -1166,4 +1166,31 @@ H2EarlyHint Link "&lt;/my.css&gt;;rel=preload;as=style"
         </usage>
     </directivesynopsis>
 
+        <directivesynopsis>
+        <name>H2MaxStreamErrors</name>
+        <description>Maximum amount of client caused errors to tolerate</description>
+        <syntax>H2MaxStreamErrors <em>n</em></syntax>
+        <default>H2MaxStreamErrors 8</default>
+        <contextlist>
+            <context>server config</context>
+            <context>virtual host</context>
+        </contextlist>
+        <compatibility>Available in version 2.5.1 and later.</compatibility>
+
+        <usage>
+            <p>
+                <directive>H2MaxStreamErrors</directive> sets the maxmimum amount
+                of tolerated HTTP/2 stream errors caused by the client.
+                When exceeding this limit, the connection will be closed.
+                Stream errors are protocol violations on an individual HTTP/2
+                stream that do not necessitate a connection close by the
+                protocol specification, but can be a sign of malicious
+                activity by a client.
+            </p>
+            <p>
+                Set to 0 to tolerate faulty clients.
+            </p>
+        </usage>
+    </directivesynopsis>
+
 </modulesynopsis>
index 51a2c24745cea161707fa9ddf239be370f13df08..94fd8d22420f52c56f4f0ae27ae6b2d76b827273 100644 (file)
@@ -78,6 +78,7 @@ typedef struct h2_config {
     apr_interval_time_t stream_timeout;/* beam timeout */
     int max_data_frame_len;          /* max # bytes in a single h2 DATA frame */
     int max_hd_block_len;            /* max # bytes in a response header block */
+    int max_stream_errors;           /* max # of tolerated stream errors */
     int proxy_requests;              /* act as forward proxy */
     int h2_websockets;               /* if mod_h2 negotiating WebSockets */
 } h2_config;
@@ -119,6 +120,7 @@ static h2_config defconf = {
     -1,                     /* beam timeout */
     0,                      /* max DATA frame len, 0 == no extra limit */
     0,                      /* max header block len, 0 == no extra limit */
+    8,                      /* max stream errors tolerated */
     0,                      /* forward proxy */
     0,                      /* WebSockets negotiation, enabled */
 };
@@ -168,6 +170,7 @@ void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
     conf->stream_timeout       = DEF_VAL;
     conf->max_data_frame_len   = DEF_VAL;
     conf->max_hd_block_len     = DEF_VAL;
+    conf->max_stream_errors    = DEF_VAL;
     conf->proxy_requests       = DEF_VAL;
     conf->h2_websockets        = DEF_VAL;
     return conf;
@@ -220,6 +223,7 @@ static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
     n->stream_timeout       = H2_CONFIG_GET(add, base, stream_timeout);
     n->max_data_frame_len   = H2_CONFIG_GET(add, base, max_data_frame_len);
     n->max_hd_block_len     = H2_CONFIG_GET(add, base, max_hd_block_len);
+    n->max_stream_errors    = H2_CONFIG_GET(add, base, max_stream_errors);
     n->proxy_requests       = H2_CONFIG_GET(add, base, proxy_requests);
     n->h2_websockets        = H2_CONFIG_GET(add, base, h2_websockets);
     return n;
@@ -319,6 +323,9 @@ static apr_int64_t h2_srv_config_geti64(const h2_config *conf, h2_config_var_t v
             return H2_CONFIG_GET(conf, &defconf, h2_websockets);
         case H2_CONF_MAX_HEADER_BLOCK_LEN:
             return H2_CONFIG_GET(conf, &defconf, max_hd_block_len);
+        case H2_CONF_MAX_STREAM_ERRORS:
+            return H2_CONFIG_GET(conf, &defconf, max_stream_errors);
+
         default:
             return DEF_VAL;
     }
@@ -389,6 +396,9 @@ static void h2_srv_config_seti(h2_config *conf, h2_config_var_t var, int val)
             break;
         case H2_CONF_MAX_HEADER_BLOCK_LEN:
             H2_CONFIG_SET(conf, max_hd_block_len, val);
+            break;
+        case H2_CONF_MAX_STREAM_ERRORS:
+            H2_CONFIG_SET(conf, max_stream_errors, val);
         default:
             break;
     }
@@ -669,6 +679,17 @@ static const char *h2_conf_set_max_hd_block_len(cmd_parms *cmd,
     return NULL;
 }
 
+static const char *h2_conf_set_max_stream_errors(cmd_parms *cmd,
+                                               void *dirconf, const char *value)
+{
+    int val = (int)apr_atoi64(value);
+    if (val < 0) {
+        return "value must be 0 or larger";
+    }
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_STREAM_ERRORS, val);
+    return NULL;
+}
+
 static const char *h2_conf_set_session_extra_files(cmd_parms *cmd,
                                                    void *dirconf, const char *value)
 {
@@ -1092,6 +1113,8 @@ const command_rec h2_cmds[] = {
                   RSRC_CONF, "maximum number of bytes in a single HTTP/2 DATA frame"),
     AP_INIT_TAKE1("H2MaxHeaderBlockLen", h2_conf_set_max_hd_block_len, NULL,
                   RSRC_CONF, "maximum number of bytes in a response header block"),
+    AP_INIT_TAKE1("H2MaxStreamErrors", h2_conf_set_max_stream_errors, NULL,
+                  RSRC_CONF, "maximum number of flow control errors tolerated"),
     AP_INIT_TAKE2("H2EarlyHint", h2_conf_add_early_hint, NULL,
                    OR_FILEINFO|OR_AUTHCFG, "add a a 'Link:' header for a 103 Early Hints response."),
     AP_INIT_TAKE1("H2ProxyRequests", h2_conf_set_proxy_requests, NULL,
index 7f3158f6c85c4cbe53d32c1dec572727e433d3ca..87fc0b18b6e0bcecc83acffc469aa8665dd764ce 100644 (file)
@@ -47,6 +47,7 @@ typedef enum {
     H2_CONF_PROXY_REQUESTS,
     H2_CONF_WEBSOCKETS,
     H2_CONF_MAX_HEADER_BLOCK_LEN,
+    H2_CONF_MAX_STREAM_ERRORS,
 } h2_config_var_t;
 
 struct apr_hash_t;
index d23f1eca27a213e38f634e3efddf21bd50ca87af..f9616aba048e33b423e1713b9c98190cb7a49a5e 100644 (file)
@@ -1084,8 +1084,9 @@ static void s_mplx_be_happy(h2_mplx *m, conn_rec *c, h2_conn_ctx_t *conn_ctx)
             m->last_mood_change = now;
             m->irritations_since = 0;
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                          H2_MPLX_MSG(m, "mood update, increasing worker limit to %d"),
-                          m->processing_limit);
+                          H2_MPLX_MSG(m, "mood update, increasing worker limit"
+                          "to %d, processing %d right now"),
+                          m->processing_limit, m->processing_count);
         }
     }
 }
@@ -1114,8 +1115,9 @@ static void m_be_annoyed(h2_mplx *m)
             m->last_mood_change = now;
             m->irritations_since = 0;
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c1,
-                          H2_MPLX_MSG(m, "mood update, decreasing worker limit to %d"),
-                          m->processing_limit);
+                          H2_MPLX_MSG(m, "mood update, decreasing worker limit "
+                          "to %d, processing %d right now"),
+                          m->processing_limit, m->processing_count);
         }
     }
 }
@@ -1139,6 +1141,7 @@ static int reset_is_acceptable(h2_stream *stream)
      * The responses to such requests continue forever otherwise.
      *
      */
+    if (stream->rst_error) return 0; /* errored stream. bad. */
     if (!stream_is_running(stream)) return 1;
     if (!(stream->id & 0x01)) return 1; /* stream initiated by us. acceptable. */
     if (!stream->response) return 0; /* no response headers produced yet. bad. */
index 2cfbb5f5d4b240dc2611b95e983eefed5c454d42..acb17a56af7b155a5fe7c81591701ce19fb42a0f 100644 (file)
@@ -49,6 +49,7 @@ typedef struct h2_proxy_stream {
     unsigned int waiting_on_ping : 1;
     unsigned int headers_ended : 1;
     uint32_t error_code;
+    int proxy_status;
 
     apr_bucket_brigade *input;
     apr_off_t data_sent;
@@ -310,6 +311,15 @@ static int on_frame_recv(nghttp2_session *ngh2, const nghttp2_frame *frame,
                     ap_send_interim_response(r, 1);
                 }
             }
+            else if (r->status >= 400) {
+                proxy_dir_conf *dconf;
+                dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
+                if (ap_proxy_should_override(dconf, r->status)) {
+                    apr_table_setn(r->notes, "proxy-error-override", "1");
+                    nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE,
+                          frame->hd.stream_id, NGHTTP2_STREAM_CLOSED);
+                }
+            }
             stream_resume(stream);
             break;
         case NGHTTP2_PING:
index dc69ec06368d22460711d1600289f40cdc043ac9..bb384b9b23af28f36dc74b104a624cc83d5d6889 100644 (file)
@@ -34,7 +34,7 @@
 APLOG_USE_MODULE(proxy_http2);
 
 /* h2_log2(n) iff n is a power of 2 */
-unsigned char h2_proxy_log2(int n)
+unsigned char h2_proxy_log2(unsigned int n)
 {
     int lz = 0;
     if (!n) {
index 202363dede8d2de41802f8091ef978f8849e53dc..610908eeb246b9fffdb2bad87d1f76d683db2eaf 100644 (file)
@@ -150,7 +150,7 @@ int h2_proxy_iq_shift(h2_proxy_iqueue *q);
  * common helpers
  ******************************************************************************/
 /* h2_proxy_log2(n) iff n is a power of 2 */
-unsigned char h2_proxy_log2(int n);
+unsigned char h2_proxy_log2(unsigned int n);
 
 /*******************************************************************************
  * HTTP/2 header helpers
index a5f1872bc203946323ba8c579ed69bdeeeec318c..21ede5c100d2e65249862c4ec312242437f9abfe 100644 (file)
@@ -61,6 +61,8 @@ static void transit(h2_session *session, const char *action,
 static void on_stream_state_enter(void *ctx, h2_stream *stream);
 static void on_stream_state_event(void *ctx, h2_stream *stream, h2_stream_event_t ev);
 static void on_stream_event(void *ctx, h2_stream *stream, h2_stream_event_t ev);
+static apr_status_t h2_session_shutdown(h2_session *session, int error,
+                                        const char *msg, int force_close);
 
 static int h2_session_status_from_apr_status(apr_status_t rv)
 {
@@ -290,6 +292,7 @@ static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
                           "closing with err=%d %s"), 
                           (int)error_code, h2_protocol_err_description(error_code));
             h2_stream_rst(stream, error_code);
+            h2_mplx_c1_client_rst(session->mplx, stream_id, stream);
         }
     }
     return 0;
@@ -608,7 +611,11 @@ static int on_frame_send_cb(nghttp2_session *ngh2,
             /* PUSH_PROMISE we report on the promised stream */
             stream_id = frame->push_promise.promised_stream_id;
             break;
-        default:    
+        case NGHTTP2_RST_STREAM:
+            if(frame->rst_stream.error_code == NGHTTP2_FLOW_CONTROL_ERROR)
+              ++session->stream_errors;
+            break;
+        default:
             break;
     }
     
@@ -652,10 +659,11 @@ static int on_frame_not_send_cb(nghttp2_session *ngh2,
 
     stream = get_stream(session, stream_id);
     h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
-    ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c1,
-                  H2_SSSN_LOG(APLOGNO(10509), session,
-                  "not sent FRAME[%s], error %d: %s"),
-                  buffer, ngh2_err, nghttp2_strerror(ngh2_err));
+    if (!stream || !stream->rst_error)
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c1,
+                      H2_SSSN_LOG(APLOGNO(10509), session,
+                      "not sent FRAME[%s], error %d: %s"),
+                      buffer, ngh2_err, nghttp2_strerror(ngh2_err));
     if(stream) {
         h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
         return 0;
@@ -968,6 +976,7 @@ apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *
     session->max_stream_count = h2_config_sgeti(s, H2_CONF_MAX_STREAMS);
     session->max_stream_mem = h2_config_sgeti(s, H2_CONF_STREAM_MAX_MEM);
     session->max_data_frame_len = h2_config_sgeti(s, H2_CONF_MAX_DATA_FRAME_LEN);
+    session->max_stream_errors = h2_config_sgeti(s, H2_CONF_MAX_STREAM_ERRORS);
 
     session->out_c1_blocked = h2_iq_create(session->pool, (int)session->max_stream_count);
     session->ready_to_process = h2_iq_create(session->pool, (int)session->max_stream_count);
@@ -1063,14 +1072,16 @@ apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *
                                   "created, max_streams=%d, stream_mem=%d, "
                                   "workers_limit=%d, workers_max=%d, "
                                   "push_diary(type=%d,N=%d), "
-                                  "max_data_frame_len=%d"),
+                                  "max_data_frame_len=%d, "
+                                  "max_stream_errors=%d"),
                       (int)session->max_stream_count, 
                       (int)session->max_stream_mem,
                       session->mplx->processing_limit,
                       session->mplx->processing_max,
                       session->push_diary->dtype, 
                       (int)session->push_diary->N,
-                      (int)session->max_data_frame_len);
+                      (int)session->max_data_frame_len,
+                      session->max_stream_errors);
     }
     
     apr_pool_pre_cleanup_register(pool, c, session_pool_cleanup);
@@ -1609,6 +1620,14 @@ static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char
     }
 }
 
+static void h2_session_ev_bad_client(h2_session *session, int arg, const char *msg)
+{
+    transit(session, msg, H2_SESSION_ST_DONE);
+    if (!session->local.shutdown) {
+        h2_session_shutdown(session, arg, msg, 1);
+    }
+}
+
 static void h2_session_ev_pre_close(h2_session *session, int arg, const char *msg)
 {
     h2_session_shutdown(session, arg, msg, 1);
@@ -1806,6 +1825,9 @@ void h2_session_dispatch_event(h2_session *session, h2_session_event_t ev,
         case H2_SESSION_EV_NO_MORE_STREAMS:
             h2_session_ev_no_more_streams(session);
             break;
+        case H2_SESSION_EV_BAD_CLIENT:
+            h2_session_ev_bad_client(session, arg, msg);
+            break;
         default:
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c1,
                           H2_SSSN_MSG(session, "unknown event %d"), ev);
@@ -1884,6 +1906,12 @@ apr_status_t h2_session_process(h2_session *session, int async,
             }
         }
 
+        if (session->max_stream_errors &&
+            session->stream_errors > session->max_stream_errors) {
+            h2_session_dispatch_event(session, H2_SESSION_EV_BAD_CLIENT,
+                                      NGHTTP2_PROTOCOL_ERROR, NULL);
+        }
+
         session->status[0] = '\0';
         
         if (h2_session_want_send(session)) {
index 7932a9e2ccfed57cf111f77880daf3b359b60dce..497f7e193605b3c166f355d0a7e19abfe7828353 100644 (file)
@@ -60,6 +60,7 @@ typedef enum {
     H2_SESSION_EV_MPM_STOPPING,     /* the process is stopping */
     H2_SESSION_EV_PRE_CLOSE,        /* connection will close after this */
     H2_SESSION_EV_NO_MORE_STREAMS,  /* no more streams to process */
+    H2_SESSION_EV_BAD_CLIENT,       /* client misbehaving badly */
 } h2_session_event_t;
 
 typedef struct h2_session {
@@ -98,7 +99,9 @@ typedef struct h2_session {
     unsigned int pushes_promised;   /* number of http/2 push promises submitted */
     unsigned int pushes_submitted;  /* number of http/2 pushed responses submitted */
     unsigned int pushes_reset;      /* number of http/2 pushed reset by client */
-    
+    unsigned int max_stream_errors; /* max client stream errors tolerated */
+    unsigned int stream_errors;     /* number of stream errors by client */
+
     apr_size_t frames_received;     /* number of http/2 frames received */
     apr_size_t frames_sent;         /* number of http/2 frames sent */
     
index f82140194047bff9e63d4e5288615f15b7e014cc..f0e671ca5d660a915758431dd91c41c5124f2ef7 100644 (file)
@@ -1510,7 +1510,8 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
             buf_len = output_data_buffered(stream, &eos, &header_blocked);
         }
         else if (APR_EOF == rv) {
-            if (!stream->output_eos) {
+            if (!stream->output_eos &&
+                !AP_STATUS_IS_HEADER_ONLY(stream->response->status)) {
                 /* Seeing APR_EOF without an EOS bucket received before indicates
                  * that stream output is incomplete. Commonly, we expect to see
                  * an ERROR bucket to have been generated. But faulty handlers
@@ -1618,8 +1619,9 @@ static apr_status_t stream_do_response(h2_stream *stream)
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c1,
                               H2_STRM_MSG(stream, "process response %d"),
                               resp->status);
-                is_empty = (e != APR_BRIGADE_SENTINEL(stream->out_buffer)
-                            && APR_BUCKET_IS_EOS(e));
+                is_empty = AP_STATUS_IS_HEADER_ONLY(resp->status) ||
+                           ((e != APR_BRIGADE_SENTINEL(stream->out_buffer) &&
+                            APR_BUCKET_IS_EOS(e)));
                 break;
             }
             else if (APR_BUCKET_IS_EOS(b)) {
index 605c348ca127fef657e55bc7ebe947ed1ed04578..b377ff77f1b1955c07475f99b50a7bf3e0095af8 100644 (file)
@@ -32,7 +32,7 @@
 #include "h2_util.h"
 
 /* h2_log2(n) iff n is a power of 2 */
-unsigned char h2_log2(int n)
+unsigned char h2_log2(unsigned int n)
 {
     int lz = 0;
     if (!n) {
@@ -1650,7 +1650,7 @@ static int contains_name(const literal *lits, size_t llen, nghttp2_nv *nv)
     for (i = 0; i < llen; ++i) {
         lit = &lits[i];
         if (lit->len == nv->namelen
-            && !ap_cstr_casecmp(lit->name, (const char *)nv->name)) {
+            && !ap_cstr_casecmpn(lit->name, (const char *)nv->name, nv->namelen)) {
             return 1;
         }
     }
@@ -1677,7 +1677,7 @@ int h2_ignore_req_trailer(const char *name, size_t len)
     nghttp2_nv nv;
 
     nv.name = (uint8_t*)name;
-    nv.namelen = strlen(name);
+    nv.namelen = len;
     return (h2_req_ignore_header(&nv)
             || contains_name(H2_LIT_ARGS(IgnoredRequestTrailers), &nv));
 }
@@ -1687,7 +1687,7 @@ int h2_ignore_resp_trailer(const char *name, size_t len)
     nghttp2_nv nv;
 
     nv.name = (uint8_t*)name;
-    nv.namelen = strlen(name);
+    nv.namelen = len;
     return (contains_name(H2_LIT_ARGS(IgnoredResponseHeaders), &nv)
             || contains_name(H2_LIT_ARGS(IgnoredResponseTrailers), &nv));
 }
@@ -1705,7 +1705,7 @@ static apr_status_t req_add_header(apr_table_t *headers, apr_pool_t *pool,
         return APR_SUCCESS;
     }
     else if (nv->namelen == sizeof("cookie")-1
-             && !ap_cstr_casecmp("cookie", (const char *)nv->name)) {
+             && !ap_cstr_casecmpn("cookie", (const char *)nv->name, nv->namelen)) {
         existing = apr_table_get(headers, "cookie");
         if (existing) {
             /* Cookie header come separately in HTTP/2, but need
@@ -1723,7 +1723,7 @@ static apr_status_t req_add_header(apr_table_t *headers, apr_pool_t *pool,
         }
     }
     else if (nv->namelen == sizeof("host")-1
-             && !ap_cstr_casecmp("host", (const char *)nv->name)) {
+             && !ap_cstr_casecmpn("host", (const char *)nv->name, nv->namelen)) {
         if (apr_table_get(headers, "Host")) {
             return APR_SUCCESS; /* ignore duplicate */
         }
@@ -1805,9 +1805,11 @@ int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
         }
         case NGHTTP2_RST_STREAM: {
             return apr_snprintf(buffer, maxlen,
-                                "RST_STREAM[length=%d, flags=%d, stream=%d]",
+                                "RST_STREAM[length=%d, flags=%d, stream=%d"
+                                ",error=%d]",
                                 (int)frame->hd.length,
-                                frame->hd.flags, frame->hd.stream_id);
+                                frame->hd.flags, frame->hd.stream_id,
+                                frame->rst_stream.error_code);
         }
         case NGHTTP2_SETTINGS: {
             if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
index c2cab4afa4547cb127997489ae9a4c470c8dc7df..29d17998da3b0e3b1f0e16d5d553142cf0167cfc 100644 (file)
@@ -323,7 +323,7 @@ apr_status_t h2_ififo_remove(h2_ififo *fifo, int id);
  * common helpers
  ******************************************************************************/
 /* h2_log2(n) iff n is a power of 2 */
-unsigned char h2_log2(int n);
+unsigned char h2_log2(unsigned int n);
 
 /**
  * Count the bytes that all key/value pairs in a table have
index 13441a093548ffb20c4ef37f08476cbe4b0a80c2..8d38c34e7865cfe9e8aca8fba70e723ec00c9581 100644 (file)
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "2.0.32"
+#define MOD_HTTP2_VERSION "2.0.35"
 
 /**
  * @macro
@@ -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 0x020020
+#define MOD_HTTP2_VERSION_NUM 0x020023
 
 
 #endif /* mod_h2_h2_version_h */
index 2881293d89241bd8f805ffd3cd2fcfc8e6059d29..d61448f42f8eced1c1944c67211a9f6c2b118a6b 100644 (file)
@@ -239,9 +239,15 @@ static void request_done(h2_proxy_ctx *ctx, request_rec *r,
                       ctx->id, touched, error_code);
         ctx->r_done = 1;
         if (touched) ctx->r_may_retry = 0;
-        ctx->r_status = error_code? HTTP_BAD_GATEWAY :
-            ((status == APR_SUCCESS)? OK :
-             ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE));
+        if (apr_table_get(r->notes, "proxy-error-override")) {
+            ctx->r_status = r->status;
+            r->status = OK;
+        }
+        else {
+          ctx->r_status = error_code? HTTP_BAD_GATEWAY :
+              ((status == APR_SUCCESS)? OK :
+               ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE));
+        }
     }
 }    
 
@@ -428,7 +434,12 @@ run_connect:
     if (ctx->cfront->aborted) goto cleanup;
     status = ctx_run(ctx);
 
-    if (ctx->r_status != APR_SUCCESS && ctx->r_may_retry && !ctx->cfront->aborted) {
+    if (apr_table_get(r->notes, "proxy-error-override")) {
+        /* pass on out */
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->cfront,
+                      "proxy-error-override status %d", ctx->r_status);
+    }
+    else if (ctx->r_status != APR_SUCCESS && ctx->r_may_retry && !ctx->cfront->aborted) {
         /* Not successfully processed, but may retry, tear down old conn and start over */
         if (ctx->p_conn) {
             ctx->p_conn->close = 1;
@@ -463,7 +474,7 @@ cleanup:
 
     ap_set_module_config(ctx->cfront->conn_config, &proxy_http2_module, NULL);
     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront,
-                  APLOGNO(03377) "leaving handler");
+                  APLOGNO(03377) "leaving handler -> %d", ctx->r_status);
     return ctx->r_status;
 }