]> granicus.if.org Git - apache/blob - modules/http2/h2_conn.c
0f99076a1c59942ad8d05e1ce686665facaea309
[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_filter.h"
32 #include "h2_mplx.h"
33 #include "h2_session.h"
34 #include "h2_stream.h"
35 #include "h2_stream_set.h"
36 #include "h2_h2.h"
37 #include "h2_task.h"
38 #include "h2_worker.h"
39 #include "h2_workers.h"
40 #include "h2_conn.h"
41
42 static struct h2_workers *workers;
43
44 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
45 static module *mpm_module;
46 static int async_mpm;
47
48 static void check_modules(int force) 
49 {
50     static int checked = 0;
51     int i;
52
53     if (force || !checked) {
54         for (i = 0; ap_loaded_modules[i]; ++i) {
55             module *m = ap_loaded_modules[i];
56             
57             if (!strcmp("event.c", m->name)) {
58                 mpm_type = H2_MPM_EVENT;
59                 mpm_module = m;
60                 break;
61             }
62             else if (!strcmp("worker.c", m->name)) {
63                 mpm_type = H2_MPM_WORKER;
64                 mpm_module = m;
65                 break;
66             }
67             else if (!strcmp("prefork.c", m->name)) {
68                 mpm_type = H2_MPM_PREFORK;
69                 mpm_module = m;
70                 break;
71             }
72         }
73         checked = 1;
74     }
75 }
76
77 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
78 {
79     const h2_config *config = h2_config_sget(s);
80     apr_status_t status = APR_SUCCESS;
81     int minw, maxw, max_tx_handles, n;
82     int max_threads_per_child = 0;
83     int idle_secs = 0;
84
85     check_modules(1);
86     
87     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
88     
89     status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
90     if (status != APR_SUCCESS) {
91         ap_log_error(APLOG_MARK, APLOG_TRACE1, status, s, "querying MPM for async");
92         /* some MPMs do not implemnent this */
93         async_mpm = 0;
94         status = APR_SUCCESS;
95     }
96
97     h2_config_init(pool);
98     
99     minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
100     maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);    
101     if (minw <= 0) {
102         minw = max_threads_per_child;
103     }
104     if (maxw <= 0) {
105         maxw = minw;
106     }
107     
108     /* How many file handles is it safe to use for transfer
109      * to the master connection to be streamed out? 
110      * Is there a portable APR rlimit on NOFILES? Have not
111      * found it. And if, how many of those would we set aside?
112      * This leads all into a process wide handle allocation strategy
113      * which ultimately would limit the number of accepted connections
114      * with the assumption of implicitly reserving n handles for every 
115      * connection and requiring modules with excessive needs to allocate
116      * from a central pool.
117      */
118     n = h2_config_geti(config, H2_CONF_SESSION_FILES);
119     if (n < 0) {
120         max_tx_handles = 256;
121     }
122     else {
123         max_tx_handles = maxw * n;
124     }
125     
126     ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
127                  "h2_workers: min=%d max=%d, mthrpchild=%d, tx_files=%d", 
128                  minw, maxw, max_threads_per_child, max_tx_handles);
129     workers = h2_workers_create(s, pool, minw, maxw, max_tx_handles);
130     
131     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
132     h2_workers_set_max_idle_secs(workers, idle_secs);
133  
134     ap_register_input_filter("H2_IN", h2_filter_core_input,
135                              NULL, AP_FTYPE_CONNECTION);
136    
137     return status;
138 }
139
140 h2_mpm_type_t h2_conn_mpm_type(void)
141 {
142     check_modules(0);
143     return mpm_type;
144 }
145
146 static module *h2_conn_mpm_module(void)
147 {
148     check_modules(0);
149     return mpm_module;
150 }
151
152 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
153 {
154     h2_session *session;
155     
156     if (!workers) {
157         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
158                       "workers not initialized");
159         return APR_EGENERAL;
160     }
161     
162     if (r) {
163         session = h2_session_rcreate(r, ctx, workers);
164     }
165     else {
166         session = h2_session_create(c, ctx, workers);
167     }
168
169     h2_ctx_session_set(ctx, session);
170     
171     ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
172
173     return APR_SUCCESS;
174 }
175
176 static apr_status_t h2_conn_process(h2_ctx *ctx)
177 {
178     h2_session *session;
179     apr_status_t status;
180     
181     session = h2_ctx_session_get(ctx);
182     if (session->c->cs) {
183         session->c->cs->sense = CONN_SENSE_DEFAULT;
184     }
185
186     status = h2_session_process(session, async_mpm);
187
188     session->c->keepalive = AP_CONN_KEEPALIVE;
189     if (session->c->cs) {
190         session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
191     }
192     
193     if (APR_STATUS_IS_EOF(status)
194         || APR_STATUS_IS_ECONNRESET(status) 
195         || APR_STATUS_IS_ECONNABORTED(status)) {
196         /* fatal, probably client just closed connection. emergency shutdown */
197         /* Make sure this connection gets closed properly. */
198         ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
199                       "h2_session(%ld): aborted", session->id);
200         session->c->keepalive = AP_CONN_CLOSE;
201         
202         h2_ctx_clear(session->c);
203         h2_session_abort(session, status);
204         h2_session_eoc_callback(session);
205         /* hereafter session might be gone */
206         return APR_ECONNABORTED;
207     }
208     
209     if (session->state == H2_SESSION_ST_CLOSING) {
210         ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
211                       "h2_session(%ld): closing", session->id);
212         /* Make sure this connection gets closed properly. */
213         session->c->keepalive = AP_CONN_CLOSE;
214         
215         h2_ctx_clear(session->c);
216         h2_session_close(session);
217         /* hereafter session may be gone */
218     }
219     else if (session->state == H2_SESSION_ST_ABORTED) {
220         ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
221                       "h2_session(%ld): already aborted", session->id);
222         return APR_ECONNABORTED;
223     }
224     
225     return APR_SUCCESS;
226 }
227
228 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
229 {
230     int mpm_state = 0;
231     do {
232         h2_conn_process(ctx);
233         
234         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
235             break;
236         }
237     } while (!async_mpm
238              && c->keepalive == AP_CONN_KEEPALIVE 
239              && mpm_state != AP_MPMQ_STOPPING);
240     
241     return DONE;
242 }
243
244
245 static void fix_event_conn(conn_rec *c, conn_rec *master);
246
247 conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *p, 
248                           apr_thread_t *thread, apr_socket_t *socket)
249 {
250     conn_rec *c;
251     
252     AP_DEBUG_ASSERT(master);
253     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
254                   "h2_conn(%ld): created from master", master->id);
255     
256     /* This is like the slave connection creation from 2.5-DEV. A
257      * very efficient way - not sure how compatible this is, since
258      * the core hooks are no longer run.
259      * But maybe it's is better this way, not sure yet.
260      */
261     c = (conn_rec *) apr_palloc(p, sizeof(conn_rec));
262     if (c == NULL) {
263         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
264                       APLOGNO(02913) "h2_task: creating conn");
265         return NULL;
266     }
267     
268     memcpy(c, master, sizeof(conn_rec));
269            
270     /* Replace these */
271     c->id                     = (master->id & (long)p);
272     c->master                 = master;
273     c->pool                   = p;        
274     c->current_thread         = thread;
275     c->conn_config            = ap_create_conn_config(p);
276     c->notes                  = apr_table_make(p, 5);
277     c->input_filters          = NULL;
278     c->output_filters         = NULL;
279     c->bucket_alloc           = apr_bucket_alloc_create(p);
280     c->cs                     = NULL;
281     c->data_in_input_filters  = 0;
282     c->data_in_output_filters = 0;
283     c->clogging_input_filters = 1;
284     c->log                    = NULL;
285     c->log_id                 = NULL;
286     
287     /* TODO: these should be unique to this thread */
288     c->sbh                    = master->sbh;
289     
290     /* Simulate that we had already a request on this connection. */
291     c->keepalives             = 1;
292     
293     ap_set_module_config(c->conn_config, &core_module, socket);
294     
295     /* This works for mpm_worker so far. Other mpm modules have 
296      * different needs, unfortunately. The most interesting one 
297      * being mpm_event...
298      */
299     switch (h2_conn_mpm_type()) {
300         case H2_MPM_WORKER:
301             /* all fine */
302             break;
303         case H2_MPM_EVENT: 
304             fix_event_conn(c, master);
305             break;
306         default:
307             /* fingers crossed */
308             break;
309     }
310     
311     return c;
312 }
313
314 /* This is an internal mpm event.c struct which is disguised
315  * as a conn_state_t so that mpm_event can have special connection
316  * state information without changing the struct seen on the outside.
317  *
318  * For our task connections we need to create a new beast of this type
319  * and fill it with enough meaningful things that mpm_event reads and
320  * starts processing out task request.
321  */
322 typedef struct event_conn_state_t event_conn_state_t;
323 struct event_conn_state_t {
324     /** APR_RING of expiration timeouts */
325     APR_RING_ENTRY(event_conn_state_t) timeout_list;
326     /** the expiration time of the next keepalive timeout */
327     apr_time_t expiration_time;
328     /** connection record this struct refers to */
329     conn_rec *c;
330     /** request record (if any) this struct refers to */
331     request_rec *r;
332     /** is the current conn_rec suspended?  (disassociated with
333      * a particular MPM thread; for suspend_/resume_connection
334      * hooks)
335      */
336     int suspended;
337     /** memory pool to allocate from */
338     apr_pool_t *p;
339     /** bucket allocator */
340     apr_bucket_alloc_t *bucket_alloc;
341     /** poll file descriptor information */
342     apr_pollfd_t pfd;
343     /** public parts of the connection state */
344     conn_state_t pub;
345 };
346 APR_RING_HEAD(timeout_head_t, event_conn_state_t);
347
348 static void fix_event_conn(conn_rec *c, conn_rec *master) 
349 {
350     event_conn_state_t *master_cs = ap_get_module_config(master->conn_config, 
351                                                          h2_conn_mpm_module());
352     event_conn_state_t *cs = apr_pcalloc(c->pool, sizeof(event_conn_state_t));
353     cs->bucket_alloc = apr_bucket_alloc_create(c->pool);
354     
355     ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cs);
356     
357     cs->c = c;
358     cs->r = NULL;
359     cs->p = master_cs->p;
360     cs->pfd = master_cs->pfd;
361     cs->pub = master_cs->pub;
362     cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
363     
364     c->cs = &(cs->pub);
365 }
366