Apache HTTPD
h2_proxy_session.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 <stddef.h>
18#include <apr_strings.h>
19#include <nghttp2/nghttp2.h>
20
21#include <mpm_common.h>
22#include <httpd.h>
23#include <http_protocol.h>
24#include <mod_proxy.h>
25
26#include "mod_http2.h"
27#include "h2.h"
28#include "h2_proxy_util.h"
29#include "h2_proxy_session.h"
30
32
60
61
63 int arg, const char *msg);
64static void ping_arrived(h2_proxy_session *session);
66static void stream_resume(h2_proxy_stream *stream);
68
69/*
70 * The H2_PING connection sub-state: a state independant of the H2_SESSION state
71 * of the connection:
72 * - H2_PING_ST_NONE: no interference with request handling, ProxyTimeout in effect.
73 * When entered, all suspended streams are unsuspended again.
74 * - H2_PING_ST_AWAIT_ANY: new requests are suspended, a possibly configured "ping"
75 * timeout is in effect. Any frame received transits to H2_PING_ST_NONE.
76 * - H2_PING_ST_AWAIT_PING: same as above, but only a PING frame transits
77 * to H2_PING_ST_NONE.
78 *
79 * An AWAIT state is entered on a new connection or when re-using a connection and
80 * the last frame received has been some time ago. The latter sends a PING frame
81 * and insists on an answer, the former is satisfied by any frame received from the
82 * backend.
83 *
84 * This works for new connections as there is always at least one SETTINGS frame
85 * that the backend sends. When re-using connection, we send a PING and insist on
86 * receiving one back, as there might be frames in our connection buffers from
87 * some time ago. Since some servers have protections against PING flooding, we
88 * only ever have one PING unanswered.
89 *
90 * Requests are suspended while in a PING state, as we do not want to send data
91 * before we can be reasonably sure that the connection is working (at least on
92 * the h2 protocol level). This also means that the session can do blocking reads
93 * when expecting PING answers.
94 */
96{
97 if (session->ping_timeout != -1 && session->save_timeout == -1) {
99
100 socket = ap_get_conn_socket(session->c);
101 if (socket) {
104 }
105 }
106}
107
109{
110 if (session->save_timeout != -1) {
112
113 socket = ap_get_conn_socket(session->c);
114 if (socket) {
116 session->save_timeout = -1;
117 }
118 }
119}
120
122{
123 if (session->ping_state == state) return;
124 switch (session->ping_state) {
125 case H2_PING_ST_NONE:
126 /* leaving NONE, enforce timeout, send frame maybe */
127 if (H2_PING_ST_AWAIT_PING == state) {
128 unset_ping_timeout(session);
129 nghttp2_submit_ping(session->ngh2, 0, (const uint8_t *)"nevergonnagiveyouup");
130 }
131 set_ping_timeout(session);
132 session->ping_state = state;
133 break;
134 default:
135 /* no switching between the != NONE states */
136 if (H2_PING_ST_NONE == state) {
137 session->ping_state = state;
138 unset_ping_timeout(session);
139 ping_arrived(session);
140 }
141 break;
142 }
143}
144
146{
147 session->save_timeout = -1;
148 session->ping_timeout = (p_conn->worker->s->ping_timeout_set?
149 p_conn->worker->s->ping_timeout : -1);
150 session->ping_state = H2_PING_ST_NONE;
152}
153
155{
156 if (H2_PING_ST_NONE == session->ping_state) {
158 if (age > apr_time_from_sec(1)) {
160 }
161 }
162}
163
165{
167 switch (session->ping_state) {
168 case H2_PING_ST_NONE:
169 /* nop */
170 break;
173 break;
175 if (NGHTTP2_PING == frame->hd.type) {
177 }
178 /* we may receive many other frames while we are waiting for the
179 * PING answer. They may come all from our connection buffers and
180 * say nothing about the current state of the backend. */
181 break;
182 }
183}
184
186{
188 h2_proxy_session *session = p_conn->data;
189
190 if (session && session->ngh2) {
191 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
192 "proxy_session(%s): pool cleanup, state=%d, streams=%d",
193 session->id, session->state,
194 (int)h2_proxy_ihash_count(session->streams));
195 session->aborted = 1;
197 nghttp2_session_del(session->ngh2);
198 session->ngh2 = NULL;
199 p_conn->data = NULL;
200 }
201 return APR_SUCCESS;
202}
203
204static int proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
205 proxy_conn_rec *p_conn,
206 conn_rec *origin, apr_bucket_brigade *bb,
207 int flush)
208{
210 apr_off_t transferred;
211
212 if (flush) {
213 apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
215 }
216 apr_brigade_length(bb, 0, &transferred);
217 if (transferred != -1)
218 p_conn->worker->s->transferred += transferred;
220 /* Cleanup the brigade now to avoid buckets lifetime
221 * issues in case of error returned below. */
223 if (status != APR_SUCCESS) {
225 "pass output failed to %pI (%s)",
226 p_conn->addr, p_conn->hostname);
227 }
228 return status;
229}
230
232 size_t length, int flags, void *user_data)
233{
234 h2_proxy_session *session = user_data;
235 apr_bucket *b;
237 int flush = 1;
238
239 if (data) {
240 b = apr_bucket_transient_create((const char*)data, length,
241 session->c->bucket_alloc);
243 }
244
246 session->p_conn, session->c,
247 session->output, flush);
249 "h2_proxy_sesssion(%s): raw_send %d bytes, flush=%d",
250 session->id, (int)length, flush);
251 if (status != APR_SUCCESS) {
253 }
254 return length;
255}
256
258 void *user_data)
259{
260 h2_proxy_session *session = user_data;
261 h2_proxy_stream *stream;
262 request_rec *r;
263 int n;
264
265 if (APLOGcdebug(session->c)) {
266 char buffer[256];
267
268 h2_proxy_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
269 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03341)
270 "h2_proxy_session(%s): recv FRAME[%s]",
271 session->id, buffer);
272 }
273
275 /* Action for frame types: */
276 switch (frame->hd.type) {
277 case NGHTTP2_HEADERS:
278 stream = nghttp2_session_get_stream_user_data(ngh2, frame->hd.stream_id);
279 if (!stream) {
281 }
282 r = stream->r;
283 if (r->status >= 100 && r->status < 200) {
284 /* By default, we will forward all interim responses when
285 * we are sitting on a HTTP/2 connection to the client */
286 int forward = session->h2_front;
287 switch(r->status) {
288 case 100:
289 if (stream->waiting_on_100) {
290 stream->waiting_on_100 = 0;
292 forward = 1;
293 }
294 break;
295 case 103:
296 /* workaround until we get this into http protocol base
297 * parts. without this, unknown codes are converted to
298 * 500... */
299 r->status_line = "103 Early Hints";
300 break;
301 default:
303 break;
304 }
306 "h2_proxy_session(%s): got interim HEADERS, "
307 "status=%d, will forward=%d",
308 session->id, r->status, forward);
309 if (forward) {
311 }
312 }
313 stream_resume(stream);
314 break;
315 case NGHTTP2_PING:
316 break;
318 break;
319 case NGHTTP2_SETTINGS:
320 if (frame->settings.niv > 0) {
322 if (n > 0) {
323 session->remote_max_concurrent = n;
324 }
325 }
326 break;
327 case NGHTTP2_GOAWAY:
328 /* we expect the remote server to tell us the highest stream id
329 * that it has started processing. */
330 session->last_stream_id = frame->goaway.last_stream_id;
332 break;
333 default:
334 break;
335 }
336 return 0;
337}
338
340 const nghttp2_frame *frame, void *user_data)
341{
342 h2_proxy_session *session = user_data;
343 if (APLOGcdebug(session->c)) {
344 char buffer[256];
345
346 h2_proxy_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
347 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03343)
348 "h2_proxy_session(%s): sent FRAME[%s]",
349 session->id, buffer);
350 }
351 return 0;
352}
353
354static int add_header(void *table, const char *n, const char *v)
355{
356 apr_table_add(table, n, v);
357 return 1;
358}
359
360static void process_proxy_header(apr_table_t *headers, h2_proxy_stream *stream,
361 const char *n, const char *v)
362{
363 static const struct {
364 const char *name;
366 } transform_hdrs[] = {
367 { "Location", ap_proxy_location_reverse_map },
368 { "Content-Location", ap_proxy_location_reverse_map },
370 { "Destination", ap_proxy_location_reverse_map },
371 { "Set-Cookie", ap_proxy_cookie_reverse_map },
372 { NULL, NULL }
373 };
374 request_rec *r = stream->r;
375 proxy_dir_conf *dconf;
376 int i;
377
378 dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
379 if (!dconf->preserve_host) {
380 for (i = 0; transform_hdrs[i].name; ++i) {
382 apr_table_add(headers, n, (*transform_hdrs[i].func)(r, dconf, v));
383 return;
384 }
385 }
386 if (!ap_cstr_casecmp("Link", n)) {
387 dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
388 apr_table_add(headers, n, h2_proxy_link_reverse_map(r, dconf,
389 stream->real_server_uri, stream->p_server_uri, v));
390 return;
391 }
392 }
393 apr_table_add(headers, n, v);
394}
395
397 const char *n, apr_size_t nlen,
398 const char *v, apr_size_t vlen)
399{
400 if (n[0] == ':') {
401 if (!stream->data_received && !strncmp(":status", n, nlen)) {
402 char *s = apr_pstrndup(stream->r->pool, v, vlen);
403
404 apr_table_setn(stream->r->notes, "proxy-status", s);
406 "h2_proxy_stream(%s-%d): got status %s",
407 stream->session->id, stream->id, s);
408 stream->r->status = (int)apr_atoi64(s);
409 if (stream->r->status <= 0) {
410 stream->r->status = 500;
411 return APR_EGENERAL;
412 }
413 }
414 return APR_SUCCESS;
415 }
416
418 "h2_proxy_stream(%s-%d): on_header %s: %s",
419 stream->session->id, stream->id, n, v);
421 char *hname, *hvalue;
422 apr_table_t *headers = (stream->headers_ended?
423 stream->r->trailers_out : stream->r->headers_out);
424
425 hname = apr_pstrndup(stream->pool, n, nlen);
427 hvalue = apr_pstrndup(stream->pool, v, vlen);
428
430 "h2_proxy_stream(%s-%d): got header %s: %s",
431 stream->session->id, stream->id, hname, hvalue);
432 process_proxy_header(headers, stream, hname, hvalue);
433 }
434 return APR_SUCCESS;
435}
436
437static int log_header(void *ctx, const char *key, const char *value)
438{
439 h2_proxy_stream *stream = ctx;
441 "h2_proxy_stream(%s-%d), header_out %s: %s",
442 stream->session->id, stream->id, key, value);
443 return 1;
444}
445
447{
448 h2_proxy_session *session = stream->session;
449 request_rec *r = stream->r;
450 apr_pool_t *p = r->pool;
451 const char *buf;
452
453 /* Now, add in the cookies from the response to the ones already saved */
454 apr_table_do(add_header, stream->saves, r->headers_out, "Set-Cookie", NULL);
455
456 /* and now load 'em all in */
457 if (!apr_is_empty_table(stream->saves)) {
458 apr_table_unset(r->headers_out, "Set-Cookie");
460 }
461
462 if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
464 }
465
466 /* handle Via header in response */
467 if (session->conf->viaopt != via_off
468 && session->conf->viaopt != via_block) {
469 const char *server_name = ap_get_server_name(stream->r);
471 char portstr[32];
472
473 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
474 * then the server name returned by ap_get_server_name() is the
475 * origin server name (which doesn't make sense with Via: headers)
476 * so we use the proxy vhost's name instead.
477 */
478 if (server_name == stream->r->hostname) {
480 }
481 if (ap_is_default_port(port, stream->r)) {
482 portstr[0] = '\0';
483 }
484 else {
485 apr_snprintf(portstr, sizeof(portstr), ":%d", port);
486 }
487
488 /* create a "Via:" response header entry and merge it */
490 (session->conf->viaopt == via_full)
491 ? apr_psprintf(p, "%d.%d %s%s (%s)",
496 : apr_psprintf(p, "%d.%d %s%s",
500 );
501 }
502 if (r->status >= 200) stream->headers_ended = 1;
503
504 if (APLOGrtrace2(stream->r)) {
506 "h2_proxy_stream(%s-%d), header_out after merging",
507 stream->session->id, stream->id);
508 apr_table_do(log_header, stream, stream->r->headers_out, NULL);
509 }
510}
511
513 int32_t stream_id, const uint8_t *data,
514 size_t len, void *user_data)
515{
516 h2_proxy_session *session = user_data;
517 h2_proxy_stream *stream;
518 apr_bucket *b;
520
521 stream = nghttp2_session_get_stream_user_data(ngh2, stream_id);
522 if (!stream) {
524 "h2_proxy_session(%s): recv data chunk for "
525 "unknown stream %d, ignored",
526 session->id, stream_id);
527 return 0;
528 }
529
530 if (!stream->data_received) {
531 /* last chance to manipulate response headers.
532 * after this, only trailers */
534 }
535 stream->data_received += len;
536 b = apr_bucket_transient_create((const char*)data, len,
537 stream->cfront->bucket_alloc);
539 /* always flush after a DATA frame, as we have no other indication
540 * of buffer use */
543
544 status = ap_pass_brigade(stream->r->output_filters, stream->output);
546 "h2_proxy_session(%s): stream=%d, response DATA %ld, %ld"
547 " total", session->id, stream_id, (long)len,
548 (long)stream->data_received);
549 if (status != APR_SUCCESS) {
551 "h2_proxy_session(%s): passing output on stream %d",
552 session->id, stream->id);
554 stream_id, NGHTTP2_STREAM_CLOSED);
556 }
557 return 0;
558}
559
560static int on_stream_close(nghttp2_session *ngh2, int32_t stream_id,
561 uint32_t error_code, void *user_data)
562{
563 h2_proxy_session *session = user_data;
564 h2_proxy_stream *stream;
565 if (!session->aborted) {
566 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03360)
567 "h2_proxy_session(%s): stream=%d, closed, err=%d",
568 session->id, stream_id, error_code);
569 stream = h2_proxy_ihash_get(session->streams, stream_id);
570 if (stream) {
571 stream->error_code = error_code;
572 }
573 dispatch_event(session, H2_PROXYS_EV_STREAM_DONE, stream_id, NULL);
574 }
575 return 0;
576}
577
579 const uint8_t *namearg, size_t nlen,
580 const uint8_t *valuearg, size_t vlen, uint8_t flags,
581 void *user_data)
582{
583 h2_proxy_session *session = user_data;
584 h2_proxy_stream *stream;
585 const char *n = (const char*)namearg;
586 const char *v = (const char*)valuearg;
587
588 (void)session;
589 if (frame->hd.type == NGHTTP2_HEADERS && nlen) {
590 stream = nghttp2_session_get_stream_user_data(ngh2, frame->hd.stream_id);
591 if (stream) {
592 if (h2_proxy_stream_add_header_out(stream, n, nlen, v, vlen)) {
594 }
595 }
596 }
597 else if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
598 }
599
600 return 0;
601}
602
604 uint8_t *buf, size_t length,
606 nghttp2_data_source *source, void *user_data)
607{
608 h2_proxy_stream *stream;
610
611 *data_flags = 0;
612 stream = nghttp2_session_get_stream_user_data(ngh2, stream_id);
613 if (!stream) {
615 "h2_proxy_stream(NULL): data_read, stream %d not found",
616 stream_id);
618 }
619
620 if (stream->session->ping_state != H2_PING_ST_NONE) {
621 /* suspend until we hear from the other side */
622 stream->waiting_on_ping = 1;
624 }
625 else if (stream->r->expecting_100) {
626 /* suspend until the answer comes */
627 stream->waiting_on_100 = 1;
629 }
630 else if (APR_BRIGADE_EMPTY(stream->input)) {
631 status = ap_get_brigade(stream->r->input_filters, stream->input,
635 "h2_proxy_stream(%s-%d): request body read",
636 stream->session->id, stream->id);
637 }
638
639 if (status == APR_SUCCESS) {
640 size_t readlen = 0;
641 while (status == APR_SUCCESS
642 && (readlen < length)
643 && !APR_BRIGADE_EMPTY(stream->input)) {
646 if (APR_BUCKET_IS_EOS(b)) {
648 }
649 else {
650 /* we do nothing more regarding any meta here */
651 }
652 }
653 else {
654 const char *bdata = NULL;
655 apr_size_t blen = 0;
657
658 if (status == APR_SUCCESS && blen > 0) {
659 size_t copylen = H2MIN(length - readlen, blen);
661 buf += copylen;
662 readlen += copylen;
663 if (copylen < blen) {
664 /* We have data left in the bucket. Split it. */
666 }
667 }
668 }
670 }
671
672 stream->data_sent += readlen;
674 "h2_proxy_stream(%d): request DATA %ld, %ld"
675 " total, flags=%d", stream->id, (long)readlen, (long)stream->data_sent,
676 (int)*data_flags);
679 "h2_proxy_stream(%d): submit trailers", stream->id);
681 submit_trailers(stream);
682 }
683 return readlen;
684 }
685 else if (APR_STATUS_IS_EAGAIN(status)) {
686 /* suspended stream, needs to be re-awakened */
688 "h2_proxy_stream(%s-%d): suspending",
689 stream->session->id, stream_id);
690 stream->suspended = 1;
691 h2_proxy_iq_add(stream->session->suspended, stream->id, NULL, NULL);
693 }
694 else {
696 stream_id, NGHTTP2_STREAM_CLOSED);
698 }
699}
700
701#ifdef H2_NG2_INVALID_HEADER_CB
703 const nghttp2_frame *frame,
704 const uint8_t *name, size_t namelen,
705 const uint8_t *value, size_t valuelen,
706 uint8_t flags, void *user_data)
707{
708 h2_proxy_session *session = user_data;
709 if (APLOGcdebug(session->c)) {
710 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03469)
711 "h2_proxy_session(%s-%d): denying stream with invalid header "
712 "'%s: %s'", session->id, (int)frame->hd.stream_id,
713 apr_pstrndup(session->pool, (const char *)name, namelen),
714 apr_pstrndup(session->pool, (const char *)value, valuelen));
715 }
717 frame->hd.stream_id,
719}
720#endif
721
723 proxy_server_conf *conf,
724 int h2_front,
725 unsigned char window_bits_connection,
726 unsigned char window_bits_stream,
728{
729 if (!p_conn->data) {
730 apr_pool_t *pool = p_conn->scpool;
731 h2_proxy_session *session;
734
735 session = apr_pcalloc(pool, sizeof(*session));
737 p_conn->data = session;
738
739 session->id = apr_pstrdup(p_conn->scpool, id);
740 session->c = p_conn->connection;
741 session->p_conn = p_conn;
742 session->conf = conf;
743 session->pool = p_conn->scpool;
744 session->state = H2_PROXYS_ST_INIT;
745 session->h2_front = h2_front;
746 session->window_bits_stream = window_bits_stream;
747 session->window_bits_connection = window_bits_connection;
749 session->suspended = h2_proxy_iq_create(pool, 5);
750 session->done = done;
751
752 session->input = apr_brigade_create(session->pool, session->c->bucket_alloc);
753 session->output = apr_brigade_create(session->pool, session->c->bucket_alloc);
754
762#ifdef H2_NG2_INVALID_HEADER_CB
764#endif
768
769 nghttp2_session_client_new2(&session->ngh2, cbs, session, option);
770
773
774 ping_new_session(session, p_conn);
775 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03362)
776 "setup session for %s", p_conn->hostname);
777 }
778 else {
779 h2_proxy_session *session = p_conn->data;
780 ping_reuse_session(session);
781 }
782 return p_conn->data;
783}
784
786{
788 int rv, add_conn_window;
790
791 s = ap_get_conn_socket(session->c);
792#if (!defined(WIN32) && !defined(NETWARE)) || defined(DOXYGEN)
793 if (s) {
795 }
796#endif
797
798 settings[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
799 settings[0].value = 0;
801 settings[1].value = (1 << session->window_bits_stream) - 1;
802
805
806 /* If the connection window is larger than our default, trigger a WINDOW_UPDATE */
807 add_conn_window = ((1 << session->window_bits_connection) - 1 -
809 if (!rv && add_conn_window != 0) {
811 }
812 return rv? APR_EGENERAL : APR_SUCCESS;
813}
814
815static apr_status_t open_stream(h2_proxy_session *session, const char *url,
816 request_rec *r, int standalone,
818{
819 h2_proxy_stream *stream;
821 const char *authority, *scheme, *path, *orig_host;
823 proxy_dir_conf *dconf;
824
825 stream = apr_pcalloc(r->pool, sizeof(*stream));
826
827 stream->pool = r->pool;
828 stream->url = url;
829 stream->r = r;
830 stream->cfront = r->connection;
831 stream->standalone = standalone;
832 stream->session = session;
833 stream->state = H2_STREAM_ST_IDLE;
834
835 stream->input = apr_brigade_create(stream->pool, stream->cfront->bucket_alloc);
836 stream->output = apr_brigade_create(stream->pool, stream->cfront->bucket_alloc);
837
838 stream->req = h2_proxy_req_create(1, stream->pool);
839
840 status = apr_uri_parse(stream->pool, url, &puri);
841 if (status != APR_SUCCESS)
842 return status;
843
844 scheme = (strcmp(puri.scheme, "h2")? "http" : "https");
846 if (orig_host == NULL) {
848 }
849
850 dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
851 if (dconf->preserve_host) {
852 authority = orig_host;
853 }
854 else {
855 authority = puri.hostname;
856 if (!ap_strchr_c(authority, ':') && puri.port
857 && apr_uri_port_of_scheme(scheme) != puri.port) {
858 /* port info missing and port is not default for scheme: append */
859 authority = apr_psprintf(stream->pool, "%s:%d", authority, puri.port);
860 }
862 "authority=%s from uri.hostname=%s and uri.port=%d",
863 authority, puri.hostname, puri.port);
864 }
865 /* See #235, we use only :authority when available and remove Host:
866 * since differing values are not acceptable, see RFC 9113 ch. 8.3.1 */
867 if (authority && strlen(authority)) {
868 apr_table_unset(r->headers_in, "Host");
869 }
870
871 /* we need this for mapping relative uris in headers ("Link") back
872 * to local uris */
873 stream->real_server_uri = apr_psprintf(stream->pool, "%s://%s", scheme, authority);
874 stream->p_server_uri = apr_psprintf(stream->pool, "%s://%s", puri.scheme, authority);
876
877 h2_proxy_req_make(stream->req, stream->pool, r->method, scheme,
878 authority, path, r->headers_in);
879
880 if (dconf->add_forwarded_headers) {
881 if (PROXYREQ_REVERSE == r->proxyreq) {
882 /* Add X-Forwarded-For: so that the upstream has a chance to
883 * determine, where the original request came from.
884 */
885 apr_table_mergen(stream->req->headers, "X-Forwarded-For",
886 r->useragent_ip);
887
888 /* Add X-Forwarded-Host: so that upstream knows what the
889 * original request hostname was.
890 */
891 if (orig_host) {
892 apr_table_mergen(stream->req->headers, "X-Forwarded-Host",
893 orig_host);
894 }
895
896 /* Add X-Forwarded-Server: so that upstream knows what the
897 * name of this proxy server is (if there are more than one)
898 * XXX: This duplicates Via: - do we strictly need it?
899 */
900 apr_table_mergen(stream->req->headers, "X-Forwarded-Server",
902 }
903 }
904
905 /* Tuck away all already existing cookies */
906 stream->saves = apr_table_make(r->pool, 2);
907 apr_table_do(add_header, stream->saves, r->headers_out, "Set-Cookie", NULL);
908
909 *pstream = stream;
910
911 return APR_SUCCESS;
912}
913
915{
918 nghttp2_data_provider provider;
919 int rv, may_have_request_body = 1;
921
922 hd = h2_proxy_util_nghd_make_req(stream->pool, stream->req);
923
924 /* If we expect a 100-continue response, we must refrain from reading
925 any input until we get it. Reading the input will possibly trigger
926 HTTP_IN filter to generate the 100-continue itself. */
927 if (stream->waiting_on_100 || stream->waiting_on_ping) {
928 /* make a small test if we get an EOF/EOS immediately */
929 status = ap_get_brigade(stream->r->input_filters, stream->input,
933 || (status == APR_SUCCESS
935 }
936
938 provider.source.fd = 0;
939 provider.source.ptr = NULL;
940 provider.read_callback = stream_request_data;
941 pp = &provider;
942 }
943
944 rv = nghttp2_submit_request(session->ngh2, NULL,
945 hd->nv, hd->nvlen, pp, stream);
946
948 "h2_proxy_session(%s): submit %s%s -> %d",
949 session->id, stream->req->authority, stream->req->path,
950 rv);
951 if (rv > 0) {
952 stream->id = rv;
953 stream->state = H2_STREAM_ST_OPEN;
954 h2_proxy_ihash_add(session->streams, stream);
956
957 return APR_SUCCESS;
958 }
959 return APR_EGENERAL;
960}
961
963{
965 int rv;
966
967 hd = h2_proxy_util_nghd_make(stream->pool, stream->r->trailers_in);
968 rv = nghttp2_submit_trailer(stream->session->ngh2, stream->id, hd->nv, hd->nvlen);
969 return rv == 0? APR_SUCCESS: APR_EGENERAL;
970}
971
973{
976 ssize_t n;
977
978 while (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(bb)) {
980
982 /* nop */
983 }
984 else {
985 const char *bdata = NULL;
986 apr_size_t blen = 0;
987
989 if (status == APR_SUCCESS && blen > 0) {
990 n = nghttp2_session_mem_recv(session->ngh2, (const uint8_t *)bdata, blen);
991 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
992 "h2_proxy_session(%s): feeding %ld bytes -> %ld",
993 session->id, (long)blen, (long)n);
994 if (n < 0) {
995 if (nghttp2_is_fatal((int)n)) {
997 }
998 }
999 else {
1000 size_t rlen = (size_t)n;
1001 readlen += rlen;
1002 if (rlen < blen) {
1003 apr_bucket_split(b, rlen);
1004 }
1005 }
1006 }
1007 }
1009 }
1010
1012 "h2_proxy_session(%s): fed %ld bytes of input to session",
1013 session->id, (long)readlen);
1014 if (readlen == 0 && status == APR_SUCCESS) {
1015 return APR_EAGAIN;
1016 }
1017 return status;
1018}
1019
1022{
1024
1025 if (APR_BRIGADE_EMPTY(session->input)) {
1027 apr_time_t save_timeout = -1;
1028
1029 if (block && timeout > 0) {
1030 socket = ap_get_conn_socket(session->c);
1031 if (socket) {
1032 apr_socket_timeout_get(socket, &save_timeout);
1034 }
1035 else {
1036 /* cannot block on timeout */
1037 ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, session->c, APLOGNO(03379)
1038 "h2_proxy_session(%s): unable to get conn socket",
1039 session->id);
1040 return APR_ENOTIMPL;
1041 }
1042 }
1043
1044 status = ap_get_brigade(session->c->input_filters, session->input,
1047 64 * 1024);
1049 "h2_proxy_session(%s): read from conn", session->id);
1050 if (socket && save_timeout != -1) {
1051 apr_socket_timeout_set(socket, save_timeout);
1052 }
1053 }
1054
1055 if (status == APR_SUCCESS) {
1056 status = feed_brigade(session, session->input);
1057 }
1058 else if (APR_STATUS_IS_TIMEUP(status)) {
1059 /* nop */
1060 }
1061 else if (!APR_STATUS_IS_EAGAIN(status)) {
1063 "h2_proxy_session(%s): read error", session->id);
1065 }
1066
1067 return status;
1068}
1069
1071 const char *url, request_rec *r,
1072 int standalone)
1073{
1074 h2_proxy_stream *stream;
1076
1077 status = open_stream(session, url, r, standalone, &stream);
1078 if (status == APR_SUCCESS) {
1080 "process stream(%d): %s %s%s, original: %s",
1081 stream->id, stream->req->method,
1082 stream->req->authority, stream->req->path,
1083 r->the_request);
1084 status = submit_stream(session, stream);
1085 }
1086 return status;
1087}
1088
1089static void stream_resume(h2_proxy_stream *stream)
1090{
1091 h2_proxy_session *session = stream->session;
1093 "h2_proxy_stream(%s-%d): resuming",
1094 session->id, stream->id);
1095 stream->suspended = 0;
1096 h2_proxy_iq_remove(session->suspended, stream->id);
1097 nghttp2_session_resume_data(session->ngh2, stream->id);
1099}
1100
1102{
1103 return ((session->ping_state != H2_PING_ST_NONE)
1104 || ((session->suspended->nelts <= 0)
1105 && !nghttp2_session_want_write(session->ngh2)
1106 && nghttp2_session_want_read(session->ngh2)));
1107}
1108
1110{
1111 h2_proxy_stream *stream;
1112 int i, stream_id;
1114
1115 for (i = 0; i < session->suspended->nelts; ++i) {
1116 stream_id = session->suspended->elts[i];
1117 stream = nghttp2_session_get_stream_user_data(session->ngh2, stream_id);
1118 if (stream) {
1119 if (stream->waiting_on_100 || stream->waiting_on_ping) {
1121 }
1122 else {
1123 status = ap_get_brigade(stream->r->input_filters, stream->input,
1126 }
1127 if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(stream->input)) {
1128 stream_resume(stream);
1129 check_suspended(session);
1130 return APR_SUCCESS;
1131 }
1132 else if (status != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(status)) {
1134 APLOGNO(03382) "h2_proxy_stream(%s-%d): check input",
1135 session->id, stream_id);
1136 stream_resume(stream);
1137 check_suspended(session);
1138 return APR_SUCCESS;
1139 }
1140 }
1141 else {
1142 /* gone? */
1143 h2_proxy_iq_remove(session->suspended, stream_id);
1144 check_suspended(session);
1145 return APR_SUCCESS;
1146 }
1147 }
1148 return APR_EAGAIN;
1149}
1150
1152 const char *msg)
1153{
1155 const char *err = msg;
1156
1157 ap_assert(session);
1158 if (!err && reason) {
1160 }
1162 reason, (uint8_t*)err, err? strlen(err):0);
1163 status = nghttp2_session_send(session->ngh2);
1165 return status;
1166}
1167
1168
1169static const char *StateNames[] = {
1170 "INIT", /* H2_PROXYS_ST_INIT */
1171 "DONE", /* H2_PROXYS_ST_DONE */
1172 "IDLE", /* H2_PROXYS_ST_IDLE */
1173 "BUSY", /* H2_PROXYS_ST_BUSY */
1174 "WAIT", /* H2_PROXYS_ST_WAIT */
1175 "LSHUTDOWN", /* H2_PROXYS_ST_LOCAL_SHUTDOWN */
1176 "RSHUTDOWN", /* H2_PROXYS_ST_REMOTE_SHUTDOWN */
1177};
1178
1179static const char *state_name(h2_proxys_state state)
1180{
1181 if (state >= (sizeof(StateNames)/sizeof(StateNames[0]))) {
1182 return "unknown";
1183 }
1184 return StateNames[state];
1185}
1186
1188{
1189 switch (session->state) {
1190 case H2_PROXYS_ST_IDLE:
1191 case H2_PROXYS_ST_BUSY:
1192 case H2_PROXYS_ST_WAIT:
1193 return 1;
1194 default:
1195 return 0;
1196 }
1197}
1198
1199static void transit(h2_proxy_session *session, const char *action,
1201{
1202 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03345)
1203 "h2_proxy_session(%s): transit [%s] -- %s --> [%s]", session->id,
1204 state_name(session->state), action, state_name(nstate));
1205 session->state = nstate;
1206}
1207
1208static void ev_init(h2_proxy_session *session, int arg, const char *msg)
1209{
1210 switch (session->state) {
1211 case H2_PROXYS_ST_INIT:
1212 if (h2_proxy_ihash_empty(session->streams)) {
1213 transit(session, "init", H2_PROXYS_ST_IDLE);
1214 }
1215 else {
1216 transit(session, "init", H2_PROXYS_ST_BUSY);
1217 }
1218 break;
1219
1220 default:
1221 /* nop */
1222 break;
1223 }
1224}
1225
1226static void ev_local_goaway(h2_proxy_session *session, int arg, const char *msg)
1227{
1228 switch (session->state) {
1230 /* already did that? */
1231 break;
1232 case H2_PROXYS_ST_IDLE:
1234 /* all done */
1235 transit(session, "local goaway", H2_PROXYS_ST_DONE);
1236 break;
1237 default:
1238 transit(session, "local goaway", H2_PROXYS_ST_LOCAL_SHUTDOWN);
1239 break;
1240 }
1241}
1242
1243static void ev_remote_goaway(h2_proxy_session *session, int arg, const char *msg)
1244{
1245 switch (session->state) {
1247 /* already received that? */
1248 break;
1249 case H2_PROXYS_ST_IDLE:
1251 /* all done */
1252 transit(session, "remote goaway", H2_PROXYS_ST_DONE);
1253 break;
1254 default:
1255 transit(session, "remote goaway", H2_PROXYS_ST_REMOTE_SHUTDOWN);
1256 break;
1257 }
1258}
1259
1260static void ev_conn_error(h2_proxy_session *session, int arg, const char *msg)
1261{
1262 switch (session->state) {
1263 case H2_PROXYS_ST_INIT:
1264 case H2_PROXYS_ST_DONE:
1266 /* just leave */
1267 transit(session, "conn error", H2_PROXYS_ST_DONE);
1268 break;
1269
1270 default:
1272 "h2_proxy_session(%s): conn error -> shutdown", session->id);
1273 session_shutdown(session, arg, msg);
1274 break;
1275 }
1276}
1277
1278static void ev_proto_error(h2_proxy_session *session, int arg, const char *msg)
1279{
1280 switch (session->state) {
1281 case H2_PROXYS_ST_DONE:
1283 /* just leave */
1284 transit(session, "proto error", H2_PROXYS_ST_DONE);
1285 break;
1286
1287 default:
1288 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1289 "h2_proxy_session(%s): proto error -> shutdown", session->id);
1290 session_shutdown(session, arg, msg);
1291 break;
1292 }
1293}
1294
1295static void ev_conn_timeout(h2_proxy_session *session, int arg, const char *msg)
1296{
1297 switch (session->state) {
1299 transit(session, "conn timeout", H2_PROXYS_ST_DONE);
1300 break;
1301 default:
1302 session_shutdown(session, arg, msg);
1303 transit(session, "conn timeout", H2_PROXYS_ST_DONE);
1304 break;
1305 }
1306}
1307
1308static void ev_no_io(h2_proxy_session *session, int arg, const char *msg)
1309{
1310 switch (session->state) {
1311 case H2_PROXYS_ST_BUSY:
1314 /* nothing for input and output to do. If we remain
1315 * in this state, we go into a tight loop and suck up
1316 * CPU cycles. Ideally, we'd like to do a blocking read, but that
1317 * is not possible if we have scheduled tasks and wait
1318 * for them to produce something. */
1319 if (h2_proxy_ihash_empty(session->streams)) {
1320 if (!is_accepting_streams(session)) {
1321 /* We are no longer accepting new streams and have
1322 * finished processing existing ones. Time to leave. */
1323 session_shutdown(session, arg, msg);
1324 transit(session, "no io", H2_PROXYS_ST_DONE);
1325 }
1326 else {
1327 /* When we have no streams, no task events are possible,
1328 * switch to blocking reads */
1329 transit(session, "no io", H2_PROXYS_ST_IDLE);
1330 }
1331 }
1332 else {
1333 /* Unable to do blocking reads, as we wait on events from
1334 * task processing in other threads. Do a busy wait with
1335 * backoff timer. */
1336 transit(session, "no io", H2_PROXYS_ST_WAIT);
1337 }
1338 break;
1339 default:
1340 /* nop */
1341 break;
1342 }
1343}
1344
1345static void ev_stream_submitted(h2_proxy_session *session, int stream_id,
1346 const char *msg)
1347{
1348 switch (session->state) {
1349 case H2_PROXYS_ST_IDLE:
1350 case H2_PROXYS_ST_WAIT:
1351 transit(session, "stream submitted", H2_PROXYS_ST_BUSY);
1352 break;
1353 default:
1354 /* nop */
1355 break;
1356 }
1357}
1358
1359static void ev_stream_done(h2_proxy_session *session, int stream_id,
1360 const char *msg)
1361{
1362 h2_proxy_stream *stream;
1363 apr_bucket *b;
1364
1365 stream = nghttp2_session_get_stream_user_data(session->ngh2, stream_id);
1366 if (stream) {
1367 /* if the stream's connection is aborted, do not send anything
1368 * more on it. */
1370 int touched = (stream->data_sent || stream->data_received ||
1371 stream_id <= session->last_stream_id);
1372 if (!stream->cfront->aborted) {
1374 "h2_proxy_sesssion(%s): stream(%d) closed "
1375 "(touched=%d, error=%d)",
1376 session->id, stream_id, touched, stream->error_code);
1377
1378 if (status != APR_SUCCESS) {
1379 /* stream failed. If we have received (and forwarded) response
1380 * data already, we need to append an error buckt to inform
1381 * consumers.
1382 * Otherwise, we have an early fail on the connection and may
1383 * retry this request on a new one. In that case, keep the
1384 * output virgin so that a new attempt can be made. */
1385 if (stream->data_received) {
1387 b = ap_bucket_error_create(http_status, NULL, stream->r->pool,
1388 stream->cfront->bucket_alloc);
1392 ap_pass_brigade(stream->r->output_filters, stream->output);
1393 }
1394 }
1395 else if (!stream->data_received) {
1396 /* if the response had no body, this is the time to flush
1397 * an empty brigade which will also write the response headers */
1399 stream->data_received = 1;
1404 ap_pass_brigade(stream->r->output_filters, stream->output);
1405 }
1406 }
1407
1408 stream->state = H2_STREAM_ST_CLOSED;
1409 h2_proxy_ihash_remove(session->streams, stream_id);
1410 h2_proxy_iq_remove(session->suspended, stream_id);
1411 if (session->done) {
1412 session->done(session, stream->r, status, touched, stream->error_code);
1413 }
1414 }
1415
1416 switch (session->state) {
1417 default:
1418 /* nop */
1419 break;
1420 }
1421}
1422
1423static void ev_stream_resumed(h2_proxy_session *session, int arg, const char *msg)
1424{
1425 switch (session->state) {
1426 case H2_PROXYS_ST_WAIT:
1427 transit(session, "stream resumed", H2_PROXYS_ST_BUSY);
1428 break;
1429 default:
1430 /* nop */
1431 break;
1432 }
1433}
1434
1435static void ev_data_read(h2_proxy_session *session, int arg, const char *msg)
1436{
1437 switch (session->state) {
1438 case H2_PROXYS_ST_IDLE:
1439 case H2_PROXYS_ST_WAIT:
1440 transit(session, "data read", H2_PROXYS_ST_BUSY);
1441 break;
1442 default:
1443 /* nop */
1444 break;
1445 }
1446}
1447
1448static void ev_ngh2_done(h2_proxy_session *session, int arg, const char *msg)
1449{
1450 switch (session->state) {
1451 case H2_PROXYS_ST_DONE:
1452 /* nop */
1453 break;
1454 default:
1455 transit(session, "nghttp2 done", H2_PROXYS_ST_DONE);
1456 break;
1457 }
1458}
1459
1460static void ev_pre_close(h2_proxy_session *session, int arg, const char *msg)
1461{
1462 switch (session->state) {
1463 case H2_PROXYS_ST_DONE:
1465 /* nop */
1466 break;
1467 default:
1468 session_shutdown(session, arg, msg);
1469 break;
1470 }
1471}
1472
1474 int arg, const char *msg)
1475{
1476 switch (ev) {
1477 case H2_PROXYS_EV_INIT:
1478 ev_init(session, arg, msg);
1479 break;
1481 ev_local_goaway(session, arg, msg);
1482 break;
1484 ev_remote_goaway(session, arg, msg);
1485 break;
1487 ev_conn_error(session, arg, msg);
1488 break;
1490 ev_proto_error(session, arg, msg);
1491 break;
1493 ev_conn_timeout(session, arg, msg);
1494 break;
1495 case H2_PROXYS_EV_NO_IO:
1496 ev_no_io(session, arg, msg);
1497 break;
1499 ev_stream_submitted(session, arg, msg);
1500 break;
1502 ev_stream_done(session, arg, msg);
1503 break;
1505 ev_stream_resumed(session, arg, msg);
1506 break;
1508 ev_data_read(session, arg, msg);
1509 break;
1511 ev_ngh2_done(session, arg, msg);
1512 break;
1514 ev_pre_close(session, arg, msg);
1515 break;
1516 default:
1517 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1518 "h2_proxy_session(%s): unknown event %d",
1519 session->id, ev);
1520 break;
1521 }
1522}
1523
1524static int send_loop(h2_proxy_session *session)
1525{
1526 while (nghttp2_session_want_write(session->ngh2)) {
1527 int rv = nghttp2_session_send(session->ngh2);
1528 if (rv < 0 && nghttp2_is_fatal(rv)) {
1529 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1530 "h2_proxy_session(%s): write, rv=%d", session->id, rv);
1532 break;
1533 }
1534 return 1;
1535 }
1536 return 0;
1537}
1538
1540{
1542 int have_written = 0, have_read = 0;
1543
1544 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1545 "h2_proxy_session(%s): process", session->id);
1546
1547run_loop:
1548 switch (session->state) {
1549 case H2_PROXYS_ST_INIT:
1550 status = session_start(session);
1551 if (status == APR_SUCCESS) {
1553 goto run_loop;
1554 }
1555 else {
1557 }
1558 break;
1559
1560 case H2_PROXYS_ST_BUSY:
1563 have_written = send_loop(session);
1564
1565 if (nghttp2_session_want_read(session->ngh2)) {
1566 status = h2_proxy_session_read(session, 0, 0);
1567 if (status == APR_SUCCESS) {
1568 have_read = 1;
1569 }
1570 }
1571
1572 if (!have_written && !have_read
1573 && !nghttp2_session_want_write(session->ngh2)) {
1575 goto run_loop;
1576 }
1577 break;
1578
1579 case H2_PROXYS_ST_WAIT:
1580 if (is_waiting_for_backend(session)) {
1581 /* we can do a blocking read with the default timeout (as
1582 * configured via ProxyTimeout in our socket. There is
1583 * nothing we want to send or check until we get more data
1584 * from the backend. */
1585 status = h2_proxy_session_read(session, 1, 0);
1586 if (status == APR_SUCCESS) {
1587 have_read = 1;
1589 }
1590 else {
1592 return status;
1593 }
1594 }
1595 else if (check_suspended(session) == APR_EAGAIN) {
1596 /* no stream has become resumed. Do a blocking read with
1597 * ever increasing timeouts... */
1598 if (session->wait_timeout < 25) {
1599 session->wait_timeout = 25;
1600 }
1601 else {
1602 session->wait_timeout = H2MIN(apr_time_from_msec(100),
1603 2*session->wait_timeout);
1604 }
1605
1606 status = h2_proxy_session_read(session, 1, session->wait_timeout);
1608 APLOGNO(03365)
1609 "h2_proxy_session(%s): WAIT read, timeout=%fms",
1610 session->id, session->wait_timeout/1000.0);
1611 if (status == APR_SUCCESS) {
1612 have_read = 1;
1614 }
1617 /* go back to checking all inputs again */
1618 transit(session, "wait cycle", H2_PROXYS_ST_BUSY);
1619 }
1620 }
1621 break;
1622
1623 case H2_PROXYS_ST_IDLE:
1624 break;
1625
1626 case H2_PROXYS_ST_DONE: /* done, session terminated */
1627 return APR_EOF;
1628
1629 default:
1631 APLOGNO(03346)"h2_proxy_session(%s): unknown state %d",
1632 session->id, session->state);
1634 break;
1635 }
1636
1637
1638 if (have_read || have_written) {
1639 session->wait_timeout = 0;
1640 }
1641
1642 if (!nghttp2_session_want_read(session->ngh2)
1643 && !nghttp2_session_want_write(session->ngh2)) {
1645 }
1646
1647 return APR_SUCCESS; /* needs to be called again */
1648}
1649
1654
1655static int cancel_iter(void *udata, void *val)
1656{
1658 h2_proxy_stream *stream = val;
1660 stream->id, 0);
1661 return 1;
1662}
1663
1665{
1666 if (!h2_proxy_ihash_empty(session->streams)) {
1668 ctx.session = session;
1669 ctx.done = session->done;
1670 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03366)
1671 "h2_proxy_session(%s): cancel %d streams",
1672 session->id, (int)h2_proxy_ihash_count(session->streams));
1674 session_shutdown(session, 0, NULL);
1675 }
1676}
1677
1678static int done_iter(void *udata, void *val)
1679{
1681 h2_proxy_stream *stream = val;
1682 int touched = (stream->data_sent || stream->data_received ||
1683 stream->id <= ctx->session->last_stream_id);
1684 if (touched && stream->output) {
1686 stream->r->pool,
1687 stream->cfront->bucket_alloc);
1691 ap_pass_brigade(stream->r->output_filters, stream->output);
1692 }
1693 ctx->done(ctx->session, stream->r, APR_ECONNABORTED, touched,
1694 stream->error_code);
1695 return 1;
1696}
1697
1700{
1701 if (!h2_proxy_ihash_empty(session->streams)) {
1703 ctx.session = session;
1704 ctx.done = done;
1705 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03519)
1706 "h2_proxy_session(%s): terminated, %d streams unfinished",
1707 session->id, (int)h2_proxy_ihash_count(session->streams));
1709 h2_proxy_ihash_clear(session->streams);
1710 }
1711}
1712
1714{
1715 return (session->state != H2_PROXYS_ST_DONE) &&
1716 h2_proxy_ihash_empty(session->streams);
1717}
1718
1719static int ping_arrived_iter(void *udata, void *val)
1720{
1721 h2_proxy_stream *stream = val;
1722 if (stream->waiting_on_ping) {
1723 stream->waiting_on_ping = 0;
1724 stream_resume(stream);
1725 }
1726 return 1;
1727}
1728
1729static void ping_arrived(h2_proxy_session *session)
1730{
1731 if (!h2_proxy_ihash_empty(session->streams)) {
1732 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03470)
1733 "h2_proxy_session(%s): ping arrived, unblocking streams",
1734 session->id);
1735 h2_proxy_ihash_iter(session->streams, ping_arrived_iter, &session);
1736 }
1737}
1738
1745
int n
Definition ap_regex.h:278
const char apr_size_t len
Definition ap_regex.h:187
#define AP_SERVER_BASEVERSION
Definition ap_release.h:75
#define socket
APR Strings library.
#define APLOG_USE_MODULE(foo)
#define ap_get_module_config(v, m)
request_rec int int apr_table_t const char * path
request_rec * r
int flush
#define HTTP_VERSION_MAJOR(number)
Definition httpd.h:271
#define HTTP_VERSION_MINOR(number)
Definition httpd.h:273
#define ap_is_default_port(port, r)
Definition httpd.h:287
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
apr_port_t ap_get_server_port(const request_rec *r)
Definition core.c:1199
const char * ap_get_server_name(request_rec *r)
Definition core.c:1145
apr_socket_t * ap_get_conn_socket(conn_rec *c)
Definition core.c:5202
#define APLOGrtrace2(r)
Definition http_log.h:247
#define APLOGNO(n)
Definition http_log.h:117
#define APLOGcdebug(c)
Definition http_log.h:256
#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_error
Definition http_log.h:370
#define ap_log_cerror
Definition http_log.h:498
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_TRACE2
Definition http_log.h:73
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
server_rec * ap_server_conf
Definition config.c:62
const unsigned char * buf
Definition util_md5.h:50
int ap_map_http_request_error(apr_status_t rv, int status)
void ap_send_interim_response(request_rec *r, int send_headers)
Definition protocol.c:2316
void ap_set_content_type(request_rec *r, const char *ct)
const char * ap_get_status_line(int status)
apr_bucket * ap_bucket_error_create(int error, const char *buf, apr_pool_t *p, apr_bucket_alloc_t *list)
const char apr_port_t port
Definition http_vhost.h:125
void const char * arg
Definition http_vhost.h:63
#define ap_sock_disable_nagle(s)
Definition mpm_common.h:200
#define APR_EAGAIN
Definition apr_errno.h:730
#define APR_EGENERAL
Definition apr_errno.h:313
#define APR_EOF
Definition apr_errno.h:461
#define APR_ENOTIMPL
Definition apr_errno.h:476
#define APR_ECONNABORTED
Definition apr_errno.h:769
#define APR_EINVAL
Definition apr_errno.h:711
#define APR_STATUS_IS_TIMEUP(s)
Definition apr_errno.h:534
#define APR_STATUS_IS_EAGAIN(s)
Definition apr_errno.h:1272
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define apr_bucket_split(e, point)
apr_bucket * e
#define APR_BUCKET_BUFF_SIZE
Definition apr_buckets.h:54
#define APR_BRIGADE_EMPTY(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
#define apr_bucket_read(e, str, len, block)
int apr_off_t * length
@ APR_BLOCK_READ
Definition apr_buckets.h:58
@ APR_NONBLOCK_READ
Definition apr_buckets.h:59
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char * url
Definition apr_escape.h:120
#define APR_URI_UNP_OMITSITEPART
Definition apr_uri.h:64
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_BAD_GATEWAY
Definition httpd.h:537
const char * ap_proxy_location_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *url)
Definition proxy_util.c:882
const char *(* ap_proxy_header_reverse_map_fn)(request_rec *, proxy_dir_conf *, const char *)
Definition mod_proxy.h:732
const char * ap_proxy_cookie_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *str)
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
#define ap_strchr_c(s, c)
Definition httpd.h:2353
#define ap_assert(exp)
Definition httpd.h:2271
#define PROXYREQ_REVERSE
Definition httpd.h:1135
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
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
char * buffer
apr_sockaddr_t apr_sockaddr_t apr_sockaddr_t * source
apr_uint16_t apr_port_t
apr_interval_time_t apr_pollcb_cb_t func
Definition apr_poll.h:422
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
apr_int32_t apr_int32_t apr_int32_t err
int reason
int int status
#define apr_time_from_msec(msec)
Definition apr_time.h:75
apr_int64_t apr_interval_time_t
Definition apr_time.h:55
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_from_sec(sec)
Definition apr_time.h:78
#define H2MAX(x, y)
Definition h2.h:100
#define H2_ALEN(a)
Definition h2.h:98
#define H2MIN(x, y)
Definition h2.h:101
static int on_stream_close(nghttp2_session *ngh2, int32_t stream_id, uint32_t error_code, void *user_data)
static int send_loop(h2_proxy_session *session)
static apr_status_t submit_trailers(h2_proxy_stream *stream)
static void ping_reuse_session(h2_proxy_session *session)
static int ping_arrived_iter(void *udata, void *val)
static void ping_ev_frame_received(h2_proxy_session *session, const nghttp2_frame *frame)
static int log_header(void *ctx, const char *key, const char *value)
static apr_status_t feed_brigade(h2_proxy_session *session, apr_bucket_brigade *bb)
static int stream_response_data(nghttp2_session *ngh2, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data)
static void enter_ping_state(h2_proxy_session *session, h2_ping_state_t state)
int h2_proxy_session_is_reusable(h2_proxy_session *session)
static apr_status_t session_start(h2_proxy_session *session)
static void ev_data_read(h2_proxy_session *session, int arg, const char *msg)
static void ev_pre_close(h2_proxy_session *session, int arg, const char *msg)
static int cancel_iter(void *udata, void *val)
static void ev_proto_error(h2_proxy_session *session, int arg, const char *msg)
static apr_status_t submit_stream(h2_proxy_session *session, h2_proxy_stream *stream)
static void ping_new_session(h2_proxy_session *session, proxy_conn_rec *p_conn)
apr_status_t h2_proxy_session_submit(h2_proxy_session *session, const char *url, request_rec *r, int standalone)
static apr_status_t h2_proxy_session_read(h2_proxy_session *session, int block, apr_interval_time_t timeout)
static void set_ping_timeout(h2_proxy_session *session)
static void ev_stream_submitted(h2_proxy_session *session, int stream_id, const char *msg)
static void transit(h2_proxy_session *session, const char *action, h2_proxys_state nstate)
static void h2_proxy_stream_end_headers_out(h2_proxy_stream *stream)
static void ev_init(h2_proxy_session *session, int arg, const char *msg)
static apr_status_t open_stream(h2_proxy_session *session, const char *url, request_rec *r, int standalone, h2_proxy_stream **pstream)
static void ev_ngh2_done(h2_proxy_session *session, int arg, const char *msg)
static int on_header(nghttp2_session *ngh2, const nghttp2_frame *frame, const uint8_t *namearg, size_t nlen, const uint8_t *valuearg, size_t vlen, uint8_t flags, void *user_data)
static int is_waiting_for_backend(h2_proxy_session *session)
static void ev_stream_resumed(h2_proxy_session *session, int arg, const char *msg)
static int on_frame_recv(nghttp2_session *ngh2, const nghttp2_frame *frame, void *user_data)
static ssize_t raw_send(nghttp2_session *ngh2, const uint8_t *data, size_t length, int flags, void *user_data)
static int add_header(void *table, const char *n, const char *v)
static int is_accepting_streams(h2_proxy_session *session)
static apr_status_t check_suspended(h2_proxy_session *session)
static void ev_no_io(h2_proxy_session *session, int arg, const char *msg)
static void ev_remote_goaway(h2_proxy_session *session, int arg, const char *msg)
static int proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, proxy_conn_rec *p_conn, conn_rec *origin, apr_bucket_brigade *bb, int flush)
apr_status_t h2_proxy_session_process(h2_proxy_session *session)
static int before_frame_send(nghttp2_session *ngh2, const nghttp2_frame *frame, void *user_data)
static apr_status_t h2_proxy_stream_add_header_out(h2_proxy_stream *stream, const char *n, apr_size_t nlen, const char *v, apr_size_t vlen)
static void stream_resume(h2_proxy_stream *stream)
void h2_proxy_session_cancel_all(h2_proxy_session *session)
static void ev_local_goaway(h2_proxy_session *session, int arg, const char *msg)
static void ev_stream_done(h2_proxy_session *session, int stream_id, const char *msg)
h2_proxy_session * h2_proxy_session_setup(const char *id, proxy_conn_rec *p_conn, proxy_server_conf *conf, int h2_front, unsigned char window_bits_connection, unsigned char window_bits_stream, h2_proxy_request_done *done)
static void process_proxy_header(apr_table_t *headers, h2_proxy_stream *stream, const char *n, const char *v)
static const char * StateNames[]
static void ev_conn_timeout(h2_proxy_session *session, int arg, const char *msg)
static void unset_ping_timeout(h2_proxy_session *session)
static const char * state_name(h2_proxys_state state)
static void ev_conn_error(h2_proxy_session *session, int arg, const char *msg)
static ssize_t stream_request_data(nghttp2_session *ngh2, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data)
static apr_status_t session_shutdown(h2_proxy_session *session, int reason, const char *msg)
static int done_iter(void *udata, void *val)
static void dispatch_event(h2_proxy_session *session, h2_proxys_event_t ev, int arg, const char *msg)
static void ping_arrived(h2_proxy_session *session)
void h2_proxy_session_cleanup(h2_proxy_session *session, h2_proxy_request_done *done)
static apr_status_t proxy_session_pre_close(void *theconn)
h2_ping_state_t
@ H2_PING_ST_AWAIT_ANY
@ H2_PING_ST_AWAIT_PING
@ H2_PING_ST_NONE
h2_proxy_stream_state_t
@ H2_STREAM_ST_IDLE
@ H2_STREAM_ST_OPEN
@ H2_STREAM_ST_CLOSED
h2_proxys_state
@ H2_PROXYS_ST_LOCAL_SHUTDOWN
@ H2_PROXYS_ST_DONE
@ H2_PROXYS_ST_BUSY
@ H2_PROXYS_ST_WAIT
@ H2_PROXYS_ST_IDLE
@ H2_PROXYS_ST_REMOTE_SHUTDOWN
@ H2_PROXYS_ST_INIT
void h2_proxy_request_done(h2_proxy_session *s, request_rec *r, apr_status_t status, int touched, int error_code)
h2_proxys_event_t
@ H2_PROXYS_EV_CONN_TIMEOUT
@ H2_PROXYS_EV_STREAM_RESUMED
@ H2_PROXYS_EV_NO_IO
@ H2_PROXYS_EV_STREAM_DONE
@ H2_PROXYS_EV_PRE_CLOSE
@ H2_PROXYS_EV_PROTO_ERROR
@ H2_PROXYS_EV_DATA_READ
@ H2_PROXYS_EV_NGH2_DONE
@ H2_PROXYS_EV_CONN_ERROR
@ H2_PROXYS_EV_REMOTE_GOAWAY
@ H2_PROXYS_EV_INIT
@ H2_PROXYS_EV_LOCAL_GOAWAY
@ H2_PROXYS_EV_STREAM_SUBMITTED
int h2_proxy_iq_remove(h2_proxy_iqueue *q, int sid)
int h2_proxy_ihash_iter(h2_proxy_ihash_t *ih, h2_proxy_ihash_iter_t *fn, void *ctx)
apr_status_t h2_proxy_req_make(h2_proxy_request *req, apr_pool_t *pool, const char *method, const char *scheme, const char *authority, const char *path, apr_table_t *headers)
h2_proxy_ngheader * h2_proxy_util_nghd_make(apr_pool_t *p, apr_table_t *headers)
int h2_proxy_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
void h2_proxy_ihash_remove(h2_proxy_ihash_t *ih, int id)
void h2_proxy_iq_add(h2_proxy_iqueue *q, int sid, h2_proxy_iq_cmp *cmp, void *ctx)
h2_proxy_request * h2_proxy_req_create(int id, apr_pool_t *pool)
h2_proxy_ngheader * h2_proxy_util_nghd_make_req(apr_pool_t *p, const h2_proxy_request *req)
void * h2_proxy_ihash_get(h2_proxy_ihash_t *ih, int id)
h2_proxy_ihash_t * h2_proxy_ihash_create(apr_pool_t *pool, size_t offset_of_int)
void h2_proxy_ihash_clear(h2_proxy_ihash_t *ih)
void h2_proxy_util_camel_case_header(char *s, size_t len)
size_t h2_proxy_ihash_count(h2_proxy_ihash_t *ih)
void h2_proxy_ihash_add(h2_proxy_ihash_t *ih, void *val)
h2_proxy_iqueue * h2_proxy_iq_create(apr_pool_t *pool, int capacity)
int h2_proxy_res_ignore_header(const char *name, size_t len)
const char * h2_proxy_link_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *real_backend_uri, const char *proxy_server_uri, const char *s)
int h2_proxy_ihash_empty(h2_proxy_ihash_t *ih)
HTTP protocol handling.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
static apr_status_t age(proxy_balancer *balancer, server_rec *s)
Proxy Extension Module for Apache.
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
Multi-Processing Modules functions.
char * name
h2_proxy_session * session
h2_proxy_request_done * done
Structure to store things which are per connection.
Definition httpd.h:1152
struct ap_filter_t * input_filters
Definition httpd.h:1195
struct ap_filter_t * output_filters
Definition httpd.h:1197
struct apr_bucket_alloc_t * bucket_alloc
Definition httpd.h:1201
unsigned aborted
Definition httpd.h:1219
const char * authority
apr_table_t * headers
const char * path
const char * method
unsigned int h2_front
h2_proxy_request_done * done
proxy_conn_rec * p_conn
apr_interval_time_t wait_timeout
apr_bucket_brigade * output
apr_bucket_brigade * input
unsigned char window_bits_stream
h2_ping_state_t ping_state
apr_time_t last_frame_received
apr_size_t remote_max_concurrent
struct h2_proxy_ihash_t * streams
proxy_server_conf * conf
unsigned char window_bits_connection
nghttp2_session * ngh2
h2_proxys_state state
struct h2_proxy_iqueue * suspended
unsigned int aborted
apr_bucket_brigade * output
unsigned int suspended
h2_proxy_session * session
const char * real_server_uri
h2_proxy_stream_state_t state
request_rec * r
h2_proxy_request * req
apr_pool_t * pool
apr_table_t * saves
unsigned int headers_ended
unsigned int waiting_on_ping
unsigned int waiting_on_100
apr_off_t data_received
apr_bucket_brigade * input
const char * p_server_uri
const char * hostname
Definition mod_proxy.h:275
apr_pool_t * scpool
Definition mod_proxy.h:277
apr_sockaddr_t * addr
Definition mod_proxy.h:276
proxy_worker * worker
Definition mod_proxy.h:273
conn_rec * connection
Definition mod_proxy.h:270
unsigned int preserve_host
Definition mod_proxy.h:242
unsigned int add_forwarded_headers
Definition mod_proxy.h:246
enum proxy_server_conf::@32 viaopt
apr_off_t transferred
Definition mod_proxy.h:462
unsigned int ping_timeout_set
Definition mod_proxy.h:471
apr_interval_time_t ping_timeout
Definition mod_proxy.h:455
proxy_worker_shared * s
Definition mod_proxy.h:505
A structure that represents the current request.
Definition httpd.h:845
int status
Definition httpd.h:891
apr_table_t * trailers_in
Definition httpd.h:1104
struct ap_filter_t * output_filters
Definition httpd.h:1070
char * useragent_ip
Definition httpd.h:1101
apr_table_t * notes
Definition httpd.h:985
unsigned expecting_100
Definition httpd.h:951
const char * hostname
Definition httpd.h:883
char * the_request
Definition httpd.h:866
apr_pool_t * pool
Definition httpd.h:847
int proxyreq
Definition httpd.h:873
conn_rec * connection
Definition httpd.h:849
int proto_num
Definition httpd.h:877
struct ap_filter_t * input_filters
Definition httpd.h:1072
apr_table_t * headers_in
Definition httpd.h:976
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * status_line
Definition httpd.h:889
const char * method
Definition httpd.h:900
apr_table_t * trailers_out
Definition httpd.h:1106
apr_table_t * headers_out
Definition httpd.h:978
char * server_hostname
Definition httpd.h:1365
h2_proxy_session * session
apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
Definition sockopt.c:75
apr_status_t apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
Definition sockopt.c:355
@ AP_MODE_READBYTES
Definition util_filter.h:43
IN ULONG IN INT timeout
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray