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