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 2023/06/01 10:38:53 UTC

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

Author: icing
Date: Thu Jun  1 10:38:53 2023
New Revision: 1910157

URL: http://svn.apache.org/viewvc?rev=1910157&view=rev
Log:
 * mod_proxy_http2: fixed using the wrong "bucket_alloc" from the backend
   connection when sending data on the frontend one. This caused crashes
   or infinite loops in rare situations.
 * mod_proxy_http2: fixed a bug in retry/response handling that could lead
   to wrong status codes or HTTP messages send at the end of response bodies
   exceeding the announced content-length.
 * mod_proxy_http2: fix retry handling to not leak temporary errors.
   On detecting that that an existing connection was shutdown by the other
   side, a 503 response leaked even though the request was retried on a
   fresh connection.
 * mod_http2: fixed a bug that did cleanup of consumed and pending buckets in
   the wrong order when a bucket_beam was destroyed.


Added:
    httpd/httpd/trunk/changes-entries/h2_v2.0.18.txt
Modified:
    httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
    httpd/httpd/trunk/modules/http2/h2_proxy_session.c
    httpd/httpd/trunk/modules/http2/h2_proxy_session.h
    httpd/httpd/trunk/modules/http2/h2_request.c
    httpd/httpd/trunk/modules/http2/h2_stream.c
    httpd/httpd/trunk/modules/http2/h2_version.h
    httpd/httpd/trunk/modules/http2/mod_proxy_http2.c

Added: httpd/httpd/trunk/changes-entries/h2_v2.0.18.txt
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/h2_v2.0.18.txt?rev=1910157&view=auto
==============================================================================
--- httpd/httpd/trunk/changes-entries/h2_v2.0.18.txt (added)
+++ httpd/httpd/trunk/changes-entries/h2_v2.0.18.txt Thu Jun  1 10:38:53 2023
@@ -0,0 +1,13 @@
+ * mod_proxy_http2: fixed using the wrong "bucket_alloc" from the backend
+   connection when sending data on the frontend one. This caused crashes
+   or infinite loops in rare situations.
+ * mod_proxy_http2: fixed a bug in retry/response handling that could lead
+   to wrong status codes or HTTP messages send at the end of response bodies
+   exceeding the announced content-length.
+ * mod_proxy_http2: fix retry handling to not leak temporary errors.
+   On detecting that that an existing connection was shutdown by the other
+   side, a 503 response leaked even though the request was retried on a
+   fresh connection.
+ * mod_http2: fixed a bug that did cleanup of consumed and pending buckets in
+   the wrong order when a bucket_beam was destroyed.
+   [Stefan Eissing]

Modified: httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_bucket_beam.c?rev=1910157&r1=1910156&r2=1910157&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_bucket_beam.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_bucket_beam.c Thu Jun  1 10:38:53 2023
@@ -272,8 +272,8 @@ static void beam_shutdown(h2_bucket_beam
 
     /* shutdown sender (or both)? */
     if (how != APR_SHUTDOWN_READ) {
-        h2_blist_cleanup(&beam->buckets_to_send);
         purge_consumed_buckets(beam);
+        h2_blist_cleanup(&beam->buckets_to_send);
     }
 }
 
@@ -585,6 +585,9 @@ cleanup:
         rv = APR_ECONNABORTED;
     }
     H2_BEAM_LOG(beam, from, APLOG_TRACE2, rv, "end send", sender_bb);
+    if(rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv) && sender_bb != NULL) {
+        apr_brigade_cleanup(sender_bb);
+    }
     apr_thread_mutex_unlock(beam->lock);
     return rv;
 }

Modified: httpd/httpd/trunk/modules/http2/h2_proxy_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_proxy_session.c?rev=1910157&r1=1910156&r2=1910157&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_proxy_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_proxy_session.c Thu Jun  1 10:38:53 2023
@@ -37,6 +37,7 @@ typedef struct h2_proxy_stream {
 
     const char *url;
     request_rec *r;
+    conn_rec *cfront;
     h2_proxy_request *req;
     const char *real_server_uri;
     const char *p_server_uri;
@@ -401,7 +402,7 @@ static apr_status_t h2_proxy_stream_add_
             char *s = apr_pstrndup(stream->r->pool, v, vlen);
             
             apr_table_setn(stream->r->notes, "proxy-status", s);
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c, 
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->cfront,
                           "h2_proxy_stream(%s-%d): got status %s", 
                           stream->session->id, stream->id, s);
             stream->r->status = (int)apr_atoi64(s);
@@ -413,7 +414,7 @@ static apr_status_t h2_proxy_stream_add_
         return APR_SUCCESS;
     }
     
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c, 
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->cfront,
                   "h2_proxy_stream(%s-%d): on_header %s: %s", 
                   stream->session->id, stream->id, n, v);
     if (!h2_proxy_res_ignore_header(n, nlen)) {
@@ -425,7 +426,7 @@ static apr_status_t h2_proxy_stream_add_
         h2_proxy_util_camel_case_header(hname, nlen);
         hvalue = apr_pstrndup(stream->pool, v, vlen);
         
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c, 
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->cfront,
                       "h2_proxy_stream(%s-%d): got header %s: %s", 
                       stream->session->id, stream->id, hname, hvalue);
         process_proxy_header(headers, stream, hname, hvalue);
@@ -532,22 +533,21 @@ static int stream_response_data(nghttp2_
         h2_proxy_stream_end_headers_out(stream);
     }
     stream->data_received += len;
-    
-    b = apr_bucket_transient_create((const char*)data, len, 
-                                    stream->r->connection->bucket_alloc);
+    b = apr_bucket_transient_create((const char*)data, len,
+                                    stream->cfront->bucket_alloc);
     APR_BRIGADE_INSERT_TAIL(stream->output, b);
     /* always flush after a DATA frame, as we have no other indication
      * of buffer use */
-    b = apr_bucket_flush_create(stream->r->connection->bucket_alloc);
+    b = apr_bucket_flush_create(stream->cfront->bucket_alloc);
     APR_BRIGADE_INSERT_TAIL(stream->output, b);
-    
+
     status = ap_pass_brigade(stream->r->output_filters, stream->output);
     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, stream->r, APLOGNO(03359)
                   "h2_proxy_session(%s): stream=%d, response DATA %ld, %ld"
                   " total", session->id, stream_id, (long)len,
                   (long)stream->data_received);
     if (status != APR_SUCCESS) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(03344)
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, stream->r, APLOGNO(03344)
                       "h2_proxy_session(%s): passing output on stream %d", 
                       session->id, stream->id);
         nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE,
@@ -827,12 +827,13 @@ static apr_status_t open_stream(h2_proxy
     stream->pool = r->pool;
     stream->url = url;
     stream->r = r;
+    stream->cfront = r->connection;
     stream->standalone = standalone;
     stream->session = session;
     stream->state = H2_STREAM_ST_IDLE;
     
-    stream->input = apr_brigade_create(stream->pool, session->c->bucket_alloc);
-    stream->output = apr_brigade_create(stream->pool, session->c->bucket_alloc);
+    stream->input = apr_brigade_create(stream->pool, stream->cfront->bucket_alloc);
+    stream->output = apr_brigade_create(stream->pool, stream->cfront->bucket_alloc);
     
     stream->req = h2_proxy_req_create(1, stream->pool);
 
@@ -856,7 +857,7 @@ static apr_status_t open_stream(h2_proxy
             /* port info missing and port is not default for scheme: append */
             authority = apr_psprintf(stream->pool, "%s:%d", authority, puri.port);
         }
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->cfront,
                       "authority=%s from uri.hostname=%s and uri.port=%d",
                       authority, puri.hostname, puri.port);
     }
@@ -943,7 +944,7 @@ static apr_status_t submit_stream(h2_pro
     rv = nghttp2_submit_request(session->ngh2, NULL, 
                                 hd->nv, hd->nvlen, pp, stream);
                                 
-    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03363)
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->cfront, APLOGNO(03363)
                   "h2_proxy_session(%s): submit %s%s -> %d", 
                   session->id, stream->req->authority, stream->req->path,
                   rv);
