You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2022/04/13 08:38:12 UTC

svn commit: r1899802 - in /httpd/httpd/trunk: changes-entries/ modules/http2/

Author: icing
Date: Wed Apr 13 08:38:12 2022
New Revision: 1899802

URL: http://svn.apache.org/viewvc?rev=1899802&view=rev
Log:
  *) mod_http2: use the new REQUEST buckets to forward request
     on secondary connections. Use the now generic
     ap_process_connection() in h2 workers to process those.


Added:
    httpd/httpd/trunk/changes-entries/h2_request_buckets.txt
Modified:
    httpd/httpd/trunk/modules/http2/h2.h
    httpd/httpd/trunk/modules/http2/h2_c2.c
    httpd/httpd/trunk/modules/http2/h2_c2.h
    httpd/httpd/trunk/modules/http2/h2_c2_filter.c
    httpd/httpd/trunk/modules/http2/h2_c2_filter.h
    httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
    httpd/httpd/trunk/modules/http2/h2_mplx.c
    httpd/httpd/trunk/modules/http2/h2_mplx.h
    httpd/httpd/trunk/modules/http2/h2_push.h
    httpd/httpd/trunk/modules/http2/h2_request.c
    httpd/httpd/trunk/modules/http2/h2_request.h
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_session.h
    httpd/httpd/trunk/modules/http2/h2_stream.c
    httpd/httpd/trunk/modules/http2/h2_stream.h
    httpd/httpd/trunk/modules/http2/h2_switch.c
    httpd/httpd/trunk/modules/http2/h2_workers.c

Added: httpd/httpd/trunk/changes-entries/h2_request_buckets.txt
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/h2_request_buckets.txt?rev=1899802&view=auto
==============================================================================
--- httpd/httpd/trunk/changes-entries/h2_request_buckets.txt (added)
+++ httpd/httpd/trunk/changes-entries/h2_request_buckets.txt Wed Apr 13 08:38:12 2022
@@ -0,0 +1,4 @@
+  *) mod_http2: use the new REQUEST buckets to forward request
+     on secondary connections. Use the now generic
+     ap_process_connection() in h2 workers to process those.
+     [Stefan Eissing]

Modified: httpd/httpd/trunk/modules/http2/h2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2.h (original)
+++ httpd/httpd/trunk/modules/http2/h2.h Wed Apr 13 08:38:12 2022
@@ -188,7 +188,6 @@ struct h2_request {
     apr_table_t *headers;
 
     apr_time_t request_time;
-    unsigned int chunked : 1;   /* iff request body needs to be forwarded as chunked */
     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

Modified: httpd/httpd/trunk/modules/http2/h2_c2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_c2.c Wed Apr 13 08:38:12 2022
@@ -57,6 +57,7 @@ static apr_socket_t *dummy_socket;
 
 static ap_filter_rec_t *c2_net_in_filter_handle;
 static ap_filter_rec_t *c2_net_out_filter_handle;
+static ap_filter_rec_t *c2_request_in_filter_handle;
 static ap_filter_rec_t *c2_notes_out_filter_handle;
 
 
@@ -165,12 +166,11 @@ static apr_status_t h2_c2_filter_in(ap_f
     apr_status_t status = APR_SUCCESS;
     apr_bucket *b;
     apr_off_t bblen;
-    const int trace1 = APLOGctrace1(f->c);
-    apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)? 
+    apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)?
                        (apr_size_t)readbytes : APR_SIZE_MAX);
     
     conn_ctx = h2_conn_ctx_get(f->c);
-    ap_assert(conn_ctx);
+    AP_DEBUG_ASSERT(conn_ctx);
 
     if (mode == AP_MODE_INIT) {
         return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes);
@@ -180,8 +180,8 @@ static apr_status_t h2_c2_filter_in(ap_f
         return APR_ECONNABORTED;
     }
     
-    if (APLOGctrace1(f->c)) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+    if (APLOGctrace3(f->c)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, f->c,
                       "h2_c2_in(%s-%d): read, mode=%d, block=%d, readbytes=%ld",
                       conn_ctx->id, conn_ctx->stream_id, mode, block, (long)readbytes);
     }
