]> granicus.if.org Git - apache/blob - modules/http2/h2_conn.c
Merge of 1761479,1761548,1762703,1763158 from 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 "h2_private.h"
30 #include "h2.h"
31 #include "h2_config.h"
32 #include "h2_ctx.h"
33 #include "h2_filter.h"
34 #include "h2_mplx.h"
35 #include "h2_session.h"
36 #include "h2_stream.h"
37 #include "h2_h2.h"
38 #include "h2_task.h"
39 #include "h2_worker.h"
40 #include "h2_workers.h"
41 #include "h2_conn.h"
42 #include "h2_version.h"
43
44 static struct h2_workers *workers;
45
46 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
47 static module *mpm_module;
48 static int async_mpm;
49 static apr_socket_t *dummy_socket;
50
51 static void check_modules(int force) 
52 {
53     static int checked = 0;
54     int i;
55
56     if (force || !checked) {
57         for (i = 0; ap_loaded_modules[i]; ++i) {
58             module *m = ap_loaded_modules[i];
59             
60             if (!strcmp("event.c", m->name)) {
61                 mpm_type = H2_MPM_EVENT;
62                 mpm_module = m;
63                 break;
64             }
65             else if (!strcmp("motorz.c", m->name)) {
66                 mpm_type = H2_MPM_MOTORZ;
67                 mpm_module = m;
68                 break;
69             }
70             else if (!strcmp("mpm_netware.c", m->name)) {
71                 mpm_type = H2_MPM_NETWARE;
72                 mpm_module = m;
73                 break;
74             }
75             else if (!strcmp("prefork.c", m->name)) {
76                 mpm_type = H2_MPM_PREFORK;
77                 mpm_module = m;
78                 break;
79             }
80             else if (!strcmp("simple_api.c", m->name)) {
81                 mpm_type = H2_MPM_SIMPLE;
82                 mpm_module = m;
83                 break;
84             }
85             else if (!strcmp("mpm_winnt.c", m->name)) {
86                 mpm_type = H2_MPM_WINNT;
87                 mpm_module = m;
88                 break;
89             }
90             else if (!strcmp("worker.c", m->name)) {
91                 mpm_type = H2_MPM_WORKER;
92                 mpm_module = m;
93                 break;
94             }
95         }
96         checked = 1;
97     }
98 }
99
100 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
101 {
102     const h2_config *config = h2_config_sget(s);
103     apr_status_t status = APR_SUCCESS;
104     int minw, maxw, max_tx_handles, n;
105     int max_threads_per_child = 0;
106     int idle_secs = 0;
107
108     check_modules(1);
109     
110     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
111     
112     status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
113     if (status != APR_SUCCESS) {
114         /* some MPMs do not implemnent this */
115         async_mpm = 0;
116         status = APR_SUCCESS;
117     }
118
119     h2_config_init(pool);
120     
121     minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
122     maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);    
123     if (minw <= 0) {
124         minw = max_threads_per_child;
125     }
126     if (maxw <= 0) {
127         maxw = minw;
128     }
129     
130     /* How many file handles is it safe to use for transfer
131      * to the master connection to be streamed out? 
132      * Is there a portable APR rlimit on NOFILES? Have not
133      * found it. And if, how many of those would we set aside?
134      * This leads all into a process wide handle allocation strategy
135      * which ultimately would limit the number of accepted connections
136      * with the assumption of implicitly reserving n handles for every 
137      * connection and requiring modules with excessive needs to allocate
138      * from a central pool.
139      */
140     n = h2_config_geti(config, H2_CONF_SESSION_FILES);
141     if (n < 0) {
142         max_tx_handles = maxw * 2;
143     }
144     else {
145         max_tx_handles = maxw * n;
146     }
147     
148     ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
149                  "h2_workers: min=%d max=%d, mthrpchild=%d, tx_files=%d", 
150                  minw, maxw, max_threads_per_child, max_tx_handles);
151     workers = h2_workers_create(s, pool, minw, maxw, max_tx_handles);
152     
153     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
154     h2_workers_set_max_idle_secs(workers, idle_secs);
155  
156     ap_register_input_filter("H2_IN", h2_filter_core_input,
157                              NULL, AP_FTYPE_CONNECTION);
158    
159     status = h2_mplx_child_init(pool, s);
160
161     if (status == APR_SUCCESS) {
162         status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
163                                    APR_PROTO_TCP, pool);
164     }
165
166     return status;
167 }
168
169 h2_mpm_type_t h2_conn_mpm_type(void)
170 {
171     check_modules(0);
172     return mpm_type;
173 }
174
175 static module *h2_conn_mpm_module(void)
176 {
177     check_modules(0);
178     return mpm_module;
179 }
180
181 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
182 {
183     h2_session *session;
184     
185     if (!workers) {
186         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
187                       "workers not initialized");
188         return APR_EGENERAL;
189     }
190     
191     if (r) {
192         session = h2_session_rcreate(r, ctx, workers);
193     }
194     else {
195         session = h2_session_create(c, ctx, workers);
196     }
197
198     h2_ctx_session_set(ctx, session);
199     
200     return APR_SUCCESS;
201 }
202
203 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
204 {
205     apr_status_t status;
206     int mpm_state = 0;
207     
208     do {
209         if (c->cs) {
210             c->cs->sense = CONN_SENSE_DEFAULT;
211         }
212         status = h2_session_process(h2_ctx_session_get(ctx), async_mpm);
213         
214         if (APR_STATUS_IS_EOF(status)) {
215             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03045)
216                           "h2_session(%ld): process, closing conn", c->id);
217             c->keepalive = AP_CONN_CLOSE;
218         }
219         else {
220             c->keepalive = AP_CONN_KEEPALIVE;
221         }
222         
223         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
224             break;
225         }
226     } while (!async_mpm
227              && c->keepalive == AP_CONN_KEEPALIVE 
228              && mpm_state != AP_MPMQ_STOPPING);
229     
230     return DONE;
231 }
232
233 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
234 {
235     apr_status_t status;
236     
237     status = h2_session_pre_close(h2_ctx_session_get(ctx), async_mpm);
238     if (status == APR_SUCCESS) {
239         return DONE; /* This is the same, right? */
240     }
241     return status;
242 }
243
244 conn_rec *h2_slave_create(conn_rec *master, apr_uint32_t slave_id, 
245                           apr_pool_t *parent, apr_allocator_t *allocator)
246 {
247     apr_pool_t *pool;
248     conn_rec *c;
249     void *cfg;
250     unsigned long l;
251     
252     AP_DEBUG_ASSERT(master);
253     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
254                   "h2_conn(%ld): create slave", master->id);
255     
256     /* We create a pool with its own allocator to be used for
257      * processing a request. This is the only way to have the processing
258      * independant of its parent pool in the sense that it can work in
259      * another thread.
260      */
261     if (!allocator) {
262         apr_allocator_create(&allocator);
263     }
264     apr_pool_create_ex(&pool, parent, NULL, allocator);
265     apr_pool_tag(pool, "h2_slave_conn");
266     apr_allocator_owner_set(allocator, pool);
267
268     c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
269     if (c == NULL) {
270         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
271                       APLOGNO(02913) "h2_task: creating conn");
272         return NULL;
273     }
274     
275     memcpy(c, master, sizeof(conn_rec));
276         
277     /* Each conn_rec->id is supposed to be unique at a point in time. Since
278      * some modules (and maybe external code) uses this id as an identifier
279      * for the request_rec they handle, it needs to be unique for slave 
280      * connections also.
281      * The connection id is generated by the MPM and most MPMs use the formula
282      *    id := (child_num * max_threads) + thread_num
283      * which means that there is a maximum id of about
284      *    idmax := max_child_count * max_threads
285      * If we assume 2024 child processes with 2048 threads max, we get
286      *    idmax ~= 2024 * 2048 = 2 ** 22
287      * On 32 bit systems, we have not much space left, but on 64 bit systems
288      * (and higher?) we can use the upper 32 bits without fear of collision.
289      * 32 bits is just what we need, since a connection can only handle so
290      * many streams. 
291      */
292     l = master->id;
293     if (sizeof(long) >= 8 && l < APR_UINT32_MAX) {
294         c->id = l|(((unsigned long)slave_id) << 32);
295     }
296     else {
297         c->id = l^(~slave_id);
298     }
299     c->master                 = master;
300     c->pool                   = pool;   
301     c->conn_config            = ap_create_conn_config(pool);
302     c->notes                  = apr_table_make(pool, 5);
303     c->input_filters          = NULL;
304     c->output_filters         = NULL;
305     c->bucket_alloc           = apr_bucket_alloc_create(pool);
306     c->data_in_input_filters  = 0;
307     c->data_in_output_filters = 0;
308     c->clogging_input_filters = 1;
309     c->log                    = NULL;
310     c->log_id                 = apr_psprintf(pool, "%ld-%d", 
311                                              master->id, slave_id);
312     /* Simulate that we had already a request on this connection. */
313     c->keepalives             = 1;
314     /* We cannot install the master connection socket on the slaves, as
315      * modules mess with timeouts/blocking of the socket, with
316      * unwanted side effects to the master connection processing.
317      * Fortunately, since we never use the slave socket, we can just install
318      * a single, process-wide dummy and everyone is happy.
319      */
320     ap_set_module_config(c->conn_config, &core_module, dummy_socket);
321     /* TODO: these should be unique to this thread */
322     c->sbh                    = master->sbh;
323     /* TODO: not all mpm modules have learned about slave connections yet.
324      * copy their config from master to slave.
325      */
326     if (h2_conn_mpm_module()) {
327         cfg = ap_get_module_config(master->conn_config, h2_conn_mpm_module());
328         ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cfg);
329     }
330
331     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
332                   "h2_task: creating conn, master=%ld, sid=%ld, logid=%s", 
333                   master->id, c->id, c->log_id);
334     return c;
335 }
336
337 void h2_slave_destroy(conn_rec *slave, apr_allocator_t **pallocator)
338 {
339     apr_pool_t *parent;
340     apr_allocator_t *allocator = apr_pool_allocator_get(slave->pool);
341     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
342                   "h2_slave_conn(%ld): destroy (task=%s)", slave->id,
343                   apr_table_get(slave->notes, H2_TASK_ID_NOTE));
344     /* Attache the allocator to the parent pool and return it for
345      * reuse, otherwise the own is still the slave pool and it will
346      * get destroyed with it. */
347     parent = apr_pool_parent_get(slave->pool);
348     if (pallocator && parent) {
349         apr_allocator_owner_set(allocator, parent);
350         *pallocator = allocator;
351     }
352     apr_pool_destroy(slave->pool);
353 }
354
355 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
356 {
357     return ap_run_pre_connection(slave, csd);
358 }
359