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 <mpm_common.h>
31 #include "h2_private.h"
33 #include "h2_config.h"
35 #include "h2_filter.h"
37 #include "h2_session.h"
38 #include "h2_stream.h"
41 #include "h2_worker.h"
42 #include "h2_workers.h"
44 #include "h2_version.h"
46 static struct h2_workers *workers;
48 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
49 static module *mpm_module;
51 static apr_socket_t *dummy_socket;
53 static void check_modules(int force)
55 static int checked = 0;
58 if (force || !checked) {
59 for (i = 0; ap_loaded_modules[i]; ++i) {
60 module *m = ap_loaded_modules[i];
62 if (!strcmp("event.c", m->name)) {
63 mpm_type = H2_MPM_EVENT;
67 else if (!strcmp("motorz.c", m->name)) {
68 mpm_type = H2_MPM_MOTORZ;
72 else if (!strcmp("mpm_netware.c", m->name)) {
73 mpm_type = H2_MPM_NETWARE;
77 else if (!strcmp("prefork.c", m->name)) {
78 mpm_type = H2_MPM_PREFORK;
82 else if (!strcmp("simple_api.c", m->name)) {
83 mpm_type = H2_MPM_SIMPLE;
87 else if (!strcmp("mpm_winnt.c", m->name)) {
88 mpm_type = H2_MPM_WINNT;
92 else if (!strcmp("worker.c", m->name)) {
93 mpm_type = H2_MPM_WORKER;
102 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
104 const h2_config *config = h2_config_sget(s);
105 apr_status_t status = APR_SUCCESS;
106 int minw, maxw, max_tx_handles, n;
107 int max_threads_per_child = 0;
112 ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
114 status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
115 if (status != APR_SUCCESS) {
116 /* some MPMs do not implemnent this */
118 status = APR_SUCCESS;
121 h2_config_init(pool);
123 minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
124 maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
126 minw = max_threads_per_child;
132 /* How many file handles is it safe to use for transfer
133 * to the master connection to be streamed out?
134 * Is there a portable APR rlimit on NOFILES? Have not
135 * found it. And if, how many of those would we set aside?
136 * This leads all into a process wide handle allocation strategy
137 * which ultimately would limit the number of accepted connections
138 * with the assumption of implicitly reserving n handles for every
139 * connection and requiring modules with excessive needs to allocate
140 * from a central pool.
142 n = h2_config_geti(config, H2_CONF_SESSION_FILES);
144 max_tx_handles = maxw * 2;
147 max_tx_handles = maxw * n;
150 ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
151 "h2_workers: min=%d max=%d, mthrpchild=%d, tx_files=%d",
152 minw, maxw, max_threads_per_child, max_tx_handles);
153 workers = h2_workers_create(s, pool, minw, maxw, max_tx_handles);
155 idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
156 h2_workers_set_max_idle_secs(workers, idle_secs);
158 ap_register_input_filter("H2_IN", h2_filter_core_input,
159 NULL, AP_FTYPE_CONNECTION);
161 status = h2_mplx_child_init(pool, s);
163 if (status == APR_SUCCESS) {
164 status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
165 APR_PROTO_TCP, pool);
171 h2_mpm_type_t h2_conn_mpm_type(void)
177 static module *h2_conn_mpm_module(void)
183 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
189 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911)
190 "workers not initialized");
195 status = h2_session_rcreate(&session, r, ctx, workers);
198 status = h2_session_create(&session, c, ctx, workers);
201 if (status == APR_SUCCESS) {
202 h2_ctx_session_set(ctx, session);
207 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
211 h2_session *session = h2_ctx_session_get(ctx);
216 c->cs->sense = CONN_SENSE_DEFAULT;
217 c->cs->state = CONN_STATE_HANDLER;
220 status = h2_session_process(session, async_mpm);
222 if (APR_STATUS_IS_EOF(status)) {
223 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
224 H2_SSSN_LOG(APLOGNO(03045), session,
225 "process, closing conn"));
226 c->keepalive = AP_CONN_CLOSE;
229 c->keepalive = AP_CONN_KEEPALIVE;
232 if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
236 && c->keepalive == AP_CONN_KEEPALIVE
237 && mpm_state != AP_MPMQ_STOPPING);
240 switch (session->state) {
241 case H2_SESSION_ST_INIT:
242 case H2_SESSION_ST_CLEANUP:
243 case H2_SESSION_ST_DONE:
244 case H2_SESSION_ST_IDLE:
245 c->cs->state = CONN_STATE_WRITE_COMPLETION;
247 case H2_SESSION_ST_BUSY:
248 case H2_SESSION_ST_WAIT:
250 c->cs->state = CONN_STATE_HANDLER;
259 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
261 h2_session *session = h2_ctx_session_get(ctx);
263 apr_status_t status = h2_session_pre_close(session, async_mpm);
264 return (status == APR_SUCCESS)? DONE : status;
269 conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
271 apr_allocator_t *allocator;
279 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
280 "h2_stream(%ld-%d): create slave", master->id, slave_id);
282 /* We create a pool with its own allocator to be used for
283 * processing a request. This is the only way to have the processing
284 * independant of its parent pool in the sense that it can work in
285 * another thread. Also, the new allocator needs its own mutex to
286 * synchronize sub-pools.
288 apr_allocator_create(&allocator);
289 apr_allocator_max_free_set(allocator, ap_max_mem_free);
290 status = apr_pool_create_ex(&pool, parent, NULL, allocator);
291 if (status != APR_SUCCESS) {
292 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master,
293 APLOGNO(10004) "h2_session(%ld-%d): create slave pool",
294 master->id, slave_id);
297 apr_allocator_owner_set(allocator, pool);
298 apr_pool_tag(pool, "h2_slave_conn");
300 c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
302 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master,
303 APLOGNO(02913) "h2_session(%ld-%d): create slave",
304 master->id, slave_id);
305 apr_pool_destroy(pool);
309 memcpy(c, master, sizeof(conn_rec));
313 c->conn_config = ap_create_conn_config(pool);
314 c->notes = apr_table_make(pool, 5);
315 c->input_filters = NULL;
316 c->output_filters = NULL;
317 c->bucket_alloc = apr_bucket_alloc_create(pool);
318 c->data_in_input_filters = 0;
319 c->data_in_output_filters = 0;
320 c->clogging_input_filters = 1;
322 c->log_id = apr_psprintf(pool, "%ld-%d",
323 master->id, slave_id);
324 /* Simulate that we had already a request on this connection. */
326 /* We cannot install the master connection socket on the slaves, as
327 * modules mess with timeouts/blocking of the socket, with
328 * unwanted side effects to the master connection processing.
329 * Fortunately, since we never use the slave socket, we can just install
330 * a single, process-wide dummy and everyone is happy.
332 ap_set_module_config(c->conn_config, &core_module, dummy_socket);
333 /* TODO: these should be unique to this thread */
334 c->sbh = master->sbh;
335 /* TODO: not all mpm modules have learned about slave connections yet.
336 * copy their config from master to slave.
338 if ((mpm = h2_conn_mpm_module()) != NULL) {
339 cfg = ap_get_module_config(master->conn_config, mpm);
340 ap_set_module_config(c->conn_config, mpm, cfg);
343 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
344 "h2_stream(%ld-%d): created slave", master->id, slave_id);
348 void h2_slave_destroy(conn_rec *slave)
350 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
351 "h2_stream(%s): destroy slave",
352 apr_table_get(slave->notes, H2_TASK_ID_NOTE));
353 apr_pool_destroy(slave->pool);
356 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
358 return ap_run_pre_connection(slave, csd);