]> granicus.if.org Git - apache/blob - modules/http2/h2_conn.c
*) mod_http2: internal code cleanups and simplifications. Common output code for
[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_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     apr_status_t status = APR_SUCCESS;
113     int minw, maxw;
114     int max_threads_per_child = 0;
115     int idle_secs = 0;
116
117     check_modules(1);
118     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
119     
120     status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
121     if (status != APR_SUCCESS) {
122         /* some MPMs do not implemnent this */
123         async_mpm = 0;
124         status = APR_SUCCESS;
125     }
126
127     h2_config_init(pool);
128     
129     h2_get_num_workers(s, &minw, &maxw);
130     
131     idle_secs = h2_config_sgeti(s, H2_CONF_MAX_WORKER_IDLE_SECS);
132     ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
133                  "h2_workers: min=%d max=%d, mthrpchild=%d, idle_secs=%d", 
134                  minw, maxw, max_threads_per_child, idle_secs);
135     workers = h2_workers_create(s, pool, minw, maxw, idle_secs);
136  
137     ap_register_input_filter("H2_IN", h2_filter_core_input,
138                              NULL, AP_FTYPE_CONNECTION);
139    
140     status = h2_mplx_child_init(pool, s);
141
142     if (status == APR_SUCCESS) {
143         status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
144                                    APR_PROTO_TCP, pool);
145     }
146
147     return status;
148 }
149
150 h2_mpm_type_t h2_conn_mpm_type(void)
151 {
152     check_modules(0);
153     return mpm_type;
154 }
155
156 const char *h2_conn_mpm_name(void)
157 {
158     check_modules(0);
159     return mpm_module? mpm_module->name : "unknown";
160 }
161
162 int h2_mpm_supported(void)
163 {
164     check_modules(0);
165     return mpm_supported;
166 }
167
168 static module *h2_conn_mpm_module(void)
169 {
170     check_modules(0);
171     return mpm_module;
172 }
173
174 apr_status_t h2_conn_setup(conn_rec *c, request_rec *r, server_rec *s)
175 {
176     h2_session *session;
177     h2_ctx *ctx;
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 (APR_SUCCESS == (status = h2_session_create(&session, c, r, s, workers))) {
187         ctx = h2_ctx_get(c, 1);
188         h2_ctx_session_set(ctx, session);
189     }
190     
191     return status;
192 }
193
194 apr_status_t h2_conn_run(conn_rec *c)
195 {
196     apr_status_t status;
197     int mpm_state = 0;
198     h2_session *session = h2_ctx_get_session(c);
199     
200     ap_assert(session);
201     do {
202         if (c->cs) {
203             c->cs->sense = CONN_SENSE_DEFAULT;
204             c->cs->state = CONN_STATE_HANDLER;
205         }
206     
207         status = h2_session_process(session, async_mpm);
208         
209         if (APR_STATUS_IS_EOF(status)) {
210             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, 
211                           H2_SSSN_LOG(APLOGNO(03045), session, 
212                           "process, closing conn"));
213             c->keepalive = AP_CONN_CLOSE;
214         }
215         else {
216             c->keepalive = AP_CONN_KEEPALIVE;
217         }
218         
219         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
220             break;
221         }
222     } while (!async_mpm
223              && c->keepalive == AP_CONN_KEEPALIVE 
224              && mpm_state != AP_MPMQ_STOPPING);
225
226     if (c->cs) {
227         switch (session->state) {
228             case H2_SESSION_ST_INIT:
229             case H2_SESSION_ST_IDLE:
230             case H2_SESSION_ST_BUSY:
231             case H2_SESSION_ST_WAIT:
232                 c->cs->state = CONN_STATE_WRITE_COMPLETION;
233                 break;
234             case H2_SESSION_ST_CLEANUP:
235             case H2_SESSION_ST_DONE:
236             default:
237                 c->cs->state = CONN_STATE_LINGER;
238             break;
239         }
240     }
241
242     return APR_SUCCESS;
243 }
244
245 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
246 {
247     h2_session *session = h2_ctx_get_session(c);
248     
249     (void)c;
250     if (session) {
251         apr_status_t status = h2_session_pre_close(session, async_mpm);
252         return (status == APR_SUCCESS)? DONE : status;
253     }
254     return DONE;
255 }
256
257 /* APR callback invoked if allocation fails. */
258 static int abort_on_oom(int retcode)
259 {
260     ap_abort_on_oom();
261     return retcode; /* unreachable, hopefully. */
262 }
263
264 conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
265 {
266     apr_allocator_t *allocator;
267     apr_status_t status;
268     apr_pool_t *pool;
269     conn_rec *c;
270     void *cfg;
271     module *mpm;
272     
273     ap_assert(master);
274     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
275                   "h2_stream(%ld-%d): create slave", master->id, slave_id);
276     
277     /* We create a pool with its own allocator to be used for
278      * processing a request. This is the only way to have the processing
279      * independant of its parent pool in the sense that it can work in
280      * another thread. Also, the new allocator needs its own mutex to
281      * synchronize sub-pools.
282      */
283     apr_allocator_create(&allocator);
284     apr_allocator_max_free_set(allocator, ap_max_mem_free);
285     status = apr_pool_create_ex(&pool, parent, NULL, allocator);
286     if (status != APR_SUCCESS) {
287         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master, 
288                       APLOGNO(10004) "h2_session(%ld-%d): create slave pool",
289                       master->id, slave_id);
290         return NULL;
291     }
292     apr_allocator_owner_set(allocator, pool);
293     apr_pool_abort_set(abort_on_oom, pool);
294     apr_pool_tag(pool, "h2_slave_conn");
295
296     c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
297     if (c == NULL) {
298         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
299                       APLOGNO(02913) "h2_session(%ld-%d): create slave",
300                       master->id, slave_id);
301         apr_pool_destroy(pool);
302         return NULL;
303     }
304     
305     memcpy(c, master, sizeof(conn_rec));
306         
307     c->master                 = master;
308     c->pool                   = pool;   
309     c->conn_config            = ap_create_conn_config(pool);
310     c->notes                  = apr_table_make(pool, 5);
311     c->input_filters          = NULL;
312     c->output_filters         = NULL;
313     c->keepalives             = 0;
314 #if AP_MODULE_MAGIC_AT_LEAST(20180903, 1)
315     c->filter_conn_ctx        = NULL;
316 #endif
317     c->bucket_alloc           = apr_bucket_alloc_create(pool);
318 #if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
319     c->data_in_input_filters  = 0;
320     c->data_in_output_filters = 0;
321 #endif
322     /* prevent mpm_event from making wrong assumptions about this connection,
323      * like e.g. using its socket for an async read check. */
324     c->clogging_input_filters = 1;
325     c->log                    = NULL;
326     c->log_id                 = apr_psprintf(pool, "%ld-%d", 
327                                              master->id, slave_id);
328     c->aborted                = 0;
329     /* We cannot install the master connection socket on the slaves, as
330      * modules mess with timeouts/blocking of the socket, with
331      * unwanted side effects to the master connection processing.
332      * Fortunately, since we never use the slave socket, we can just install
333      * a single, process-wide dummy and everyone is happy.
334      */
335     ap_set_module_config(c->conn_config, &core_module, dummy_socket);
336     /* TODO: these should be unique to this thread */
337     c->sbh                    = master->sbh;
338     /* TODO: not all mpm modules have learned about slave connections yet.
339      * copy their config from master to slave.
340      */
341     if ((mpm = h2_conn_mpm_module()) != NULL) {
342         cfg = ap_get_module_config(master->conn_config, mpm);
343         ap_set_module_config(c->conn_config, mpm, cfg);
344     }
345
346     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, 
347                   "h2_slave(%s): created", c->log_id);
348     return c;
349 }
350
351 void h2_slave_destroy(conn_rec *slave)
352 {
353     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, slave, "h2_slave(%s): destroy", slave->log_id);
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     ap_assert(slave->output_filters);
378     return APR_SUCCESS;
379 }
380