@@ -1088,7 +1089,7 @@ apr_status_t h2_proxy_session_submit(h2_
 static void stream_resume(h2_proxy_stream *stream)
 {
     h2_proxy_session *session = stream->session;
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, 
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->cfront,
                   "h2_proxy_stream(%s-%d): resuming", 
                   session->id, stream->id);
     stream->suspended = 0;
@@ -1129,7 +1130,7 @@ static apr_status_t check_suspended(h2_p
                 return APR_SUCCESS;
             }
             else if (status != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(status)) {
-                ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, session->c, 
+                ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, stream->cfront,
                               APLOGNO(03382) "h2_proxy_stream(%s-%d): check input", 
                               session->id, stream_id);
                 stream_resume(stream);
@@ -1366,27 +1367,39 @@ static void ev_stream_done(h2_proxy_sess
         /* if the stream's connection is aborted, do not send anything
          * more on it. */
         apr_status_t status = (stream->error_code == 0)? APR_SUCCESS : APR_EINVAL;
-        int touched = (stream->data_sent ||
+        int touched = (stream->data_sent || stream->data_received ||
                        stream_id <= session->last_stream_id);
-        if (!session->c->aborted) {
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03364)
+        if (!stream->cfront->aborted) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->cfront, APLOGNO(03364)
                           "h2_proxy_sesssion(%s): stream(%d) closed "
                           "(touched=%d, error=%d)",
                           session->id, stream_id, touched, stream->error_code);
 
             if (status != APR_SUCCESS) {
-              /* stream failed, error reporting is done by caller
-               * of proxy_session, e.g. mod_proxy_http2 which also
-               * decides about retries. */
+              /* stream failed. If we have received (and forwarded) response
+               * data already, we need to append an error buckt to inform
+               * consumers.
+               * Otherwise, we have an early fail on the connection and may
+               * retry this request on a new one. In that case, keep the
+               * output virgin so that a new attempt can be made. */
+              if (stream->data_received) {
+                int http_status = ap_map_http_request_error(status, HTTP_BAD_REQUEST);
+                b = ap_bucket_error_create(http_status, NULL, stream->r->pool,
+                                           stream->cfront->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(stream->output, b);
+                b = apr_bucket_eos_create(stream->cfront->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(stream->output, b);
+                ap_pass_brigade(stream->r->output_filters, stream->output);
+              }
             }
             else if (!stream->data_received) {
                 /* if the response had no body, this is the time to flush
                  * an empty brigade which will also write the response headers */
                 h2_proxy_stream_end_headers_out(stream);
                 stream->data_received = 1;
-                b = apr_bucket_flush_create(stream->r->connection->bucket_alloc);
+                b = apr_bucket_flush_create(stream->cfront->bucket_alloc);
                 APR_BRIGADE_INSERT_TAIL(stream->output, b);
-                b = apr_bucket_eos_create(stream->r->connection->bucket_alloc);
+                b = apr_bucket_eos_create(stream->cfront->bucket_alloc);
                 APR_BRIGADE_INSERT_TAIL(stream->output, b);
                 ap_pass_brigade(stream->r->output_filters, stream->output);
             }
@@ -1396,7 +1409,7 @@ static void ev_stream_done(h2_proxy_sess
         h2_proxy_ihash_remove(session->streams, stream_id);
         h2_proxy_iq_remove(session->suspended, stream_id);
         if (session->done) {
-            session->done(session, stream->r, status, touched);
+            session->done(session, stream->r, status, touched, stream->error_code);
         }
     }
     
@@ -1666,9 +1679,9 @@ static int done_iter(void *udata, void *
 {
     cleanup_iter_ctx *ctx = udata;
     h2_proxy_stream *stream = val;
-    int touched = (stream->data_sent || 
+    int touched = (stream->data_sent || stream->data_received ||
                    stream->id <= ctx->session->last_stream_id);
-    ctx->done(ctx->session, stream->r, APR_ECONNABORTED, touched);
+    ctx->done(ctx->session, stream->r, APR_ECONNABORTED, touched, stream->error_code);
     return 1;
 }
 
@@ -1687,6 +1700,12 @@ void h2_proxy_session_cleanup(h2_proxy_s
     }
 }
 
+int h2_proxy_session_is_reusable(h2_proxy_session *session)
+{
+    return (session->state != H2_PROXYS_ST_DONE) &&
+           h2_proxy_ihash_empty(session->streams);
+}
+
 static int ping_arrived_iter(void *udata, void *val)
 {
     h2_proxy_stream *stream = val;

Modified: httpd/httpd/trunk/modules/http2/h2_proxy_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_proxy_session.h?rev=1910157&r1=1910156&r2=1910157&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_proxy_session.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_proxy_session.h Thu Jun  1 10:38:53 2023
@@ -68,7 +68,8 @@ typedef enum {
 
 typedef struct h2_proxy_session h2_proxy_session;
 typedef void h2_proxy_request_done(h2_proxy_session *s, request_rec *r,
-                                   apr_status_t status, int touched);
+                                   apr_status_t status, int touched,
+                                   int error_code);
 
 struct h2_proxy_session {
     const char *id;
@@ -130,4 +131,6 @@ void h2_proxy_session_cleanup(h2_proxy_s
 
 #define H2_PROXY_REQ_URL_NOTE   "h2-proxy-req-url"
 
+int h2_proxy_session_is_reusable(h2_proxy_session *s);
+
 #endif /* h2_proxy_session_h */

Modified: httpd/httpd/trunk/modules/http2/h2_request.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_request.c?rev=1910157&r1=1910156&r2=1910157&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_request.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_request.c Thu Jun  1 10:38:53 2023
@@ -375,6 +375,9 @@ request_rec *h2_create_request_rec(const
     }
     else if (req->scheme && ap_cstr_casecmp(req->scheme, "http")
              && ap_cstr_casecmp(req->scheme, "https")) {
+        /* FIXME: we also need to create absolute uris when we are
+         * in a forward proxy configuration! But there is currently
+         * no way to detect that. */
         /* Client sent a ':scheme' pseudo header for something else
          * than what we handle by default. Make an absolute URI. */
         r->the_request = apr_psprintf(r->pool, "%s %s://%s%s HTTP/2.0",

Modified: httpd/httpd/trunk/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.c?rev=1910157&r1=1910156&r2=1910157&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.c Thu Jun  1 10:38:53 2023
@@ -1573,6 +1573,8 @@ static apr_status_t stream_do_response(h
          * denies it, submit resources to push */
         const char *s = apr_table_get(resp->notes, H2_PUSH_MODE_NOTE);
         if (!s || strcmp(s, "0")) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c1,
+                          H2_STRM_MSG(stream, "submit pushes, note=%s"), s);
             h2_stream_submit_pushes(stream, resp);
         }
     }

Modified: httpd/httpd/trunk/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_version.h?rev=1910157&r1=1910156&r2=1910157&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_version.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_version.h Thu Jun  1 10:38:53 2023
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "2.0.15"
+#define MOD_HTTP2_VERSION "2.0.18-git"
 
 /**
  * @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 0x02000f
+#define MOD_HTTP2_VERSION_NUM 0x020012
 
 
 #endif /* mod_h2_h2_version_h */

Modified: httpd/httpd/trunk/modules/http2/mod_proxy_http2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_proxy_http2.c?rev=1910157&r1=1910156&r2=1910157&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_proxy_http2.c (original)
+++ httpd/httpd/trunk/modules/http2/mod_proxy_http2.c Thu Jun  1 10:38:53 2023
@@ -50,8 +50,7 @@ static int (*is_h2)(conn_rec *c);
 
 typedef struct h2_proxy_ctx {
     const char *id;
-    conn_rec *master;
-    conn_rec *owner;
+    conn_rec *cfront;
     apr_pool_t *pool;
     server_rec *server;
     const char *proxy_func;
@@ -69,7 +68,7 @@ typedef struct h2_proxy_ctx {
     apr_status_t r_status;     /* status of request work */
     int r_done;                /* request was processed, not necessarily successfully */
     int r_may_retry;           /* request may be retried */
-    h2_proxy_session *session; /* current http2 session against backend */
+    int has_reusable_session;  /* http2 session is live and clean */
 } h2_proxy_ctx;
 
 static int h2_proxy_post_config(apr_pool_t *p, apr_pool_t *plog,
@@ -160,10 +159,15 @@ static int proxy_http2_canon(request_rec
         }
         else {
             core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
+ #ifdef PROXY_CANONENC_NOENCODEDSLASHENCODING
             int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0;
 
             path = ap_proxy_canonenc_ex(r->pool, url, (int)strlen(url),
                                         enc_path, flags, r->proxyreq);
+#else
+            path = ap_proxy_canonenc(r->pool, url, (int)strlen(url),
+                                     enc_path, 0, r->proxyreq);
+#endif
             if (!path) {
                 return HTTP_BAD_REQUEST;
             }
@@ -227,79 +231,81 @@ static apr_status_t add_request(h2_proxy
 }
 
 static void request_done(h2_proxy_ctx *ctx, request_rec *r,
-                         apr_status_t status, int touched)
+                         apr_status_t status, int touched, int error_code)
 {   
     if (r == ctx->r) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, 
-                      "h2_proxy_session(%s): request done, touched=%d",
-                      ctx->id, touched);
+                      "h2_proxy_session(%s): request done, touched=%d, error=%d",
+                      ctx->id, touched, error_code);
         ctx->r_done = 1;
         if (touched) ctx->r_may_retry = 0;
-        ctx->r_status = ((status == APR_SUCCESS)? APR_SUCCESS
-                         : HTTP_SERVICE_UNAVAILABLE);
+        ctx->r_status = error_code? HTTP_BAD_GATEWAY :
+            ((status == APR_SUCCESS)? OK :
+             ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE));
     }
 }    
 
 static void session_req_done(h2_proxy_session *session, request_rec *r,
-                             apr_status_t status, int touched)
+                             apr_status_t status, int touched, int error_code)
 {
-    request_done(session->user_data, r, status, touched);
+    request_done(session->user_data, r, status, touched, error_code);
 }
 
 static apr_status_t ctx_run(h2_proxy_ctx *ctx) {
     apr_status_t status = OK;
+    h2_proxy_session *session;
     int h2_front;
     
     /* Step Four: Send the Request in a new HTTP/2 stream and
      * loop until we got the response or encounter errors.
      */
-    h2_front = is_h2? is_h2(ctx->owner) : 0;
-    ctx->session = h2_proxy_session_setup(ctx->id, ctx->p_conn, ctx->conf,
-                                          h2_front, 30, 
-                                          h2_proxy_log2((int)ctx->req_buffer_size), 
-                                          session_req_done);
-    if (!ctx->session) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, 
+    ctx->has_reusable_session = 0; /* don't know yet */
+    h2_front = is_h2? is_h2(ctx->cfront) : 0;
+    session = h2_proxy_session_setup(ctx->id, ctx->p_conn, ctx->conf,
+                                     h2_front, 30,
+                                     h2_proxy_log2((int)ctx->req_buffer_size),
+                                     session_req_done);
+    if (!session) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront,
                       APLOGNO(03372) "session unavailable");
         return HTTP_SERVICE_UNAVAILABLE;
     }
     
-    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03373)
-                  "eng(%s): run session %s", ctx->id, ctx->session->id);
-    ctx->session->user_data = ctx;
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(03373)
+                  "eng(%s): run session %s", ctx->id, session->id);
+    session->user_data = ctx;
     
     ctx->r_done = 0;
-    add_request(ctx->session, ctx->r);
+    add_request(session, ctx->r);
     
-    while (!ctx->owner->aborted && !ctx->r_done) {
+    while (!ctx->cfront->aborted && !ctx->r_done) {
     
-        status = h2_proxy_session_process(ctx->session);
+        status = h2_proxy_session_process(session);
         if (status != APR_SUCCESS) {
             /* Encountered an error during session processing */
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront,
                           APLOGNO(03375) "eng(%s): end of session %s", 
-                          ctx->id, ctx->session->id);
+                          ctx->id, session->id);
             /* Any open stream of that session needs to
              * a) be reopened on the new session iff safe to do so
              * b) reported as done (failed) otherwise
              */
-            h2_proxy_session_cleanup(ctx->session, session_req_done);
+            h2_proxy_session_cleanup(session, session_req_done);
             goto out;
         }
     }
     
 out:
