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.
21 #include <http_core.h>
22 #include <http_config.h>
24 #include <http_connection.h>
25 #include <http_protocol.h>
26 #include <http_request.h>
28 #include "h2_private.h"
29 #include "h2_config.h"
31 #include "h2_filter.h"
33 #include "h2_session.h"
34 #include "h2_stream.h"
35 #include "h2_stream_set.h"
38 #include "h2_worker.h"
39 #include "h2_workers.h"
42 static struct h2_workers *workers;
44 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
45 static module *mpm_module;
48 static void check_modules(int force)
50 static int checked = 0;
53 if (force || !checked) {
54 for (i = 0; ap_loaded_modules[i]; ++i) {
55 module *m = ap_loaded_modules[i];
57 if (!strcmp("event.c", m->name)) {
58 mpm_type = H2_MPM_EVENT;
62 else if (!strcmp("worker.c", m->name)) {
63 mpm_type = H2_MPM_WORKER;
67 else if (!strcmp("prefork.c", m->name)) {
68 mpm_type = H2_MPM_PREFORK;
77 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
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;
87 ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
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 */
99 minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
100 maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
102 minw = max_threads_per_child;
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.
118 n = h2_config_geti(config, H2_CONF_SESSION_FILES);
120 max_tx_handles = 256;
123 max_tx_handles = maxw * n;
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);
131 idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
132 h2_workers_set_max_idle_secs(workers, idle_secs);
134 ap_register_input_filter("H2_IN", h2_filter_core_input,
135 NULL, AP_FTYPE_CONNECTION);
140 h2_mpm_type_t h2_conn_mpm_type(void)
146 static module *h2_conn_mpm_module(void)
152 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
157 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911)
158 "workers not initialized");
163 session = h2_session_rcreate(r, ctx, workers);
166 session = h2_session_create(c, ctx, workers);
169 h2_ctx_session_set(ctx, session);
171 ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
176 static apr_status_t h2_conn_process(h2_ctx *ctx)
181 session = h2_ctx_session_get(ctx);
182 if (session->c->cs) {
183 session->c->cs->sense = CONN_SENSE_DEFAULT;
186 status = h2_session_process(session, async_mpm);
188 session->c->keepalive = AP_CONN_KEEPALIVE;
189 if (session->c->cs) {
190 session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
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;
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;
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;
215 h2_ctx_clear(session->c);
216 h2_session_close(session);
217 /* hereafter session may be gone */
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;
228 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
232 h2_conn_process(ctx);
234 if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
238 && c->keepalive == AP_CONN_KEEPALIVE
239 && mpm_state != AP_MPMQ_STOPPING);
245 static void fix_event_conn(conn_rec *c, conn_rec *master);
247 conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *p,
248 apr_thread_t *thread, apr_socket_t *socket)
252 AP_DEBUG_ASSERT(master);
253 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
254 "h2_conn(%ld): created from master", master->id);
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.
261 c = (conn_rec *) apr_palloc(p, sizeof(conn_rec));
263 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master,
264 APLOGNO(02913) "h2_task: creating conn");
268 memcpy(c, master, sizeof(conn_rec));
271 c->id = (master->id & (long)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);
281 c->data_in_input_filters = 0;
282 c->data_in_output_filters = 0;
283 c->clogging_input_filters = 1;
287 /* TODO: these should be unique to this thread */
288 c->sbh = master->sbh;
290 /* Simulate that we had already a request on this connection. */
293 ap_set_module_config(c->conn_config, &core_module, socket);
295 /* This works for mpm_worker so far. Other mpm modules have
296 * different needs, unfortunately. The most interesting one
299 switch (h2_conn_mpm_type()) {
304 fix_event_conn(c, master);
307 /* fingers crossed */
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.
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.
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 */
330 /** request record (if any) this struct refers to */
332 /** is the current conn_rec suspended? (disassociated with
333 * a particular MPM thread; for suspend_/resume_connection
337 /** memory pool to allocate from */
339 /** bucket allocator */
340 apr_bucket_alloc_t *bucket_alloc;
341 /** poll file descriptor information */
343 /** public parts of the connection state */
346 APR_RING_HEAD(timeout_head_t, event_conn_state_t);
348 static void fix_event_conn(conn_rec *c, conn_rec *master)
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);
355 ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cs);
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;