1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <apr_strings.h>
22 #include <http_core.h>
23 #include <http_config.h>
25 #include <http_connection.h>
26 #include <http_protocol.h>
27 #include <http_request.h>
29 #include "h2_private.h"
31 #include "h2_config.h"
33 #include "h2_filter.h"
35 #include "h2_session.h"
36 #include "h2_stream.h"
39 #include "h2_worker.h"
40 #include "h2_workers.h"
42 #include "h2_version.h"
44 static struct h2_workers *workers;
46 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
47 static module *mpm_module;
49 static apr_socket_t *dummy_socket;
51 static void check_modules(int force)
53 static int checked = 0;
56 if (force || !checked) {
57 for (i = 0; ap_loaded_modules[i]; ++i) {
58 module *m = ap_loaded_modules[i];
60 if (!strcmp("event.c", m->name)) {
61 mpm_type = H2_MPM_EVENT;
65 else if (!strcmp("motorz.c", m->name)) {
66 mpm_type = H2_MPM_MOTORZ;
70 else if (!strcmp("mpm_netware.c", m->name)) {
71 mpm_type = H2_MPM_NETWARE;
75 else if (!strcmp("prefork.c", m->name)) {
76 mpm_type = H2_MPM_PREFORK;
80 else if (!strcmp("simple_api.c", m->name)) {
81 mpm_type = H2_MPM_SIMPLE;
85 else if (!strcmp("mpm_winnt.c", m->name)) {
86 mpm_type = H2_MPM_WINNT;
90 else if (!strcmp("worker.c", m->name)) {
91 mpm_type = H2_MPM_WORKER;
100 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
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;
110 ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
112 status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
113 if (status != APR_SUCCESS) {
114 /* some MPMs do not implemnent this */
116 status = APR_SUCCESS;
119 h2_config_init(pool);
121 minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
122 maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
124 minw = max_threads_per_child;
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.
140 n = h2_config_geti(config, H2_CONF_SESSION_FILES);
142 max_tx_handles = maxw * 2;
145 max_tx_handles = maxw * n;
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);
153 idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
154 h2_workers_set_max_idle_secs(workers, idle_secs);
156 ap_register_input_filter("H2_IN", h2_filter_core_input,
157 NULL, AP_FTYPE_CONNECTION);
159 status = h2_mplx_child_init(pool, s);
161 if (status == APR_SUCCESS) {
162 status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
163 APR_PROTO_TCP, pool);
169 h2_mpm_type_t h2_conn_mpm_type(void)
175 static module *h2_conn_mpm_module(void)
181 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
186 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911)
187 "workers not initialized");
192 session = h2_session_rcreate(r, ctx, workers);
195 session = h2_session_create(c, ctx, workers);
198 h2_ctx_session_set(ctx, session);
203 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
210 c->cs->sense = CONN_SENSE_DEFAULT;
212 status = h2_session_process(h2_ctx_session_get(ctx), async_mpm);
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;
220 c->keepalive = AP_CONN_KEEPALIVE;
223 if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
227 && c->keepalive == AP_CONN_KEEPALIVE
228 && mpm_state != AP_MPMQ_STOPPING);
233 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
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? */
244 conn_rec *h2_slave_create(conn_rec *master, apr_uint32_t slave_id,
245 apr_pool_t *parent, apr_allocator_t *allocator)
252 AP_DEBUG_ASSERT(master);
253 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
254 "h2_conn(%ld): create slave", master->id);
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
262 apr_allocator_create(&allocator);
264 apr_pool_create_ex(&pool, parent, NULL, allocator);
265 apr_pool_tag(pool, "h2_slave_conn");
266 apr_allocator_owner_set(allocator, pool);
268 c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
270 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master,
271 APLOGNO(02913) "h2_task: creating conn");
275 memcpy(c, master, sizeof(conn_rec));
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
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
293 if (sizeof(long) >= 8 && l < APR_UINT32_MAX) {
294 c->id = l|(((unsigned long)slave_id) << 32);
297 c->id = l^(~slave_id);
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;
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. */
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.
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.
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);
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);
337 void h2_slave_destroy(conn_rec *slave, apr_allocator_t **pallocator)
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;
352 apr_pool_destroy(slave->pool);
355 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
357 return ap_run_pre_connection(slave, csd);