]> granicus.if.org Git - apache/blob - modules/http2/h2_conn.c
On the trunk:
[apache] / modules / http2 / h2_conn.c
1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <assert.h>
17 #include <apr_strings.h>
18
19 #include <ap_mpm.h>
20
21 #include <httpd.h>
22 #include <http_core.h>
23 #include <http_config.h>
24 #include <http_log.h>
25 #include <http_connection.h>
26 #include <http_protocol.h>
27 #include <http_request.h>
28
29 #include <mpm_common.h>
30
31 #include "h2_private.h"
32 #include "h2.h"
33 #include "h2_config.h"
34 #include "h2_ctx.h"
35 #include "h2_filter.h"
36 #include "h2_mplx.h"
37 #include "h2_session.h"
38 #include "h2_stream.h"
39 #include "h2_h2.h"
40 #include "h2_task.h"
41 #include "h2_workers.h"
42 #include "h2_conn.h"
43 #include "h2_version.h"
44
45 static struct h2_workers *workers;
46
47 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
48 static module *mpm_module;
49 static int async_mpm;
50 static apr_socket_t *dummy_socket;
51
52 static void check_modules(int force) 
53 {
54     static int checked = 0;
55     int i;
56
57     if (force || !checked) {
58         for (i = 0; ap_loaded_modules[i]; ++i) {
59             module *m = ap_loaded_modules[i];
60             
61             if (!strcmp("event.c", m->name)) {
62                 mpm_type = H2_MPM_EVENT;
63                 mpm_module = m;
64                 break;
65             }
66             else if (!strcmp("motorz.c", m->name)) {
67                 mpm_type = H2_MPM_MOTORZ;
68                 mpm_module = m;
69                 break;
70             }
71             else if (!strcmp("mpm_netware.c", m->name)) {
72                 mpm_type = H2_MPM_NETWARE;
73                 mpm_module = m;
74                 break;
75             }
76             else if (!strcmp("prefork.c", m->name)) {
77                 mpm_type = H2_MPM_PREFORK;
78                 mpm_module = m;
79                 break;
80             }
81             else if (!strcmp("simple_api.c", m->name)) {
82                 mpm_type = H2_MPM_SIMPLE;
83                 mpm_module = m;
84                 break;
85             }
86             else if (!strcmp("mpm_winnt.c", m->name)) {
87                 mpm_type = H2_MPM_WINNT;
88                 mpm_module = m;
89                 break;
90             }
91             else if (!strcmp("worker.c", m->name)) {
92                 mpm_type = H2_MPM_WORKER;
93                 mpm_module = m;
94                 break;
95             }
96         }
97         checked = 1;
98     }
99 }
100
101 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
102 {
103     const h2_config *config = h2_config_sget(s);
104     apr_status_t status = APR_SUCCESS;
105     int minw, maxw;
106     int max_threads_per_child = 0;
107     int idle_secs = 0;
108
109     check_modules(1);
110     
111     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
112     
113     status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
114     if (status != APR_SUCCESS) {
115         /* some MPMs do not implemnent this */
116         async_mpm = 0;
117         status = APR_SUCCESS;
118     }
119
120     h2_config_init(pool);
121     
122     minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
123     maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);    
124     if (minw <= 0) {
125         minw = max_threads_per_child;
126     }
127     if (maxw <= 0) {
128         maxw = minw;
129     }
130     
131     idle_secs = h2_config_geti(config, 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 static module *h2_conn_mpm_module(void)
157 {
158     check_modules(0);
159     return mpm_module;
160 }
161
162 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
163 {
164     h2_session *session;
165     apr_status_t status;
166     
167     if (!workers) {
168         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
169                       "workers not initialized");
170         return APR_EGENERAL;
171     }
172     
173     if (r) {
174         status = h2_session_rcreate(&session, r, ctx, workers);
175     }
176     else {
177         status = h2_session_create(&session, c, ctx, workers);
178     }
179
180     if (status == APR_SUCCESS) {
181         h2_ctx_session_set(ctx, session);
182     }
183     return status;
184 }
185
186 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
187 {
188     apr_status_t status;
189     int mpm_state = 0;
190     h2_session *session = h2_ctx_session_get(ctx);
191     
192     ap_assert(session);
193     do {
194         if (c->cs) {
195             c->cs->sense = CONN_SENSE_DEFAULT;
196             c->cs->state = CONN_STATE_HANDLER;
197         }
198     
199         status = h2_session_process(session, async_mpm);
200         
201         if (APR_STATUS_IS_EOF(status)) {
202             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, 
203                           H2_SSSN_LOG(APLOGNO(03045), session, 
204                           "process, closing conn"));
205             c->keepalive = AP_CONN_CLOSE;
206         }
207         else {
208             c->keepalive = AP_CONN_KEEPALIVE;
209         }
210         
211         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
212             break;
213         }
214     } while (!async_mpm
215              && c->keepalive == AP_CONN_KEEPALIVE 
216              && mpm_state != AP_MPMQ_STOPPING);
217     
218     if (c->cs) {
219         switch (session->state) {
220             case H2_SESSION_ST_INIT:
221             case H2_SESSION_ST_CLEANUP:
222             case H2_SESSION_ST_DONE:
223             case H2_SESSION_ST_IDLE:
224                 c->cs->state = CONN_STATE_WRITE_COMPLETION;
225                 break;
226             case H2_SESSION_ST_BUSY:
227             case H2_SESSION_ST_WAIT:
228             default:
229                 c->cs->state = CONN_STATE_HANDLER;
230                 break;
231                 
232         }
233     }
234     
235     return DONE;
236 }
237
238 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
239 {
240     h2_session *session = h2_ctx_session_get(ctx);
241     if (session) {
242         apr_status_t status = h2_session_pre_close(session, async_mpm);
243         return (status == APR_SUCCESS)? DONE : status;
244     }
245     return DONE;
246 }
247
248 conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
249 {
250     apr_allocator_t *allocator;
251     apr_status_t status;
252     apr_pool_t *pool;
253     conn_rec *c;
254     void *cfg;
255     module *mpm;
256     
257     ap_assert(master);
258     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
259                   "h2_stream(%ld-%d): create slave", master->id, slave_id);
260     
261     /* We create a pool with its own allocator to be used for
262      * processing a request. This is the only way to have the processing
263      * independant of its parent pool in the sense that it can work in
264      * another thread. Also, the new allocator needs its own mutex to
265      * synchronize sub-pools.
266      */
267     apr_allocator_create(&allocator);
268     apr_allocator_max_free_set(allocator, ap_max_mem_free);
269     status = apr_pool_create_ex(&pool, parent, NULL, allocator);
270     if (status != APR_SUCCESS) {
271         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master, 
272                       APLOGNO(10004) "h2_session(%ld-%d): create slave pool",
273                       master->id, slave_id);
274         return NULL;
275     }
276     apr_allocator_owner_set(allocator, pool);
277     apr_pool_tag(pool, "h2_slave_conn");
278  
279     c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
280     if (c == NULL) {
281         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
282                       APLOGNO(02913) "h2_session(%ld-%d): create slave",
283                       master->id, slave_id);
284         apr_pool_destroy(pool);
285         return NULL;
286     }
287     
288     memcpy(c, master, sizeof(conn_rec));
289         
290     c->master                 = master;
291     c->pool                   = pool;   
292     c->conn_config            = ap_create_conn_config(pool);
293     c->notes                  = apr_table_make(pool, 5);
294     c->input_filters          = NULL;
295     c->output_filters         = NULL;
296     c->bucket_alloc           = apr_bucket_alloc_create(pool);
297     c->data_in_input_filters  = 0;
298     c->data_in_output_filters = 0;
299     /* prevent mpm_event from making wrong assumptions about this connection,
300      * like e.g. using its socket for an async read check. */
301     c->clogging_input_filters = 1;
302     c->log                    = NULL;
303     c->log_id                 = apr_psprintf(pool, "%ld-%d", 
304                                              master->id, slave_id);
305     /* Simulate that we had already a request on this connection. */
306     c->keepalives             = 1;
307     /* We cannot install the master connection socket on the slaves, as
308      * modules mess with timeouts/blocking of the socket, with
309      * unwanted side effects to the master connection processing.
310      * Fortunately, since we never use the slave socket, we can just install
311      * a single, process-wide dummy and everyone is happy.
312      */
313     ap_set_module_config(c->conn_config, &core_module, dummy_socket);
314     /* TODO: these should be unique to this thread */
315     c->sbh                    = master->sbh;
316     /* TODO: not all mpm modules have learned about slave connections yet.
317      * copy their config from master to slave.
318      */
319     if ((mpm = h2_conn_mpm_module()) != NULL) {
320         cfg = ap_get_module_config(master->conn_config, mpm);
321         ap_set_module_config(c->conn_config, mpm, cfg);
322     }
323
324     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
325                   "h2_stream(%ld-%d): created slave", master->id, slave_id);
326     return c;
327 }
328
329 void h2_slave_destroy(conn_rec *slave)
330 {
331     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
332                   "h2_stream(%s): destroy slave", 
333                   apr_table_get(slave->notes, H2_TASK_ID_NOTE));
334     apr_pool_destroy(slave->pool);
335 }
336
337 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
338 {
339     return ap_run_pre_connection(slave, csd);
340 }
341