1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <apr_strings.h>
24 #include <http_core.h>
25 #include <http_config.h>
27 #include <http_connection.h>
28 #include <http_protocol.h>
29 #include <http_request.h>
31 #include <mpm_common.h>
33 #include "h2_private.h"
35 #include "h2_config.h"
37 #include "h2_filter.h"
39 #include "h2_session.h"
40 #include "h2_stream.h"
43 #include "h2_workers.h"
45 #include "h2_version.h"
47 static struct h2_workers *workers;
49 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
50 static module *mpm_module;
52 static int mpm_supported = 1;
53 static apr_socket_t *dummy_socket;
55 static void check_modules(int force)
57 static int checked = 0;
60 if (force || !checked) {
61 for (i = 0; ap_loaded_modules[i]; ++i) {
62 module *m = ap_loaded_modules[i];
64 if (!strcmp("event.c", m->name)) {
65 mpm_type = H2_MPM_EVENT;
69 else if (!strcmp("motorz.c", m->name)) {
70 mpm_type = H2_MPM_MOTORZ;
74 else if (!strcmp("mpm_netware.c", m->name)) {
75 mpm_type = H2_MPM_NETWARE;
79 else if (!strcmp("prefork.c", m->name)) {
80 mpm_type = H2_MPM_PREFORK;
82 /* While http2 can work really well on prefork, it collides
83 * today's use case for prefork: runnning single-thread app engines
84 * like php. If we restrict h2_workers to 1 per process, php will
85 * work fine, but browser will be limited to 1 active request at a
90 else if (!strcmp("simple_api.c", m->name)) {
91 mpm_type = H2_MPM_SIMPLE;
96 else if (!strcmp("mpm_winnt.c", m->name)) {
97 mpm_type = H2_MPM_WINNT;
101 else if (!strcmp("worker.c", m->name)) {
102 mpm_type = H2_MPM_WORKER;
111 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
113 const h2_config *config = h2_config_sget(s);
114 apr_status_t status = APR_SUCCESS;
116 int max_threads_per_child = 0;
120 ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
122 status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
123 if (status != APR_SUCCESS) {
124 /* some MPMs do not implemnent this */
126 status = APR_SUCCESS;
129 h2_config_init(pool);
131 h2_get_num_workers(s, &minw, &maxw);
133 idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
134 ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
135 "h2_workers: min=%d max=%d, mthrpchild=%d, idle_secs=%d",
136 minw, maxw, max_threads_per_child, idle_secs);
137 workers = h2_workers_create(s, pool, minw, maxw, idle_secs);
139 ap_register_input_filter("H2_IN", h2_filter_core_input,
140 NULL, AP_FTYPE_CONNECTION);
142 status = h2_mplx_child_init(pool, s);
144 if (status == APR_SUCCESS) {
145 status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
146 APR_PROTO_TCP, pool);
152 h2_mpm_type_t h2_conn_mpm_type(void)
158 const char *h2_conn_mpm_name(void)
161 return mpm_module? mpm_module->name : "unknown";
164 int h2_mpm_supported(void)
167 return mpm_supported;
170 static module *h2_conn_mpm_module(void)
176 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
182 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911)
183 "workers not initialized");
188 status = h2_session_rcreate(&session, r, ctx, workers);
191 status = h2_session_create(&session, c, ctx, workers);
194 if (status == APR_SUCCESS) {
195 h2_ctx_session_set(ctx, session);
200 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
204 h2_session *session = h2_ctx_session_get(ctx);
209 c->cs->sense = CONN_SENSE_DEFAULT;
210 c->cs->state = CONN_STATE_HANDLER;
213 status = h2_session_process(session, async_mpm);
215 if (APR_STATUS_IS_EOF(status)) {
216 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
217 H2_SSSN_LOG(APLOGNO(03045), session,
218 "process, closing conn"));
219 c->keepalive = AP_CONN_CLOSE;
222 c->keepalive = AP_CONN_KEEPALIVE;
225 if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
229 && c->keepalive == AP_CONN_KEEPALIVE
230 && mpm_state != AP_MPMQ_STOPPING);
233 switch (session->state) {
234 case H2_SESSION_ST_INIT:
235 case H2_SESSION_ST_IDLE:
236 case H2_SESSION_ST_BUSY:
237 case H2_SESSION_ST_WAIT:
238 c->cs->state = CONN_STATE_WRITE_COMPLETION;
240 case H2_SESSION_ST_CLEANUP:
241 case H2_SESSION_ST_DONE:
243 c->cs->state = CONN_STATE_LINGER;
251 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
253 h2_session *session = h2_ctx_session_get(ctx);
257 apr_status_t status = h2_session_pre_close(session, async_mpm);
258 return (status == APR_SUCCESS)? DONE : status;
263 /* APR callback invoked if allocation fails. */
264 static int abort_on_oom(int retcode)
267 return retcode; /* unreachable, hopefully. */
270 conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
272 apr_allocator_t *allocator;
280 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
281 "h2_stream(%ld-%d): create slave", master->id, slave_id);
283 /* We create a pool with its own allocator to be used for
284 * processing a request. This is the only way to have the processing
285 * independant of its parent pool in the sense that it can work in
286 * another thread. Also, the new allocator needs its own mutex to
287 * synchronize sub-pools.
289 apr_allocator_create(&allocator);
290 apr_allocator_max_free_set(allocator, ap_max_mem_free);
291 status = apr_pool_create_ex(&pool, parent, NULL, allocator);
292 if (status != APR_SUCCESS) {
293 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master,
294 APLOGNO(10004) "h2_session(%ld-%d): create slave pool",
295 master->id, slave_id);
298 apr_allocator_owner_set(allocator, pool);
299 apr_pool_abort_set(abort_on_oom, pool);
300 apr_pool_tag(pool, "h2_slave_conn");
302 c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
304 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master,
305 APLOGNO(02913) "h2_session(%ld-%d): create slave",
306 master->id, slave_id);
307 apr_pool_destroy(pool);
311 memcpy(c, master, sizeof(conn_rec));
315 c->conn_config = ap_create_conn_config(pool);
316 c->notes = apr_table_make(pool, 5);
317 c->input_filters = NULL;
318 c->output_filters = NULL;
319 #if AP_MODULE_MAGIC_AT_LEAST(20180903, 1)
320 c->filter_conn_ctx = NULL;
322 c->bucket_alloc = apr_bucket_alloc_create(pool);
323 #if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
324 c->data_in_input_filters = 0;
325 c->data_in_output_filters = 0;
327 /* prevent mpm_event from making wrong assumptions about this connection,
328 * like e.g. using its socket for an async read check. */
329 c->clogging_input_filters = 1;
331 c->log_id = apr_psprintf(pool, "%ld-%d",
332 master->id, slave_id);
334 /* We cannot install the master connection socket on the slaves, as
335 * modules mess with timeouts/blocking of the socket, with
336 * unwanted side effects to the master connection processing.
337 * Fortunately, since we never use the slave socket, we can just install
338 * a single, process-wide dummy and everyone is happy.
340 ap_set_module_config(c->conn_config, &core_module, dummy_socket);
341 /* TODO: these should be unique to this thread */
342 c->sbh = master->sbh;
343 /* TODO: not all mpm modules have learned about slave connections yet.
344 * copy their config from master to slave.
346 if ((mpm = h2_conn_mpm_module()) != NULL) {
347 cfg = ap_get_module_config(master->conn_config, mpm);
348 ap_set_module_config(c->conn_config, mpm, cfg);
351 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
352 "h2_stream(%ld-%d): created slave", master->id, slave_id);
356 void h2_slave_destroy(conn_rec *slave)
358 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
359 "h2_stream(%s): destroy slave",
360 apr_table_get(slave->notes, H2_TASK_ID_NOTE));
362 apr_pool_destroy(slave->pool);
365 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
367 if (slave->keepalives == 0) {
368 /* Simulate that we had already a request on this connection. Some
369 * hooks trigger special behaviour when keepalives is 0.
370 * (Not necessarily in pre_connection, but later. Set it here, so it
372 slave->keepalives = 1;
374 /* We signal that this connection will be closed after the request.
375 * Which is true in that sense that we throw away all traffic data
376 * on this slave connection after each requests. Although we might
377 * reuse internal structures like memory pools.
378 * The wanted effect of this is that httpd does not try to clean up
379 * any dangling data on this connection when a request is done. Which
380 * is unneccessary on a h2 stream.
382 slave->keepalive = AP_CONN_CLOSE;
383 return ap_run_pre_connection(slave, csd);