]> granicus.if.org Git - apache/blob - modules/http2/h2_conn.c
removed HackMpm and MaxWrite config directives, added dynamic write size behaviour...
[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
18 #include <ap_mpm.h>
19
20 #include <httpd.h>
21 #include <http_core.h>
22 #include <http_config.h>
23 #include <http_log.h>
24 #include <http_connection.h>
25 #include <http_protocol.h>
26 #include <http_request.h>
27
28 #include "h2_private.h"
29 #include "h2_config.h"
30 #include "h2_ctx.h"
31 #include "h2_mplx.h"
32 #include "h2_session.h"
33 #include "h2_stream.h"
34 #include "h2_stream_set.h"
35 #include "h2_task.h"
36 #include "h2_worker.h"
37 #include "h2_workers.h"
38 #include "h2_conn.h"
39
40 static struct h2_workers *workers;
41
42 static apr_status_t h2_session_process(h2_session *session);
43
44 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
45 static module *mpm_module;
46 static module *ssl_module;
47 static int checked;
48
49 static void check_modules() 
50 {
51     int i;
52     if (!checked) {
53         for (i = 0; ap_loaded_modules[i]; ++i) {
54             module *m = ap_loaded_modules[i];
55             if (!strcmp("event.c", m->name)) {
56                 mpm_type = H2_MPM_EVENT;
57                 mpm_module = m;
58             }
59             else if (!strcmp("worker.c", m->name)) {
60                 mpm_type = H2_MPM_WORKER;
61                 mpm_module = m;
62             }
63             else if (!strcmp("prefork.c", m->name)) {
64                 mpm_type = H2_MPM_PREFORK;
65                 mpm_module = m;
66             }
67             else if (!strcmp("mod_ssl.c", m->name)) {
68                 ssl_module = m;
69             }
70         }
71         checked = 1;
72     }
73 }
74
75 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
76 {
77     h2_config *config = h2_config_sget(s);
78     apr_status_t status = APR_SUCCESS;
79     int minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
80     int maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
81     
82     int max_threads_per_child = 0;
83     int threads_limit = 0;
84     int idle_secs = 0;
85     int i;
86
87     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
88     ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &threads_limit);
89     
90     if (minw <= 0) {
91         minw = max_threads_per_child;
92     }
93     if (maxw <= 0) {
94         maxw = threads_limit;
95         if (maxw < minw) {
96             maxw = minw;
97         }
98     }
99     
100     for (i = 0; ap_loaded_modules[i]; ++i) {
101         module *m = ap_loaded_modules[i];
102         if (!strcmp("event.c", m->name)) {
103             mpm_type = H2_MPM_EVENT;
104             mpm_module = m;
105         }
106         else if (!strcmp("worker.c", m->name)) {
107             mpm_type = H2_MPM_WORKER;
108             mpm_module = m;
109         }
110         else if (!strcmp("prefork.c", m->name)) {
111             mpm_type = H2_MPM_PREFORK;
112             mpm_module = m;
113         }
114         else if (!strcmp("mod_ssl.c", m->name)) {
115             ssl_module = m;
116         }
117     }
118     
119     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
120                  "h2_workers: min=%d max=%d, mthrpchild=%d, thr_limit=%d", 
121                  minw, maxw, max_threads_per_child, threads_limit);
122     
123     workers = h2_workers_create(s, pool, minw, maxw);
124     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
125     h2_workers_set_max_idle_secs(workers, idle_secs);
126     
127     return status;
128 }
129
130 h2_mpm_type_t h2_conn_mpm_type(void) {
131     check_modules();
132     return mpm_type;
133 }
134
135 module *h2_conn_mpm_module(void) {
136     check_modules();
137     return mpm_module;
138 }
139
140 apr_status_t h2_conn_rprocess(request_rec *r)
141 {
142     h2_config *config = h2_config_rget(r);
143     h2_session *session;
144     
145     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "h2_conn_process start");
146     if (!workers) {
147         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02911) 
148                       "workers not initialized");
149         return APR_EGENERAL;
150     }
151     
152     session = h2_session_rcreate(r, config, workers);
153     if (!session) {
154         return APR_EGENERAL;
155     }
156     
157     return h2_session_process(session);
158 }
159
160 apr_status_t h2_conn_main(conn_rec *c)
161 {
162     h2_config *config = h2_config_get(c);
163     h2_session *session;
164     
165     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_main start");
166     if (!workers) {
167         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02912) 
168                       "workers not initialized");
169         return APR_EGENERAL;
170     }
171     
172     session = h2_session_create(c, config, workers);
173     if (!session) {
174         return APR_EGENERAL;
175     }
176     
177     return h2_session_process(session);
178 }
179
180 apr_status_t h2_session_process(h2_session *session)
181 {
182     apr_status_t status = APR_SUCCESS;
183     int rv = 0;
184     apr_interval_time_t wait_micros = 0;
185     static const int MAX_WAIT_MICROS = 200 * 1000;
186     
187     /* Start talking to the client. Apart from protocol meta data,
188      * we mainly will see new http/2 streams opened by the client, which
189      * basically are http requests we need to dispatch.
190      *
191      * There will be bursts of new streams, to be served concurrently,
192      * followed by long pauses of no activity.
193      *
194      * Since the purpose of http/2 is to allow siumultaneous streams, we
195      * need to dispatch the handling of each stream into a separate worker
196      * thread, keeping this thread open for sending responses back as
197      * soon as they arrive.
198      * At the same time, we need to continue reading new frames from
199      * our client, which may be meta (WINDOWS_UPDATEs, PING, SETTINGS) or
200      * new streams.
201      *
202      * As long as we have streams open in this session, we cannot really rest
203      * since there are two conditions to wait on: 1. new data from the client,
204      * 2. new data from the open streams to send back.
205      *
206      * Only when we have no more streams open, can we do a blocking read
207      * on our connection.
208      *
209      * TODO: implement graceful GO_AWAY after configurable idle time
210      */
211     
212     ap_update_child_status_from_conn(session->c->sbh, SERVER_BUSY_READ, 
213                                      session->c);
214
215     if (APLOGctrace2(session->c)) {
216         ap_filter_t *filter = session->c->input_filters;
217         while (filter) {
218             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
219                           "h2_conn(%ld), has connection filter %s",
220                           session->id, filter->frec->name);
221             filter = filter->next;
222         }
223     }
224     
225     status = h2_session_start(session, &rv);
226     
227     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
228                   "h2_session(%ld): starting on %s:%d", session->id,
229                   session->c->base_server->defn_name,
230                   session->c->local_addr->port);
231     if (status != APR_SUCCESS) {
232         h2_session_abort(session, status, rv);
233         h2_session_destroy(session);
234         return status;
235     }
236     
237     while (!h2_session_is_done(session)) {
238         int have_written = 0;
239         int have_read = 0;
240         int got_streams;
241         
242         status = h2_session_write(session, wait_micros);
243         if (status == APR_SUCCESS) {
244             have_written = 1;
245             wait_micros = 0;
246         }
247         else if (status == APR_EAGAIN) {
248             /* nop */
249         }
250         else if (status == APR_TIMEUP) {
251             wait_micros *= 2;
252             if (wait_micros > MAX_WAIT_MICROS) {
253                 wait_micros = MAX_WAIT_MICROS;
254             }
255         }
256         else {
257             ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
258                           "h2_session(%ld): writing, terminating",
259                           session->id);
260             h2_session_abort(session, status, 0);
261             break;
262         }
263         
264         /* We would like to do blocking reads as often as possible as they
265          * are more efficient in regard to server resources.
266          * We can do them under the following circumstances:
267          * - we have no open streams and therefore have nothing to write
268          * - we have just started the session and are waiting for the first
269          *   two frames to come in. There will always be at least 2 frames as
270          *   * h2 will send SETTINGS and SETTINGS-ACK
271          *   * h2c will count the header settings as one frame and we
272          *     submit our settings and need the ACK.
273          */
274         got_streams = !h2_stream_set_is_empty(session->streams);
275         status = h2_session_read(session, 
276                                  (!got_streams 
277                                   || session->frames_received <= 1)?
278                                  APR_BLOCK_READ : APR_NONBLOCK_READ);
279         switch (status) {
280             case APR_SUCCESS:
281                 /* successful read, reset our idle timers */
282                 have_read = 1;
283                 wait_micros = 0;
284                 break;
285             case APR_EAGAIN:
286                 break;
287             case APR_EBADF:
288             case APR_EOF:
289             case APR_ECONNABORTED:
290             case ECONNRESET:
291                 ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
292                               "h2_session(%ld): reading",
293                               session->id);
294                 h2_session_abort(session, status, 0);
295                 break;
296             default:
297                 ap_log_cerror( APLOG_MARK, APLOG_WARNING, status, session->c,
298                               APLOGNO(02950) 
299                               "h2_session(%ld): error reading, terminating",
300                               session->id);
301                 h2_session_abort(session, status, 0);
302                 break;
303         }
304         
305         if (!have_read && !have_written
306             && !h2_stream_set_is_empty(session->streams)) {
307             /* Nothing to read or write, we have streams, but
308              * the have no data yet ready to be delivered. Slowly
309              * back off to give others a chance to do their work.
310              */
311             if (wait_micros == 0) {
312                 wait_micros = 10;
313             }
314         }
315     }
316     
317     ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
318                   "h2_session(%ld): done", session->id);
319     
320     ap_update_child_status_from_conn(session->c->sbh, SERVER_CLOSING, 
321                                      session->c);
322
323     h2_session_close(session);
324     h2_session_destroy(session);
325     
326     return DONE;
327 }
328
329
330 static void fix_event_conn(conn_rec *c, conn_rec *master);
331
332 conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool)
333 {
334     apr_socket_t *socket;
335     conn_rec *c;
336     
337     AP_DEBUG_ASSERT(master);
338     
339     /* CAVEAT: it seems necessary to setup the conn_rec in the master
340      * connection thread. Other attempts crashed. 
341      * HOWEVER: we setup the connection using the pools and other items
342      * from the master connection, since we do not want to allocate 
343      * lots of resources here. 
344      * Lets allocated pools and everything else when we actually start
345      * working on this new connection.
346      */
347     /* Not sure about the scoreboard handle. Reusing the one from the main
348      * connection could make sense, is not really correct, but we cannot
349      * easily create new handles for our worker threads either.
350      * TODO
351      */
352     socket = ap_get_module_config(master->conn_config, &core_module);
353     c = ap_run_create_connection(pool, master->base_server,
354                                            socket,
355                                            master->id^((long)pool), 
356                                            master->sbh,
357                                            master->bucket_alloc);
358     if (c == NULL) {
359         ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool, 
360                       APLOGNO(02913) "h2_task: creating conn");
361         return NULL;
362     }
363     /* TODO: we simulate that we had already a request on this connection.
364      * This keeps the mod_ssl SNI vs. Host name matcher from answering 
365      * 400 Bad Request
366      * when names do not match. We prefer a predictable 421 status.
367      */
368     c->keepalives = 1;
369
370     return c;
371 }
372
373 apr_status_t h2_conn_prep(h2_task_env *env, conn_rec *master, h2_worker *worker)
374 {
375     h2_config *cfg = h2_config_get(master);
376     
377     ap_log_perror(APLOG_MARK, APLOG_TRACE3, 0, env->pool,
378                   "h2_conn(%ld): created from master", master->id);
379     
380     /* Ok, we are just about to start processing the connection and
381      * the worker is calling us to setup all necessary resources.
382      * We can borrow some from the worker itself and some we do as
383      * sub-resources from it, so that we get a nice reuse of
384      * pools.
385      */
386     env->c.pool = env->pool;
387     env->c.bucket_alloc = h2_worker_get_bucket_alloc(worker);
388     env->c.current_thread = h2_worker_get_thread(worker);
389     
390     env->c.conn_config = ap_create_conn_config(env->pool);
391     env->c.notes = apr_table_make(env->pool, 5);
392     
393     ap_set_module_config(env->c.conn_config, &core_module, 
394                          h2_worker_get_socket(worker));
395     
396     if (ssl_module) {
397         /* See #19, there is a range of SSL variables to be gotten from
398          * the main connection that should be available in request handlers
399          */
400         void *sslcfg = ap_get_module_config(master->conn_config, ssl_module);
401         if (sslcfg) {
402             ap_set_module_config(env->c.conn_config, ssl_module, sslcfg);
403         }
404     }
405     
406     /* This works for mpm_worker so far. Other mpm modules have 
407      * different needs, unfortunately. The most interesting one 
408      * being mpm_event...
409      */
410     switch (h2_conn_mpm_type()) {
411         case H2_MPM_WORKER:
412             /* all fine */
413             break;
414         case H2_MPM_EVENT: 
415             fix_event_conn(&env->c, master);
416             break;
417         default:
418             /* fingers crossed */
419             break;
420     }
421     
422     return APR_SUCCESS;
423 }
424
425 apr_status_t h2_conn_setup(struct h2_task_env *env, struct h2_worker *worker)
426 {
427     return h2_conn_prep(env, env->mplx->c, worker);
428 }
429
430 apr_status_t h2_conn_init(struct h2_task_env *env, struct h2_worker *worker)
431 {
432     conn_rec *master = env->mplx->c;
433     h2_config *cfg = h2_config_get(master);
434     
435     apr_socket_t *socket = ap_get_module_config(master->conn_config, 
436                                                 &core_module);
437     conn_rec *c = ap_run_create_connection(env->pool, master->base_server,
438                                            socket,
439                                            master->id^((long)env->pool), 
440                                            master->sbh,
441                                            master->bucket_alloc);
442     if (c == NULL) {
443         ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, env->pool,
444                       APLOGNO(02914) "h2_task: creating conn");
445         return APR_ENOMEM;
446     }
447     
448     env->c = *c;
449     env->c.bucket_alloc = h2_worker_get_bucket_alloc(worker);
450     env->c.current_thread = h2_worker_get_thread(worker);
451     
452     ap_set_module_config(env->c.conn_config, &core_module, socket);
453     if (ssl_module) {
454         /* See #19, there is a range of SSL variables to be gotten from
455          * the main connection that should be available in request handlers
456          */
457         void *sslcfg = ap_get_module_config(master->conn_config, ssl_module);
458         if (sslcfg) {
459             ap_set_module_config(env->c.conn_config, ssl_module, sslcfg);
460         }
461     }
462     
463     /* This works for mpm_worker so far. Other mpm modules have 
464      * different needs, unfortunately. The most interesting one 
465      * being mpm_event...
466      */
467     switch (h2_conn_mpm_type()) {
468         case H2_MPM_WORKER:
469             /* all fine */
470             break;
471         case H2_MPM_EVENT: 
472             fix_event_conn(&env->c, master);
473             break;
474         default:
475             /* fingers crossed */
476             break;
477     }
478     
479     return APR_SUCCESS;
480 }
481
482 apr_status_t h2_conn_post(conn_rec *c, h2_worker *worker)
483 {
484     (void)worker;
485     
486     /* be sure no one messes with this any more */
487     memset(c, 0, sizeof(*c)); 
488     return APR_SUCCESS;
489 }
490
491 apr_status_t h2_conn_process(conn_rec *c, apr_socket_t *socket)
492 {
493     AP_DEBUG_ASSERT(c);
494     
495     c->clogging_input_filters = 1;
496     ap_process_connection(c, socket);
497     
498     return APR_SUCCESS;
499 }
500
501 /* This is an internal mpm event.c struct which is disguised
502  * as a conn_state_t so that mpm_event can have special connection
503  * state information without changing the struct seen on the outside.
504  *
505  * For our task connections we need to create a new beast of this type
506  * and fill it with enough meaningful things that mpm_event reads and
507  * starts processing out task request.
508  */
509 typedef struct event_conn_state_t event_conn_state_t;
510 struct event_conn_state_t {
511     /** APR_RING of expiration timeouts */
512     APR_RING_ENTRY(event_conn_state_t) timeout_list;
513     /** the expiration time of the next keepalive timeout */
514     apr_time_t expiration_time;
515     /** connection record this struct refers to */
516     conn_rec *c;
517     /** request record (if any) this struct refers to */
518     request_rec *r;
519     /** is the current conn_rec suspended?  (disassociated with
520      * a particular MPM thread; for suspend_/resume_connection
521      * hooks)
522      */
523     int suspended;
524     /** memory pool to allocate from */
525     apr_pool_t *p;
526     /** bucket allocator */
527     apr_bucket_alloc_t *bucket_alloc;
528     /** poll file descriptor information */
529     apr_pollfd_t pfd;
530     /** public parts of the connection state */
531     conn_state_t pub;
532 };
533 APR_RING_HEAD(timeout_head_t, event_conn_state_t);
534
535 static void fix_event_conn(conn_rec *c, conn_rec *master) 
536 {
537     event_conn_state_t *master_cs = ap_get_module_config(master->conn_config, 
538                                                          h2_conn_mpm_module());
539     event_conn_state_t *cs = apr_pcalloc(c->pool, sizeof(event_conn_state_t));
540     cs->bucket_alloc = apr_bucket_alloc_create(c->pool);
541     
542     ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cs);
543     
544     cs->c = c;
545     cs->r = NULL;
546     cs->p = master_cs->p;
547     cs->pfd = master_cs->pfd;
548     cs->pub = master_cs->pub;
549     cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
550     
551     c->cs = &(cs->pub);
552 }
553