@@ -198,7 +198,7 @@ static apr_status_t h2_c2_filter_in(ap_f
     
     while (APR_BRIGADE_EMPTY(fctx->bb)) {
         /* Get more input data for our request. */
-        if (APLOGctrace1(f->c)) {
+        if (APLOGctrace2(f->c)) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c,
                           "h2_c2_in(%s-%d): get more data from mplx, block=%d, "
                           "readbytes=%ld",
@@ -260,8 +260,8 @@ receive:
         return status;
     }
 
-    if (trace1) {
-        h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE2,
+    if (APLOGctrace3(f->c)) {
+        h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE3,
                     "c2 input.bb", fctx->bb);
     }
            
@@ -357,9 +357,27 @@ static apr_status_t beam_out(conn_rec *c
 static apr_status_t h2_c2_filter_out(ap_filter_t* f, apr_bucket_brigade* bb)
 {
     h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
+    apr_bucket *e;
     apr_status_t rv;
 
     ap_assert(conn_ctx);
+    if (!conn_ctx->has_final_response) {
+        for (e = APR_BRIGADE_FIRST(bb);
+             e != APR_BRIGADE_SENTINEL(bb);
+             e = APR_BUCKET_NEXT(e))
+        {
+            if (AP_BUCKET_IS_RESPONSE(e)) {
+                ap_bucket_response *resp = e->data;
+                if (resp->status >= 200) {
+                    conn_ctx->has_final_response = 1;
+                    break;
+                }
+            }
+            if (APR_BUCKET_IS_EOS(e)) {
+                break;
+            }
+        }
+    }
     rv = beam_out(f->c, conn_ctx, bb);
 
     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, f->c,
@@ -371,8 +389,19 @@ static apr_status_t h2_c2_filter_out(ap_
     return rv;
 }
 
-static apr_status_t c2_run_pre_connection(conn_rec *c2, apr_socket_t *csd)
+static int c2_hook_pre_connection(conn_rec *c2, void *csd)
 {
+    h2_conn_ctx_t *conn_ctx;
+
+    if (!c2->master || !(conn_ctx = h2_conn_ctx_get(c2)) || !conn_ctx->stream_id) {
+        return DECLINED;
+    }
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c2,
+                  "h2_c2(%s-%d), adding filters",
+                  conn_ctx->id, conn_ctx->stream_id);
+    ap_add_input_filter_handle(c2_net_in_filter_handle, NULL, NULL, c2);
+    ap_add_output_filter_handle(c2_net_out_filter_handle, NULL, NULL, c2);
     if (c2->keepalives == 0) {
         /* Simulate that we had already a request on this connection. Some
          * hooks trigger special behaviour when keepalives is 0.
@@ -388,101 +417,61 @@ static apr_status_t c2_run_pre_connectio
          * is unnecessary on a h2 stream.
          */
         c2->keepalive = AP_CONN_CLOSE;
-        return ap_run_pre_connection(c2, csd);
     }
-    ap_assert(c2->output_filters);
-    return APR_SUCCESS;
+    return OK;
 }
 
-apr_status_t h2_c2_process(conn_rec *c2, apr_thread_t *thread, int worker_id)
+static void check_push(request_rec *r, const char *tag)
 {
-    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c2);
+    apr_array_header_t *push_list = h2_config_push_list(r);
 
-    ap_assert(conn_ctx);
-    ap_assert(conn_ctx->mplx);
+    if (!r->expecting_100 && push_list && push_list->nelts > 0) {
+        int i, old_status;
+        const char *old_line;
 
-    /* See the discussion at <https://github.com/icing/mod_h2/issues/195>
-     *
-     * Each conn_rec->id is supposed to be unique at a point in time. Since
-     * some modules (and maybe external code) uses this id as an identifier
-     * for the request_rec they handle, it needs to be unique for secondary
-     * connections also.
-     *
-     * The MPM module assigns the connection ids and mod_unique_id is using
-     * that one to generate identifier for requests. While the implementation
-     * works for HTTP/1.x, the parallel execution of several requests per
-     * connection will generate duplicate identifiers on load.
-     *
-     * The original implementation for secondary connection identifiers used
-     * to shift the master connection id up and assign the stream id to the
-     * lower bits. This was cramped on 32 bit systems, but on 64bit there was
-     * enough space.
-     *
-     * As issue 195 showed, mod_unique_id only uses the lower 32 bit of the
-     * connection id, even on 64bit systems. Therefore collisions in request ids.
-     *
-     * The way master connection ids are generated, there is some space "at the
-     * top" of the lower 32 bits on allmost all systems. If you have a setup
-     * with 64k threads per child and 255 child processes, you live on the edge.
-     *
-     * The new implementation shifts 8 bits and XORs in the worker
-     * id. This will experience collisions with > 256 h2 workers and heavy
-     * load still. There seems to be no way to solve this in all possible
-     * configurations by mod_h2 alone.
-     */
-    c2->id = (c2->master->id << 8)^worker_id;
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+                      "%s, early announcing %d resources for push",
+                      tag, push_list->nelts);
+        for (i = 0; i < push_list->nelts; ++i) {
+            h2_push_res *push = &APR_ARRAY_IDX(push_list, i, h2_push_res);
+            apr_table_add(r->headers_out, "Link",
+                           apr_psprintf(r->pool, "<%s>; rel=preload%s",
+                                        push->uri_ref, push->critical? "; critical" : ""));
+        }
+        old_status = r->status;
+        old_line = r->status_line;
+        r->status = 103;
+        r->status_line = "103 Early Hints";
+        ap_send_interim_response(r, 1);
+        r->status = old_status;
+        r->status_line = old_line;
+    }
+}
 
-    if (!conn_ctx->pre_conn_done) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c2,
-                      "h2_c2(%s-%d), adding filters",
-                      conn_ctx->id, conn_ctx->stream_id);
-        ap_add_input_filter_handle(c2_net_in_filter_handle, NULL, NULL, c2);
-        ap_add_output_filter_handle(c2_net_out_filter_handle, NULL, NULL, c2);
+static void c2_pre_read_request(request_rec *r, conn_rec *c2)
+{
+    h2_conn_ctx_t *conn_ctx;
 
-        c2_run_pre_connection(c2, ap_get_conn_socket(c2));
-        conn_ctx->pre_conn_done = 1;
+    if (!c2->master || !(conn_ctx = h2_conn_ctx_get(c2)) || !conn_ctx->stream_id) {
+        return;
     }
-
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c2,
-                  "h2_c2(%s-%d): process connection",
-                  conn_ctx->id, conn_ctx->stream_id);
-                  
-    c2->current_thread = thread;
-    ap_run_process_connection(c2);
-    
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c2,
-                  "h2_c2(%s-%d): processing done",
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+                  "h2_c2(%s-%d): adding request filters",
                   conn_ctx->id, conn_ctx->stream_id);
-
-    return APR_SUCCESS;
+    ap_add_input_filter_handle(c2_request_in_filter_handle, NULL, r, r->connection);
+    ap_add_output_filter_handle(c2_notes_out_filter_handle, NULL, r, r->connection);
 }
 
