]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
*) mod_http2:
authorStefan Eissing <icing@apache.org>
Tue, 6 Jul 2021 13:06:00 +0000 (13:06 +0000)
committerStefan Eissing <icing@apache.org>
Tue, 6 Jul 2021 13:06:00 +0000 (13:06 +0000)
   - Aborting requests via RST_STREAM no longer affect the available
     resources of a connection when the first chunk of the response
     body has been sent.
   - H2Min/MaxWorkers behave as intended again. The module will initially
     create H2MinWorkers threads and add up to H2MaxWorkers when needed. These
     additional workers time out when idle after H2MaxWorkerIdleSeconds and
     disappear again.
   - When the shutdown of a child is detected (e.g. graceful shutdown), the
     module will terminate all idle workers above H2MinWorkers right away.
     This detection currently only happens when a HTTP/2 connection is active.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1891312 13f79535-47bb-0310-9956-ffa450edef68

changes-entries/h2_workers_dynamic.txt [new file with mode: 0644]
modules/http2/h2_mplx.c
modules/http2/h2_request.c
modules/http2/h2_session.c
modules/http2/h2_task.c
modules/http2/h2_version.h
modules/http2/h2_workers.c
modules/http2/h2_workers.h
modules/http2/mod_proxy_http2.c

diff --git a/changes-entries/h2_workers_dynamic.txt b/changes-entries/h2_workers_dynamic.txt
new file mode 100644 (file)
index 0000000..1d3115c
--- /dev/null
@@ -0,0 +1,13 @@
+  *) mod_http2:
+   - Aborting requests via RST_STREAM no longer affect the available
+     resources of a connection when the first chunk of the response
+     body has been sent.
+   - H2Min/MaxWorkers behave as intended again. The module will initially
+     create H2MinWorkers threads and add up to H2MaxWorkers when needed. These
+     additional workers time out when idle after H2MaxWorkerIdleSeconds and
+     disappear again.
+   - When the shutdown of a child is detected (e.g. graceful shutdown), the
+     module will terminate all idle workers above H2MinWorkers right away.
+     This detection currently only happens when a HTTP/2 connection is active.
+     [Stefan Eissing]
+
index d787e0d09dcdbcde22e61e84923e378378f45b54..7ab5ec9300906ac5a7bfdff2f38e5746cdf20302 100644 (file)
@@ -739,6 +739,12 @@ static h2_task *s_next_stream_task(h2_mplx *m)
             return stream->task;
         }
     }
+    if (m->tasks_active >= m->limit_active && !h2_iq_empty(m->q)) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
+                      "h2_session(%ld): delaying request processing. "
+                      "Current limit is %d and %d workers are in use.",
+                      m->id, m->limit_active, m->tasks_active);
+    }
     return NULL;
 }
 
@@ -1132,14 +1138,36 @@ int h2_mplx_m_awaits_data(h2_mplx *m)
     return waiting;
 }
 
