]> granicus.if.org Git - apache/blob - modules/http2/h2_conn.c
mod_http2: enable re-use of slave connections again.
[apache] / modules / http2 / h2_conn.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16  
17 #include <assert.h>
18 #include <apr_strings.h>
19
20 #include <ap_mpm.h>
21 #include <ap_mmn.h>
22
23 #include <httpd.h>
24 #include <http_core.h>
25 #include <http_config.h>
26 #include <http_log.h>
27 #include <http_connection.h>
28 #include <http_protocol.h>
29 #include <http_request.h>
30
31 #include <mpm_common.h>
32
33 #include "h2_private.h"
34 #include "h2.h"
35 #include "h2_config.h"
36 #include "h2_ctx.h"
37 #include "h2_filter.h"
38 #include "h2_mplx.h"
39 #include "h2_session.h"
40 #include "h2_stream.h"
41 #include "h2_h2.h"
42 #include "h2_task.h"
43 #include "h2_workers.h"
44 #include "h2_conn.h"
45 #include "h2_version.h"
46
47 static struct h2_workers *workers;
48
49 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
50 static module *mpm_module;
51 static int async_mpm;
52 static int mpm_supported = 1;
53 static apr_socket_t *dummy_socket;
54
55 static void check_modules(int force) 
56 {
57     static int checked = 0;
58     int i;
59
60     if (force || !checked) {
61         for (i = 0; ap_loaded_modules[i]; ++i) {
62             module *m = ap_loaded_modules[i];
63             
64             if (!strcmp("event.c", m->name)) {
65                 mpm_type = H2_MPM_EVENT;
66                 mpm_module = m;
67                 break;
68             }
69             else if (!strcmp("motorz.c", m->name)) {
70                 mpm_type = H2_MPM_MOTORZ;
71                 mpm_module = m;
72                 break;
73             }
74             else if (!strcmp("mpm_netware.c", m->name)) {
75                 mpm_type = H2_MPM_NETWARE;
76                 mpm_module = m;
77                 break;
78             }
79             else if (!strcmp("prefork.c", m->name)) {
80                 mpm_type = H2_MPM_PREFORK;
81                 mpm_module = m;
82                 /* While http2 can work really well on prefork, it collides
83                  * today's use case for prefork: runnning single-thread app engines
84                  * like php. If we restrict h2_workers to 1 per process, php will
85                  * work fine, but browser will be limited to 1 active request at a
86                  * time. */
87                 mpm_supported = 0;
88                 break;
89             }
90             else if (!strcmp("simple_api.c", m->name)) {
91                 mpm_type = H2_MPM_SIMPLE;
92                 mpm_module = m;
93                 mpm_supported = 0;
94                 break;
95             }
96             else if (!strcmp("mpm_winnt.c", m->name)) {
97                 mpm_type = H2_MPM_WINNT;
98                 mpm_module = m;
99                 break;
100             }
101             else if (!strcmp("worker.c", m->name)) {
102                 mpm_type = H2_MPM_WORKER;
103                 mpm_module = m;
104                 break;
105             }
106         }
107         checked = 1;
108     }
109 }
110
111 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
112 {
113     const h2_config *config = h2_config_sget(s);
114     apr_status_t status = APR_SUCCESS;
115     int minw, maxw;
116     int max_threads_per_child = 0;
117     int idle_secs = 0;
118
119     check_modules(1);
120     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
121     
122     status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
123     if (status != APR_SUCCESS) {
124         /* some MPMs do not implemnent this */
125         async_mpm = 0;
126         status = APR_SUCCESS;
127     }
128
129     h2_config_init(pool);
130     
131     h2_get_num_workers(s, &minw, &maxw);
132     
133     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
134     ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
135                  "h2_workers: min=%d max=%d, mthrpchild=%d, idle_secs=%d", 
136                  minw, maxw, max_threads_per_child, idle_secs);
137     workers = h2_workers_create(s, pool, minw, maxw, idle_secs);
138  
139     ap_register_input_filter("H2_IN", h2_filter_core_input,
140                              NULL, AP_FTYPE_CONNECTION);
141    
142     status = h2_mplx_child_init(pool, s);
143
144     if (status == APR_SUCCESS) {
145         status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
146                                    APR_PROTO_TCP, pool);
147     }
148
149     return status;
150 }
151
152 h2_mpm_type_t h2_conn_mpm_type(void)
153 {
154     check_modules(0);
155     return mpm_type;
156 }
157
158 const char *h2_conn_mpm_name(void)
159 {
160     check_modules(0);
161     return mpm_module? mpm_module->name : "unknown";
162 }
163
164 int h2_mpm_supported(void)
165 {
166     check_modules(0);
167     return mpm_supported;
168 }
169
170 static module *h2_conn_mpm_module(void)
171 {
172     check_modules(0);
173     return mpm_module;
174 }
175
176 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
177 {
178     h2_session *session;
179     apr_status_t status;
180     
181     if (!workers) {
182         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
183                       "workers not initialized");
184         return APR_EGENERAL;
185     }
186     
187     if (r) {
188         status = h2_session_rcreate(&session, r, ctx, workers);
189     }
190     else {
191         status = h2_session_create(&session, c, ctx, workers);
192     }
193
194     if (status == APR_SUCCESS) {
195         h2_ctx_session_set(ctx, session);
196     }
197     return status;
198 }
199
200 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
201 {
202     apr_status_t status;
203     int mpm_state = 0;
204     h2_session *session = h2_ctx_session_get(ctx);
205     
206     ap_assert(session);
207     do {
208         if (c->cs) {
209             c->cs->sense = CONN_SENSE_DEFAULT;
210             c->cs->state = CONN_STATE_HANDLER;
211         }
212     
213         status = h2_session_process(session, async_mpm);
214         
215         if (APR_STATUS_IS_EOF(status)) {
216             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, 
217                           H2_SSSN_LOG(APLOGNO(03045), session, 
218                           "process, closing conn"));
219             c->keepalive = AP_CONN_CLOSE;
220         }
221         else {
222             c->keepalive = AP_CONN_KEEPALIVE;
223         }
224         
225         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
226             break;
227         }
228     } while (!async_mpm
229              && c->keepalive == AP_CONN_KEEPALIVE 
230              && mpm_state != AP_MPMQ_STOPPING);
231
232     if (c->cs) {
233         switch (session->state) {
234             case H2_SESSION_ST_INIT:
235             case H2_SESSION_ST_IDLE:
236             case H2_SESSION_ST_BUSY:
237             case H2_SESSION_ST_WAIT:
238                 c->cs->state = CONN_STATE_WRITE_COMPLETION;
239                 break;
240             case H2_SESSION_ST_CLEANUP:
241             case H2_SESSION_ST_DONE:
242             default:
243                 c->cs->state = CONN_STATE_LINGER;
244             break;
245         }
246     }
247
248     return APR_SUCCESS;
249 }
250
251 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
252 {
253     h2_session *session = h2_ctx_session_get(ctx);
254     
255     (void)c;
256     if (session) {
257         apr_status_t status = h2_session_pre_close(session, async_mpm);
258         return (status == APR_SUCCESS)? DONE : status;
259     }
260     return DONE;
261 }
262
263 /* APR callback invoked if allocation fails. */
264 static int abort_on_oom(int retcode)
265 {
266     ap_abort_on_oom();
267     return retcode; /* unreachable, hopefully. */
268 }
269
270 conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
271 {
272     apr_allocator_t *allocator;
273     apr_status_t status;
274     apr_pool_t *pool;
275     conn_rec *c;
276     void *cfg;
277     module *mpm;
278     
279     ap_assert(master);
280     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
281                   "h2_stream(%ld-%d): create slave", master->id, slave_id);
282     
283     /* We create a pool with its own allocator to be used for
284      * processing a request. This is the only way to have the processing
285      * independant of its parent pool in the sense that it can work in
286      * another thread. Also, the new allocator needs its own mutex to
287      * synchronize sub-pools.
288      */
289     apr_allocator_create(&allocator);
290     apr_allocator_max_free_set(allocator, ap_max_mem_free);
291     status = apr_pool_create_ex(&pool, parent, NULL, allocator);
292     if (status != APR_SUCCESS) {
293         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master, 
294                       APLOGNO(10004) "h2_session(%ld-%d): create slave pool",
295                       master->id, slave_id);
296         return NULL;
297     }
298     apr_allocator_owner_set(allocator, pool);
299     apr_pool_abort_set(abort_on_oom, pool);
300     apr_pool_tag(pool, "h2_slave_conn");
301
302     c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
303     if (c == NULL) {
304         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
305                       APLOGNO(02913) "h2_session(%ld-%d): create slave",
306                       master->id, slave_id);
307         apr_pool_destroy(pool);
308         return NULL;
309     }
310     
311     memcpy(c, master, sizeof(conn_rec));
312         
313     c->master                 = master;
314     c->pool                   = pool;   
315     c->conn_config            = ap_create_conn_config(pool);
316     c->notes                  = apr_table_make(pool, 5);
317     c->input_filters          = NULL;
318     c->output_filters         = NULL;
319 #if AP_MODULE_MAGIC_AT_LEAST(20180903, 1)
320     c->filter_conn_ctx        = NULL;
321 #endif
322     c->bucket_alloc           = apr_bucket_alloc_create(pool);
323 #if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
324     c->data_in_input_filters  = 0;
325     c->data_in_output_filters = 0;
326 #endif
327     /* prevent mpm_event from making wrong assumptions about this connection,
328      * like e.g. using its socket for an async read check. */
329     c->clogging_input_filters = 1;
330     c->log                    = NULL;
331     c->log_id                 = apr_psprintf(pool, "%ld-%d", 
332                                              master->id, slave_id);
333     c->aborted                = 0;
334     /* We cannot install the master connection socket on the slaves, as
335      * modules mess with timeouts/blocking of the socket, with
336      * unwanted side effects to the master connection processing.
337      * Fortunately, since we never use the slave socket, we can just install
338      * a single, process-wide dummy and everyone is happy.
339      */
340     ap_set_module_config(c->conn_config, &core_module, dummy_socket);
341     /* TODO: these should be unique to this thread */
342     c->sbh                    = master->sbh;
343     /* TODO: not all mpm modules have learned about slave connections yet.
344      * copy their config from master to slave.
345      */
346     if ((mpm = h2_conn_mpm_module()) != NULL) {
347         cfg = ap_get_module_config(master->conn_config, mpm);
348         ap_set_module_config(c->conn_config, mpm, cfg);
349     }
350
351     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
352                   "h2_stream(%ld-%d): created slave", master->id, slave_id);
353     return c;
354 }
355
356 void h2_slave_destroy(conn_rec *slave)
357 {
358     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
359                   "h2_stream(%s): destroy slave", 
360                   apr_table_get(slave->notes, H2_TASK_ID_NOTE));
361     slave->sbh = NULL;
362     apr_pool_destroy(slave->pool);
363 }
364
365 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
366 {
367     if (slave->keepalives == 0) {
368         /* Simulate that we had already a request on this connection. Some
369          * hooks trigger special behaviour when keepalives is 0. 
370          * (Not necessarily in pre_connection, but later. Set it here, so it
371          * is in place.) */
372         slave->keepalives = 1;
373     }
374     /* We signal that this connection will be closed after the request.
375      * Which is true in that sense that we throw away all traffic data
376      * on this slave connection after each requests. Although we might
377      * reuse internal structures like memory pools.
378      * The wanted effect of this is that httpd does not try to clean up
379      * any dangling data on this connection when a request is done. Which
380      * is unneccessary on a h2 stream.
381      */
382     slave->keepalive = AP_CONN_CLOSE;
383     return ap_run_pre_connection(slave, csd);
384 }
385