-static apr_status_t c2_process(h2_conn_ctx_t *conn_ctx, conn_rec *c)
+static int c2_post_read_request(request_rec *r)
 {
-    const h2_request *req = conn_ctx->request;
-    conn_state_t *cs = c->cs;
-    request_rec *r;
+    h2_conn_ctx_t *conn_ctx;
+    conn_rec *c2 = r->connection;
 
-    r = h2_create_request_rec(conn_ctx->request, c);
-    if (!r) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                      "h2_c2(%s-%d): create request_rec failed, r=NULL",
-                      conn_ctx->id, conn_ctx->stream_id);
-        goto cleanup;
-    }
-    if (r->status != HTTP_OK) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                      "h2_c2(%s-%d): create request_rec failed, r->status=%d",
-                      conn_ctx->id, conn_ctx->stream_id, r->status);
-        goto cleanup;
+    if (!c2->master || !(conn_ctx = h2_conn_ctx_get(c2)) || !conn_ctx->stream_id) {
+        return DECLINED;
     }
-
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                  "h2_c2(%s-%d): created request_rec for %s",
-                  conn_ctx->id, conn_ctx->stream_id, r->the_request);
+    /* Now that the request_rec is fully initialized, set relevant params */
     conn_ctx->server = r->server;
-
-    /* the request_rec->server carries the timeout value that applies */
     h2_conn_ctx_set_timeout(conn_ctx, r->server->timeout);
     /* We only handle this one request on the connection and tell everyone
      * that there is no need to keep it "clean" if something fails. Also,
@@ -496,105 +485,21 @@ static apr_status_t c2_process(h2_conn_c
     }
 
     if (h2_config_sgeti(conn_ctx->server, H2_CONF_COPY_FILES)) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
                       "h2_mplx(%s-%d): copy_files in output",
                       conn_ctx->id, conn_ctx->stream_id);
         h2_beam_set_copy_files(conn_ctx->beam_out, 1);
     }
 
-    ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
-    if (cs) {
-        cs->state = CONN_STATE_HANDLER;
-    }
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                  "h2_c2(%s-%d): start process_request",
-                  conn_ctx->id, conn_ctx->stream_id);
-
     /* Add the raw bytes of the request (e.g. header frame lengths to
      * the logio for this request. */
-    if (req->raw_bytes && h2_c_logio_add_bytes_in) {
-        h2_c_logio_add_bytes_in(c, req->raw_bytes);
+    if (conn_ctx->request->raw_bytes && h2_c_logio_add_bytes_in) {
+        h2_c_logio_add_bytes_in(c2, conn_ctx->request->raw_bytes);
     }
-
-    ap_process_request(r);
-    /* After the call to ap_process_request, the
-     * request pool may have been deleted. */
-    r = NULL;
-
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                  "h2_c2(%s-%d): process_request done",
-                  conn_ctx->id, conn_ctx->stream_id);
-    if (cs)
-        cs->state = CONN_STATE_WRITE_COMPLETION;
-
-cleanup:
-    return APR_SUCCESS;
+    return OK;
 }
 
-static int h2_c2_hook_process(conn_rec* c)
-{
-    h2_conn_ctx_t *ctx;
-    
-    if (!c->master) {
-        return DECLINED;
-    }
-    
-    ctx = h2_conn_ctx_get(c);
-    if (ctx->stream_id) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                      "h2_h2, processing request directly");
-        c2_process(ctx, c);
-        return DONE;
-    }
-    else {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
-                      "secondary_conn(%ld): no h2 stream assing?", c->id);
-    }
-    return DECLINED;
-}
-
-static void check_push(request_rec *r, const char *tag)
-{
-    apr_array_header_t *push_list = h2_config_push_list(r);
-
-    if (!r->expecting_100 && push_list && push_list->nelts > 0) {
-        int i, old_status;
-        const char *old_line;
-
-        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
-                      "%s, early announcing %d resources for push",
-                      tag, push_list->nelts);
-        for (i = 0; i < push_list->nelts; ++i) {
-            h2_push_res *push = &APR_ARRAY_IDX(push_list, i, h2_push_res);
-            apr_table_add(r->headers_out, "Link",
-                           apr_psprintf(r->pool, "<%s>; rel=preload%s",
-                                        push->uri_ref, push->critical? "; critical" : ""));
-        }
-        old_status = r->status;
-        old_line = r->status_line;
-        r->status = 103;
-        r->status_line = "103 Early Hints";
-        ap_send_interim_response(r, 1);
-        r->status = old_status;
-        r->status_line = old_line;
-    }
-}
-
-static int h2_c2_hook_post_read_request(request_rec *r)
-{
-    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(r->connection);
-
-    if (conn_ctx && conn_ctx->stream_id) {
-
-        ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
-                      "h2_c2(%s-%d): adding request filters",
-                      conn_ctx->id, conn_ctx->stream_id);
-        ap_add_output_filter_handle(c2_notes_out_filter_handle, NULL, r, r->connection);
-    }
-    return DECLINED;
-}
-
-static int h2_c2_hook_fixups(request_rec *r)
+static int c2_hook_fixups(request_rec *r)
 {
     conn_rec *c2 = r->connection;
     h2_conn_ctx_t *conn_ctx;
@@ -613,12 +518,14 @@ void h2_c2_register_hooks(void)
     /* When the connection processing actually starts, we might
      * take over, if the connection is for a h2 stream.
      */
-    ap_hook_process_connection(h2_c2_hook_process,
-                               NULL, NULL, APR_HOOK_FIRST);
+    ap_hook_pre_connection(c2_hook_pre_connection,
+                           NULL, NULL, APR_HOOK_MIDDLE);
+
     /* We need to manipulate the standard HTTP/1.1 protocol filters and
      * install our own. This needs to be done very early. */
-    ap_hook_post_read_request(h2_c2_hook_post_read_request, NULL, NULL, APR_HOOK_REALLY_FIRST);
-    ap_hook_fixups(h2_c2_hook_fixups, NULL, NULL, APR_HOOK_LAST);
+    ap_hook_pre_read_request(c2_pre_read_request, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_post_read_request(c2_post_read_request, NULL, NULL, APR_HOOK_REALLY_FIRST);
+    ap_hook_fixups(c2_hook_fixups, NULL, NULL, APR_HOOK_LAST);
 
     c2_net_in_filter_handle =
         ap_register_input_filter("H2_C2_NET_IN", h2_c2_filter_in,
@@ -626,6 +533,9 @@ void h2_c2_register_hooks(void)
     c2_net_out_filter_handle =
         ap_register_output_filter("H2_C2_NET_OUT", h2_c2_filter_out,
                                   NULL, AP_FTYPE_NETWORK);
+    c2_request_in_filter_handle =
+        ap_register_input_filter("H2_C2_REQUEST_IN", h2_c2_filter_request_in,
+                                 NULL, AP_FTYPE_PROTOCOL);
     c2_notes_out_filter_handle =
         ap_register_output_filter("H2_C2_NOTES_OUT", h2_c2_filter_notes_out,
                                   NULL, AP_FTYPE_PROTOCOL);

Modified: httpd/httpd/trunk/modules/http2/h2_c2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_c2.h Wed Apr 13 08:38:12 2022
@@ -38,11 +38,6 @@ void h2_c2_destroy(conn_rec *c2);
  */
 void h2_c2_abort(conn_rec *c2, conn_rec *from);
 
-/**
- * Process a secondary connection for a HTTP/2 stream request.
- */
-apr_status_t h2_c2_process(conn_rec *c, apr_thread_t *thread, int worker_id);
-
 void h2_c2_register_hooks(void);
 
 #endif /* defined(__mod_h2__h2_c2__) */

Modified: httpd/httpd/trunk/modules/http2/h2_c2_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2_filter.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2_filter.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_c2_filter.c Wed Apr 13 08:38:12 2022
@@ -87,3 +87,40 @@ apr_status_t h2_c2_filter_notes_out(ap_f
 pass:
     return ap_pass_brigade(f->next, bb);
 }
+
+apr_status_t h2_c2_filter_request_in(ap_filter_t *f,
+                                     apr_bucket_brigade *bb,
+                                     ap_input_mode_t mode,
+                                     apr_read_type_e block,
+                                     apr_off_t readbytes)
+{
+    h2_conn_ctx_t *conn_ctx;
+    apr_bucket *b;
+
+    /* just get out of the way for things we don't want to handle. */
+    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
+        return ap_get_brigade(f->next, bb, mode, block, readbytes);
+    }
+
+    /* This filter is a one-time wonder */
+    ap_remove_input_filter(f);
+
+    if (f->c->master && (conn_ctx = h2_conn_ctx_get(f->c)) && conn_ctx->stream_id) {
+        if (conn_ctx->request->http_status != H2_HTTP_STATUS_UNSET) {
+            /* error was encountered preparing this request */
+            b = ap_bucket_error_create(conn_ctx->request->http_status, NULL, f->r->pool,
+                                       f->c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, b);
+            return APR_SUCCESS;
+        }
+        b = h2_request_create_bucket(conn_ctx->request, f->r);
+        APR_BRIGADE_INSERT_TAIL(bb, b);
+        if (!conn_ctx->beam_in) {
+            b = apr_bucket_eos_create(f->c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, b);
+        }
+        return APR_SUCCESS;
+    }
+
+    return ap_get_brigade(f->next, bb, mode, block, readbytes);
+}

Modified: httpd/httpd/trunk/modules/http2/h2_c2_filter.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2_filter.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2_filter.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_c2_filter.h Wed Apr 13 08:38:12 2022
@@ -18,21 +18,20 @@
 #define __mod_h2__h2_c2_filter__
 
 /**
- * h2_from_h1 parses a HTTP/1.1 response into
- * - response status
- * - a list of header values
- * - a series of bytes that represent the response body alone, without
- *   any meta data, such as inserted by chunked transfer encoding.
- *
- * All data is allocated from the stream memory pool. 
- *
- * Again, see comments in h2_request: ideally we would take the headers
- * and status from the httpd structures instead of parsing them here, but
- * we need to have all handlers and filters involved in request/response
- * processing, so this seems to be the way for now.
+ * Output filter that inspects the request_rec->notes of the request
+ * itself and possible internal redirects to detect conditions that
+ * merit specific HTTP/2 response codes, such as 421.
  */
-struct h2_response_parser;
-
 apr_status_t h2_c2_filter_notes_out(ap_filter_t *f, apr_bucket_brigade *bb);
 
+/**
+ * Input filter on secondary connections that insert the REQUEST bucket
+ * with the request to perform and then removes itself.
+ */
+apr_status_t h2_c2_filter_request_in(ap_filter_t *f,
+                                     apr_bucket_brigade *bb,
+                                     ap_input_mode_t mode,
+                                     apr_read_type_e block,
+                                     apr_off_t readbytes);
+
 #endif /* defined(__mod_h2__h2_c2_filter__) */

Modified: httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn_ctx.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn_ctx.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_conn_ctx.h Wed Apr 13 08:38:12 2022
@@ -43,7 +43,6 @@ struct h2_conn_ctx_t {
     struct h2_mplx *mplx;           /* c2: the multiplexer */
     struct h2_c2_transit *transit;  /* c2: transit pool and bucket_alloc */
 
-    int pre_conn_done;               /* has pre_connection setup run? */
     int stream_id;                  /* c1: 0, c2: stream id processed */
     apr_pool_t *req_pool;            /* c2: a c2 child pool for a request */
     const struct h2_request *request; /* c2: the request to process */

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.c Wed Apr 13 08:38:12 2022
@@ -715,27 +715,6 @@ void h2_mplx_c1_process(h2_mplx *m,
     H2_MPLX_LEAVE(m);
 }
 
-apr_status_t h2_mplx_c1_fwd_input(h2_mplx *m, struct h2_iqueue *input_pending,
-                                  h2_stream_get_fn *get_stream,
-                                  struct h2_session *session)
-{
-    int sid;
-
-    H2_MPLX_ENTER(m);
-
-    while ((sid = h2_iq_shift(input_pending)) > 0) {
-        h2_stream *stream = get_stream(session, sid);
-        if (stream) {
-            H2_MPLX_LEAVE(m);
-            h2_stream_flush_input(stream);
-            H2_MPLX_ENTER(m);
-        }
-    }
-
-    H2_MPLX_LEAVE(m);
-    return APR_SUCCESS;
-}
-
 static void c2_beam_input_write_notify(void *ctx, h2_bucket_beam *beam)
 {
     conn_rec *c = ctx;
@@ -1081,6 +1060,31 @@ apr_status_t h2_mplx_c1_client_rst(h2_mp
     }
     H2_MPLX_LEAVE(m);
     return status;
+}
+
+apr_status_t h2_mplx_c1_input_closed(h2_mplx *m, int stream_id)
+{
+    h2_stream *stream;
+    h2_conn_ctx_t *c2_ctx;
+    apr_status_t status = APR_EAGAIN;
+
+    H2_MPLX_ENTER_ALWAYS(m);
+    stream = h2_ihash_get(m->streams, stream_id);
+    if (stream && (c2_ctx = h2_conn_ctx_get(stream->c2))) {
+        if (c2_ctx->beam_in) {
+            apr_bucket_brigade *tmp =apr_brigade_create(
+                stream->pool, m->c1->bucket_alloc);
+            apr_bucket *eos = apr_bucket_eos_create(m->c1->bucket_alloc);
+            apr_off_t written;
+
+            APR_BRIGADE_INSERT_TAIL(tmp, eos);
+            status = h2_beam_send(c2_ctx->beam_in, m->c1,
+                      tmp, APR_BLOCK_READ, &written);
+            apr_brigade_destroy(tmp);
+        }
+    }
+    H2_MPLX_LEAVE(m);
+    return status;
 }
 
 static apr_status_t mplx_pollset_create(h2_mplx *m)

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.h Wed Apr 13 08:38:12 2022
@@ -153,11 +153,6 @@ void h2_mplx_c1_process(h2_mplx *m,
                         struct h2_session *session,
                         int *pstream_count);
 
-apr_status_t h2_mplx_c1_fwd_input(h2_mplx *m, struct h2_iqueue *input_pending,
-                                  h2_stream_get_fn *get_stream,
-                                  struct h2_session *session);
-
-
 /**
  * Stream priorities have changed, reschedule pending requests.
  * 
@@ -200,6 +195,15 @@ apr_status_t h2_mplx_c1_streams_do(h2_mp
 apr_status_t h2_mplx_c1_client_rst(h2_mplx *m, int stream_id);
 
 /**
+ * Input for stream has been closed. Notify a possibly started
+ * and waiting stream by sending an EOS.
+ * @param m the mplx
+ * @param stream_id the closed stream
+ * @return APR_SUCCESS iff EOS was sent, APR_EAGAIN if not necessary
+ */
+apr_status_t h2_mplx_c1_input_closed(h2_mplx *m, int stream_id);
+
+/**
  * Get readonly access to a stream for a secondary connection.
  */
 const struct h2_stream *h2_mplx_c2_stream_get(h2_mplx *m, int stream_id);

Modified: httpd/httpd/trunk/modules/http2/h2_push.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_push.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_push.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_push.h Wed Apr 13 08:38:12 2022
@@ -17,6 +17,8 @@
 #ifndef __mod_h2__h2_push__
 #define __mod_h2__h2_push__
 
+#include <http_protocol.h>
+
 #include "h2.h"
 
 struct h2_request;

Modified: httpd/httpd/trunk/modules/http2/h2_request.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_request.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_request.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_request.c Wed Apr 13 08:38:12 2022
@@ -205,13 +205,9 @@ apr_status_t h2_request_end_headers(h2_r
         apr_table_setn(req->headers, "Host", req->authority);
     }
 
-    s = apr_table_get(req->headers, "Content-Length");
-    if (!s) {
-        /* HTTP/2 does not need a Content-Length for framing, but our
-         * internal request processing is used to HTTP/1.1, so we
-         * need to either add a Content-Length or a Transfer-Encoding
-         * if any content can be expected. */
-        if (eos && apr_table_get(req->headers, "Content-Type")) {
+    if (eos) {
+        s = apr_table_get(req->headers, "Content-Length");
+        if (!s && apr_table_get(req->headers, "Content-Type")) {
             /* If we have a content-type, but already seen eos, no more
              * data will come. Signal a zero content length explicitly.
              */
@@ -291,6 +287,27 @@ static request_rec *my_ap_create_request
 }
 #endif
 
+apr_bucket *h2_request_create_bucket(const h2_request *req, request_rec *r)
+{
+    conn_rec *c = r->connection;
+    apr_table_t *headers = apr_table_copy(r->pool, req->headers);
+    const char *uri = req->path;
+
+    AP_DEBUG_ASSERT(req->authority);
+    if (req->scheme && (ap_cstr_casecmp(req->scheme,
+                        ap_ssl_conn_is_ssl(c->master? c->master : c)? "https" : "http")
+                        || !ap_cstr_casecmp("CONNECT", req->method))) {
+        /* Client sent a non-matching ':scheme' pseudo header or CONNECT.
+         * In this case, we use an absolute URI.
+         */
+        uri = apr_psprintf(r->pool, "%s://%s%s",
+                           req->scheme, req->authority, req->path ? req->path : "");
+    }
+
+    return ap_bucket_request_create(req->method, uri, "HTTP/2.0", headers,
+                                    r->pool, c->bucket_alloc);
+}
+
 request_rec *h2_create_request_rec(const h2_request *req, conn_rec *c)
 {
     int access_status = HTTP_OK;    

Modified: httpd/httpd/trunk/modules/http2/h2_request.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_request.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_request.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_request.h Wed Apr 13 08:38:12 2022
@@ -49,5 +49,7 @@ h2_request *h2_request_clone(apr_pool_t
  */
 request_rec *h2_create_request_rec(const h2_request *req, conn_rec *conn);
 
+apr_bucket *h2_request_create_bucket(const h2_request *req, request_rec *r);
+
 
 #endif /* defined(__mod_h2__h2_request__) */

Modified: httpd/httpd/trunk/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.c Wed Apr 13 08:38:12 2022
@@ -879,7 +879,6 @@ apr_status_t h2_session_create(h2_sessio
     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->in_pending = h2_iq_create(session->pool, (int)session->max_stream_count);
     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);
 
@@ -1635,7 +1634,7 @@ static void on_stream_event(void *ctx, h
     h2_session *session = ctx;
     switch (ev) {
         case H2_SEV_IN_DATA_PENDING:
-            h2_iq_append(session->in_pending, stream->id);
+            session->input_flushed = 1;
             break;
         case H2_SEV_OUT_C1_BLOCK:
             h2_iq_append(session->out_c1_blocked, stream->id);
@@ -1787,10 +1786,9 @@ apr_status_t h2_session_process(h2_sessi
             transit(session, "scheduled stream", H2_SESSION_ST_BUSY);
         }
 
-        if (!h2_iq_empty(session->in_pending)) {
-            h2_mplx_c1_fwd_input(session->mplx, session->in_pending,
-                                 get_stream, session);
+        if (session->input_flushed) {
             transit(session, "forwarded input", H2_SESSION_ST_BUSY);
+            session->input_flushed = 0;
         }
 
         if (!h2_iq_empty(session->out_c1_blocked)) {

Modified: httpd/httpd/trunk/modules/http2/h2_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.h Wed Apr 13 08:38:12 2022
@@ -112,8 +112,8 @@ typedef struct h2_session {
     char status[64];                /* status message for scoreboard */
     int last_status_code;           /* the one already reported */
     const char *last_status_msg;    /* the one already reported */
-    
-    struct h2_iqueue *in_pending;   /* all streams with input pending */
+
+    int input_flushed;              /* stream input was flushed */
     struct h2_iqueue *out_c1_blocked;  /* all streams with output blocked on c1 buffer full */
     struct h2_iqueue *ready_to_process;  /* all streams ready for processing */
 

Modified: httpd/httpd/trunk/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.c Wed Apr 13 08:38:12 2022
@@ -211,6 +211,30 @@ cleanup:
     return APR_SUCCESS;
 }
 
+static apr_status_t input_flush(h2_stream *stream)
+{
+    apr_status_t status = APR_SUCCESS;
+    apr_off_t written;
+
+    if (!stream->in_buffer) goto cleanup;
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c1,
+                  H2_STRM_MSG(stream, "flush input"));
+    if (!stream->input) {
+        h2_stream_setup_input(stream);
+    }
+    status = h2_beam_send(stream->input, stream->session->c1,
+                          stream->in_buffer, APR_BLOCK_READ, &written);
+    stream->in_last_write = apr_time_now();
+    if (APR_SUCCESS != status && stream->state == H2_SS_CLOSED_L) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, stream->session->c1,
+                      H2_STRM_MSG(stream, "send input error"));
+        h2_stream_dispatch(stream, H2_SEV_IN_ERROR);
+    }
+cleanup:
+    return status;
+}
+
 static void input_append_bucket(h2_stream *stream, apr_bucket *b)
 {
     if (!stream->in_buffer) {
@@ -252,11 +276,18 @@ static apr_status_t close_input(h2_strea
     }
 
     stream->input_closed = 1;
-    if (stream->in_buffer || stream->input) {
+    if (stream->in_buffer) {
         b = apr_bucket_eos_create(c->bucket_alloc);
         input_append_bucket(stream, b);
+        input_flush(stream);
         h2_stream_dispatch(stream, H2_SEV_IN_DATA_PENDING);
     }
+    else {
+        rv = h2_mplx_c1_input_closed(stream->session->mplx, stream->id);
+        if (APR_SUCCESS == rv) {
+            h2_stream_dispatch(stream, H2_SEV_IN_DATA_PENDING);
+        }
+    }
 cleanup:
     return rv;
 }
@@ -472,29 +503,6 @@ leave:
     return status;
 }
 
-apr_status_t h2_stream_flush_input(h2_stream *stream)
-{
-    apr_status_t status = APR_SUCCESS;
-    apr_off_t written;
-
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c1,
-                  H2_STRM_MSG(stream, "flush input"));
-    if (stream->in_buffer && !APR_BRIGADE_EMPTY(stream->in_buffer)) {
-        if (!stream->input) {
-            h2_stream_setup_input(stream);
-        }
-        status = h2_beam_send(stream->input, stream->session->c1,
-                              stream->in_buffer, APR_BLOCK_READ, &written);
-        stream->in_last_write = apr_time_now();
-        if (APR_SUCCESS != status && stream->state == H2_SS_CLOSED_L) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, stream->session->c1,
-                          H2_STRM_MSG(stream, "send input error"));
-            h2_stream_dispatch(stream, H2_SEV_IN_ERROR);
-        }
-    }
-    return status;
-}
-
 apr_status_t h2_stream_recv_DATA(h2_stream *stream, uint8_t flags,
                                     const uint8_t *data, size_t len)
 {
@@ -515,6 +523,7 @@ apr_status_t h2_stream_recv_DATA(h2_stre
         }
         stream->in_data_octets += len;
         input_append_data(stream, (const char*)data, len);
+        input_flush(stream);
         h2_stream_dispatch(stream, H2_SEV_IN_DATA_PENDING);
     }
     return status;
@@ -858,6 +867,20 @@ cleanup:
     if (APR_SUCCESS == status) {
         stream->request = req;
         stream->rtmp = NULL;
+
+        if (APLOGctrace4(stream->session->c1)) {
+            int i;
+            const apr_array_header_t *t_h = apr_table_elts(req->headers);
+            const apr_table_entry_t *t_elt = (apr_table_entry_t *)t_h->elts;
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, stream->session->c1,
+                          H2_STRM_MSG(stream,"headers received from client:"));
+            for (i = 0; i < t_h->nelts; i++, t_elt++) {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, stream->session->c1,
+                              H2_STRM_MSG(stream, "  %s: %s"),
+                              ap_escape_logitem(stream->pool, t_elt->key),
+                              ap_escape_logitem(stream->pool, t_elt->val));
+            }
+        }
     }
     return status;
 }
@@ -911,13 +934,18 @@ static apr_status_t buffer_output_receiv
         goto cleanup;
     }
 
-    H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "pre");
-    rv = h2_beam_receive(stream->output, stream->session->c1, stream->out_buffer,
-                         APR_NONBLOCK_READ, stream->session->max_stream_mem - buf_len);
-    if (APR_SUCCESS != rv) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c1,
-                      H2_STRM_MSG(stream, "out_buffer, receive unsuccessful"));
-        goto cleanup;
+    if (stream->output_eos) {
+        rv = APR_BRIGADE_EMPTY(stream->out_buffer)? APR_EOF : APR_SUCCESS;
+    }
+    else {
+        H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "pre");
+        rv = h2_beam_receive(stream->output, stream->session->c1, stream->out_buffer,
+                             APR_NONBLOCK_READ, stream->session->max_stream_mem - buf_len);
+        if (APR_SUCCESS != rv) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c1,
+                          H2_STRM_MSG(stream, "out_buffer, receive unsuccessful"));
+            goto cleanup;
+        }
     }
 
     /* get rid of buckets we have no need for */
@@ -930,6 +958,9 @@ static apr_status_t buffer_output_receiv
                     APR_BUCKET_REMOVE(b);
                     apr_bucket_destroy(b);
                 }
+                else if (APR_BUCKET_IS_EOS(b)) {
+                    stream->output_eos = 1;
+                }
             }
             else if (b->length == 0) {  /* zero length data */
                 APR_BUCKET_REMOVE(b);
@@ -1281,7 +1312,7 @@ apr_status_t h2_stream_in_consumed(h2_st
     return APR_SUCCESS;   
 }
 
-static apr_off_t buffer_output_data_to_send(h2_stream *stream, int *peos)
+static apr_off_t output_data_buffered(h2_stream *stream, int *peos)
 {
     /* How much data do we have in our buffers that we can write? */
     apr_off_t buf_len = 0;
@@ -1380,7 +1411,7 @@ static ssize_t stream_data_cb(nghttp2_se
     }
 
     /* How much data do we have in our buffers that we can write? */
-    buf_len = buffer_output_data_to_send(stream, &eos);
+    buf_len = output_data_buffered(stream, &eos);
     if (buf_len < length && !eos) {
         /* read more? */
         ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c1,
@@ -1388,7 +1419,7 @@ static ssize_t stream_data_cb(nghttp2_se
                       session->id, (int)stream_id, (long)length, (long)buf_len);
         rv = buffer_output_receive(stream);
         /* process all headers sitting at the buffer head. */
-        while (APR_SUCCESS == rv) {
+        while (APR_SUCCESS == rv && !eos && !stream->sent_trailers) {
             rv = buffer_output_process_headers(stream);
             if (APR_SUCCESS != rv && APR_EAGAIN != rv) {
                 ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c1,
@@ -1396,17 +1427,20 @@ static ssize_t stream_data_cb(nghttp2_se
                               "data_cb, error processing headers"));
                 return NGHTTP2_ERR_CALLBACK_FAILURE;
             }
-            buf_len = buffer_output_data_to_send(stream, &eos);
+            buf_len = output_data_buffered(stream, &eos);
         }
 
-        if (APR_EOF == rv) {
-            eos = 1;
-        }
-        else if (APR_SUCCESS != rv && !APR_STATUS_IS_EAGAIN(rv)) {
+        if (APR_SUCCESS != rv && !APR_STATUS_IS_EAGAIN(rv)) {
             ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c1,
                           H2_STRM_LOG(APLOGNO(02938), stream, "data_cb, reading data"));
             return NGHTTP2_ERR_CALLBACK_FAILURE;
         }
+
+        if (stream->sent_trailers) {
+            AP_DEBUG_ASSERT(eos);
+            AP_DEBUG_ASSERT(buf_len == 0);
+            return NGHTTP2_ERR_DEFERRED;
+        }
     }
 
     if (buf_len > (apr_off_t)length) {

Modified: httpd/httpd/trunk/modules/http2/h2_stream.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.h?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.h Wed Apr 13 08:38:12 2022
@@ -17,6 +17,8 @@
 #ifndef __mod_h2__h2_stream__
 #define __mod_h2__h2_stream__
 
+#include <http_protocol.h>
+
 #include "h2.h"
 
 /**
@@ -26,7 +28,7 @@
  * connection to the client. The h2_session writes to the h2_stream,
  * adding HEADERS and DATA and finally an EOS. When headers are done,
  * h2_stream is scheduled for handling, which is expected to produce
- * RESPONSE buclets.
+ * RESPONSE buckets.
  */
 
 struct h2_mplx;
@@ -207,8 +209,6 @@ apr_status_t h2_stream_recv_frame(h2_str
 apr_status_t h2_stream_recv_DATA(h2_stream *stream, uint8_t flags,
                                  const uint8_t *data, size_t len);
 
-apr_status_t h2_stream_flush_input(h2_stream *stream);
-
 /**
  * Reset the stream. Stream write/reads will return errors afterwards.
  *

Modified: httpd/httpd/trunk/modules/http2/h2_switch.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_switch.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_switch.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_switch.c Wed Apr 13 08:38:12 2022
@@ -125,6 +125,28 @@ static int h2_protocol_propose(conn_rec
     return proposed? DECLINED : OK;
 }
 
+static void remove_output_filters_below(ap_filter_t *f, ap_filter_type ftype)
+{
+    ap_filter_t *fnext;
+
+    while (f && f->frec->ftype < ftype) {
+        fnext = f->next;
+        ap_remove_output_filter(f);
+        f = fnext;
+    }
+}
+
+static void remove_input_filters_below(ap_filter_t *f, ap_filter_type ftype)
+{
+    ap_filter_t *fnext;
+
+    while (f && f->frec->ftype < ftype) {
+        fnext = f->next;
+        ap_remove_input_filter(f);
+        f = fnext;
+    }
+}
+
 static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
                               const char *protocol)
 {
@@ -155,11 +177,12 @@ static int h2_protocol_switch(conn_rec *
             /* Switching in the middle of a request means that
              * we have to send out the response to this one in h2
              * format. So we need to take over the connection
-             * right away.
+             * and remove all old filters with type up to the
+             * CONNEDCTION/NETWORK ones.
              */
-            ap_remove_input_filter_byhandle(r->input_filters, "http_in");
-            ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
-            
+            remove_input_filters_below(r->input_filters, AP_FTYPE_CONNECTION);
+            remove_output_filters_below(r->output_filters, AP_FTYPE_CONNECTION);
+
             /* Ok, start an h2_conn on this one. */
             status = h2_c1_setup(c, r, s);
             

Modified: httpd/httpd/trunk/modules/http2/h2_workers.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_workers.c?rev=1899802&r1=1899801&r2=1899802&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_workers.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_workers.c Wed Apr 13 08:38:12 2022
@@ -21,6 +21,7 @@
 
 #include <mpm_common.h>
 #include <httpd.h>
+#include <http_connection.h>
 #include <http_core.h>
 #include <http_log.h>
 #include <http_protocol.h>
@@ -256,8 +257,41 @@ static void* APR_THREAD_FUNC slot_run(ap
     
     /* Get the next c2 from mplx to process. */
     while (get_next(slot)) {
-        ap_assert(slot->connection != NULL);
-        h2_c2_process(slot->connection, thread, slot->id);
+        /* See the discussion at <https://github.com/icing/mod_h2/issues/195>
+         *
+         * Each conn_rec->id is supposed to be unique at a point in time. Since
+         * some modules (and maybe external code) uses this id as an identifier
+         * for the request_rec they handle, it needs to be unique for secondary
+         * connections also.
+         *
+         * The MPM module assigns the connection ids and mod_unique_id is using
+         * that one to generate identifier for requests. While the implementation
+         * works for HTTP/1.x, the parallel execution of several requests per
+         * connection will generate duplicate identifiers on load.
+         *
+         * The original implementation for secondary connection identifiers used
+         * to shift the master connection id up and assign the stream id to the
+         * lower bits. This was cramped on 32 bit systems, but on 64bit there was
+         * enough space.
+         *
+         * As issue 195 showed, mod_unique_id only uses the lower 32 bit of the
+         * connection id, even on 64bit systems. Therefore collisions in request ids.
+         *
+         * The way master connection ids are generated, there is some space "at the
+         * top" of the lower 32 bits on allmost all systems. If you have a setup
+         * with 64k threads per child and 255 child processes, you live on the edge.
+         *
+         * The new implementation shifts 8 bits and XORs in the worker
+         * id. This will experience collisions with > 256 h2 workers and heavy
+         * load still. There seems to be no way to solve this in all possible
+         * configurations by mod_h2 alone.
+         */
+        AP_DEBUG_ASSERT(slot->connection != NULL);
+        slot->connection->id = (slot->connection->master->id << 8)^slot->id;
+        slot->connection->current_thread = thread;
+
+        ap_process_connection(slot->connection, ap_get_conn_socket(slot->connection));
+
         h2_mplx_worker_c2_done(slot->connection);
         slot->connection = NULL;
     }