-    if (ctx->owner->aborted) {
+    if (ctx->cfront->aborted) {
         /* master connection gone */
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront,
                       APLOGNO(03374) "eng(%s): master connection gone", ctx->id);
         /* cancel all ongoing requests */
-        h2_proxy_session_cancel_all(ctx->session);
-        h2_proxy_session_process(ctx->session);
+        h2_proxy_session_cancel_all(session);
+        h2_proxy_session_process(session);
     }
-    
-    ctx->session->user_data = NULL;
-    ctx->session = NULL;
+    ctx->has_reusable_session = h2_proxy_session_is_reusable(session);
+    session->user_data = NULL;
     return status;
 }
 
@@ -344,9 +350,8 @@ static int proxy_http2_handler(request_r
     }
 
     ctx = apr_pcalloc(r->pool, sizeof(*ctx));
-    ctx->master = r->connection->master? r->connection->master : r->connection;
-    ctx->id = apr_psprintf(r->pool, "%ld", (long)ctx->master->id);
-    ctx->owner = r->connection;
+    ctx->id = apr_psprintf(r->pool, "%ld", (long)r->connection->id);
+    ctx->cfront = r->connection;
     ctx->pool = r->pool;
     ctx->server = r->server;
     ctx->proxy_func = proxy_func;
@@ -359,7 +364,7 @@ static int proxy_http2_handler(request_r
     ctx->r_done = 0;
     ctx->r_may_retry =  1;
     
-    ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, ctx);
+    ap_set_module_config(ctx->cfront->conn_config, &proxy_http2_module, ctx);
 
     /* scheme says, this is for us. */
     apr_table_setn(ctx->r->notes, H2_PROXY_REQ_URL_NOTE, url);
@@ -367,7 +372,7 @@ static int proxy_http2_handler(request_r
                   "H2: serving URL %s", url);
     
 run_connect:    
-    if (ctx->owner->aborted) goto cleanup;
+    if (ctx->cfront->aborted) goto cleanup;
 
     /* Get a proxy_conn_rec from the worker, might be a new one, might
      * be one still open from another request, or it might fail if the
@@ -395,7 +400,7 @@ run_connect:
      * backend->hostname. */
     if (ap_proxy_connect_backend(ctx->proxy_func, ctx->p_conn, ctx->worker, 
                                  ctx->server)) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03352)
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(03352)
                       "H2: failed to make connection to backend: %s",
                       ctx->p_conn->hostname);
         goto cleanup;
@@ -404,7 +409,7 @@ run_connect:
     /* Step Three: Create conn_rec for the socket we have open now. */
     status = ap_proxy_connection_create_ex(ctx->proxy_func, ctx->p_conn, ctx->r);
     if (status != OK) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03353)
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront, APLOGNO(03353)
                       "setup new connection: is_ssl=%d %s %s %s", 
                       ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, 
                       locurl, ctx->p_conn->hostname);
