Apache HTTPD
h2_stream.c
Go to the documentation of this file.
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.h"
21#include "apr_strings.h"
22#include "apr_lib.h"
23#include "apr_strmatch.h"
24
25#include <httpd.h>
26#include <http_core.h>
27#include <http_connection.h>
28#include <http_log.h>
29#include <http_protocol.h>
30#include <http_ssl.h>
31
32#include <nghttp2/nghttp2.h>
33
34#include "h2_private.h"
35#include "h2.h"
36#include "h2_bucket_beam.h"
37#include "h2_c1.h"
38#include "h2_config.h"
39#include "h2_protocol.h"
40#include "h2_mplx.h"
41#include "h2_push.h"
42#include "h2_request.h"
43#include "h2_headers.h"
44#include "h2_session.h"
45#include "h2_stream.h"
46#include "h2_c2.h"
47#include "h2_conn_ctx.h"
48#include "h2_c2.h"
49#include "h2_util.h"
50
51
52static const char *h2_ss_str(const h2_stream_state_t state)
53{
54 switch (state) {
55 case H2_SS_IDLE:
56 return "IDLE";
57 case H2_SS_RSVD_L:
58 return "RESERVED_LOCAL";
59 case H2_SS_RSVD_R:
60 return "RESERVED_REMOTE";
61 case H2_SS_OPEN:
62 return "OPEN";
63 case H2_SS_CLOSED_L:
64 return "HALF_CLOSED_LOCAL";
65 case H2_SS_CLOSED_R:
66 return "HALF_CLOSED_REMOTE";
67 case H2_SS_CLOSED:
68 return "CLOSED";
69 case H2_SS_CLEANUP:
70 return "CLEANUP";
71 default:
72 return "UNKNOWN";
73 }
74}
75
76const char *h2_stream_state_str(const h2_stream *stream)
77{
78 return h2_ss_str(stream->state);
79}
80
81/* Abbreviations for stream transit tables */
82#define S_XXX (-2) /* Programming Error */
83#define S_ERR (-1) /* Protocol Error */
84#define S_NOP (0) /* No Change */
85#define S_IDL (H2_SS_IDL + 1)
86#define S_RS_L (H2_SS_RSVD_L + 1)
87#define S_RS_R (H2_SS_RSVD_R + 1)
88#define S_OPEN (H2_SS_OPEN + 1)
89#define S_CL_L (H2_SS_CLOSED_L + 1)
90#define S_CL_R (H2_SS_CLOSED_R + 1)
91#define S_CLS (H2_SS_CLOSED + 1)
92#define S_CLN (H2_SS_CLEANUP + 1)
93
94/* state transisitions when certain frame types are sent */
95static int trans_on_send[][H2_SS_MAX] = {
96/*S_IDLE,S_RS_R, S_RS_L, S_OPEN, S_CL_R, S_CL_L, S_CLS, S_CLN, */
97{ S_ERR, S_ERR, S_ERR, S_NOP, S_NOP, S_ERR, S_NOP, S_NOP, },/* DATA */
98{ S_ERR, S_ERR, S_CL_R, S_NOP, S_NOP, S_ERR, S_NOP, S_NOP, },/* HEADERS */
99{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* PRIORITY */
100{ S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_NOP, S_NOP, },/* RST_STREAM */
101{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* SETTINGS */
102{ S_RS_L,S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PUSH_PROMISE */
103{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PING */
104{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* GOAWAY */
105{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* WINDOW_UPDATE */
106{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* CONT */
107};
108/* state transisitions when certain frame types are received */
109static int trans_on_recv[][H2_SS_MAX] = {
110/*S_IDLE,S_RS_R, S_RS_L, S_OPEN, S_CL_R, S_CL_L, S_CLS, S_CLN, */
111{ S_ERR, S_ERR, S_ERR, S_NOP, S_ERR, S_NOP, S_NOP, S_NOP, },/* DATA */
112{ S_OPEN,S_CL_L, S_ERR, S_NOP, S_ERR, S_NOP, S_NOP, S_NOP, },/* HEADERS */
113{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* PRIORITY */
114{ S_ERR, S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_NOP, S_NOP, },/* RST_STREAM */
115{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* SETTINGS */
116{ S_RS_R,S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PUSH_PROMISE */
117{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PING */
118{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* GOAWAY */
119{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* WINDOW_UPDATE */
120{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* CONT */
121};
122/* state transisitions when certain events happen */
123static int trans_on_event[][H2_SS_MAX] = {
124/*S_IDLE,S_RS_R, S_RS_L, S_OPEN, S_CL_R, S_CL_L, S_CLS, S_CLN, */
125{ S_XXX, S_ERR, S_ERR, S_CL_L, S_CLS, S_XXX, S_XXX, S_XXX, },/* EV_CLOSED_L*/
126{ S_ERR, S_ERR, S_ERR, S_CL_R, S_ERR, S_CLS, S_NOP, S_NOP, },/* EV_CLOSED_R*/
127{ S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_NOP, S_NOP, },/* EV_CANCELLED*/
128{ S_NOP, S_XXX, S_XXX, S_XXX, S_XXX, S_CLS, S_CLN, S_NOP, },/* EV_EOS_SENT*/
129{ S_NOP, S_XXX, S_CLS, S_XXX, S_XXX, S_CLS, S_XXX, S_XXX, },/* EV_IN_ERROR*/
130};
131
132static int on_map(h2_stream_state_t state, int map[H2_SS_MAX])
133{
134 int op = map[state];
135 switch (op) {
136 case S_XXX:
137 case S_ERR:
138 return op;
139 case S_NOP:
140 return state;
141 default:
142 return op-1;
143 }
144}
145
146static int on_frame(h2_stream_state_t state, int frame_type,
147 int frame_map[][H2_SS_MAX], apr_size_t maxlen)
148{
149 ap_assert(frame_type >= 0);
150 ap_assert(state >= 0);
151 if ((apr_size_t)frame_type >= maxlen) {
152 return state; /* NOP, ignore unknown frame types */
153 }
154 return on_map(state, frame_map[frame_type]);
155}
156
158{
160}
161
163{
165}
166
168{
170 if (stream->monitor && stream->monitor->on_event) {
171 stream->monitor->on_event(stream->monitor->ctx, stream, ev);
172 }
173 if (ev < H2_ALEN(trans_on_event)) {
174 return on_map(stream->state, trans_on_event[ev]);
175 }
176 return stream->state;
177}
178
180 int32_t stream_id,
181 uint8_t *buf,
182 size_t length,
185 void *puser);
186
187static void H2_STREAM_OUT_LOG(int lvl, h2_stream *s, const char *tag)
188{
189 if (APLOG_C_IS_LEVEL(s->session->c1, lvl)) {
190 conn_rec *c = s->session->c1;
191 char buffer[4 * 1024];
192 apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]);
193
194 len = h2_util_bb_print(buffer, bmax, tag, "", s->out_buffer);
196 H2_STRM_MSG(s, "out-buffer(%s)"), len? buffer : "empty");
197 }
198}
199
200static void stream_setup_input(h2_stream *stream)
201{
202 if (stream->input != NULL) return;
203 ap_assert(!stream->input_closed);
205 H2_STRM_MSG(stream, "setup input beam"));
206 h2_beam_create(&stream->input, stream->session->c1,
207 stream->pool, stream->id,
208 "input", 0, stream->session->s->timeout);
209}
210
212{
213 /* Right before processing starts, last chance to decide if
214 * there is need to an input beam. */
215 if (!stream->input_closed) {
216 stream_setup_input(stream);
217 }
218 return APR_SUCCESS;
219}
220
222{
223 return !stream->in_buffer || APR_BRIGADE_EMPTY(stream->in_buffer);
224}
225
227{
229 apr_off_t written;
230
231 if (input_buffer_is_empty(stream)) goto cleanup;
232
234 H2_STRM_MSG(stream, "flush input"));
235 status = h2_beam_send(stream->input, stream->session->c1,
236 stream->in_buffer, APR_BLOCK_READ, &written);
237 stream->in_last_write = apr_time_now();
240 H2_STRM_MSG(stream, "send input error"));
242 }
243cleanup:
244 return status;
245}
246
248{
249 if (!stream->in_buffer) {
250 stream_setup_input(stream);
252 stream->pool, stream->session->c1->bucket_alloc);
253 }
255}
256
257static void input_append_data(h2_stream *stream, const char *data, apr_size_t len)
258{
259 if (!stream->in_buffer) {
260 stream_setup_input(stream);
262 stream->pool, stream->session->c1->bucket_alloc);
263 }
265}
266
267
269{
270 conn_rec *c = stream->session->c1;
272 apr_bucket *b;
273
274 if (stream->input_closed) goto cleanup;
275
277 H2_STRM_MSG(stream, "closing input"));
278 if (!stream->rst_error
279 && stream->trailers_in
280 && !apr_is_empty_table(stream->trailers_in)) {
282 H2_STRM_MSG(stream, "adding trailers"));
283#if AP_HAS_RESPONSE_BUCKETS
285 stream->pool, c->bucket_alloc);
286#else
287 b = h2_bucket_headers_create(c->bucket_alloc,
289 stream->in_trailer_octets, stream->pool));
290#endif
291 input_append_bucket(stream, b);
292 stream->trailers_in = NULL;
293 }
294
295 stream->input_closed = 1;
296 if (stream->input) {
297 b = apr_bucket_eos_create(c->bucket_alloc);
298 input_append_bucket(stream, b);
299 input_flush(stream);
302 H2_STRM_MSG(stream, "input flush + EOS"));
303 }
304
305cleanup:
306 return rv;
307}
308
309static void on_state_enter(h2_stream *stream)
310{
311 if (stream->monitor && stream->monitor->on_state_enter) {
312 stream->monitor->on_state_enter(stream->monitor->ctx, stream);
313 }
314}
315
317{
318 if (stream->monitor && stream->monitor->on_state_event) {
319 stream->monitor->on_state_event(stream->monitor->ctx, stream, ev);
320 }
321}
322
323static void on_state_invalid(h2_stream *stream)
324{
325 if (stream->monitor && stream->monitor->on_state_invalid) {
326 stream->monitor->on_state_invalid(stream->monitor->ctx, stream);
327 }
328 /* stream got an event/frame invalid in its state */
330 H2_STRM_MSG(stream, "invalid state event"));
331 switch (stream->state) {
332 case H2_SS_OPEN:
333 case H2_SS_RSVD_L:
334 case H2_SS_RSVD_R:
335 case H2_SS_CLOSED_L:
336 case H2_SS_CLOSED_R:
338 break;
339 default:
340 break;
341 }
342}
343
345{
346 if ((h2_stream_state_t)new_state == stream->state) {
347 return APR_SUCCESS;
348 }
349 else if (new_state < 0) {
351 H2_STRM_LOG(APLOGNO(03081), stream, "invalid transition"));
352 on_state_invalid(stream);
353 return APR_EINVAL;
354 }
355
357 H2_STRM_MSG(stream, "transit to [%s]"), h2_ss_str(new_state));
358 stream->state = new_state;
359 switch (new_state) {
360 case H2_SS_IDLE:
361 break;
362 case H2_SS_RSVD_L:
363 close_input(stream);
364 break;
365 case H2_SS_RSVD_R:
366 break;
367 case H2_SS_OPEN:
368 break;
369 case H2_SS_CLOSED_L:
370 break;
371 case H2_SS_CLOSED_R:
372 close_input(stream);
373 break;
374 case H2_SS_CLOSED:
375 close_input(stream);
376 if (stream->out_buffer) {
378 }
379 break;
380 case H2_SS_CLEANUP:
381 break;
382 }
383 on_state_enter(stream);
384 return APR_SUCCESS;
385}
386
388{
389 stream->monitor = monitor;
390}
391
393{
394 int new_state;
395
398 H2_STRM_MSG(stream, "dispatch event %d"), ev);
399 new_state = on_event(stream, ev);
400 if (new_state < 0) {
402 H2_STRM_LOG(APLOGNO(10002), stream, "invalid event %d"), ev);
403 on_state_invalid(stream);
405 return;
406 }
407 else if ((h2_stream_state_t)new_state == stream->state) {
408 /* nop */
410 H2_STRM_MSG(stream, "non-state event %d"), ev);
411 return;
412 }
413 else {
414 on_state_event(stream, ev);
415 transit(stream, new_state);
416 }
417}
418
419static void set_policy_for(h2_stream *stream, h2_request *r)
420{
422 stream->push_policy = h2_push_policy_determine(r->headers, stream->pool, enabled);
423}
424
426{
428 int new_state, eos = 0;
429
431 new_state = on_frame_send(stream->state, ftype);
432 if (new_state < 0) {
434 H2_STRM_MSG(stream, "invalid frame %d send"), ftype);
436 return transit(stream, new_state);
437 }
438
439 ++stream->out_frames;
440 stream->out_frame_octets += frame_len;
441 if(stream->c2) {
443 if(conn_ctx)
444 conn_ctx->bytes_sent = stream->out_frame_octets;
445 }
446
447 switch (ftype) {
448 case NGHTTP2_DATA:
450 break;
451
452 case NGHTTP2_HEADERS:
454 break;
455
457 /* start pushed stream */
458 ap_assert(stream->request == NULL);
459 ap_assert(stream->rtmp != NULL);
460 status = h2_stream_end_headers(stream, 1, 0);
461 if (status != APR_SUCCESS) goto leave;
462 break;
463
464 default:
465 break;
466 }
467 status = transit(stream, new_state);
468 if (status == APR_SUCCESS && eos) {
469 status = transit(stream, on_event(stream, H2_SEV_CLOSED_L));
470 }
471leave:
472 return status;
473}
474
476{
478 int new_state, eos = 0;
479
481 new_state = on_frame_recv(stream->state, ftype);
482 if (new_state < 0) {
484 H2_STRM_MSG(stream, "invalid frame %d recv"), ftype);
486 return transit(stream, new_state);
487 }
488
489 switch (ftype) {
490 case NGHTTP2_DATA:
492 break;
493
494 case NGHTTP2_HEADERS:
496 if (h2_stream_is_at_or_past(stream, H2_SS_OPEN)) {
497 /* trailer HEADER */
498 if (!eos) {
500 }
501 stream->in_trailer_octets += frame_len;
502 }
503 else {
504 /* request HEADER */
505 ap_assert(stream->request == NULL);
506 if (stream->rtmp == NULL) {
507 /* This can only happen, if the stream has received no header
508 * name/value pairs at all. The latest nghttp2 version have become
509 * pretty good at detecting this early. In any case, we have
510 * to abort the connection here, since this is clearly a protocol error */
511 return APR_EINVAL;
512 }
514 if (status != APR_SUCCESS) goto leave;
515 }
516 break;
517
518 default:
519 break;
520 }
521 status = transit(stream, new_state);
522 if (status == APR_SUCCESS && eos) {
523 status = transit(stream, on_event(stream, H2_SEV_CLOSED_R));
524 }
525leave:
526 return status;
527}
528
530 const uint8_t *data, size_t len)
531{
532 h2_session *session = stream->session;
534
536 stream->in_data_frames++;
537 if (len > 0) {
538 if (APLOGctrace3(session->c1)) {
539 const char *load = apr_pstrndup(stream->pool, (const char *)data, len);
541 H2_STRM_MSG(stream, "recv DATA, len=%d: -->%s<--"),
542 (int)len, load);
543 }
544 else {
546 H2_STRM_MSG(stream, "recv DATA, len=%d"), (int)len);
547 }
548 stream->in_data_octets += len;
549 input_append_data(stream, (const char*)data, len);
550 input_flush(stream);
552 }
553 return status;
554}
555
556#ifdef AP_DEBUG
558{
559 h2_stream *stream = data;
560 switch (stream->magic) {
561 case H2_STRM_MAGIC_OK:
563 H2_STRM_MSG(stream, "was not destroyed explicitly"));
565 break;
567 /* stream has been explicitly destroyed, as it should */
569 break;
572 H2_STRM_MSG(stream, "already pool destroyed"));
574 break;
575 default:
577 }
578 return APR_SUCCESS;
579}
580#endif
581
583 h2_stream_monitor *monitor, int initiated_on)
584{
585 h2_stream *stream = apr_pcalloc(pool, sizeof(h2_stream));
586
588 stream->id = id;
589 stream->initiated_on = initiated_on;
590 stream->created = apr_time_now();
591 stream->state = H2_SS_IDLE;
592 stream->pool = pool;
593 stream->session = session;
594 stream->monitor = monitor;
595#ifdef AP_DEBUG
596 if (id) { /* stream 0 has special lifetime */
599 }
600#endif
601
602#ifdef H2_NG2_LOCAL_WIN_SIZE
603 if (id) {
604 stream->in_window_size =
606 stream->session->ngh2, stream->id);
607 }
608#endif
610 H2_STRM_LOG(APLOGNO(03082), stream, "created"));
611 on_state_enter(stream);
612 return stream;
613}
614
616{
617 /* Stream is done on c1. There might still be processing on a c2
618 * going on. The input/output beams get aborted and the stream's
619 * end of the in/out notifications get closed.
620 */
621 ap_assert(stream);
623 if (stream->out_buffer) {
625 }
626}
627
629{
630 ap_assert(stream);
633 H2_STRM_MSG(stream, "destroy"));
635 apr_pool_destroy(stream->pool);
636}
637
638void h2_stream_rst(h2_stream *stream, int error_code)
639{
641 stream->rst_error = error_code;
642 if (stream->c2) {
643 h2_c2_abort(stream->c2, stream->session->c1);
644 }
646 H2_STRM_MSG(stream, "reset, error=%d"), error_code);
648}
649
651 request_rec *r, int eos)
652{
653 h2_request *req;
655
657 ap_assert(stream->request == NULL);
658 ap_assert(stream->rtmp == NULL);
659 if (stream->rst_error) {
660 return APR_ECONNRESET;
661 }
662 status = h2_request_rcreate(&req, stream->pool, r);
663 if (status == APR_SUCCESS) {
665 H2_STRM_LOG(APLOGNO(03058), stream,
666 "set_request_rec %s host=%s://%s%s"),
667 req->method, req->scheme, req->authority, req->path);
668 stream->rtmp = req;
669 /* simulate the frames that led to this */
672 }
673 return status;
674}
675
677{
679 ap_assert(stream->request == NULL);
680 ap_assert(stream->rtmp == NULL);
681 stream->rtmp = h2_request_clone(stream->pool, r);
682}
683
684static void set_error_response(h2_stream *stream, int http_status)
685{
686 if (!h2_stream_is_ready(stream) && stream->rtmp) {
687 stream->rtmp->http_status = http_status;
688 }
689}
690
692 const char *name, size_t nlen,
693 const char *value, size_t vlen,
694 size_t max_field_len, int *pwas_added)
695{
696 conn_rec *c = stream->session->c1;
697 char *hname, *hvalue;
698 const char *existing;
699
700 *pwas_added = 0;
701 if (nlen == 0 || name[0] == ':') {
703 H2_STRM_LOG(APLOGNO(03060), stream,
704 "pseudo header in trailer"));
705 return APR_EINVAL;
706 }
708 return APR_SUCCESS;
709 }
710 if (!stream->trailers_in) {
711 stream->trailers_in = apr_table_make(stream->pool, 5);
712 }
713 hname = apr_pstrndup(stream->pool, name, nlen);
716 if (max_field_len
717 && ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len)) {
718 /* "key: (oldval, )?nval" is too long */
719 return APR_EINVAL;
720 }
721 if (!existing) *pwas_added = 1;
722 hvalue = apr_pstrndup(stream->pool, value, vlen);
725 H2_STRM_MSG(stream, "added trailer '%s: %s'"), hname, hvalue);
726
727 return APR_SUCCESS;
728}
729
731 const char *name, size_t nlen,
732 const char *value, size_t vlen)
733{
734 h2_session *session = stream->session;
735 int error = 0, was_added = 0;
737
739 if (stream->response) {
740 return APR_EINVAL;
741 }
742
743 if (name[0] == ':') {
744 if (vlen > APR_INT32_MAX || (int)vlen > session->s->limit_req_line) {
745 /* pseudo header: approximation of request line size check */
746 if (!h2_stream_is_ready(stream)) {
748 H2_STRM_LOG(APLOGNO(10178), stream,
749 "Request pseudo header exceeds "
750 "LimitRequestFieldSize: %s"), name);
751 }
753 goto cleanup;
754 }
755 }
756
757 if (session->s->limit_req_fields > 0
758 && stream->request_headers_added > session->s->limit_req_fields) {
759 /* already over limit, count this attempt, but do not take it in */
760 ++stream->request_headers_added;
761 }
762 else if (H2_SS_IDLE == stream->state) {
763 if (!stream->rtmp) {
764 stream->rtmp = h2_request_create(stream->id, stream->pool,
765 NULL, NULL, NULL, NULL, NULL);
766 }
767 status = h2_request_add_header(stream->rtmp, stream->pool,
768 name, nlen, value, vlen,
769 session->s->limit_req_fieldsize, &was_added);
771 H2_STRM_MSG(stream, "add_header: '%.*s: %.*s"),
772 (int)nlen, name, (int)vlen, value);
773 if (was_added) ++stream->request_headers_added;
774 }
775 else if (H2_SS_OPEN == stream->state) {
776 status = add_trailer(stream, name, nlen, value, vlen,
777 session->s->limit_req_fieldsize, &was_added);
778 if (was_added) ++stream->request_headers_added;
779 }
780 else {
782 goto cleanup;
783 }
784
785 if (APR_EINVAL == status) {
786 /* header too long */
787 if (!h2_stream_is_ready(stream)) {
789 H2_STRM_LOG(APLOGNO(10180), stream,"Request header exceeds "
790 "LimitRequestFieldSize: %.*s"),
791 (int)H2MIN(nlen, 80), name);
792 }
794 goto cleanup;
795 }
796
797 if (session->s->limit_req_fields > 0
798 && stream->request_headers_added > session->s->limit_req_fields) {
799 /* too many header lines */
800 if (stream->request_headers_added > session->s->limit_req_fields + 100) {
801 /* yeah, right, this request is way over the limit, say goodbye */
803 return APR_ECONNRESET;
804 }
805 if (!h2_stream_is_ready(stream)) {
807 H2_STRM_LOG(APLOGNO(10181), stream, "Number of request headers "
808 "exceeds LimitRequestFields"));
809 }
811 goto cleanup;
812 }
813
814cleanup:
815 if (error) {
816 ++stream->request_headers_failed;
817 set_error_response(stream, error);
818 return APR_EINVAL;
819 }
820 else if (status != APR_SUCCESS) {
822 H2_STRM_MSG(stream, "header %s not accepted"), name);
824 }
825 return status;
826}
827
828typedef struct {
830 const char *failed_key;
832
833static int table_check_val_len(void *baton, const char *key, const char *value)
834{
836
837 if (strlen(value) <= ctx->maxlen) return 1;
838 ctx->failed_key = key;
839 return 0;
840}
841
842apr_status_t h2_stream_end_headers(h2_stream *stream, int eos, size_t raw_bytes)
843{
847 h2_request *req = stream->rtmp;
848
850 status = h2_request_end_headers(req, stream->pool, raw_bytes);
852 goto cleanup;
853 }
854
855 /* keep on returning APR_SUCCESS for error responses, so that we
856 * send it and do not RST the stream.
857 */
858 set_policy_for(stream, req);
859
860 ctx.maxlen = stream->session->s->limit_req_fieldsize;
861 ctx.failed_key = NULL;
863 if (ctx.failed_key) {
864 if (!h2_stream_is_ready(stream)) {
866 H2_STRM_LOG(APLOGNO(10230), stream,"Request header exceeds "
867 "LimitRequestFieldSize: %.*s"),
868 (int)H2MIN(strlen(ctx.failed_key), 80), ctx.failed_key);
869 }
871 goto cleanup;
872 }
873
874 /* http(s) scheme. rfc7540, ch. 8.1.2.3:
875 * This [:path] pseudo-header field MUST NOT be empty for "http" or "https"
876 * URIs; "http" or "https" URIs that do not contain a path component
877 * MUST include a value of '/'. The exception to this rule is an
878 * OPTIONS request for an "http" or "https" URI that does not include
879 * a path component; these MUST include a ":path" pseudo-header field
880 * with a value of '*'
881 *
882 * All HTTP/2 requests MUST include exactly one valid value for the
883 * ":method", ":scheme", and ":path" pseudo-header fields, unless it is
884 * a CONNECT request.
885 */
886 is_http_or_https = (!req->scheme
887 || !(ap_cstr_casecmpn(req->scheme, "http", 4) != 0
888 || (req->scheme[4] != '\0'
889 && (apr_tolower(req->scheme[4]) != 's'
890 || req->scheme[5] != '\0'))));
891
892 /* CONNECT. rfc7540, ch. 8.3:
893 * In HTTP/2, the CONNECT method is used to establish a tunnel over a
894 * single HTTP/2 stream to a remote host for similar purposes. The HTTP
895 * header field mapping works as defined in Section 8.1.2.3 ("Request
896 * Pseudo-Header Fields"), with a few differences. Specifically:
897 * o The ":method" pseudo-header field is set to "CONNECT".
898 * o The ":scheme" and ":path" pseudo-header fields MUST be omitted.
899 * o The ":authority" pseudo-header field contains the host and port to
900 * connect to (equivalent to the authority-form of the request-target
901 * of CONNECT requests (see [RFC7230], Section 5.3)).
902 */
903 if (!ap_cstr_casecmp(req->method, "CONNECT")) {
904 if (req->protocol) {
905 if (!strcmp("websocket", req->protocol)) {
906 if (!req->scheme || !req->path) {
908 H2_STRM_LOG(APLOGNO(10457), stream, "Request to websocket CONNECT "
909 "without :scheme or :path, sending 400 answer"));
911 goto cleanup;
912 }
913 }
914 else {
915 /* do not know that protocol */
917 "':protocol: %s' header present in %s request",
918 req->protocol, req->method);
920 goto cleanup;
921 }
922 }
923 else if (req->scheme || req->path) {
925 H2_STRM_LOG(APLOGNO(10384), stream, "Request to CONNECT "
926 "with :scheme or :path specified, sending 400 answer"));
928 goto cleanup;
929 }
930 }
931 else if (is_http_or_https) {
932 if (!req->path) {
934 H2_STRM_LOG(APLOGNO(10385), stream, "Request for http(s) "
935 "resource without :path, sending 400 answer"));
937 goto cleanup;
938 }
939 if (!req->scheme) {
940 req->scheme = ap_ssl_conn_is_ssl(stream->session->c1)? "https" : "http";
941 }
942 }
943
944 if (req->scheme && (req->path && req->path[0] != '/')) {
945 /* We still have a scheme, which means we need to pass an absolute URI into
946 * our HTTP protocol handling and the missing '/' at the start will prevent
947 * us from doing so (as it then confuses path and authority). */
949 H2_STRM_LOG(APLOGNO(10379), stream, "Request :scheme '%s' and "
950 "path '%s' do not allow creating an absolute URL. Failing "
951 "request with 400."), req->scheme, req->path);
953 goto cleanup;
954 }
955
956cleanup:
957 if (APR_SUCCESS == status) {
958 stream->request = req;
959 stream->rtmp = NULL;
960
961 if (APLOGctrace4(stream->session->c1)) {
962 int i;
966 H2_STRM_MSG(stream,"headers received from client:"));
967 for (i = 0; i < t_h->nelts; i++, t_elt++) {
969 H2_STRM_MSG(stream, " %s: %s"),
970 ap_escape_logitem(stream->pool, t_elt->key),
971 ap_escape_logitem(stream->pool, t_elt->val));
972 }
973 }
974 }
975 return status;
976}
977
979{
980 if (bb) {
982 while (b != APR_BRIGADE_SENTINEL(bb)) {
983#if AP_HAS_RESPONSE_BUCKETS
985 return b;
986 }
987#else
988 if (H2_BUCKET_IS_HEADERS(b)) {
989 return b;
990 }
991#endif
993 }
994 }
995 return NULL;
996}
997
999{
1000 int err = ((ap_bucket_error *)(b->data))->status;
1002 H2_STRM_MSG(stream, "error bucket received, err=%d"), err);
1003 if (err >= 500) {
1005 }
1006 else if (err >= 400) {
1008 }
1009 else {
1011 }
1012 h2_stream_rst(stream, err);
1013}
1014
1016{
1019 conn_rec *c1 = stream->session->c1;
1020 apr_bucket *b, *e;
1021
1022 if (!stream->output) {
1023 goto cleanup;
1024 }
1025 if (stream->rst_error) {
1026 rv = APR_ECONNRESET;
1027 goto cleanup;
1028 }
1029
1030 if (!stream->out_buffer) {
1031 stream->out_buffer = apr_brigade_create(stream->pool, c1->bucket_alloc);
1032 buf_len = 0;
1033 }
1034 else {
1035 /* if the brigade contains a file bucket, its normal report length
1036 * might be megabytes, but the memory used is tiny. For buffering,
1037 * we are only interested in the memory footprint. */
1039 }
1040
1042 || (apr_size_t)buf_len >= stream->session->max_stream_mem) {
1043 /* we have buffered enough. No need to read more.
1044 * However, we have now output pending for which we may not
1045 * receive another poll event. We need to make sure that this
1046 * stream is not suspended so we keep on processing output.
1047 */
1049 H2_STRM_MSG(stream, "out_buffer, already has %ld length"),
1050 (long)buf_len);
1051 rv = APR_SUCCESS;
1052 goto cleanup;
1053 }
1054
1055 if (stream->output_eos) {
1057 }
1058 else {
1059 H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "pre");
1060 rv = h2_beam_receive(stream->output, stream->session->c1, stream->out_buffer,
1062 if (APR_SUCCESS != rv) {
1063 if (APR_EAGAIN != rv) {
1065 H2_STRM_MSG(stream, "out_buffer, receive unsuccessful"));
1066 }
1067 }
1068 }
1069
1070 /* get rid of buckets we have no need for */
1071 if (!APR_BRIGADE_EMPTY(stream->out_buffer)) {
1072 b = APR_BRIGADE_FIRST(stream->out_buffer);
1073 while (b != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
1074 e = APR_BUCKET_NEXT(b);
1076 if (APR_BUCKET_IS_FLUSH(b)) { /* we flush any c1 data already */
1079 }
1080 else if (APR_BUCKET_IS_EOS(b)) {
1081 stream->output_eos = 1;
1082 }
1083 else if (AP_BUCKET_IS_ERROR(b)) {
1084 stream_do_error_bucket(stream, b);
1085 break;
1086 }
1087 }
1088 else if (b->length == 0) { /* zero length data */
1091 }
1092 b = e;
1093 }
1094 }
1095 H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "out_buffer, after receive");
1096
1097cleanup:
1098 return rv;
1099}
1100
1102{
1103#if AP_HAS_RESPONSE_BUCKETS
1104 return !AP_BUCKET_IS_RESPONSE(b)
1106 && !APR_BUCKET_IS_EOS(b);
1107#else
1109#endif
1110}
1111
1113 apr_off_t *plen, int *peos)
1114{
1116
1118 if (stream->rst_error) {
1119 return APR_ECONNRESET;
1120 }
1122 if (APR_SUCCESS == rv && !*peos && !*plen) {
1123 rv = APR_EAGAIN;
1124 }
1125 return rv;
1126}
1127
1129{
1130 conn_rec *c1 = stream->session->c1;
1131 int ngrv;
1132 h2_ngheader *nh = NULL;
1133 apr_bucket *b, *e;
1134#if AP_HAS_RESPONSE_BUCKETS
1135 ap_bucket_headers *headers = NULL;
1136#else
1137 h2_headers *headers = NULL;
1138#endif
1139 apr_status_t rv;
1140
1141 ap_assert(stream->response);
1142 ap_assert(stream->out_buffer);
1143
1144 b = APR_BRIGADE_FIRST(stream->out_buffer);
1145 while (b != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
1146 e = APR_BUCKET_NEXT(b);
1148#if AP_HAS_RESPONSE_BUCKETS
1149 if (AP_BUCKET_IS_HEADERS(b)) {
1150 headers = b->data;
1151#else /* AP_HAS_RESPONSE_BUCKETS */
1152 if (H2_BUCKET_IS_HEADERS(b)) {
1153 headers = h2_bucket_headers_get(b);
1154#endif /* else AP_HAS_RESPONSE_BUCKETS */
1158 H2_STRM_MSG(stream, "process trailers"));
1159 break;
1160 }
1161 else if (APR_BUCKET_IS_EOS(b)) {
1162 break;
1163 }
1164 }
1165 else {
1166 break;
1167 }
1168 b = e;
1169 }
1170
1171 if (!headers) {
1172 rv = APR_EAGAIN;
1173 goto cleanup;
1174 }
1175
1176 rv = h2_res_create_ngtrailer(&nh, stream->pool, headers);
1178 H2_STRM_LOG(APLOGNO(03072), stream, "submit %d trailers"),
1179 (int)nh->nvlen);
1180 if (APR_SUCCESS != rv) {
1182 H2_STRM_LOG(APLOGNO(10024), stream, "invalid trailers"));
1184 goto cleanup;
1185 }
1186
1187 ngrv = nghttp2_submit_trailer(stream->session->ngh2, stream->id, nh->nv, nh->nvlen);
1188 if (nghttp2_is_fatal(ngrv)) {
1189 rv = APR_EGENERAL;
1193 APLOGNO(02940) "submit_response: %s",
1194 nghttp2_strerror(rv));
1195 }
1196 stream->sent_trailers = 1;
1197
1198cleanup:
1199 return rv;
1200}
1201
1202#if AP_HAS_RESPONSE_BUCKETS
1204#else
1206#endif
1207{
1209 apr_array_header_t *pushes;
1210 int i;
1211
1213 pushes = h2_push_collect_update(stream, stream->request, response);
1214 if (pushes && !apr_is_empty_array(pushes)) {
1216 H2_STRM_MSG(stream, "found %d push candidates"),
1217 pushes->nelts);
1218 for (i = 0; i < pushes->nelts; ++i) {
1219 h2_push *push = APR_ARRAY_IDX(pushes, i, h2_push*);
1220 h2_stream *s = h2_session_push(stream->session, stream, push);
1221 if (!s) {
1223 break;
1224 }
1225 }
1226 }
1227 return status;
1228}
1229
1235
1236#if AP_HAS_RESPONSE_BUCKETS
1238 ap_bucket_response *response)
1239#else
1241 h2_headers *response)
1242#endif
1243{
1245 if (response && stream->initiated_on) {
1246 const char *ctype = apr_table_get(response->headers, "content-type");
1247 if (ctype) {
1248 /* FIXME: Not good enough, config needs to come from request->server */
1249 return h2_cconfig_get_priority(stream->session->c1, ctype);
1250 }
1251 }
1252 return NULL;
1253}
1254
1256{
1257 /* Have we sent a response or do we have the response in our buffer? */
1259 if (stream->response) {
1260 return 1;
1261 }
1262 else if (stream->out_buffer && get_first_response_bucket(stream->out_buffer)) {
1263 return 1;
1264 }
1265 return 0;
1266}
1267
1269{
1271 return h2_stream_is_ready(stream) &&
1272 ((stream->out_buffer && !APR_BRIGADE_EMPTY(stream->out_buffer)) ||
1273 (stream->output && !h2_beam_empty(stream->output)));
1274}
1275
1277{
1279 return stream->state == state;
1280}
1281
1283{
1285 switch (state) {
1286 case H2_SS_IDLE:
1287 return 1; /* by definition */
1288 case H2_SS_RSVD_R: /*fall through*/
1289 case H2_SS_RSVD_L: /*fall through*/
1290 case H2_SS_OPEN:
1291 return stream->state == state || stream->state >= H2_SS_OPEN;
1292 case H2_SS_CLOSED_R: /*fall through*/
1293 case H2_SS_CLOSED_L: /*fall through*/
1294 case H2_SS_CLOSED:
1295 return stream->state == state || stream->state >= H2_SS_CLOSED;
1296 case H2_SS_CLEANUP:
1297 return stream->state == state;
1298 default:
1299 return 0;
1300 }
1301}
1302
1304{
1305 h2_session *session = stream->session;
1306
1308 if (amount > 0) {
1310
1311 while (consumed > 0) {
1312 int len = (consumed > INT_MAX)? INT_MAX : (int)consumed;
1313 nghttp2_session_consume(session->ngh2, stream->id, len);
1314 consumed -= len;
1315 }
1316
1317#ifdef H2_NG2_LOCAL_WIN_SIZE
1318 if (1) {
1320 session->ngh2, stream->id);
1321 int win = stream->in_window_size;
1322 int thigh = win * 8/10;
1323 int tlow = win * 2/10;
1324 const int win_max = 2*1024*1024;
1325 const int win_min = 32*1024;
1326
1327 /* Work in progress, probably should add directives for these
1328 * values once this stabilizes somewhat. The general idea is
1329 * to adapt stream window sizes if the input window changes
1330 * a) very quickly (< good RTT) from full to empty
1331 * b) only a little bit (> bad RTT)
1332 * where in a) it grows and in b) it shrinks again.
1333 */
1334 if (cur_size > thigh && amount > thigh && win < win_max) {
1335 /* almost empty again with one reported consumption, how
1336 * long did this take? */
1337 long ms = apr_time_msec(apr_time_now() - stream->in_last_write);
1338 if (ms < 40) {
1339 win = H2MIN(win_max, win + (64*1024));
1340 }
1341 }
1343 /* staying full, for how long already? */
1344 long ms = apr_time_msec(apr_time_now() - stream->in_last_write);
1345 if (ms > 700) {
1346 win = H2MAX(win_min, win - (32*1024));
1347 }
1348 }
1349
1350 if (win != stream->in_window_size) {
1351 stream->in_window_size = win;
1353 NGHTTP2_FLAG_NONE, stream->id, win);
1354 }
1356 H2_STRM_MSG(stream, "consumed %ld bytes, window now %d/%d"),
1357 (long)amount, cur_size, stream->in_window_size);
1358 }
1359#endif /* #ifdef H2_NG2_LOCAL_WIN_SIZE */
1360 }
1361 return APR_SUCCESS;
1362}
1363
1365{
1366 /* How much data do we have in our buffers that we can write? */
1367 apr_off_t buf_len = 0;
1368 apr_bucket *b;
1369
1370 *peos = *pheader_blocked = 0;
1371 if (stream->out_buffer) {
1372 b = APR_BRIGADE_FIRST(stream->out_buffer);
1373 while (b != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
1375 if (APR_BUCKET_IS_EOS(b)) {
1376 *peos = 1;
1377 break;
1378 }
1379#if AP_HAS_RESPONSE_BUCKETS
1380 else if (AP_BUCKET_IS_RESPONSE(b)) {
1381 break;
1382 }
1383 else if (AP_BUCKET_IS_HEADERS(b)) {
1384 *pheader_blocked = 1;
1385 break;
1386 }
1387#else
1388 else if (H2_BUCKET_IS_HEADERS(b)) {
1389 *pheader_blocked = 1;
1390 break;
1391 }
1392#endif
1393 }
1394 else {
1395 buf_len += b->length;
1396 }
1397 b = APR_BUCKET_NEXT(b);
1398 }
1399 }
1400 return buf_len;
1401}
1402
1404 int32_t stream_id,
1405 uint8_t *buf,
1406 size_t length,
1409 void *puser)
1410{
1411 h2_session *session = (h2_session *)puser;
1412 conn_rec *c1 = session->c1;
1414 int eos, header_blocked;
1415 apr_status_t rv;
1416 h2_stream *stream;
1417
1418 /* nghttp2 wants to send more DATA for the stream.
1419 * we should have submitted the final response at this time
1420 * after receiving output via stream_do_responses() */
1421 ap_assert(session);
1422 (void)ng2s;
1423 (void)buf;
1424 (void)source;
1425 stream = nghttp2_session_get_stream_user_data(session->ngh2, stream_id);
1426
1427 if (!stream) {
1429 APLOGNO(02937)
1430 H2_SSSN_STRM_MSG(session, stream_id, "data_cb, stream not found"));
1432 }
1434 if (!stream->output || !stream->response || !stream->out_buffer) {
1435 return NGHTTP2_ERR_DEFERRED;
1436 }
1437 if (stream->rst_error) {
1438 return NGHTTP2_ERR_DEFERRED;
1439 }
1440 if (h2_c1_io_needs_flush(&session->io)) {
1441 rv = h2_c1_io_pass(&session->io);
1442 if (APR_STATUS_IS_EAGAIN(rv)) {
1444 H2_SSSN_STRM_MSG(session, stream_id, "suspending on c1 out needs flush"));
1446 return NGHTTP2_ERR_DEFERRED;
1447 }
1448 else if (rv) {
1451 }
1452 }
1453
1454 /* determine how much we'd like to send. We cannot send more than
1455 * is requested. But we can reduce the size in case the master
1456 * connection operates in smaller chunks. (TSL warmup) */
1457 if (stream->session->io.write_size > 0) {
1459 if (length > chunk_len) {
1460 length = chunk_len;
1461 }
1462 }
1463 /* We allow configurable max DATA frame length. */
1464 if (stream->session->max_data_frame_len > 0
1465 && length > stream->session->max_data_frame_len) {
1467 }
1468
1469 /* How much data do we have in our buffers that we can write?
1470 * if not enough, receive more. */
1472 if (buf_len < (apr_off_t)length && !eos
1473 && !header_blocked && !stream->rst_error) {
1474 /* read more? */
1476 H2_SSSN_STRM_MSG(session, stream_id,
1477 "need more (read len=%ld, %ld in buffer)"),
1478 (long)length, (long)buf_len);
1479 rv = buffer_output_receive(stream);
1481 H2_SSSN_STRM_MSG(session, stream_id,
1482 "buffer_output_received"));
1483 if (APR_STATUS_IS_EAGAIN(rv)) {
1484 /* currently, no more is available */
1485 }
1486 else if (APR_SUCCESS == rv) {
1487 /* got some, re-assess */
1489 }
1490 else if (APR_EOF == rv) {
1491 if (!stream->output_eos) {
1492 /* Seeing APR_EOF without an EOS bucket received before indicates
1493 * that stream output is incomplete. Commonly, we expect to see
1494 * an ERROR bucket to have been generated. But faulty handlers
1495 * may not have generated one.
1496 * We need to RST the stream bc otherwise the client thinks
1497 * it is all fine. */
1499 H2_SSSN_STRM_MSG(session, stream_id, "rst stream"));
1501 return NGHTTP2_ERR_DEFERRED;
1502 }
1504 H2_SSSN_STRM_MSG(session, stream_id,
1505 "eof on receive (read len=%ld, %ld in buffer)"),
1506 (long)length, (long)buf_len);
1507 eos = 1;
1508 rv = APR_SUCCESS;
1509 }
1510 else if (APR_ECONNRESET == rv || APR_ECONNABORTED == rv) {
1512 H2_STRM_LOG(APLOGNO(10471), stream, "data_cb, reading data"));
1514 return NGHTTP2_ERR_DEFERRED;
1515 }
1516 else {
1518 H2_STRM_LOG(APLOGNO(02938), stream, "data_cb, reading data"));
1520 return NGHTTP2_ERR_DEFERRED;
1521 }
1522 }
1523
1524 if (stream->rst_error) {
1525 return NGHTTP2_ERR_DEFERRED;
1526 }
1527
1528 if (buf_len == 0 && header_blocked) {
1529 rv = stream_do_trailers(stream);
1530 if (APR_SUCCESS != rv && !APR_STATUS_IS_EAGAIN(rv)) {
1532 H2_STRM_LOG(APLOGNO(10300), stream,
1533 "data_cb, error processing trailers"));
1535 }
1536 length = 0;
1537 eos = 0;
1538 }
1539 else if (buf_len > (apr_off_t)length) {
1540 eos = 0; /* Any EOS we have in the buffer does not apply yet */
1541 }
1542 else {
1544 }
1545
1546 if (length) {
1548 H2_STRM_MSG(stream, "data_cb, sending len=%ld, eos=%d"),
1549 (long)length, eos);
1551 }
1552 else if (!eos && !stream->sent_trailers) {
1553 /* We have not reached the end of DATA yet, DEFER sending */
1555 H2_STRM_LOG(APLOGNO(03071), stream, "data_cb, suspending"));
1556 return NGHTTP2_ERR_DEFERRED;
1557 }
1558
1559 if (eos) {
1561 }
1562 return length;
1563}
1564
1566{
1567 conn_rec *c1 = stream->session->c1;
1569 int ngrv, is_empty = 0;
1570 h2_ngheader *nh = NULL;
1571 apr_bucket *b, *e;
1572#if AP_HAS_RESPONSE_BUCKETS
1574#else
1575 h2_headers *resp = NULL;
1576#endif
1578
1580 ap_assert(!stream->response);
1581 ap_assert(stream->out_buffer);
1582
1583 b = APR_BRIGADE_FIRST(stream->out_buffer);
1584 while (b != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
1585 e = APR_BUCKET_NEXT(b);
1587#if AP_HAS_RESPONSE_BUCKETS
1588 if (AP_BUCKET_IS_RESPONSE(b)) {
1589 resp = b->data;
1590#else /* AP_HAS_RESPONSE_BUCKETS */
1591 if (H2_BUCKET_IS_HEADERS(b)) {
1593#endif /* else AP_HAS_RESPONSE_BUCKETS */
1597 H2_STRM_MSG(stream, "process response %d"),
1598 resp->status);
1600 && APR_BUCKET_IS_EOS(e));
1601 break;
1602 }
1603 else if (APR_BUCKET_IS_EOS(b)) {
1605 rv = APR_EINVAL;
1606 goto cleanup;
1607 }
1608 else if (AP_BUCKET_IS_ERROR(b)) {
1609 stream_do_error_bucket(stream, b);
1610 rv = APR_EINVAL;
1611 goto cleanup;
1612 }
1613 }
1614 else {
1615 /* data buckets before response headers, an error */
1617 rv = APR_EINVAL;
1618 goto cleanup;
1619 }
1620 b = e;
1621 }
1622
1623 if (!resp) {
1624 rv = APR_EAGAIN;
1625 goto cleanup;
1626 }
1627
1628 if (resp->status < 100) {
1629 h2_stream_rst(stream, resp->status);
1630 goto cleanup;
1631 }
1632
1633 if (resp->status == HTTP_FORBIDDEN && resp->notes) {
1634 const char *cause = apr_table_get(resp->notes, "ssl-renegotiate-forbidden");
1635 if (cause) {
1636 /* This request triggered a TLS renegotiation that is not allowed
1637 * in HTTP/2. Tell the client that it should use HTTP/1.1 for this.
1638 */
1640 H2_STRM_LOG(APLOGNO(03061), stream,
1641 "renegotiate forbidden, cause: %s"), cause);
1643 goto cleanup;
1644 }
1645 }
1646
1648 H2_STRM_LOG(APLOGNO(03073), stream,
1649 "submit response %d"), resp->status);
1650
1651 /* If this stream is not a pushed one itself,
1652 * and HTTP/2 server push is enabled here,
1653 * and the response HTTP status is not sth >= 400,
1654 * and the remote side has pushing enabled,
1655 * -> find and perform any pushes on this stream
1656 * *before* we submit the stream response itself.
1657 * This helps clients avoid opening new streams on Link
1658 * resp that get pushed right afterwards.
1659 *
1660 * *) the response code is relevant, as we do not want to
1661 * make pushes on 401 or 403 codes and friends.
1662 * And if we see a 304, we do not push either
1663 * as the client, having this resource in its cache, might
1664 * also have the pushed ones as well.
1665 */
1666 if (!stream->initiated_on
1667 && !stream->response
1668 && stream->request && stream->request->method
1669 && !strcmp("GET", stream->request->method)
1670 && (resp->status < 400)
1671 && (resp->status != 304)
1672 && h2_session_push_enabled(stream->session)) {
1673 /* PUSH is possible and enabled on server, unless the request
1674 * denies it, submit resources to push */
1675 const char *s = apr_table_get(resp->notes, H2_PUSH_MODE_NOTE);
1676 if (!s || strcmp(s, "0")) {
1678 H2_STRM_MSG(stream, "submit pushes, note=%s"), s);
1680 }
1681 }
1682
1683 if (!stream->pref_priority) {
1684 stream->pref_priority = h2_stream_get_priority(stream, resp);
1685 }
1686 h2_session_set_prio(stream->session, stream, stream->pref_priority);
1687
1688 if (resp->status == 103
1690 /* suppress sending this to the client, it might have triggered
1691 * pushes and served its purpose nevertheless */
1692 rv = APR_SUCCESS;
1693 goto cleanup;
1694 }
1695 if (resp->status >= 200) {
1696 stream->response = resp;
1697 }
1698
1699 if (!is_empty) {
1700 memset(&provider, 0, sizeof(provider));
1701 provider.source.fd = stream->id;
1702 provider.read_callback = stream_data_cb;
1703 pprovider = &provider;
1704 }
1705
1706 rv = h2_res_create_ngheader(&nh, stream->pool, resp);
1707 if (APR_SUCCESS != rv) {
1709 H2_STRM_LOG(APLOGNO(10025), stream, "invalid response"));
1711 goto cleanup;
1712 }
1713
1714 ngrv = nghttp2_submit_response(stream->session->ngh2, stream->id,
1715 nh->nv, nh->nvlen, pprovider);
1716 if (nghttp2_is_fatal(ngrv)) {
1717 rv = APR_EGENERAL;
1721 APLOGNO(10402) "submit_response: %s",
1722 nghttp2_strerror(rv));
1723 goto cleanup;
1724 }
1725
1726 if (stream->initiated_on) {
1727 ++stream->session->pushes_submitted;
1728 }
1729 else {
1730 ++stream->session->responses_submitted;
1731 }
1732
1733cleanup:
1734 return rv;
1735}
1736
1737static void stream_do_responses(h2_stream *stream)
1738{
1739 h2_session *session = stream->session;
1740 conn_rec *c1 = session->c1;
1741 apr_status_t rv;
1742
1743 ap_assert(!stream->response);
1745 H2_STRM_MSG(stream, "do_response"));
1746 rv = buffer_output_receive(stream);
1748 H2_SSSN_STRM_MSG(session, stream->id,
1749 "buffer_output_received2"));
1750 if (APR_SUCCESS != rv && APR_EAGAIN != rv) {
1752 }
1753 else {
1754 /* process all headers sitting at the buffer head. */
1755 do {
1756 rv = stream_do_response(stream);
1757 } while (APR_SUCCESS == rv
1758 && !stream->rst_error
1759 && !stream->response);
1760 }
1761}
1762
1764{
1765 conn_rec *c1 = stream->session->c1;
1767
1768 /* stream->pout_recv_write signalled a change. Check what has happend, read
1769 * from it and act on seeing a response/data. */
1771 if (!stream->output) {
1772 /* c2 has not assigned the output beam to the stream (yet). */
1774 H2_STRM_MSG(stream, "read_output, no output beam registered"));
1775 }
1776 else if (h2_stream_is_at_or_past(stream, H2_SS_CLOSED)) {
1778 H2_STRM_LOG(APLOGNO(10301), stream, "already closed"));
1779 }
1780 else if (h2_stream_is_at(stream, H2_SS_CLOSED_L)) {
1781 /* We have delivered a response to a stream that was not closed
1782 * by the client. This could be a POST with body that we negate
1783 * and we need to RST_STREAM to end if. */
1785 H2_STRM_LOG(APLOGNO(10313), stream, "remote close missing"));
1787 }
1788 else {
1789 /* stream is not closed, a change in output happened. There are
1790 * two modes of operation here:
1791 * 1) the final response has been submitted. nghttp2 is invoking
1792 * stream_data_cb() to progress the stream. This handles DATA,
1793 * trailers, EOS and ERRORs.
1794 * When stream_data_cb() runs out of things to send, it returns
1795 * NGHTTP2_ERR_DEFERRED and nghttp2 *suspends* further processing
1796 * until we tell it to resume.
1797 * 2) We have not seen the *final* response yet. The stream can not
1798 * send any response DATA. The nghttp2 stream_data_cb() is not
1799 * invoked. We need to receive output, expecting not DATA but
1800 * RESPONSEs (intermediate may arrive) and submit those. On
1801 * the final response, nghttp2 will start calling stream_data_cb().
1802 */
1803 if (stream->response) {
1804 nghttp2_session_resume_data(stream->session->ngh2, stream->id);
1806 H2_STRM_MSG(stream, "resumed"));
1807 }
1808 else {
1809 stream_do_responses(stream);
1810 if (!stream->rst_error) {
1811 nghttp2_session_resume_data(stream->session->ngh2, stream->id);
1812 }
1813 }
1814 }
1815}
1816
1818{
1820 ap_assert(stream->input);
1822 if (h2_stream_is_at(stream, H2_SS_CLOSED_L)
1823 && !h2_mplx_c1_stream_is_running(stream->session->mplx, stream)) {
1825 H2_STRM_LOG(APLOGNO(10026), stream, "remote close missing"));
1827 }
1828}
const char apr_size_t len
Definition ap_regex.h:187
HANDLE char int int nh
APR general purpose library routines.
APR Strings library.
APR-UTIL string matching routines.
request_rec * r
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_TRACE4
Definition http_log.h:75
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_TRACE3
Definition http_log.h:74
#define ap_log_cerror
Definition http_log.h:498
#define APLOGctrace4(c)
Definition http_log.h:260
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_C_IS_LEVEL(c, level)
Definition http_log.h:225
#define APLOG_TRACE2
Definition http_log.h:73
#define APLOGctrace3(c)
Definition http_log.h:259
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
const unsigned char * buf
Definition util_md5.h:50
#define AP_BUCKET_IS_ERROR(e)
int ap_ssl_conn_is_ssl(conn_rec *c)
Definition ssl.c:90
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
#define APR_EAGAIN
Definition apr_errno.h:730
#define APR_EGENERAL
Definition apr_errno.h:313
#define APR_ECONNRESET
Definition apr_errno.h:776
#define APR_EOF
Definition apr_errno.h:461
#define APR_ECONNABORTED
Definition apr_errno.h:769
#define APR_EINVAL
Definition apr_errno.h:711
#define APR_STATUS_IS_EAGAIN(s)
Definition apr_errno.h:1272
int enabled
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BUCKET_REMOVE(e)
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define APR_BUCKET_NEXT(e)
apr_bucket * e
#define APR_BRIGADE_EMPTY(b)
#define APR_BRIGADE_SENTINEL(b)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
int apr_off_t * length
#define apr_bucket_destroy(e)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
@ APR_NONBLOCK_READ
Definition apr_buckets.h:59
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
const char apr_ssize_t int flags
Definition apr_encode.h:168
apr_memcache_server_t * ms
#define HTTP_OK
Definition httpd.h:490
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_REQUEST_URI_TOO_LARGE
Definition httpd.h:522
#define HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
Definition httpd.h:533
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_NOT_IMPLEMENTED
Definition httpd.h:536
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
char * ap_escape_logitem(apr_pool_t *p, const char *str)
Definition util.c:2183
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define ap_assert(exp)
Definition httpd.h:2271
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define apr_tolower(c)
Definition apr_lib.h:231
const char * value
Definition apr_env.h:51
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
void * data
void const char apr_status_t(* cleanup)(void *))
char * buffer
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_sockaddr_t apr_sockaddr_t apr_sockaddr_t * source
const char apr_uint32_t * id
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
#define APR_ARRAY_IDX(ary, i, type)
Definition apr_tables.h:141
apr_int32_t apr_int32_t apr_int32_t err
int int status
#define apr_time_msec(time)
Definition apr_time.h:69
h2_stream_state_t
Definition h2.h:141
@ H2_SS_RSVD_L
Definition h2.h:144
@ H2_SS_CLOSED
Definition h2.h:148
@ H2_SS_CLOSED_R
Definition h2.h:146
@ H2_SS_CLOSED_L
Definition h2.h:147
@ H2_SS_IDLE
Definition h2.h:142
@ H2_SS_MAX
Definition h2.h:150
@ H2_SS_OPEN
Definition h2.h:145
@ H2_SS_RSVD_R
Definition h2.h:143
@ H2_SS_CLEANUP
Definition h2.h:149
#define H2_ERR_NO_ERROR
Definition h2.h:58
#define H2MAX(x, y)
Definition h2.h:100
#define H2_ALEN(a)
Definition h2.h:98
#define H2_ERR_HTTP_1_1_REQUIRED
Definition h2.h:71
h2_stream_event_t
Definition h2.h:153
@ H2_SEV_IN_ERROR
Definition h2.h:158
@ H2_SEV_CLOSED_L
Definition h2.h:154
@ H2_SEV_CLOSED_R
Definition h2.h:155
@ H2_SEV_IN_DATA_PENDING
Definition h2.h:159
@ H2_SEV_OUT_C1_BLOCK
Definition h2.h:160
@ H2_SEV_CANCELLED
Definition h2.h:156
#define H2_PUSH_MODE_NOTE
Definition h2.h:200
#define H2_ERR_STREAM_CLOSED
Definition h2.h:63
#define H2_FRAME_HDR_LEN
Definition h2.h:86
#define H2_ERR_ENHANCE_YOUR_CALM
Definition h2.h:69
#define H2MIN(x, y)
Definition h2.h:101
#define H2_ERR_PROTOCOL_ERROR
Definition h2.h:59
#define H2_ERR_INTERNAL_ERROR
Definition h2.h:60
#define H2_HTTP_STATUS_UNSET
Definition h2.h:190
int h2_beam_empty(h2_bucket_beam *beam)
apr_status_t h2_beam_create(h2_bucket_beam **pbeam, conn_rec *from, apr_pool_t *pool, int id, const char *tag, apr_size_t max_buf_size, apr_interval_time_t timeout)
apr_status_t h2_beam_send(h2_bucket_beam *beam, conn_rec *from, apr_bucket_brigade *sender_bb, apr_read_type_e block, apr_off_t *pwritten)
int h2_beam_report_consumption(h2_bucket_beam *beam)
apr_status_t h2_beam_receive(h2_bucket_beam *beam, conn_rec *to, apr_bucket_brigade *bb, apr_read_type_e block, apr_off_t readbytes)
apr_status_t h2_c1_io_pass(h2_c1_io *io)
Definition h2_c1_io.c:339
int h2_c1_io_needs_flush(h2_c1_io *io)
Definition h2_c1_io.c:329
void h2_c2_abort(conn_rec *c2, conn_rec *from)
Definition h2_c2.c:157
const struct h2_priority * h2_cconfig_get_priority(conn_rec *c, const char *content_type)
Definition h2_config.c:561
int h2_config_sgeti(server_rec *s, h2_config_var_t var)
Definition h2_config.c:506
@ H2_CONF_EARLY_HINTS
Definition h2_config.h:41
#define h2_conn_ctx_get(c)
Definition h2_conn_ctx.h:78
h2_headers * h2_bucket_headers_get(apr_bucket *b)
Definition h2_headers.c:85
h2_headers * h2_headers_create(int status, const apr_table_t *headers_in, const apr_table_t *notes, apr_off_t raw_bytes, apr_pool_t *pool)
Definition h2_headers.c:119
apr_bucket * h2_bucket_headers_create(apr_bucket_alloc_t *list, h2_headers *r)
Definition h2_headers.c:73
#define H2_BUCKET_IS_HEADERS(e)
Definition h2_headers.h:37
int h2_mplx_c1_stream_is_running(h2_mplx *m, h2_stream *stream)
Definition h2_mplx.c:118
apr_array_header_t * h2_push_collect_update(struct h2_stream *stream, const struct h2_request *req, const struct h2_headers *res)
Definition h2_push.c:692
apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool, request_rec *r)
Definition h2_request.c:77
apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, const char *name, size_t nlen, const char *value, size_t vlen, size_t max_field_len, int *pwas_added)
Definition h2_request.c:134
h2_request * h2_request_create(int id, apr_pool_t *pool, const char *method, const char *scheme, const char *authority, const char *path, apr_table_t *header)
Definition h2_request.c:47
apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, size_t raw_bytes)
Definition h2_request.c:194
h2_request * h2_request_clone(apr_pool_t *p, const h2_request *src)
Definition h2_request.c:216
int h2_session_push_enabled(h2_session *session)
apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, const h2_priority *prio)
void h2_session_dispatch_event(h2_session *session, h2_session_event_t ev, apr_status_t arg, const char *msg)
struct h2_stream * h2_session_push(h2_session *session, h2_stream *is, h2_push *push)
@ H2_SESSION_EV_PROTO_ERROR
Definition h2_session.h:56
@ H2_SESSION_EV_CONN_ERROR
Definition h2_session.h:55
#define H2_SSSN_STRM_MSG(s, stream_id, msg)
Definition h2_session.h:203
static void H2_STREAM_OUT_LOG(int lvl, h2_stream *s, const char *tag)
Definition h2_stream.c:187
static int trans_on_event[][H2_SS_MAX]
Definition h2_stream.c:123
static int on_event(h2_stream *stream, h2_stream_event_t ev)
Definition h2_stream.c:167
static int trans_on_recv[][H2_SS_MAX]
Definition h2_stream.c:109
static void stream_do_responses(h2_stream *stream)
Definition h2_stream.c:1737
#define S_RS_R
Definition h2_stream.c:87
apr_status_t h2_stream_add_header(h2_stream *stream, const char *name, size_t nlen, const char *value, size_t vlen)
Definition h2_stream.c:730
apr_status_t h2_stream_set_request_rec(h2_stream *stream, request_rec *r, int eos)
Definition h2_stream.c:650
void h2_stream_dispatch(h2_stream *stream, h2_stream_event_t ev)
Definition h2_stream.c:392
void h2_stream_set_request(h2_stream *stream, const h2_request *r)
Definition h2_stream.c:676
static const char * h2_ss_str(const h2_stream_state_t state)
Definition h2_stream.c:52
static apr_status_t add_trailer(h2_stream *stream, const char *name, size_t nlen, const char *value, size_t vlen, size_t max_field_len, int *pwas_added)
Definition h2_stream.c:691
static apr_status_t transit(h2_stream *stream, int new_state)
Definition h2_stream.c:344
h2_stream * h2_stream_create(int id, apr_pool_t *pool, h2_session *session, h2_stream_monitor *monitor, int initiated_on)
Definition h2_stream.c:582
apr_status_t h2_stream_recv_DATA(h2_stream *stream, uint8_t flags, const uint8_t *data, size_t len)
Definition h2_stream.c:529
apr_status_t h2_stream_send_frame(h2_stream *stream, int ftype, int flags, size_t frame_len)
Definition h2_stream.c:425
const char * h2_stream_state_str(const h2_stream *stream)
Definition h2_stream.c:76
#define S_XXX
Definition h2_stream.c:82
const h2_priority * h2_stream_get_priority(h2_stream *stream, h2_headers *response)
Definition h2_stream.c:1240
static void set_error_response(h2_stream *stream, int http_status)
Definition h2_stream.c:684
#define S_ERR
Definition h2_stream.c:83
static int table_check_val_len(void *baton, const char *key, const char *value)
Definition h2_stream.c:833
static int bucket_pass_to_c1(apr_bucket *b)
Definition h2_stream.c:1101
apr_status_t h2_stream_in_consumed(h2_stream *stream, apr_off_t amount)
Definition h2_stream.c:1303
int h2_stream_is_at(const h2_stream *stream, h2_stream_state_t state)
Definition h2_stream.c:1276
static void stream_setup_input(h2_stream *stream)
Definition h2_stream.c:200
#define S_RS_L
Definition h2_stream.c:86
static void on_state_invalid(h2_stream *stream)
Definition h2_stream.c:323
void h2_stream_destroy(h2_stream *stream)
Definition h2_stream.c:628
void h2_stream_set_monitor(h2_stream *stream, h2_stream_monitor *monitor)
Definition h2_stream.c:387
void h2_stream_cleanup(h2_stream *stream)
Definition h2_stream.c:615
static apr_status_t close_input(h2_stream *stream)
Definition h2_stream.c:268
void h2_stream_on_output_change(h2_stream *stream)
Definition h2_stream.c:1763
#define S_CLN
Definition h2_stream.c:92
static apr_status_t input_flush(h2_stream *stream)
Definition h2_stream.c:226
void h2_stream_rst(h2_stream *stream, int error_code)
Definition h2_stream.c:638
static int input_buffer_is_empty(h2_stream *stream)
Definition h2_stream.c:221
static ssize_t stream_data_cb(nghttp2_session *ng2s, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *puser)
Definition h2_stream.c:1403
apr_status_t h2_stream_end_headers(h2_stream *stream, int eos, size_t raw_bytes)
Definition h2_stream.c:842
static apr_status_t stream_do_response(h2_stream *stream)
Definition h2_stream.c:1565
#define S_CL_R
Definition h2_stream.c:90
static void on_state_event(h2_stream *stream, h2_stream_event_t ev)
Definition h2_stream.c:316
int h2_stream_is_at_or_past(const h2_stream *stream, h2_stream_state_t state)
Definition h2_stream.c:1282
static void on_state_enter(h2_stream *stream)
Definition h2_stream.c:309
static void input_append_bucket(h2_stream *stream, apr_bucket *b)
Definition h2_stream.c:247
static apr_off_t output_data_buffered(h2_stream *stream, int *peos, int *pheader_blocked)
Definition h2_stream.c:1364
void h2_stream_on_input_change(h2_stream *stream)
Definition h2_stream.c:1817
apr_status_t h2_stream_prepare_processing(h2_stream *stream)
Definition h2_stream.c:211
int h2_stream_is_ready(h2_stream *stream)
Definition h2_stream.c:1255
int h2_stream_wants_send_data(h2_stream *stream)
Definition h2_stream.c:1268
static int on_frame_send(h2_stream_state_t state, int frame_type)
Definition h2_stream.c:157
static int on_frame(h2_stream_state_t state, int frame_type, int frame_map[][H2_SS_MAX], apr_size_t maxlen)
Definition h2_stream.c:146
apr_table_t * h2_stream_get_trailers(h2_stream *stream)
Definition h2_stream.c:1230
apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, apr_off_t *plen, int *peos)
Definition h2_stream.c:1112
static void set_policy_for(h2_stream *stream, h2_request *r)
Definition h2_stream.c:419
static void stream_do_error_bucket(h2_stream *stream, apr_bucket *b)
Definition h2_stream.c:998
static apr_status_t stream_do_trailers(h2_stream *stream)
Definition h2_stream.c:1128
#define S_OPEN
Definition h2_stream.c:88
#define S_NOP
Definition h2_stream.c:84
static int on_frame_recv(h2_stream_state_t state, int frame_type)
Definition h2_stream.c:162
apr_status_t h2_stream_submit_pushes(h2_stream *stream, h2_headers *response)
Definition h2_stream.c:1205
apr_status_t h2_stream_recv_frame(h2_stream *stream, int ftype, int flags, size_t frame_len)
Definition h2_stream.c:475
static int on_map(h2_stream_state_t state, int map[H2_SS_MAX])
Definition h2_stream.c:132
static apr_status_t buffer_output_receive(h2_stream *stream)
Definition h2_stream.c:1015
static void input_append_data(h2_stream *stream, const char *data, apr_size_t len)
Definition h2_stream.c:257
#define S_CLS
Definition h2_stream.c:91
#define S_CL_L
Definition h2_stream.c:89
static apr_bucket * get_first_response_bucket(apr_bucket_brigade *bb)
Definition h2_stream.c:978
static int trans_on_send[][H2_SS_MAX]
Definition h2_stream.c:95
#define H2_STRM_MSG(s, msg)
Definition h2_stream.h:338
#define H2_STRM_ASSERT_MAGIC(s, m)
Definition h2_stream.h:75
#define H2_STRM_ASSIGN_MAGIC(s, m)
Definition h2_stream.h:74
#define H2_STRM_LOG(aplogno, s, msg)
Definition h2_stream.h:342
apr_status_t h2_append_brigade(apr_bucket_brigade *to, apr_bucket_brigade *from, apr_off_t *plen, int *peos, h2_bucket_gate *should_append)
Definition h2_util.c:1320
apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p, h2_headers *headers)
Definition h2_util.c:1551
int h2_ignore_req_trailer(const char *name, size_t len)
Definition h2_util.c:1675
apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p, h2_headers *headers)
Definition h2_util.c:1544
void h2_util_camel_case_header(char *s, size_t len)
Definition h2_util.c:80
int h2_push_policy_determine(apr_table_t *headers, apr_pool_t *p, int push_enabled)
Definition h2_util.c:1852
apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb)
Definition h2_util.c:1368
apr_size_t h2_util_bb_print(char *buffer, apr_size_t bmax, const char *tag, const char *sep, apr_bucket_brigade *bb)
Definition h2_util.c:1290
Apache connection library.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
SSL protocol handling.
HTTP Daemon routines.
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
A bucket referring to an HTTP error.
Definition apr_tables.h:81
Structure to store things which are per connection.
Definition httpd.h:1152
struct apr_bucket_alloc_t * bucket_alloc
Definition httpd.h:1201
apr_size_t write_size
Definition h2_c1_io.h:38
apr_table_t * headers
Definition h2_headers.h:29
const char * method
Definition h2.h:170
const char * authority
Definition h2.h:172
const char * scheme
Definition h2.h:171
int http_status
Definition h2.h:179
const char * path
Definition h2.h:173
apr_table_t * headers
Definition h2.h:175
const char * protocol
Definition h2.h:174
unsigned int pushes_submitted
Definition h2_session.h:98
unsigned int responses_submitted
Definition h2_session.h:95
h2_c1_io io
Definition h2_session.h:75
apr_size_t max_data_frame_len
Definition h2_session.h:106
struct h2_mplx * mplx
Definition h2_session.h:72
conn_rec * c1
Definition h2_session.h:67
apr_size_t max_stream_mem
Definition h2_session.h:105
server_rec * s
Definition h2_session.h:70
struct nghttp2_session * ngh2
Definition h2_session.h:78
h2_stream_state_cb * on_state_enter
Definition h2_stream.h:57
h2_stream_event_cb * on_state_event
Definition h2_stream.h:60
h2_stream_event_cb * on_event
Definition h2_stream.h:62
h2_stream_state_cb * on_state_invalid
Definition h2_stream.h:58
conn_rec * c2
Definition h2_stream.h:118
apr_off_t in_data_frames
Definition h2_stream.h:125
struct h2_headers * response
Definition h2_stream.h:99
struct h2_bucket_beam * output
Definition h2_stream.h:107
unsigned int push_policy
Definition h2_stream.h:114
unsigned int input_closed
Definition h2_stream.h:113
apr_off_t in_trailer_octets
Definition h2_stream.h:127
struct h2_request * rtmp
Definition h2_stream.h:91
h2_stream_monitor * monitor
Definition h2_stream.h:129
int rst_error
Definition h2_stream.h:110
struct h2_bucket_beam * input
Definition h2_stream.h:102
int request_headers_failed
Definition h2_stream.h:94
unsigned int output_eos
Definition h2_stream.h:116
const h2_priority * pref_priority
Definition h2_stream.h:120
int in_window_size
Definition h2_stream.h:104
apr_table_t * trailers_in
Definition h2_stream.h:92
h2_stream_state_t state
Definition h2_stream.h:86
apr_bucket_brigade * out_buffer
Definition h2_stream.h:108
apr_time_t created
Definition h2_stream.h:88
int initiated_on
Definition h2_stream.h:83
int request_headers_added
Definition h2_stream.h:93
unsigned int sent_trailers
Definition h2_stream.h:115
apr_off_t out_frame_octets
Definition h2_stream.h:122
const struct h2_request * request
Definition h2_stream.h:90
apr_pool_t * pool
Definition h2_stream.h:84
apr_off_t out_frames
Definition h2_stream.h:121
apr_bucket_brigade * in_buffer
Definition h2_stream.h:103
struct h2_session * session
Definition h2_stream.h:85
apr_off_t in_data_octets
Definition h2_stream.h:126
apr_time_t in_last_write
Definition h2_stream.h:105
A structure that represents the current request.
Definition httpd.h:845
apr_interval_time_t timeout
Definition httpd.h:1372
int limit_req_line
Definition httpd.h:1391
int limit_req_fieldsize
Definition httpd.h:1393
int limit_req_fields
Definition httpd.h:1395
apr_size_t maxlen
Definition h2_stream.c:829
const char * failed_key
Definition h2_stream.c:830
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray