]> granicus.if.org Git - apache/blobdiff - modules/http2/h2_conn.c
Merge r1818804, r1818951, r1818958, r1818960, r1819027, r1819214, r1820035 from trunk:
[apache] / modules / http2 / h2_conn.c
index 4ddf1b7029eb9766b05217d1250da3c05ad5b8e6..f37d95172740924019a0b78da44b83ca018e3861 100644 (file)
@@ -1,3 +1,19 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +30,7 @@
  */
 
 #include <assert.h>
+#include <apr_strings.h>
 
 #include <ap_mpm.h>
 
@@ -25,6 +42,8 @@
 #include <http_protocol.h>
 #include <http_request.h>
 
+#include <mpm_common.h>
+
 #include "h2_private.h"
 #include "h2.h"
 #include "h2_config.h"
@@ -35,7 +54,6 @@
 #include "h2_stream.h"
 #include "h2_h2.h"
 #include "h2_task.h"
-#include "h2_worker.h"
 #include "h2_workers.h"
 #include "h2_conn.h"
 #include "h2_version.h"
@@ -45,6 +63,7 @@ static struct h2_workers *workers;
 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
 static module *mpm_module;
 static int async_mpm;
+static int mpm_supported = 1;
 static apr_socket_t *dummy_socket;
 
 static void check_modules(int force) 
@@ -74,11 +93,18 @@ static void check_modules(int force)
             else if (!strcmp("prefork.c", m->name)) {
                 mpm_type = H2_MPM_PREFORK;
                 mpm_module = m;
+                /* While http2 can work really well on prefork, it collides
+                 * today's use case for prefork: runnning single-thread app engines
+                 * like php. If we restrict h2_workers to 1 per process, php will
+                 * work fine, but browser will be limited to 1 active request at a
+                 * time. */
+                mpm_supported = 0;
                 break;
             }
             else if (!strcmp("simple_api.c", m->name)) {
                 mpm_type = H2_MPM_SIMPLE;
                 mpm_module = m;
+                mpm_supported = 0;
                 break;
             }
             else if (!strcmp("mpm_winnt.c", m->name)) {
@@ -100,12 +126,11 @@ apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
 {
     const h2_config *config = h2_config_sget(s);
     apr_status_t status = APR_SUCCESS;
-    int minw, maxw, max_tx_handles, n;
+    int minw, maxw;
     int max_threads_per_child = 0;
     int idle_secs = 0;
 
     check_modules(1);
-    
     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
     
     status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
@@ -123,34 +148,18 @@ apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
         minw = max_threads_per_child;
     }
     if (maxw <= 0) {
-        maxw = minw;
+        /* As a default, this seems to work quite well under mpm_event. 
+         * For people enabling http2 under mpm_prefork, start 4 threads unless 
+         * configured otherwise. People get unhappy if their http2 requests are 
+         * blocking each other. */
+        maxw = H2MAX(3 * minw / 2, 4);
     }
     
-    /* How many file handles is it safe to use for transfer
-     * to the master connection to be streamed out? 
-     * Is there a portable APR rlimit on NOFILES? Have not
-     * found it. And if, how many of those would we set aside?
-     * This leads all into a process wide handle allocation strategy
-     * which ultimately would limit the number of accepted connections
-     * with the assumption of implicitly reserving n handles for every 
-     * connection and requiring modules with excessive needs to allocate
-     * from a central pool.
-     */
-    n = h2_config_geti(config, H2_CONF_SESSION_FILES);
-    if (n < 0) {
-        max_tx_handles = maxw * 2;
-    }
-    else {
-        max_tx_handles = maxw * n;
-    }
-    
-    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
-                 "h2_workers: min=%d max=%d, mthrpchild=%d, tx_files=%d", 
-                 minw, maxw, max_threads_per_child, max_tx_handles);
-    workers = h2_workers_create(s, pool, minw, maxw, max_tx_handles);
-    
     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
-    h2_workers_set_max_idle_secs(workers, idle_secs);
+    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
+                 "h2_workers: min=%d max=%d, mthrpchild=%d, idle_secs=%d", 
+                 minw, maxw, max_threads_per_child, idle_secs);
+    workers = h2_workers_create(s, pool, minw, maxw, idle_secs);
  
     ap_register_input_filter("H2_IN", h2_filter_core_input,
                              NULL, AP_FTYPE_CONNECTION);
@@ -171,6 +180,18 @@ h2_mpm_type_t h2_conn_mpm_type(void)
     return mpm_type;
 }
 
+const char *h2_conn_mpm_name(void)
+{
+    check_modules(0);
+    return mpm_module? mpm_module->name : "unknown";
+}
+
+int h2_mpm_supported(void)
+{
+    check_modules(0);
+    return mpm_supported;
+}
+
 static module *h2_conn_mpm_module(void)
 {
     check_modules(0);
@@ -180,6 +201,7 @@ static module *h2_conn_mpm_module(void)
 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
 {
     h2_session *session;
+    apr_status_t status;
     
     if (!workers) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
@@ -188,31 +210,37 @@ apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
     }
     
     if (r) {
-        session = h2_session_rcreate(r, ctx, workers);
+        status = h2_session_rcreate(&session, r, ctx, workers);
     }
     else {
-        session = h2_session_create(c, ctx, workers);
+        status = h2_session_create(&session, c, ctx, workers);
     }
 
-    h2_ctx_session_set(ctx, session);
-    
-    return APR_SUCCESS;
+    if (status == APR_SUCCESS) {
+        h2_ctx_session_set(ctx, session);
+    }
+    return status;
 }
 
 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
 {
     apr_status_t status;
     int mpm_state = 0;
+    h2_session *session = h2_ctx_session_get(ctx);
     
+    ap_assert(session);
     do {
         if (c->cs) {
             c->cs->sense = CONN_SENSE_DEFAULT;
+            c->cs->state = CONN_STATE_HANDLER;
         }
-        status = h2_session_process(h2_ctx_session_get(ctx), async_mpm);
+    
+        status = h2_session_process(session, async_mpm);
         
         if (APR_STATUS_IS_EOF(status)) {
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03045)
-                          "h2_session(%ld): process, closing conn", c->id);
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, 
+                          H2_SSSN_LOG(APLOGNO(03045), session, 
+                          "process, closing conn"));
             c->keepalive = AP_CONN_CLOSE;
         }
         else {
@@ -225,54 +253,66 @@ apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
     } while (!async_mpm
              && c->keepalive == AP_CONN_KEEPALIVE 
              && mpm_state != AP_MPMQ_STOPPING);
-    
-    return DONE;
+
+    if (c->cs) {
+        c->cs->state = CONN_STATE_LINGER;
+    }
+
+    return APR_SUCCESS;
 }
 
 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
 {
-    apr_status_t status;
-    
-    status = h2_session_pre_close(h2_ctx_session_get(ctx), async_mpm);
-    if (status == APR_SUCCESS) {
-        return DONE; /* This is the same, right? */
+    h2_session *session = h2_ctx_session_get(ctx);
+    if (session) {
+        apr_status_t status = h2_session_pre_close(session, async_mpm);
+        return (status == APR_SUCCESS)? DONE : status;
     }
-    return status;
+    return DONE;
 }
 
-conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,
-                          apr_allocator_t *allocator)
+conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
 {
+    apr_allocator_t *allocator;
+    apr_status_t status;
     apr_pool_t *pool;
     conn_rec *c;
     void *cfg;
+    module *mpm;
     
-    AP_DEBUG_ASSERT(master);
+    ap_assert(master);
     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
-                  "h2_conn(%ld): create slave", master->id);
+                  "h2_stream(%ld-%d): create slave", master->id, slave_id);
     
     /* We create a pool with its own allocator to be used for
      * processing a request. This is the only way to have the processing
      * independant of its parent pool in the sense that it can work in
-     * another thread.
+     * another thread. Also, the new allocator needs its own mutex to
+     * synchronize sub-pools.
      */
-    if (!allocator) {
-        apr_allocator_create(&allocator);
+    apr_allocator_create(&allocator);
+    apr_allocator_max_free_set(allocator, ap_max_mem_free);
+    status = apr_pool_create_ex(&pool, parent, NULL, allocator);
+    if (status != APR_SUCCESS) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master, 
+                      APLOGNO(10004) "h2_session(%ld-%d): create slave pool",
+                      master->id, slave_id);
+        return NULL;
     }
-    apr_pool_create_ex(&pool, parent, NULL, allocator);
-    apr_pool_tag(pool, "h2_slave_conn");
     apr_allocator_owner_set(allocator, pool);
-
+    apr_pool_tag(pool, "h2_slave_conn");
     c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
     if (c == NULL) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
-                      APLOGNO(02913) "h2_task: creating conn");
+                      APLOGNO(02913) "h2_session(%ld-%d): create slave",
+                      master->id, slave_id);
+        apr_pool_destroy(pool);
         return NULL;
     }
     
     memcpy(c, master, sizeof(conn_rec));
-           
-    /* Replace these */
+        
     c->master                 = master;
     c->pool                   = pool;   
     c->conn_config            = ap_create_conn_config(pool);
@@ -282,11 +322,15 @@ conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,
     c->bucket_alloc           = apr_bucket_alloc_create(pool);
     c->data_in_input_filters  = 0;
     c->data_in_output_filters = 0;
+    /* prevent mpm_event from making wrong assumptions about this connection,
+     * like e.g. using its socket for an async read check. */
     c->clogging_input_filters = 1;
     c->log                    = NULL;
-    c->log_id                 = NULL;
+    c->log_id                 = apr_psprintf(pool, "%ld-%d", 
+                                             master->id, slave_id);
     /* Simulate that we had already a request on this connection. */
     c->keepalives             = 1;
+    c->aborted                = 0;
     /* We cannot install the master connection socket on the slaves, as
      * modules mess with timeouts/blocking of the socket, with
      * unwanted side effects to the master connection processing.
@@ -299,29 +343,22 @@ conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,
     /* TODO: not all mpm modules have learned about slave connections yet.
      * copy their config from master to slave.
      */
-    if (h2_conn_mpm_module()) {
-        cfg = ap_get_module_config(master->conn_config, h2_conn_mpm_module());
-        ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cfg);
+    if ((mpm = h2_conn_mpm_module()) != NULL) {
+        cfg = ap_get_module_config(master->conn_config, mpm);
+        ap_set_module_config(c->conn_config, mpm, cfg);
     }
 
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
+                  "h2_stream(%ld-%d): created slave", master->id, slave_id);
     return c;
 }
 
-void h2_slave_destroy(conn_rec *slave, apr_allocator_t **pallocator)
+void h2_slave_destroy(conn_rec *slave)
 {
-    apr_pool_t *parent;
-    apr_allocator_t *allocator = apr_pool_allocator_get(slave->pool);
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
-                  "h2_slave_conn(%ld): destroy (task=%s)", slave->id,
+                  "h2_stream(%s): destroy slave", 
                   apr_table_get(slave->notes, H2_TASK_ID_NOTE));
-    /* Attache the allocator to the parent pool and return it for
-     * reuse, otherwise the own is still the slave pool and it will
-     * get destroyed with it. */
-    parent = apr_pool_parent_get(slave->pool);
-    if (pallocator && parent) {
-        apr_allocator_owner_set(allocator, parent);
-        *pallocator = allocator;
-    }
+    slave->sbh = NULL;
     apr_pool_destroy(slave->pool);
 }