+static int reset_is_acceptable(h2_stream *stream)
+{
+    /* client may terminate a stream via H2 RST_STREAM message at any time.
+     * This is annyoing when we have committed resources (e.g. worker threads)
+     * to it, so our mood (e.g. willingness to commit resources on this
+     * connection in the future) goes down.
+     *
+     * This is a DoS protection. We do not want to make it too easy for
+     * a client to eat up server resources.
+     *
+     * However: there are cases where a RST_STREAM is the only way to end
+     * a request. This includes websockets and server-side-event streams (SSEs).
+     * The responses to such requests continue forever otherwise.
+     *
+     */
+    if (!stream->task) return 1; /* have not started or already ended for us. acceptable. */
+    if (!(stream->id & 0x01)) return 1; /* stream initiated by us. acceptable. */
+    if (!stream->has_response) return 0; /* no response headers produced yet. bad. */
+    if (!stream->out_data_frames) return 0; /* no response body data sent yet. bad. */
+    return 1; /* otherwise, be forgiving */
+}
+
 apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id)
 {
     h2_stream *stream;
     apr_status_t status = APR_SUCCESS;
-    
+
     H2_MPLX_ENTER_ALWAYS(m);
     stream = h2_ihash_get(m->streams, stream_id);
-    if (stream && stream->task) {
+    if (stream && !reset_is_acceptable(stream)) {
         status = m_be_annoyed(m);
     }
     H2_MPLX_LEAVE(m);
index 5adf84151e9315e33dbdf7907c65fe27ddeb9e2c..7c4fb95ea482ac6871ab8dc52da06a6d2e727be9 100644 (file)
@@ -210,12 +210,74 @@ h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
     return dst;
 }
 
+#if !AP_MODULE_MAGIC_AT_LEAST(20120211, 106)
+static request_rec *my_ap_create_request(conn_rec *c)
+{
+    apr_pool_t *p;
+    request_rec *r;
+
+    apr_pool_create(&p, c->pool);
+    apr_pool_tag(p, "request");
+    r = apr_pcalloc(p, sizeof(request_rec));
+    AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)c);
+    r->pool            = p;
+    r->connection      = c;
+    r->server          = c->base_server;
+
+    r->user            = NULL;
+    r->ap_auth_type    = NULL;
+
+    r->allowed_methods = ap_make_method_list(p, 2);
+
+    r->headers_in      = apr_table_make(r->pool, 5);
+    r->trailers_in     = apr_table_make(r->pool, 5);
+    r->subprocess_env  = apr_table_make(r->pool, 25);
+    r->headers_out     = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->trailers_out    = apr_table_make(r->pool, 5);
+    r->notes           = apr_table_make(r->pool, 5);
+
+    r->request_config  = ap_create_request_config(r->pool);
+    /* Must be set before we run create request hook */
+
+    r->proto_output_filters = c->output_filters;
+    r->output_filters  = r->proto_output_filters;
+    r->proto_input_filters = c->input_filters;
+    r->input_filters   = r->proto_input_filters;
+    ap_run_create_request(r);
+    r->per_dir_config  = r->server->lookup_defaults;
+
+    r->sent_bodyct     = 0;                      /* bytect isn't for body */
+
+    r->read_length     = 0;
+    r->read_body       = REQUEST_NO_BODY;
+
+    r->status          = HTTP_OK;  /* Until further notice */
+    r->header_only     = 0;
+    r->the_request     = NULL;
+
+    /* Begin by presuming any module can make its own path_info assumptions,
+     * until some module interjects and changes the value.
+     */
+    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+
+    r->useragent_addr = c->client_addr;
+    r->useragent_ip = c->client_ip;
+    return r;
+}
+#endif
+
 request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
 {
     int access_status = HTTP_OK;    
 
+#if AP_MODULE_MAGIC_AT_LEAST(20120211, 106)
     request_rec *r = ap_create_request(c);
+#else
+    request_rec *r = my_ap_create_request(c);
+#endif
 
+#if AP_MODULE_MAGIC_AT_LEAST(20120211, 107)
     ap_run_pre_read_request(r, c);
 
     /* Time to populate r with the data we have. */
@@ -244,6 +306,49 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
         r->status = HTTP_OK;
         goto die;
     }
+#else
+    {
+        const char *s;
+
+        r->headers_in = apr_table_clone(r->pool, req->headers);
+        ap_run_pre_read_request(r, c);
+
+        /* Time to populate r with the data we have. */
+        r->request_time = req->request_time;
+        r->method = apr_pstrdup(r->pool, req->method);
+        /* Provide quick information about the request method as soon as known */
+        r->method_number = ap_method_number_of(r->method);
+        if (r->method_number == M_GET && r->method[0] == 'H') {
+            r->header_only = 1;
+        }
+        ap_parse_uri(r, req->path ? req->path : "");
+        r->protocol = (char*)"HTTP/2.0";
+        r->proto_num = HTTP_VERSION(2, 0);
+        r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
+                                      r->method, req->path ? req->path : "");
+
+        /* Start with r->hostname = NULL, ap_check_request_header() will get it
+         * form Host: header, otherwise we get complains about port numbers.
+         */
+        r->hostname = NULL;
+        ap_update_vhost_from_headers(r);
+
+         /* we may have switched to another server */
+         r->per_dir_config = r->server->lookup_defaults;
+
+         s = apr_table_get(r->headers_in, "Expect");
+         if (s && s[0]) {
+            if (ap_cstr_casecmp(s, "100-continue") == 0) {
+                r->expecting_100 = 1;
+            }
+            else {
+                r->status = HTTP_EXPECTATION_FAILED;
+                access_status = r->status;
+                goto die;
+            }
+         }
+    }
+#endif
 
     /* we may have switched to another server */
     r->per_dir_config = r->server->lookup_defaults;
index 191585579259b4ee1c96ac77cce3866f361a241d..4cc2c96be307dd1fd395c15e4ae7083e10e03bc8 100644 (file)
@@ -1908,6 +1908,7 @@ static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char
             break;
         default:
             h2_session_shutdown_notice(session);
+            h2_workers_graceful_shutdown(session->workers);
             break;
     }
 }
index 399c40b60fdb642ec784d2339031bb2fe423bf03..5b32656a912d725657a34b35ce660e48f2170c9a 100644 (file)
@@ -593,7 +593,7 @@ apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread, int worker_id)
          * configurations by mod_h2 alone. 
          */
         task->c->id = (c->master->id << 8)^worker_id;
-        task->id = apr_psprintf(task->pool, "%ld-%d", c->master->id, 
+        task->id = apr_psprintf(task->pool, "%ld-%d", task->mplx->id,
                                 task->stream_id);
     }
         
index c8b110643963e8674c0424b7fca9b9b973f4bf8a..38a219044497774cd1c699092a8ebad953258cd3 100644 (file)
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.15.18"
+#define MOD_HTTP2_VERSION "1.15.21"
 
 /**
  * @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 0x010f12
+#define MOD_HTTP2_VERSION_NUM 0x010f15
 
 
 #endif /* mod_h2_h2_version_h */
index bb64a1a50694ae7fadfc19b88533841daabbbf24..7832ac0e383952ab39761911c307a3bca61f449c 100644 (file)
@@ -41,6 +41,7 @@ struct h2_slot {
     apr_thread_t *thread;
     apr_thread_mutex_t *lock;
     apr_thread_cond_t *not_idle;
+    volatile apr_uint32_t timed_out;
 };
 
 static h2_slot *pop_slot(h2_slot *volatile *phead) 
@@ -71,47 +72,47 @@ static void push_slot(h2_slot *volatile *phead, h2_slot *slot)
 }
 
 static void* APR_THREAD_FUNC slot_run(apr_thread_t *thread, void *wctx);
+static void slot_done(h2_slot *slot);
 
 static apr_status_t activate_slot(h2_workers *workers, h2_slot *slot) 
 {
-    apr_status_t status;
+    apr_status_t rv;
     
     slot->workers = workers;
     slot->task = NULL;
 
+    apr_thread_mutex_lock(workers->lock);
     if (!slot->lock) {
-        status = apr_thread_mutex_create(&slot->lock,
+        rv = apr_thread_mutex_create(&slot->lock,
                                          APR_THREAD_MUTEX_DEFAULT,
                                          workers->pool);
-        if (status != APR_SUCCESS) {
-            push_slot(&workers->free, slot);
-            return status;
-        }
+        if (rv != APR_SUCCESS) goto cleanup;
     }
 
     if (!slot->not_idle) {
-        status = apr_thread_cond_create(&slot->not_idle, workers->pool);
-        if (status != APR_SUCCESS) {
-            push_slot(&workers->free, slot);
-            return status;
-        }
+        rv = apr_thread_cond_create(&slot->not_idle, workers->pool);
+        if (rv != APR_SUCCESS) goto cleanup;
     }
     
-    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, workers->s,
+    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, workers->s,
                  "h2_workers: new thread for slot %d", slot->id); 
 
     /* thread will either immediately start work or add itself
      * to the idle queue */
     apr_atomic_inc32(&workers->worker_count);
-    status = apr_thread_create(&slot->thread, workers->thread_attr,
+    slot->timed_out = 0;
+    rv = apr_thread_create(&slot->thread, workers->thread_attr,
                                slot_run, slot, workers->pool);
-    if (status != APR_SUCCESS) {
+    if (rv != APR_SUCCESS) {
         apr_atomic_dec32(&workers->worker_count);
+    }
+
+cleanup:
+    apr_thread_mutex_unlock(workers->lock);
+    if (rv != APR_SUCCESS) {
         push_slot(&workers->free, slot);
-        return status;
     }
-    
-    return APR_SUCCESS;
+    return rv;
 }
 
 static apr_status_t add_worker(h2_workers *workers)
@@ -127,11 +128,19 @@ static void wake_idle_worker(h2_workers *workers)
 {
     h2_slot *slot = pop_slot(&workers->idle);
     if (slot) {
+        int timed_out = 0;
         apr_thread_mutex_lock(slot->lock);
-        apr_thread_cond_signal(slot->not_idle);
+        timed_out = slot->timed_out;
+        if (!timed_out) {
+            apr_thread_cond_signal(slot->not_idle);
+        }
         apr_thread_mutex_unlock(slot->lock);
+        if (timed_out) {
+            slot_done(slot);
+            wake_idle_worker(workers);
+        }
     }
-    else if (workers->dynamic) {
+    else if (workers->dynamic && !workers->shutdown) {
         add_worker(workers);
     }
 }
@@ -185,13 +194,18 @@ static h2_fifo_op_t mplx_peek(void *head, void *ctx)
 static int get_next(h2_slot *slot)
 {
     h2_workers *workers = slot->workers;
+    int non_essential = slot->id >= workers->min_workers;
+    apr_status_t rv;
 
-    while (!workers->aborted) {
+    while (!workers->aborted && !slot->timed_out) {
         ap_assert(slot->task == NULL);
+        if (non_essential && workers->shutdown) {
+            /* Terminate non-essential worker on shutdown */
+            break;
+        }
         if (h2_fifo_try_peek(workers->mplxs, mplx_peek, slot) == APR_EOF) {
             /* The queue is terminated with the MPM child being cleaned up,
-             * just leave.
-             */
+             * just leave. */
             break;
         }
         if (slot->task) {
@@ -202,8 +216,18 @@ static int get_next(h2_slot *slot)
 
         apr_thread_mutex_lock(slot->lock);
         if (!workers->aborted) {
+
             push_slot(&workers->idle, slot);
-            apr_thread_cond_wait(slot->not_idle, slot->lock);
+            if (non_essential && workers->max_idle_duration) {
+                rv = apr_thread_cond_timedwait(slot->not_idle, slot->lock,
+                                               workers->max_idle_duration);
+                if (APR_TIMEUP == rv) {
+                    slot->timed_out = 1;
+                }
+            }
+            else {
+                apr_thread_cond_wait(slot->not_idle, slot->lock);
+            }
         }
         apr_thread_mutex_unlock(slot->lock);
     }
@@ -251,17 +275,36 @@ static void* APR_THREAD_FUNC slot_run(apr_thread_t *thread, void *wctx)
         } while (slot->task);
     }
 
-    slot_done(slot);
+    if (!slot->timed_out) {
+        slot_done(slot);
+    }
 
     apr_thread_exit(thread, APR_SUCCESS);
     return NULL;
 }
 
-static apr_status_t workers_pool_cleanup(void *data)
+static void wake_non_essential_workers(h2_workers *workers)
 {
-    h2_workers *workers = data;
     h2_slot *slot;
-    
+    /* pop all idle, signal the non essentials and add the others again */
+    if ((slot = pop_slot(&workers->idle))) {
+        wake_non_essential_workers(workers);
+        if (slot->id > workers->min_workers) {
+            apr_thread_mutex_lock(slot->lock);
+            apr_thread_cond_signal(slot->not_idle);
+            apr_thread_mutex_unlock(slot->lock);
+        }
+        else {
+            push_slot(&workers->idle, slot);
+        }
+    }
+}
+
+static void workers_abort_idle(h2_workers *workers)
+{
+    h2_slot *slot;
+
+    workers->shutdown = 1;
     workers->aborted = 1;
     h2_fifo_term(workers->mplxs);
 
@@ -271,6 +314,13 @@ static apr_status_t workers_pool_cleanup(void *data)
         apr_thread_cond_signal(slot->not_idle);
         apr_thread_mutex_unlock(slot->lock);
     }
+}
+
+static apr_status_t workers_pool_cleanup(void *data)
+{
+    h2_workers *workers = data;
+
+    workers_abort_idle(workers);
 
     /* wait for all the workers to become zombies and join them */
     apr_thread_mutex_lock(workers->lock);
@@ -287,7 +337,7 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
                               int min_workers, int max_workers,
                               int idle_secs)
 {
-    apr_status_t status;
+    apr_status_t rv;
     h2_workers *workers;
     apr_pool_t *pool;
     int i, n;
@@ -311,7 +361,7 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
     workers->pool = pool;
     workers->min_workers = min_workers;
     workers->max_workers = max_workers;
-    workers->max_idle_secs = (idle_secs > 0)? idle_secs : 10;
+    workers->max_idle_duration = apr_time_from_sec((idle_secs > 0)? idle_secs : 10);
 
     /* FIXME: the fifo set we use here has limited capacity. Once the
      * set is full, connections with new requests do a wait. Unfortunately,
@@ -324,16 +374,12 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
      * For now, we just make enough room to have many connections inside one
      * process.
      */
-    status = h2_fifo_set_create(&workers->mplxs, pool, 8 * 1024);
-    if (status != APR_SUCCESS) {
-        return NULL;
-    }
-    
-    status = apr_threadattr_create(&workers->thread_attr, workers->pool);
-    if (status != APR_SUCCESS) {
-        return NULL;
-    }
-    
+    rv = h2_fifo_set_create(&workers->mplxs, pool, 8 * 1024);
+    if (rv != APR_SUCCESS) goto cleanup;
+
+    rv = apr_threadattr_create(&workers->thread_attr, workers->pool);
+    if (rv != APR_SUCCESS) goto cleanup;
+
     if (ap_thread_stacksize != 0) {
         apr_threadattr_stacksize_set(workers->thread_attr,
                                      ap_thread_stacksize);
@@ -342,38 +388,39 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
                      (long)ap_thread_stacksize);
     }
     
-    status = apr_thread_mutex_create(&workers->lock,
-                                     APR_THREAD_MUTEX_DEFAULT,
-                                     workers->pool);
-    if (status == APR_SUCCESS) {        
-        status = apr_thread_cond_create(&workers->all_done, workers->pool);
+    rv = apr_thread_mutex_create(&workers->lock,
+                                 APR_THREAD_MUTEX_DEFAULT,
+                                 workers->pool);
+    if (rv != APR_SUCCESS) goto cleanup;
+    rv = apr_thread_cond_create(&workers->all_done, workers->pool);
+    if (rv != APR_SUCCESS) goto cleanup;
+
+    n = workers->nslots = workers->max_workers;
+    workers->slots = apr_pcalloc(workers->pool, n * sizeof(h2_slot));
+    if (workers->slots == NULL) {
+        n = workers->nslots = 0;
+        rv = APR_ENOMEM;
+        goto cleanup;
     }
-    if (status == APR_SUCCESS) {        
-        n = workers->nslots = workers->max_workers;
-        workers->slots = apr_pcalloc(workers->pool, n * sizeof(h2_slot));
-        if (workers->slots == NULL) {
-            n = workers->nslots = 0;
-            status = APR_ENOMEM;
-        }
-        for (i = 0; i < n; ++i) {
-            workers->slots[i].id = i;
-        }
+    for (i = 0; i < n; ++i) {
+        workers->slots[i].id = i;
     }
-    if (status == APR_SUCCESS) {
-        /* we activate all for now, TODO: support min_workers again.
-         * do this in reverse for vanity reasons so slot 0 will most
-         * likely be at head of idle queue. */
-        n = workers->max_workers;
-        for (i = n-1; i >= 0; --i) {
-            status = activate_slot(workers, &workers->slots[i]);
-        }
-        /* the rest of the slots go on the free list */
-        for(i = n; i < workers->nslots; ++i) {
-            push_slot(&workers->free, &workers->slots[i]);
-        }
-        workers->dynamic = (workers->worker_count < workers->max_workers);
+    /* we activate all for now, TODO: support min_workers again.
+     * do this in reverse for vanity reasons so slot 0 will most
+     * likely be at head of idle queue. */
+    n = workers->min_workers;
+    for (i = n-1; i >= 0; --i) {
+        rv = activate_slot(workers, &workers->slots[i]);
+        if (rv != APR_SUCCESS) goto cleanup;
+    }
+    /* the rest of the slots go on the free list */
+    for(i = n; i < workers->nslots; ++i) {
+        push_slot(&workers->free, &workers->slots[i]);
     }
-    if (status == APR_SUCCESS) {
+    workers->dynamic = (workers->worker_count < workers->max_workers);
+
+cleanup:
+    if (rv == APR_SUCCESS) {
         /* Stop/join the workers threads when the MPM child exits (pchild is
          * destroyed), and as a pre_cleanup of pchild thus before the threads
          * pools (children of workers->pool) so that they are not destroyed
@@ -396,3 +443,10 @@ apr_status_t h2_workers_unregister(h2_workers *workers, struct h2_mplx *m)
 {
     return h2_fifo_remove(workers->mplxs, m);
 }
+
+void h2_workers_graceful_shutdown(h2_workers *workers)
+{
+    workers->shutdown = 1;
+    h2_fifo_term(workers->mplxs);
+    wake_non_essential_workers(workers);
+}
index 6298c813970e3548f8c8b218e0c87b6ba014c790..cc310c9b75b8d4678d4f16d7b48cce61c96f56b7 100644 (file)
@@ -40,9 +40,10 @@ struct h2_workers {
     int next_worker_id;
     apr_uint32_t min_workers;
     apr_uint32_t max_workers;
-    int max_idle_secs;
+    apr_interval_time_t max_idle_duration;
     
     volatile int aborted;
+    volatile int shutdown;
     int dynamic;
 
     apr_threadattr_t *thread_attr;
@@ -80,4 +81,9 @@ apr_status_t h2_workers_register(h2_workers *workers, struct h2_mplx *m);
  */
 apr_status_t h2_workers_unregister(h2_workers *workers, struct h2_mplx *m);
 
+/**
+ *  Shut down processing gracefully by terminating all idle workers.
+ */
+void h2_workers_graceful_shutdown(h2_workers *workers);
+
 #endif /* defined(__mod_h2__h2_workers__) */
index 893aa8fd316773e51099a27f9a38fc7b24e9aad2..4ea4fb97413d0074cb1901ea879fa726dbcfeeff 100644 (file)
@@ -397,18 +397,9 @@ run_connect:
     
     if (!ctx->p_conn->data && ctx->is_ssl) {
         /* New SSL connection: set a note on the connection about what
-         * protocol we want.
-         */
+         * protocol we need. */
         apr_table_setn(ctx->p_conn->connection->notes,
                        "proxy-request-alpn-protos", "h2");
-        if (ctx->p_conn->ssl_hostname) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner,
-                          "set SNI to %s for (%s)",
-                          ctx->p_conn->ssl_hostname,
-                          ctx->p_conn->hostname);
-            apr_table_setn(ctx->p_conn->connection->notes,
-                           "proxy-request-hostname", ctx->p_conn->ssl_hostname);
-        }
     }
 
     if (ctx->master->aborted) goto cleanup;