@@ -419,10 +424,10 @@ run_connect:
                        "proxy-request-alpn-protos", "h2");
     }
 
-    if (ctx->owner->aborted) goto cleanup;
+    if (ctx->cfront->aborted) goto cleanup;
     status = ctx_run(ctx);
 
-    if (ctx->r_status != APR_SUCCESS && ctx->r_may_retry && !ctx->owner->aborted) {
+    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;
@@ -436,15 +441,16 @@ run_connect:
         if (reconnects < 2) {
             goto run_connect;
         } 
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(10023)
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(10023)
                       "giving up after %d reconnects, request-done=%d",
                       reconnects, ctx->r_done);
     }
     
 cleanup:
     if (ctx->p_conn) {
-        if (status != APR_SUCCESS) {
-            /* close socket when errors happened or session shut down (EOF) */
+        if (status != APR_SUCCESS || !ctx->has_reusable_session) {
+            /* close socket when errors happened or session is not "clean",
+             * meaning in a working condition with no open streams */
             ctx->p_conn->close = 1;
         }
 #if AP_MODULE_MAGIC_AT_LEAST(20140207, 2)
@@ -454,8 +460,8 @@ cleanup:
         ctx->p_conn = NULL;
     }
 
-    ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, NULL);
-    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
+    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");
     return ctx->r_status;
 }