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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 #include <assert.h>
18 #include <stddef.h>
19
20 #include <apr_atomic.h>
21 #include <apr_strings.h>
22
23 #include <httpd.h>
24 #include <http_core.h>
25 #include <http_config.h>
26 #include <http_connection.h>
27 #include <http_protocol.h>
28 #include <http_request.h>
29 #include <http_log.h>
30 #include <http_vhost.h>
31 #include <util_filter.h>
32 #include <ap_mmn.h>
33 #include <ap_mpm.h>
34 #include <mpm_common.h>
35 #include <mod_core.h>
36 #include <scoreboard.h>
37
38 #include "h2_private.h"
39 #include "h2.h"
40 #include "h2_bucket_beam.h"
41 #include "h2_c1.h"
42 #include "h2_config.h"
43 #include "h2_conn_ctx.h"
44 #include "h2_c2_filter.h"
45 #include "h2_protocol.h"
46 #include "h2_mplx.h"
47 #include "h2_request.h"
48 #include "h2_headers.h"
49 #include "h2_session.h"
50 #include "h2_stream.h"
51 #include "h2_c2.h"
52 #include "h2_util.h"
53
54
55 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
56 static module *mpm_module;
57 static int mpm_supported = 1;
58 static apr_socket_t *dummy_socket;
59
check_modules(int force)60 static void check_modules(int force)
61 {
62 static int checked = 0;
63 int i;
64
65 if (force || !checked) {
66 for (i = 0; ap_loaded_modules[i]; ++i) {
67 module *m = ap_loaded_modules[i];
68
69 if (!strcmp("event.c", m->name)) {
70 mpm_type = H2_MPM_EVENT;
71 mpm_module = m;
72 break;
73 }
74 else if (!strcmp("motorz.c", m->name)) {
75 mpm_type = H2_MPM_MOTORZ;
76 mpm_module = m;
77 break;
78 }
79 else if (!strcmp("mpm_netware.c", m->name)) {
80 mpm_type = H2_MPM_NETWARE;
81 mpm_module = m;
82 break;
83 }
84 else if (!strcmp("prefork.c", m->name)) {
85 mpm_type = H2_MPM_PREFORK;
86 mpm_module = m;
87 /* While http2 can work really well on prefork, it collides
88 * today's use case for prefork: running single-thread app engines
89 * like php. If we restrict h2_workers to 1 per process, php will
90 * work fine, but browser will be limited to 1 active request at a
91 * time. */
92 mpm_supported = 0;
93 break;
94 }
95 else if (!strcmp("simple_api.c", m->name)) {
96 mpm_type = H2_MPM_SIMPLE;
97 mpm_module = m;
98 mpm_supported = 0;
99 break;
100 }
101 else if (!strcmp("mpm_winnt.c", m->name)) {
102 mpm_type = H2_MPM_WINNT;
103 mpm_module = m;
104 break;
105 }
106 else if (!strcmp("worker.c", m->name)) {
107 mpm_type = H2_MPM_WORKER;
108 mpm_module = m;
109 break;
110 }
111 }
112 checked = 1;
113 }
114 }
115
h2_conn_mpm_type(void)116 h2_mpm_type_t h2_conn_mpm_type(void)
117 {
118 check_modules(0);
119 return mpm_type;
120 }
121
h2_conn_mpm_name(void)122 const char *h2_conn_mpm_name(void)
123 {
124 check_modules(0);
125 return mpm_module? mpm_module->name : "unknown";
126 }
127
h2_mpm_supported(void)128 int h2_mpm_supported(void)
129 {
130 check_modules(0);
131 return mpm_supported;
132 }
133
h2_conn_mpm_module(void)134 static module *h2_conn_mpm_module(void)
135 {
136 check_modules(0);
137 return mpm_module;
138 }
139
h2_c2_child_init(apr_pool_t * pool,server_rec * s)140 apr_status_t h2_c2_child_init(apr_pool_t *pool, server_rec *s)
141 {
142 check_modules(1);
143 return apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
144 APR_PROTO_TCP, pool);
145 }
146
147 /* APR callback invoked if allocation fails. */
abort_on_oom(int retcode)148 static int abort_on_oom(int retcode)
149 {
150 ap_abort_on_oom();
151 return retcode; /* unreachable, hopefully. */
152 }
153
h2_c2_create(conn_rec * c1,apr_pool_t * parent)154 conn_rec *h2_c2_create(conn_rec *c1, apr_pool_t *parent)
155 {
156 apr_allocator_t *allocator;
157 apr_status_t status;
158 apr_pool_t *pool;
159 conn_rec *c2;
160 void *cfg;
161 module *mpm;
162
163 ap_assert(c1);
164 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c1,
165 "h2_c2: create for c1(%ld)", c1->id);
166
167 /* We create a pool with its own allocator to be used for
168 * processing a request. This is the only way to have the processing
169 * independent of its parent pool in the sense that it can work in
170 * another thread.
171 */
172 apr_allocator_create(&allocator);
173 apr_allocator_max_free_set(allocator, ap_max_mem_free);
174 status = apr_pool_create_ex(&pool, parent, NULL, allocator);
175 if (status != APR_SUCCESS) {
176 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c1,
177 APLOGNO(10004) "h2_c2: create pool");
178 return NULL;
179 }
180 apr_allocator_owner_set(allocator, pool);
181 apr_pool_abort_set(abort_on_oom, pool);
182 apr_pool_tag(pool, "h2_c2_conn");
183
184 c2 = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
185 memcpy(c2, c1, sizeof(conn_rec));
186
187 c2->master = c1;
188 c2->pool = pool;
189 c2->conn_config = ap_create_conn_config(pool);
190 c2->notes = apr_table_make(pool, 5);
191 c2->input_filters = NULL;
192 c2->output_filters = NULL;
193 c2->keepalives = 0;
194 #if AP_MODULE_MAGIC_AT_LEAST(20180903, 1)
195 c2->filter_conn_ctx = NULL;
196 #endif
197 c2->bucket_alloc = apr_bucket_alloc_create(pool);
198 #if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
199 c2->data_in_input_filters = 0;
200 c2->data_in_output_filters = 0;
201 #endif
202 /* prevent mpm_event from making wrong assumptions about this connection,
203 * like e.g. using its socket for an async read check. */
204 c2->clogging_input_filters = 1;
205 c2->log = NULL;
206 c2->aborted = 0;
207 /* We cannot install the master connection socket on the secondary, as
208 * modules mess with timeouts/blocking of the socket, with
209 * unwanted side effects to the master connection processing.
210 * Fortunately, since we never use the secondary socket, we can just install
211 * a single, process-wide dummy and everyone is happy.
212 */
213 ap_set_module_config(c2->conn_config, &core_module, dummy_socket);
214 /* TODO: these should be unique to this thread */
215 c2->sbh = NULL; /*c1->sbh;*/
216 /* TODO: not all mpm modules have learned about secondary connections yet.
217 * copy their config from master to secondary.
218 */
219 if ((mpm = h2_conn_mpm_module()) != NULL) {
220 cfg = ap_get_module_config(c1->conn_config, mpm);
221 ap_set_module_config(c2->conn_config, mpm, cfg);
222 }
223
224 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c2,
225 "h2_c2(%s): created", c2->log_id);
226 return c2;
227 }
228
h2_c2_destroy(conn_rec * c2)229 void h2_c2_destroy(conn_rec *c2)
230 {
231 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c2,
232 "h2_c2(%s): destroy", c2->log_id);
233 apr_pool_destroy(c2->pool);
234 }
235
236 typedef struct {
237 apr_bucket_brigade *bb; /* c2: data in holding area */
238 } h2_c2_fctx_in_t;
239
h2_c2_filter_in(ap_filter_t * f,apr_bucket_brigade * bb,ap_input_mode_t mode,apr_read_type_e block,apr_off_t readbytes)240 static apr_status_t h2_c2_filter_in(ap_filter_t* f,
241 apr_bucket_brigade* bb,
242 ap_input_mode_t mode,
243 apr_read_type_e block,
244 apr_off_t readbytes)
245 {
246 h2_conn_ctx_t *conn_ctx;
247 h2_c2_fctx_in_t *fctx = f->ctx;
248 apr_status_t status = APR_SUCCESS;
249 apr_bucket *b, *next;
250 apr_off_t bblen;
251 const int trace1 = APLOGctrace1(f->c);
252 apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)?
253 (apr_size_t)readbytes : APR_SIZE_MAX);
254
255 conn_ctx = h2_conn_ctx_get(f->c);
256 ap_assert(conn_ctx);
257
258 if (trace1) {
259 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
260 "h2_c2_in(%s-%d): read, mode=%d, block=%d, readbytes=%ld",
261 conn_ctx->id, conn_ctx->stream_id, mode, block, (long)readbytes);
262 }
263
264 if (mode == AP_MODE_INIT) {
265 return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes);
266 }
267
268 if (f->c->aborted) {
269 return APR_ECONNABORTED;
270 }
271
272 if (!fctx) {
273 fctx = apr_pcalloc(f->c->pool, sizeof(*fctx));
274 f->ctx = fctx;
275 fctx->bb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
276 if (!conn_ctx->beam_in) {
277 b = apr_bucket_eos_create(f->c->bucket_alloc);
278 APR_BRIGADE_INSERT_TAIL(fctx->bb, b);
279 }
280 }
281
282 /* Cleanup brigades from those nasty 0 length non-meta buckets
283 * that apr_brigade_split_line() sometimes produces. */
284 for (b = APR_BRIGADE_FIRST(fctx->bb);
285 b != APR_BRIGADE_SENTINEL(fctx->bb); b = next) {
286 next = APR_BUCKET_NEXT(b);
287 if (b->length == 0 && !APR_BUCKET_IS_METADATA(b)) {
288 apr_bucket_delete(b);
289 }
290 }
291
292 while (APR_BRIGADE_EMPTY(fctx->bb)) {
293 /* Get more input data for our request. */
294 if (trace1) {
295 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
296 "h2_c2_in(%s-%d): get more data from mplx, block=%d, "
297 "readbytes=%ld",
298 conn_ctx->id, conn_ctx->stream_id, block, (long)readbytes);
299 }
300 if (conn_ctx->beam_in) {
301 if (conn_ctx->pipe_in_prod[H2_PIPE_OUT]) {
302 receive:
303 status = h2_beam_receive(conn_ctx->beam_in, f->c, fctx->bb, APR_NONBLOCK_READ,
304 conn_ctx->mplx->stream_max_mem);
305 if (APR_STATUS_IS_EAGAIN(status) && APR_BLOCK_READ == block) {
306 status = h2_util_wait_on_pipe(conn_ctx->pipe_in_prod[H2_PIPE_OUT]);
307 if (APR_SUCCESS == status) {
308 goto receive;
309 }
310 }
311 }
312 else {
313 status = h2_beam_receive(conn_ctx->beam_in, f->c, fctx->bb, block,
314 conn_ctx->mplx->stream_max_mem);
315 }
316 }
317 else {
318 status = APR_EOF;
319 }
320
321 if (trace1) {
322 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c,
323 "h2_c2_in(%s-%d): read returned",
324 conn_ctx->id, conn_ctx->stream_id);
325 }
326 if (APR_STATUS_IS_EAGAIN(status)
327 && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) {
328 /* chunked input handling does not seem to like it if we
329 * return with APR_EAGAIN from a GETLINE read...
330 * upload 100k test on test-ser.example.org hangs */
331 status = APR_SUCCESS;
332 }
333 else if (APR_STATUS_IS_EOF(status)) {
334 break;
335 }
336 else if (status != APR_SUCCESS) {
337 conn_ctx->last_err = status;
338 return status;
339 }
340
341 if (trace1) {
342 h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE2,
343 "c2 input recv raw", fctx->bb);
344 }
345 if (h2_c_logio_add_bytes_in) {
346 apr_brigade_length(bb, 0, &bblen);
347 h2_c_logio_add_bytes_in(f->c, bblen);
348 }
349 }
350
351 /* Nothing there, no more data to get. Return. */
352 if (status == APR_EOF && APR_BRIGADE_EMPTY(fctx->bb)) {
353 return status;
354 }
355
356 if (trace1) {
357 h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE2,
358 "c2 input.bb", fctx->bb);
359 }
360
361 if (APR_BRIGADE_EMPTY(fctx->bb)) {
362 if (trace1) {
363 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
364 "h2_c2_in(%s-%d): no data",
365 conn_ctx->id, conn_ctx->stream_id);
366 }
367 return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF;
368 }
369
370 if (mode == AP_MODE_EXHAUSTIVE) {
371 /* return all we have */
372 APR_BRIGADE_CONCAT(bb, fctx->bb);
373 }
374 else if (mode == AP_MODE_READBYTES) {
375 status = h2_brigade_concat_length(bb, fctx->bb, rmax);
376 }
377 else if (mode == AP_MODE_SPECULATIVE) {
378 status = h2_brigade_copy_length(bb, fctx->bb, rmax);
379 }
380 else if (mode == AP_MODE_GETLINE) {
381 /* we are reading a single LF line, e.g. the HTTP headers.
382 * this has the nasty side effect to split the bucket, even
383 * though it ends with CRLF and creates a 0 length bucket */
384 status = apr_brigade_split_line(bb, fctx->bb, block,
385 HUGE_STRING_LEN);
386 if (APLOGctrace1(f->c)) {
387 char buffer[1024];
388 apr_size_t len = sizeof(buffer)-1;
389 apr_brigade_flatten(bb, buffer, &len);
390 buffer[len] = 0;
391 if (trace1) {
392 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
393 "h2_c2_in(%s-%d): getline: %s",
394 conn_ctx->id, conn_ctx->stream_id, buffer);
395 }
396 }
397 }
398 else {
399 /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
400 * to support it. Seems to work. */
401 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
402 APLOGNO(03472)
403 "h2_c2_in(%s-%d), unsupported READ mode %d",
404 conn_ctx->id, conn_ctx->stream_id, mode);
405 status = APR_ENOTIMPL;
406 }
407
408 if (trace1) {
409 apr_brigade_length(bb, 0, &bblen);
410 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
411 "h2_c2_in(%s-%d): %ld data bytes",
412 conn_ctx->id, conn_ctx->stream_id, (long)bblen);
413 }
414 return status;
415 }
416
beam_out(conn_rec * c2,h2_conn_ctx_t * conn_ctx,apr_bucket_brigade * bb)417 static apr_status_t beam_out(conn_rec *c2, h2_conn_ctx_t *conn_ctx, apr_bucket_brigade* bb)
418 {
419 apr_off_t written, header_len = 0;
420 apr_status_t rv;
421
422 if (h2_c_logio_add_bytes_out) {
423 /* mod_logio wants to report the number of bytes written in a
424 * response, including header and footer fields. Since h2 converts
425 * those during c1 processing into the HPACKed h2 HEADER frames,
426 * we need to give mod_logio something here and count just the
427 * raw lengths of all headers in the buckets. */
428 apr_bucket *b;
429 for (b = APR_BRIGADE_FIRST(bb);
430 b != APR_BRIGADE_SENTINEL(bb);
431 b = APR_BUCKET_NEXT(b)) {
432 if (H2_BUCKET_IS_HEADERS(b)) {
433 header_len += (apr_off_t)h2_bucket_headers_headers_length(b);
434 }
435 }
436 }
437
438 rv = h2_beam_send(conn_ctx->beam_out, c2, bb, APR_BLOCK_READ, &written);
439
440 if (APR_STATUS_IS_EAGAIN(rv)) {
441 rv = APR_SUCCESS;
442 }
443 if (written && h2_c_logio_add_bytes_out) {
444 h2_c_logio_add_bytes_out(c2, written + header_len);
445 }
446 return rv;
447 }
448
h2_c2_filter_out(ap_filter_t * f,apr_bucket_brigade * bb)449 static apr_status_t h2_c2_filter_out(ap_filter_t* f, apr_bucket_brigade* bb)
450 {
451 h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
452 apr_status_t rv;
453
454 ap_assert(conn_ctx);
455 rv = beam_out(f->c, conn_ctx, bb);
456
457 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, f->c,
458 "h2_c2(%s-%d): output leave",
459 conn_ctx->id, conn_ctx->stream_id);
460 if (APR_SUCCESS != rv) {
461 if (!conn_ctx->done) {
462 h2_beam_abort(conn_ctx->beam_out, f->c);
463 }
464 f->c->aborted = 1;
465 }
466 return rv;
467 }
468
c2_run_pre_connection(conn_rec * c2,apr_socket_t * csd)469 static apr_status_t c2_run_pre_connection(conn_rec *c2, apr_socket_t *csd)
470 {
471 if (c2->keepalives == 0) {
472 /* Simulate that we had already a request on this connection. Some
473 * hooks trigger special behaviour when keepalives is 0.
474 * (Not necessarily in pre_connection, but later. Set it here, so it
475 * is in place.) */
476 c2->keepalives = 1;
477 /* We signal that this connection will be closed after the request.
478 * Which is true in that sense that we throw away all traffic data
479 * on this c2 connection after each requests. Although we might
480 * reuse internal structures like memory pools.
481 * The wanted effect of this is that httpd does not try to clean up
482 * any dangling data on this connection when a request is done. Which
483 * is unnecessary on a h2 stream.
484 */
485 c2->keepalive = AP_CONN_CLOSE;
486 return ap_run_pre_connection(c2, csd);
487 }
488 ap_assert(c2->output_filters);
489 return APR_SUCCESS;
490 }
491
h2_c2_process(conn_rec * c2,apr_thread_t * thread,int worker_id)492 apr_status_t h2_c2_process(conn_rec *c2, apr_thread_t *thread, int worker_id)
493 {
494 h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c2);
495
496 ap_assert(conn_ctx);
497 ap_assert(conn_ctx->mplx);
498
499 /* See the discussion at <https://github.com/icing/mod_h2/issues/195>
500 *
501 * Each conn_rec->id is supposed to be unique at a point in time. Since
502 * some modules (and maybe external code) uses this id as an identifier
503 * for the request_rec they handle, it needs to be unique for secondary
504 * connections also.
505 *
506 * The MPM module assigns the connection ids and mod_unique_id is using
507 * that one to generate identifier for requests. While the implementation
508 * works for HTTP/1.x, the parallel execution of several requests per
509 * connection will generate duplicate identifiers on load.
510 *
511 * The original implementation for secondary connection identifiers used
512 * to shift the master connection id up and assign the stream id to the
513 * lower bits. This was cramped on 32 bit systems, but on 64bit there was
514 * enough space.
515 *
516 * As issue 195 showed, mod_unique_id only uses the lower 32 bit of the
517 * connection id, even on 64bit systems. Therefore collisions in request ids.
518 *
519 * The way master connection ids are generated, there is some space "at the
520 * top" of the lower 32 bits on allmost all systems. If you have a setup
521 * with 64k threads per child and 255 child processes, you live on the edge.
522 *
523 * The new implementation shifts 8 bits and XORs in the worker
524 * id. This will experience collisions with > 256 h2 workers and heavy
525 * load still. There seems to be no way to solve this in all possible
526 * configurations by mod_h2 alone.
527 */
528 c2->id = (c2->master->id << 8)^worker_id;
529
530 if (!conn_ctx->pre_conn_done) {
531 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c2,
532 "h2_c2(%s-%d), adding filters",
533 conn_ctx->id, conn_ctx->stream_id);
534 ap_add_input_filter("H2_C2_NET_IN", NULL, NULL, c2);
535 ap_add_output_filter("H2_C2_NET_CATCH_H1", NULL, NULL, c2);
536 ap_add_output_filter("H2_C2_NET_OUT", NULL, NULL, c2);
537
538 c2_run_pre_connection(c2, ap_get_conn_socket(c2));
539 conn_ctx->pre_conn_done = 1;
540 }
541
542 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c2,
543 "h2_c2(%s-%d): process connection",
544 conn_ctx->id, conn_ctx->stream_id);
545
546 c2->current_thread = thread;
547 ap_run_process_connection(c2);
548
549 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c2,
550 "h2_c2(%s-%d): processing done",
551 conn_ctx->id, conn_ctx->stream_id);
552
553 return APR_SUCCESS;
554 }
555
c2_process(h2_conn_ctx_t * conn_ctx,conn_rec * c)556 static apr_status_t c2_process(h2_conn_ctx_t *conn_ctx, conn_rec *c)
557 {
558 const h2_request *req = conn_ctx->request;
559 conn_state_t *cs = c->cs;
560 request_rec *r;
561
562 r = h2_create_request_rec(conn_ctx->request, c);
563 if (!r) {
564 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
565 "h2_c2(%s-%d): create request_rec failed, r=NULL",
566 conn_ctx->id, conn_ctx->stream_id);
567 goto cleanup;
568 }
569 if (r->status != HTTP_OK) {
570 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
571 "h2_c2(%s-%d): create request_rec failed, r->status=%d",
572 conn_ctx->id, conn_ctx->stream_id, r->status);
573 goto cleanup;
574 }
575
576 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
577 "h2_c2(%s-%d): created request_rec",
578 conn_ctx->id, conn_ctx->stream_id);
579 conn_ctx->server = r->server;
580
581 /* the request_rec->server carries the timeout value that applies */
582 h2_conn_ctx_set_timeout(conn_ctx, r->server->timeout);
583
584 if (h2_config_sgeti(conn_ctx->server, H2_CONF_COPY_FILES)) {
585 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
586 "h2_mplx(%s-%d): copy_files in output",
587 conn_ctx->id, conn_ctx->stream_id);
588 h2_beam_set_copy_files(conn_ctx->beam_out, 1);
589 }
590
591 ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
592 if (cs) {
593 cs->state = CONN_STATE_HANDLER;
594 }
595 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
596 "h2_c2(%s-%d): start process_request",
597 conn_ctx->id, conn_ctx->stream_id);
598
599 /* Add the raw bytes of the request (e.g. header frame lengths to
600 * the logio for this request. */
601 if (req->raw_bytes && h2_c_logio_add_bytes_in) {
602 h2_c_logio_add_bytes_in(c, req->raw_bytes);
603 }
604
605 ap_process_request(r);
606 /* After the call to ap_process_request, the
607 * request pool may have been deleted. */
608 r = NULL;
609
610 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
611 "h2_c2(%s-%d): process_request done",
612 conn_ctx->id, conn_ctx->stream_id);
613 if (cs)
614 cs->state = CONN_STATE_WRITE_COMPLETION;
615
616 cleanup:
617 return APR_SUCCESS;
618 }
619
h2_c2_hook_process(conn_rec * c)620 static int h2_c2_hook_process(conn_rec* c)
621 {
622 h2_conn_ctx_t *ctx;
623
624 if (!c->master) {
625 return DECLINED;
626 }
627
628 ctx = h2_conn_ctx_get(c);
629 if (ctx->stream_id) {
630 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
631 "h2_h2, processing request directly");
632 c2_process(ctx, c);
633 return DONE;
634 }
635 else {
636 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
637 "secondary_conn(%ld): no h2 stream assing?", c->id);
638 }
639 return DECLINED;
640 }
641
check_push(request_rec * r,const char * tag)642 static void check_push(request_rec *r, const char *tag)
643 {
644 apr_array_header_t *push_list = h2_config_push_list(r);
645
646 if (!r->expecting_100 && push_list && push_list->nelts > 0) {
647 int i, old_status;
648 const char *old_line;
649
650 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
651 "%s, early announcing %d resources for push",
652 tag, push_list->nelts);
653 for (i = 0; i < push_list->nelts; ++i) {
654 h2_push_res *push = &APR_ARRAY_IDX(push_list, i, h2_push_res);
655 apr_table_add(r->headers_out, "Link",
656 apr_psprintf(r->pool, "<%s>; rel=preload%s",
657 push->uri_ref, push->critical? "; critical" : ""));
658 }
659 old_status = r->status;
660 old_line = r->status_line;
661 r->status = 103;
662 r->status_line = "103 Early Hints";
663 ap_send_interim_response(r, 1);
664 r->status = old_status;
665 r->status_line = old_line;
666 }
667 }
668
h2_c2_hook_post_read_request(request_rec * r)669 static int h2_c2_hook_post_read_request(request_rec *r)
670 {
671 h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(r->connection);
672
673 if (conn_ctx && conn_ctx->stream_id) {
674
675 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
676 "h2_c2(%s-%d): adding request filters",
677 conn_ctx->id, conn_ctx->stream_id);
678
679 /* setup the correct filters to process the request for h2 */
680 ap_add_input_filter("H2_C2_REQUEST_IN", NULL, r, r->connection);
681
682 /* replace the core http filter that formats response headers
683 * in HTTP/1 with our own that collects status and headers */
684 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
685
686 ap_add_output_filter("H2_C2_RESPONSE_OUT", NULL, r, r->connection);
687 ap_add_output_filter("H2_C2_TRAILERS_OUT", NULL, r, r->connection);
688 }
689 return DECLINED;
690 }
691
h2_c2_hook_fixups(request_rec * r)692 static int h2_c2_hook_fixups(request_rec *r)
693 {
694 /* secondary connection? */
695 if (r->connection->master) {
696 h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(r->connection);
697 if (conn_ctx) {
698 check_push(r, "late_fixup");
699 }
700 }
701 return DECLINED;
702 }
703
h2_c2_register_hooks(void)704 void h2_c2_register_hooks(void)
705 {
706 /* When the connection processing actually starts, we might
707 * take over, if the connection is for a h2 stream.
708 */
709 ap_hook_process_connection(h2_c2_hook_process,
710 NULL, NULL, APR_HOOK_FIRST);
711 /* We need to manipulate the standard HTTP/1.1 protocol filters and
712 * install our own. This needs to be done very early. */
713 ap_hook_post_read_request(h2_c2_hook_post_read_request, NULL, NULL, APR_HOOK_REALLY_FIRST);
714 ap_hook_fixups(h2_c2_hook_fixups, NULL, NULL, APR_HOOK_LAST);
715
716 ap_register_input_filter("H2_C2_NET_IN", h2_c2_filter_in,
717 NULL, AP_FTYPE_NETWORK);
718 ap_register_output_filter("H2_C2_NET_OUT", h2_c2_filter_out,
719 NULL, AP_FTYPE_NETWORK);
720 ap_register_output_filter("H2_C2_NET_CATCH_H1", h2_c2_filter_catch_h1_out,
721 NULL, AP_FTYPE_NETWORK);
722
723 ap_register_input_filter("H2_C2_REQUEST_IN", h2_c2_filter_request_in,
724 NULL, AP_FTYPE_PROTOCOL);
725 ap_register_output_filter("H2_C2_RESPONSE_OUT", h2_c2_filter_response_out,
726 NULL, AP_FTYPE_PROTOCOL);
727 ap_register_output_filter("H2_C2_TRAILERS_OUT", h2_c2_filter_trailers_out,
728 NULL, AP_FTYPE_PROTOCOL);
729 }
730
731