Apache HTTPD
h2_proxy_util.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 <apr_lib.h>
19#include <apr_strings.h>
20#include <apr_thread_mutex.h>
21#include <apr_thread_cond.h>
22
23#include <httpd.h>
24#include <http_core.h>
25#include <http_log.h>
26#include <http_request.h>
27#include <mod_proxy.h>
28
29#include <nghttp2/nghttp2.h>
30
31#include "h2.h"
32#include "h2_proxy_util.h"
33
35
36/* h2_log2(n) iff n is a power of 2 */
37unsigned char h2_proxy_log2(int n)
38{
39 int lz = 0;
40 if (!n) {
41 return 0;
42 }
43 if (!(n & 0xffff0000u)) {
44 lz += 16;
45 n = (n << 16);
46 }
47 if (!(n & 0xff000000u)) {
48 lz += 8;
49 n = (n << 8);
50 }
51 if (!(n & 0xf0000000u)) {
52 lz += 4;
53 n = (n << 4);
54 }
55 if (!(n & 0xc0000000u)) {
56 lz += 2;
57 n = (n << 2);
58 }
59 if (!(n & 0x80000000u)) {
60 lz += 1;
61 }
62
63 return 31 - lz;
64}
65
66/*******************************************************************************
67 * ihash - hash for structs with int identifier
68 ******************************************************************************/
73
74static unsigned int ihash(const char *key, apr_ssize_t *klen)
75{
76 return (unsigned int)(*((int*)key));
77}
78
86
88{
89 return apr_hash_count(ih->hash);
90}
91
93{
94 return apr_hash_count(ih->hash) == 0;
95}
96
98{
99 return apr_hash_get(ih->hash, &id, sizeof(id));
100}
101
102typedef struct {
104 void *ctx;
105} iter_ctx;
106
107static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen,
108 const void *val)
109{
110 iter_ctx *ictx = ctx;
111 return ictx->iter(ictx->ctx, (void*)val); /* why is this passed const?*/
112}
113
115{
117 ictx.iter = fn;
118 ictx.ctx = ctx;
119 return apr_hash_do(ihash_iter, &ictx, ih->hash);
120}
121
123{
124 apr_hash_set(ih->hash, ((char *)val + ih->ioff), sizeof(int), val);
125}
126
128{
129 apr_hash_set(ih->hash, &id, sizeof(id), NULL);
130}
131
133{
134 int id = *((int*)((char *)val + ih->ioff));
135 apr_hash_set(ih->hash, &id, sizeof(id), NULL);
136}
137
138
143
144typedef struct {
146 void **buffer;
147 size_t max;
148 size_t len;
150
151static int collect_iter(void *x, void *val)
152{
153 collect_ctx *ctx = x;
154 if (ctx->len < ctx->max) {
155 ctx->buffer[ctx->len++] = val;
156 return 1;
157 }
158 return 0;
159}
160
162{
164 size_t i;
165
166 ctx.ih = ih;
167 ctx.buffer = buffer;
168 ctx.max = max;
169 ctx.len = 0;
171 for (i = 0; i < ctx.len; ++i) {
173 }
174 return ctx.len;
175}
176
177typedef struct {
179 int *buffer;
180 size_t max;
181 size_t len;
183
184static int icollect_iter(void *x, void *val)
185{
186 icollect_ctx *ctx = x;
187 if (ctx->len < ctx->max) {
188 ctx->buffer[ctx->len++] = *((int*)((char *)val + ctx->ih->ioff));
189 return 1;
190 }
191 return 0;
192}
193
195{
197 size_t i;
198
199 ctx.ih = ih;
200 ctx.buffer = buffer;
201 ctx.max = max;
202 ctx.len = 0;
204 for (i = 0; i < ctx.len; ++i) {
206 }
207 return ctx.len;
208}
209
210/*******************************************************************************
211 * iqueue - sorted list of int
212 ******************************************************************************/
213
214static void iq_grow(h2_proxy_iqueue *q, int nlen);
215static void iq_swap(h2_proxy_iqueue *q, int i, int j);
216static int iq_bubble_up(h2_proxy_iqueue *q, int i, int top,
217 h2_proxy_iq_cmp *cmp, void *ctx);
218static int iq_bubble_down(h2_proxy_iqueue *q, int i, int bottom,
219 h2_proxy_iq_cmp *cmp, void *ctx);
220
222{
224 if (q) {
225 q->pool = pool;
226 iq_grow(q, capacity);
227 q->nelts = 0;
228 }
229 return q;
230}
231
233{
234 return q->nelts == 0;
235}
236
238{
239 return q->nelts;
240}
241
242
244{
245 int i;
246
247 if (q->nelts >= q->nalloc) {
248 iq_grow(q, q->nalloc * 2);
249 }
250
251 i = (q->head + q->nelts) % q->nalloc;
252 q->elts[i] = sid;
253 ++q->nelts;
254
255 if (cmp) {
256 /* bubble it to the front of the queue */
257 iq_bubble_up(q, i, q->head, cmp, ctx);
258 }
259}
260
262{
263 int i;
264 for (i = 0; i < q->nelts; ++i) {
265 if (sid == q->elts[(q->head + i) % q->nalloc]) {
266 break;
267 }
268 }
269
270 if (i < q->nelts) {
271 ++i;
272 for (; i < q->nelts; ++i) {
273 q->elts[(q->head+i-1)%q->nalloc] = q->elts[(q->head+i)%q->nalloc];
274 }
275 --q->nelts;
276 return 1;
277 }
278 return 0;
279}
280
282{
283 q->nelts = 0;
284}
285
287{
288 /* Assume that changes in ordering are minimal. This needs,
289 * best case, q->nelts - 1 comparisons to check that nothing
290 * changed.
291 */
292 if (q->nelts > 0) {
293 int i, ni, prev, last;
294
295 /* Start at the end of the queue and create a tail of sorted
296 * entries. Make that tail one element longer in each iteration.
297 */
298 last = i = (q->head + q->nelts - 1) % q->nalloc;
299 while (i != q->head) {
300 prev = (q->nalloc + i - 1) % q->nalloc;
301
302 ni = iq_bubble_up(q, i, prev, cmp, ctx);
303 if (ni == prev) {
304 /* i bubbled one up, bubble the new i down, which
305 * keeps all tasks below i sorted. */
306 iq_bubble_down(q, i, last, cmp, ctx);
307 }
308 i = prev;
309 };
310 }
311}
312
313
315{
316 int sid;
317
318 if (q->nelts <= 0) {
319 return 0;
320 }
321
322 sid = q->elts[q->head];
323 q->head = (q->head + 1) % q->nalloc;
324 q->nelts--;
325
326 return sid;
327}
328
329static void iq_grow(h2_proxy_iqueue *q, int nlen)
330{
331 if (nlen > q->nalloc) {
332 int *nq = apr_pcalloc(q->pool, sizeof(int) * nlen);
333 if (q->nelts > 0) {
334 int l = ((q->head + q->nelts) % q->nalloc) - q->head;
335
336 memmove(nq, q->elts + q->head, sizeof(int) * l);
337 if (l < q->nelts) {
338 /* elts wrapped, append elts in [0, remain] to nq */
339 int remain = q->nelts - l;
340 memmove(nq + l, q->elts, sizeof(int) * remain);
341 }
342 }
343 q->elts = nq;
344 q->nalloc = nlen;
345 q->head = 0;
346 }
347}
348
349static void iq_swap(h2_proxy_iqueue *q, int i, int j)
350{
351 int x = q->elts[i];
352 q->elts[i] = q->elts[j];
353 q->elts[j] = x;
354}
355
356static int iq_bubble_up(h2_proxy_iqueue *q, int i, int top,
357 h2_proxy_iq_cmp *cmp, void *ctx)
358{
359 int prev;
360 while (((prev = (q->nalloc + i - 1) % q->nalloc), i != top)
361 && (*cmp)(q->elts[i], q->elts[prev], ctx) < 0) {
362 iq_swap(q, prev, i);
363 i = prev;
364 }
365 return i;
366}
367
368static int iq_bubble_down(h2_proxy_iqueue *q, int i, int bottom,
369 h2_proxy_iq_cmp *cmp, void *ctx)
370{
371 int next;
372 while (((next = (q->nalloc + i + 1) % q->nalloc), i != bottom)
373 && (*cmp)(q->elts[i], q->elts[next], ctx) > 0) {
374 iq_swap(q, next, i);
375 i = next;
376 }
377 return i;
378}
379
380/*******************************************************************************
381 * h2_proxy_ngheader
382 ******************************************************************************/
383#define H2_HD_MATCH_LIT_CS(l, name) \
384 ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
385
386static int h2_util_ignore_header(const char *name)
387{
388 /* never forward, ch. 8.1.2.2 */
389 return (H2_HD_MATCH_LIT_CS("connection", name)
390 || H2_HD_MATCH_LIT_CS("proxy-connection", name)
391 || H2_HD_MATCH_LIT_CS("upgrade", name)
392 || H2_HD_MATCH_LIT_CS("keep-alive", name)
393 || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
394}
395
396static int count_header(void *ctx, const char *key, const char *value)
397{
399 (*((size_t*)ctx))++;
400 }
401 return 1;
402}
403
404#define NV_ADD_LIT_CS(nv, k, v) add_header(nv, k, sizeof(k) - 1, v, strlen(v))
405#define NV_ADD_CS_CS(nv, k, v) add_header(nv, k, strlen(k), v, strlen(v))
406
408 const char *key, size_t key_len,
409 const char *value, size_t val_len)
410{
411 nghttp2_nv *nv = &ngh->nv[ngh->nvlen++];
412
413 nv->name = (uint8_t*)key;
414 nv->namelen = key_len;
415 nv->value = (uint8_t*)value;
416 nv->valuelen = val_len;
417 return 1;
418}
419
420static int add_table_header(void *ctx, const char *key, const char *value)
421{
423 add_header(ctx, key, strlen(key), value, strlen(value));
424 }
425 return 1;
426}
427
429 const h2_proxy_request *req)
430{
431
433 size_t n;
434
435 ap_assert(req);
436 ap_assert(req->scheme);
437 ap_assert(req->authority);
438 ap_assert(req->path);
439 ap_assert(req->method);
440
441 n = 4;
443
444 ngh = apr_pcalloc(p, sizeof(h2_proxy_ngheader));
445 ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
446 NV_ADD_LIT_CS(ngh, ":scheme", req->scheme);
447 NV_ADD_LIT_CS(ngh, ":authority", req->authority);
448 NV_ADD_LIT_CS(ngh, ":path", req->path);
449 NV_ADD_LIT_CS(ngh, ":method", req->method);
451
452 return ngh;
453}
454
456{
457
459 size_t n;
460
461 n = 0;
462 apr_table_do(count_header, &n, headers, NULL);
463
464 ngh = apr_pcalloc(p, sizeof(h2_proxy_ngheader));
465 ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
466 apr_table_do(add_table_header, ngh, headers, NULL);
467
468 return ngh;
469}
470
471/*******************************************************************************
472 * header HTTP/1 <-> HTTP/2 conversions
473 ******************************************************************************/
474
475typedef struct {
476 const char *name;
477 size_t len;
478} literal;
479
480#define H2_DEF_LITERAL(n) { (n), (sizeof(n)-1) }
481#define H2_LIT_ARGS(a) (a),H2_ALEN(a)
482
484 H2_DEF_LITERAL("upgrade"),
485 H2_DEF_LITERAL("connection"),
486 H2_DEF_LITERAL("keep-alive"),
487 H2_DEF_LITERAL("http2-settings"),
488 H2_DEF_LITERAL("proxy-connection"),
489 H2_DEF_LITERAL("transfer-encoding"),
490};
492 H2_DEF_LITERAL("alt-svc"),
493};
494
495static int ignore_header(const literal *lits, size_t llen,
496 const char *name, size_t nlen)
497{
498 const literal *lit;
499 size_t i;
500
501 for (i = 0; i < llen; ++i) {
502 lit = &lits[i];
503 if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
504 return 1;
505 }
506 }
507 return 0;
508}
509
510static int h2_proxy_req_ignore_header(const char *name, size_t len)
511{
513}
514
520
522{
523 size_t start = 1;
524 size_t i;
525 for (i = 0; i < len; ++i) {
526 if (start) {
527 if (s[i] >= 'a' && s[i] <= 'z') {
528 s[i] -= 'a' - 'A';
529 }
530
531 start = 0;
532 }
533 else if (s[i] == '-') {
534 start = 1;
535 }
536 }
537}
538
539/*******************************************************************************
540 * h2 request handling
541 ******************************************************************************/
542
544#define H2_HD_MATCH_LIT(l, name, nlen) \
545 ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
546
548 const char *name, size_t nlen,
549 const char *value, size_t vlen)
550{
551 char *hname, *hvalue;
552
554 return APR_SUCCESS;
555 }
556 else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
557 const char *existing = apr_table_get(headers, "cookie");
558 if (existing) {
559 char *nval;
560
561 /* Cookie header come separately in HTTP/2, but need
562 * to be merged by "; " (instead of default ", ")
563 */
565 nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
566 apr_table_setn(headers, "Cookie", nval);
567 return APR_SUCCESS;
568 }
569 }
570 else if (H2_HD_MATCH_LIT("host", name, nlen)) {
571 if (apr_table_get(headers, "Host")) {
572 return APR_SUCCESS; /* ignore duplicate */
573 }
574 }
575
579 apr_table_mergen(headers, hname, hvalue);
580
581 return APR_SUCCESS;
582}
583
585 const char *scheme, const char *authority,
586 const char *path, apr_table_t *header)
587{
589
590 req->method = method;
591 req->scheme = scheme;
592 req->authority = authority;
593 req->path = path;
594 req->headers = header? header : apr_table_make(pool, 10);
595 req->request_time = apr_time_now();
596
597 return req;
598}
599
604
609
610static int set_h1_header(void *ctx, const char *key, const char *value)
611{
612 h1_ctx *x = ctx;
613 size_t klen = strlen(key);
615 h2_headers_add_h1(x->headers, x->pool, key, klen, value, strlen(value));
616 }
617 return 1;
618}
619
621 const char *method, const char *scheme,
622 const char *authority, const char *path,
623 apr_table_t *headers)
624{
625 h1_ctx x;
626 const char *val;
627
628 req->method = method;
629 req->scheme = scheme;
630 req->authority = authority;
631 req->path = path;
632
633 ap_assert(req->scheme);
634 ap_assert(req->authority);
635 ap_assert(req->path);
636 ap_assert(req->method);
637
638 x.pool = pool;
639 x.headers = req->headers;
640 apr_table_do(set_h1_header, &x, headers, NULL);
641 if ((val = apr_table_get(headers, "TE")) && ap_find_token(pool, val, "trailers")) {
642 /* client accepts trailers, forward this information */
643 apr_table_addn(req->headers, "TE", "trailers");
644 }
645 apr_table_setn(req->headers, "te", "trailers");
646 return APR_SUCCESS;
647}
648
649/*******************************************************************************
650 * frame logging
651 ******************************************************************************/
652
653int h2_proxy_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
654{
655 char scratch[128];
656 size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
657
658 switch (frame->hd.type) {
659 case NGHTTP2_DATA: {
660 return apr_snprintf(buffer, maxlen,
661 "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
662 (int)frame->hd.length, frame->hd.flags,
663 frame->hd.stream_id, (int)frame->data.padlen);
664 }
665 case NGHTTP2_HEADERS: {
666 return apr_snprintf(buffer, maxlen,
667 "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
668 (int)frame->hd.length,
669 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
670 frame->hd.stream_id,
671 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
672 }
673 case NGHTTP2_PRIORITY: {
674 return apr_snprintf(buffer, maxlen,
675 "PRIORITY[length=%d, flags=%d, stream=%d]",
676 (int)frame->hd.length,
677 frame->hd.flags, frame->hd.stream_id);
678 }
679 case NGHTTP2_RST_STREAM: {
680 return apr_snprintf(buffer, maxlen,
681 "RST_STREAM[length=%d, flags=%d, stream=%d]",
682 (int)frame->hd.length,
683 frame->hd.flags, frame->hd.stream_id);
684 }
685 case NGHTTP2_SETTINGS: {
686 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
687 return apr_snprintf(buffer, maxlen,
688 "SETTINGS[ack=1, stream=%d]",
689 frame->hd.stream_id);
690 }
691 return apr_snprintf(buffer, maxlen,
692 "SETTINGS[length=%d, stream=%d]",
693 (int)frame->hd.length, frame->hd.stream_id);
694 }
696 return apr_snprintf(buffer, maxlen,
697 "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
698 (int)frame->hd.length,
699 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
700 frame->hd.stream_id);
701 }
702 case NGHTTP2_PING: {
703 return apr_snprintf(buffer, maxlen,
704 "PING[length=%d, ack=%d, stream=%d]",
705 (int)frame->hd.length,
706 frame->hd.flags&NGHTTP2_FLAG_ACK,
707 frame->hd.stream_id);
708 }
709 case NGHTTP2_GOAWAY: {
710 size_t len = (frame->goaway.opaque_data_len < s_len)?
711 frame->goaway.opaque_data_len : s_len-1;
712 memcpy(scratch, frame->goaway.opaque_data, len);
713 scratch[len] = '\0';
714 return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s', "
715 "last_stream=%d]", frame->goaway.error_code,
716 scratch, frame->goaway.last_stream_id);
717 }
719 return apr_snprintf(buffer, maxlen,
720 "WINDOW_UPDATE[stream=%d, incr=%d]",
721 frame->hd.stream_id,
722 frame->window_update.window_size_increment);
723 }
724 default:
725 return apr_snprintf(buffer, maxlen,
726 "type=%d[length=%d, flags=%d, stream=%d]",
727 frame->hd.type, (int)frame->hd.length,
728 frame->hd.flags, frame->hd.stream_id);
729 }
730}
731
732/*******************************************************************************
733 * link header handling
734 ******************************************************************************/
735
736typedef struct {
740 const char *s;
741 int slen;
742 int i;
743 const char *server_uri;
745 const char *real_backend_uri;
747 const char *p_server_uri;
751} link_ctx;
752
753static int attr_char(char c)
754{
755 switch (c) {
756 case '!':
757 case '#':
758 case '$':
759 case '&':
760 case '+':
761 case '-':
762 case '.':
763 case '^':
764 case '_':
765 case '`':
766 case '|':
767 case '~':
768 return 1;
769 default:
770 return apr_isalnum(c);
771 }
772}
773
774static int ptoken_char(char c)
775{
776 switch (c) {
777 case '!':
778 case '#':
779 case '$':
780 case '&':
781 case '\'':
782 case '(':
783 case ')':
784 case '*':
785 case '+':
786 case '-':
787 case '.':
788 case '/':
789 case ':':
790 case '<':
791 case '=':
792 case '>':
793 case '?':
794 case '@':
795 case '[':
796 case ']':
797 case '^':
798 case '_':
799 case '`':
800 case '{':
801 case '|':
802 case '}':
803 case '~':
804 return 1;
805 default:
806 return apr_isalnum(c);
807 }
808}
809
810static int skip_ws(link_ctx *ctx)
811{
812 char c;
813 while (ctx->i < ctx->slen
814 && (((c = ctx->s[ctx->i]) == ' ') || (c == '\t'))) {
815 ++ctx->i;
816 }
817 return (ctx->i < ctx->slen);
818}
819
820static int find_chr(link_ctx *ctx, char c, int *pidx)
821{
822 int j;
823 for (j = ctx->i; j < ctx->slen; ++j) {
824 if (ctx->s[j] == c) {
825 *pidx = j;
826 return 1;
827 }
828 }
829 return 0;
830}
831
832static int read_chr(link_ctx *ctx, char c)
833{
834 if (ctx->i < ctx->slen && ctx->s[ctx->i] == c) {
835 ++ctx->i;
836 return 1;
837 }
838 return 0;
839}
840
842{
843 if (skip_ws(ctx) && read_chr(ctx, '\"')) {
844 int end;
845 if (find_chr(ctx, '\"', &end)) {
846 ctx->i = end + 1;
847 return 1;
848 }
849 }
850 return 0;
851}
852
854{
855 if (skip_ws(ctx)) {
856 int i;
857 for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) {
858 /* nop */
859 }
860 if (i > ctx->i) {
861 ctx->i = i;
862 return 1;
863 }
864 }
865 return 0;
866}
867
868
870{
871 ctx->link_start = ctx->link_end = 0;
872 if (skip_ws(ctx) && read_chr(ctx, '<')) {
873 int end;
874 if (find_chr(ctx, '>', &end)) {
875 ctx->link_start = ctx->i;
876 ctx->link_end = end;
877 ctx->i = end + 1;
878 return 1;
879 }
880 }
881 return 0;
882}
883
885{
886 if (skip_ws(ctx)) {
887 int i;
888 for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) {
889 /* nop */
890 }
891 if (i > ctx->i) {
892 ctx->i = i;
893 return 1;
894 }
895 }
896 return 0;
897}
898
900{
901 if (skip_ws(ctx) && read_chr(ctx, '=')) {
902 if (skip_qstring(ctx) || skip_ptoken(ctx)) {
903 return 1;
904 }
905 }
906 return 0;
907}
908
910{
911 if (skip_ws(ctx) && read_chr(ctx, ';')) {
912 if (skip_pname(ctx)) {
913 skip_pvalue(ctx); /* value is optional */
914 return 1;
915 }
916 }
917 return 0;
918}
919
921{
922 if (skip_ws(ctx) && read_chr(ctx, ',')) {
923 return 1;
924 }
925 return 0;
926}
927
928static size_t subst_str(link_ctx *ctx, int start, int end, const char *ns)
929{
930 int olen, nlen, plen;
931 int delta;
932 char *p;
933
934 olen = end - start;
935 nlen = (int)strlen(ns);
936 delta = nlen - olen;
937 plen = ctx->slen + delta + 1;
938 p = apr_palloc(ctx->pool, plen);
939 memcpy(p, ctx->s, start);
940 memcpy(p + start, ns, nlen);
941 strcpy(p + start + nlen, ctx->s + end);
942 ctx->s = p;
943 ctx->slen = plen - 1; /* (int)strlen(p) */
944 if (ctx->i >= end) {
945 ctx->i += delta;
946 }
947 return nlen;
948}
949
950static void map_link(link_ctx *ctx)
951{
952 if (ctx->link_start < ctx->link_end) {
954 size_t need_len, link_len, buffer_len, prepend_p_server;
955 const char *mapped;
956
957 buffer[0] = '\0';
958 buffer_len = 0;
959 link_len = ctx->link_end - ctx->link_start;
960 need_len = link_len + 1;
961 prepend_p_server = (ctx->s[ctx->link_start] == '/');
962 if (prepend_p_server) {
963 /* common to use relative uris in link header, for mappings
964 * to work need to prefix the backend server uri */
965 need_len += ctx->psu_len;
966 apr_cpystrn(buffer, ctx->p_server_uri, sizeof(buffer));
967 buffer_len = ctx->psu_len;
968 }
969 if (need_len > sizeof(buffer)) {
971 "link_reverse_map uri too long, skipped: %s", ctx->s);
972 return;
973 }
974 apr_cpystrn(buffer + buffer_len, ctx->s + ctx->link_start, link_len + 1);
976 && strcmp(ctx->real_backend_uri, ctx->p_server_uri)
977 && !strncmp(buffer, ctx->real_backend_uri, ctx->rbu_len)) {
978 /* the server uri and our local proxy uri we use differ, for mapping
979 * to work, we need to use the proxy uri */
980 int path_start = ctx->link_start + ctx->rbu_len;
981 link_len -= ctx->rbu_len;
982 memcpy(buffer, ctx->p_server_uri, ctx->psu_len);
983 memcpy(buffer + ctx->psu_len, ctx->s + path_start, link_len);
984 buffer_len = ctx->psu_len + link_len;
985 buffer[buffer_len] = '\0';
986 }
989 "reverse_map[%s] %s --> %s", ctx->p_server_uri, buffer, mapped);
990 if (mapped != buffer) {
991 if (prepend_p_server) {
992 if (ctx->server_uri == NULL) {
993 ctx->server_uri = ap_construct_url(ctx->pool, "", ctx->r);
994 ctx->su_len = (int)strlen(ctx->server_uri);
995 }
996 if (!strncmp(mapped, ctx->server_uri, ctx->su_len)) {
997 mapped += ctx->su_len;
998 }
999 }
1000 subst_str(ctx, ctx->link_start, ctx->link_end, mapped);
1001 }
1002 }
1003}
1004
1005/* RFC 5988 <https://tools.ietf.org/html/rfc5988#section-6.2.1>
1006 Link = "Link" ":" #link-value
1007 link-value = "<" URI-Reference ">" *( ";" link-param )
1008 link-param = ( ( "rel" "=" relation-types )
1009 | ( "anchor" "=" <"> URI-Reference <"> )
1010 | ( "rev" "=" relation-types )
1011 | ( "hreflang" "=" Language-Tag )
1012 | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
1013 | ( "title" "=" quoted-string )
1014 | ( "title*" "=" ext-value )
1015 | ( "type" "=" ( media-type | quoted-mt ) )
1016 | ( link-extension ) )
1017 link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
1018 | ( ext-name-star "=" ext-value )
1019 ext-name-star = parmname "*" ; reserved for RFC2231-profiled
1020 ; extensions. Whitespace NOT
1021 ; allowed in between.
1022 ptoken = 1*ptokenchar
1023 ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "("
1024 | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
1025 | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
1026 | "[" | "]" | "^" | "_" | "`" | "{" | "|"
1027 | "}" | "~"
1028 media-type = type-name "/" subtype-name
1029 quoted-mt = <"> media-type <">
1030 relation-types = relation-type
1031 | <"> relation-type *( 1*SP relation-type ) <">
1032 relation-type = reg-rel-type | ext-rel-type
1033 reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
1034 ext-rel-type = URI
1035
1036 and from <https://tools.ietf.org/html/rfc5987>
1037 parmname = 1*attr-char
1038 attr-char = ALPHA / DIGIT
1039 / "!" / "#" / "$" / "&" / "+" / "-" / "."
1040 / "^" / "_" / "`" / "|" / "~"
1041 */
1042
1044 proxy_dir_conf *conf,
1045 const char *real_backend_uri,
1046 const char *proxy_server_uri,
1047 const char *s)
1048{
1049 link_ctx ctx;
1050
1051 if (r->proxyreq != PROXYREQ_REVERSE) {
1052 return s;
1053 }
1054 memset(&ctx, 0, sizeof(ctx));
1055 ctx.r = r;
1056 ctx.pool = r->pool;
1057 ctx.conf = conf;
1058 ctx.real_backend_uri = real_backend_uri;
1059 ctx.rbu_len = (int)strlen(ctx.real_backend_uri);
1060 ctx.p_server_uri = proxy_server_uri;
1061 ctx.psu_len = (int)strlen(ctx.p_server_uri);
1062 ctx.s = s;
1063 ctx.slen = (int)strlen(s);
1064 while (read_link(&ctx)) {
1065 while (skip_param(&ctx)) {
1066 /* nop */
1067 }
1068 map_link(&ctx);
1069 if (!read_sep(&ctx)) {
1070 break;
1071 }
1072 }
1074 "link_reverse_map %s --> %s", s, ctx.s);
1075 return ctx.s;
1076}
1077
1078/*******************************************************************************
1079 * FIFO queue
1080 ******************************************************************************/
1081
1093
1094static int nth_index(h2_proxy_fifo *fifo, int n)
1095{
1096 return (fifo->head + n) % fifo->nelems;
1097}
1098
1100{
1102
1103 apr_thread_cond_destroy(fifo->not_empty);
1104 apr_thread_cond_destroy(fifo->not_full);
1106
1107 return APR_SUCCESS;
1108}
1109
1110static int index_of(h2_proxy_fifo *fifo, void *elem)
1111{
1112 int i;
1113
1114 for (i = 0; i < fifo->count; ++i) {
1115 if (elem == fifo->elems[nth_index(fifo, i)]) {
1116 return i;
1117 }
1118 }
1119 return -1;
1120}
1121
1123 int capacity, int as_set)
1124{
1125 apr_status_t rv;
1127
1128 fifo = apr_pcalloc(pool, sizeof(*fifo));
1129 if (fifo == NULL) {
1130 return APR_ENOMEM;
1131 }
1132
1133 rv = apr_thread_mutex_create(&fifo->lock,
1135 if (rv != APR_SUCCESS) {
1136 return rv;
1137 }
1138
1139 rv = apr_thread_cond_create(&fifo->not_empty, pool);
1140 if (rv != APR_SUCCESS) {
1141 return rv;
1142 }
1143
1144 rv = apr_thread_cond_create(&fifo->not_full, pool);
1145 if (rv != APR_SUCCESS) {
1146 return rv;
1147 }
1148
1149 fifo->elems = apr_pcalloc(pool, capacity * sizeof(void*));
1150 if (fifo->elems == NULL) {
1151 return APR_ENOMEM;
1152 }
1153 fifo->nelems = capacity;
1154 fifo->set = as_set;
1155
1156 *pfifo = fifo;
1158
1159 return APR_SUCCESS;
1160}
1161
1163{
1164 return create_int(pfifo, pool, capacity, 0);
1165}
1166
1168{
1169 return create_int(pfifo, pool, capacity, 1);
1170}
1171
1173{
1174 apr_status_t rv;
1175 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1176 fifo->aborted = 1;
1178 }
1179 return rv;
1180}
1181
1183{
1184 apr_status_t rv;
1185 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1186 apr_thread_cond_broadcast(fifo->not_empty);
1189 }
1190 return rv;
1191}
1192
1194{
1195 return fifo->count;
1196}
1197
1199{
1200 return fifo->nelems;
1201}
1202
1204{
1205 if (fifo->count == 0) {
1206 if (!block) {
1207 return APR_EAGAIN;
1208 }
1209 while (fifo->count == 0) {
1210 if (fifo->aborted) {
1211 return APR_EOF;
1212 }
1213 apr_thread_cond_wait(fifo->not_empty, fifo->lock);
1214 }
1215 }
1216 return APR_SUCCESS;
1217}
1218
1220{
1221 apr_status_t rv;
1222
1223 if (fifo->aborted) {
1224 return APR_EOF;
1225 }
1226
1227 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1228 if (fifo->set && index_of(fifo, elem) >= 0) {
1229 /* set mode, elem already member */
1231 return APR_EEXIST;
1232 }
1233 else if (fifo->count == fifo->nelems) {
1234 if (block) {
1235 while (fifo->count == fifo->nelems) {
1236 if (fifo->aborted) {
1238 return APR_EOF;
1239 }
1240 apr_thread_cond_wait(fifo->not_full, fifo->lock);
1241 }
1242 }
1243 else {
1245 return APR_EAGAIN;
1246 }
1247 }
1248
1249 ap_assert(fifo->count < fifo->nelems);
1250 fifo->elems[nth_index(fifo, fifo->count)] = elem;
1251 ++fifo->count;
1252 if (fifo->count == 1) {
1253 apr_thread_cond_broadcast(fifo->not_empty);
1254 }
1256 }
1257 return rv;
1258}
1259
1264
1269
1271{
1272 void *elem;
1273
1274 ap_assert(fifo->count > 0);
1275 elem = fifo->elems[fifo->head];
1276 --fifo->count;
1277 if (fifo->count > 0) {
1278 fifo->head = nth_index(fifo, 1);
1279 if (fifo->count+1 == fifo->nelems) {
1281 }
1282 }
1283 return elem;
1284}
1285
1287{
1288 apr_status_t rv;
1289
1290 if (fifo->aborted) {
1291 return APR_EOF;
1292 }
1293
1294 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1295 if ((rv = check_not_empty(fifo, block)) != APR_SUCCESS) {
1297 *pelem = NULL;
1298 return rv;
1299 }
1300
1301 ap_assert(fifo->count > 0);
1302 *pelem = pull_head(fifo);
1303
1305 }
1306 return rv;
1307}
1308
1313
1318
1320{
1321 apr_status_t rv;
1322
1323 if (fifo->aborted) {
1324 return APR_EOF;
1325 }
1326
1327 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1328 int i, rc;
1329 void *e;
1330
1331 rc = 0;
1332 for (i = 0; i < fifo->count; ++i) {
1333 e = fifo->elems[nth_index(fifo, i)];
1334 if (e == elem) {
1335 ++rc;
1336 }
1337 else if (rc) {
1338 fifo->elems[nth_index(fifo, i-rc)] = e;
1339 }
1340 }
1341 if (rc) {
1342 fifo->count -= rc;
1343 if (fifo->count + rc == fifo->nelems) {
1345 }
1346 rv = APR_SUCCESS;
1347 }
1348 else {
1349 rv = APR_EAGAIN;
1350 }
1351
1353 }
1354 return rv;
1355}
int n
Definition ap_regex.h:278
const char apr_size_t len
Definition ap_regex.h:187
APR general purpose library routines.
APR Strings library.
APR Condition Variable Routines.
APR Thread Mutex Routines.
#define APLOG_USE_MODULE(foo)
request_rec int int apr_table_t const char * path
request_rec * r
#define HUGE_STRING_LEN
Definition httpd.h:303
char * ap_construct_url(apr_pool_t *p, const char *uri, request_rec *r)
Definition core.c:1246
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#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 APR_EAGAIN
Definition apr_errno.h:730
#define APR_EOF
Definition apr_errno.h:461
#define APR_ENOMEM
Definition apr_errno.h:683
#define APR_EEXIST
Definition apr_errno.h:648
apr_file_t apr_off_t start
apr_bucket * e
apr_brigade_flush void * ctx
const char apr_int32_t apr_uint32_t * nv
apr_redis_t * rc
Definition apr_redis.h:173
const char * ap_proxy_location_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *url)
Definition proxy_util.c:882
int ap_find_token(apr_pool_t *p, const char *line, const char *tok)
Definition util.c:1726
#define ap_assert(exp)
Definition httpd.h:2271
#define PROXYREQ_REVERSE
Definition httpd.h:1135
apr_size_t size
apr_uint32_t apr_uint32_t cmp
Definition apr_atomic.h:106
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define apr_isalnum(c)
Definition apr_lib.h:203
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
#define memmove(a, b, c)
apr_ssize_t * klen
Definition apr_hash.h:71
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_uint32_t apr_pool_t apr_uint32_t apr_pollset_method_e method
Definition apr_poll.h:195
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char char ** end
const char char ** last
const char * s
Definition apr_strings.h:95
int nelts
Definition apr_tables.h:122
apr_size_t apr_size_t max
Definition apr_time.h:220
apr_status_t h2_proxy_fifo_try_push(h2_proxy_fifo *fifo, void *elem)
apr_status_t h2_proxy_fifo_try_pull(h2_proxy_fifo *fifo, void **pelem)
int h2_proxy_iq_remove(h2_proxy_iqueue *q, int sid)
static int add_header(h2_proxy_ngheader *ngh, const char *key, size_t key_len, const char *value, size_t val_len)
int h2_proxy_ihash_iter(h2_proxy_ihash_t *ih, h2_proxy_ihash_iter_t *fn, void *ctx)
static int iq_bubble_up(h2_proxy_iqueue *q, int i, int top, h2_proxy_iq_cmp *cmp, void *ctx)
void h2_proxy_ihash_remove_val(h2_proxy_ihash_t *ih, void *val)
static void iq_grow(h2_proxy_iqueue *q, int nlen)
apr_status_t h2_proxy_fifo_remove(h2_proxy_fifo *fifo, void *elem)
static int collect_iter(void *x, void *val)
static apr_status_t check_not_empty(h2_proxy_fifo *fifo, int block)
apr_status_t h2_proxy_fifo_set_create(h2_proxy_fifo **pfifo, apr_pool_t *pool, int capacity)
#define H2_DEF_LITERAL(n)
static int skip_ws(link_ctx *ctx)
static int read_link(link_ctx *ctx)
#define H2_HD_MATCH_LIT(l, name, nlen)
static void map_link(link_ctx *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)
void h2_proxy_iq_clear(h2_proxy_iqueue *q)
static int read_chr(link_ctx *ctx, char c)
int h2_proxy_iq_shift(h2_proxy_iqueue *q)
static literal IgnoredRequestHeaders[]
h2_proxy_ngheader * h2_proxy_util_nghd_make(apr_pool_t *p, apr_table_t *headers)
int h2_proxy_fifo_count(h2_proxy_fifo *fifo)
apr_status_t h2_proxy_fifo_pull(h2_proxy_fifo *fifo, void **pelem)
static h2_proxy_request * h2_proxy_req_createn(int id, apr_pool_t *pool, const char *method, const char *scheme, const char *authority, const char *path, apr_table_t *header)
static apr_status_t fifo_destroy(void *data)
unsigned char h2_proxy_log2(int n)
int h2_proxy_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
static int h2_proxy_req_ignore_header(const char *name, size_t len)
apr_status_t h2_proxy_fifo_interrupt(h2_proxy_fifo *fifo)
static int attr_char(char c)
static literal IgnoredProxyRespHds[]
void h2_proxy_ihash_remove(h2_proxy_ihash_t *ih, int id)
int h2_proxy_fifo_capacity(h2_proxy_fifo *fifo)
static apr_status_t fifo_pull(h2_proxy_fifo *fifo, void **pelem, int block)
static int count_header(void *ctx, const char *key, const char *value)
static int iq_bubble_down(h2_proxy_iqueue *q, int i, int bottom, h2_proxy_iq_cmp *cmp, void *ctx)
static int ignore_header(const literal *lits, size_t llen, const char *name, size_t nlen)
size_t h2_proxy_ihash_shift(h2_proxy_ihash_t *ih, void **buffer, size_t max)
void h2_proxy_iq_add(h2_proxy_iqueue *q, int sid, h2_proxy_iq_cmp *cmp, void *ctx)
int h2_proxy_iq_empty(h2_proxy_iqueue *q)
h2_proxy_request * h2_proxy_req_create(int id, apr_pool_t *pool)
static int skip_pvalue(link_ctx *ctx)
size_t h2_proxy_ihash_ishift(h2_proxy_ihash_t *ih, int *buffer, size_t max)
h2_proxy_ngheader * h2_proxy_util_nghd_make_req(apr_pool_t *p, const h2_proxy_request *req)
static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen, const void *val)
static int nth_index(h2_proxy_fifo *fifo, int n)
static size_t subst_str(link_ctx *ctx, int start, int end, const char *ns)
static int read_sep(link_ctx *ctx)
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)
static apr_status_t create_int(h2_proxy_fifo **pfifo, apr_pool_t *pool, int capacity, int as_set)
static int index_of(h2_proxy_fifo *fifo, void *elem)
static apr_status_t fifo_push(h2_proxy_fifo *fifo, void *elem, int block)
static int find_chr(link_ctx *ctx, char c, int *pidx)
static unsigned int ihash(const char *key, apr_ssize_t *klen)
void h2_proxy_ihash_clear(h2_proxy_ihash_t *ih)
static int h2_util_ignore_header(const char *name)
void h2_proxy_util_camel_case_header(char *s, size_t len)
int h2_proxy_iq_count(h2_proxy_iqueue *q)
apr_status_t h2_proxy_fifo_term(h2_proxy_fifo *fifo)
static apr_status_t h2_headers_add_h1(apr_table_t *headers, apr_pool_t *pool, const char *name, size_t nlen, const char *value, size_t vlen)
size_t h2_proxy_ihash_count(h2_proxy_ihash_t *ih)
void h2_proxy_ihash_add(h2_proxy_ihash_t *ih, void *val)
void h2_proxy_iq_sort(h2_proxy_iqueue *q, h2_proxy_iq_cmp *cmp, void *ctx)
static void * pull_head(h2_proxy_fifo *fifo)
static int skip_param(link_ctx *ctx)
#define H2_HD_MATCH_LIT_CS(l, name)
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)
static int skip_ptoken(link_ctx *ctx)
static int icollect_iter(void *x, void *val)
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)
apr_status_t h2_proxy_fifo_push(h2_proxy_fifo *fifo, void *elem)
static int skip_pname(link_ctx *ctx)
static int ptoken_char(char c)
static int add_table_header(void *ctx, const char *key, const char *value)
static int set_h1_header(void *ctx, const char *key, const char *value)
int h2_proxy_ihash_empty(h2_proxy_ihash_t *ih)
#define NV_ADD_LIT_CS(nv, k, v)
static int skip_qstring(link_ctx *ctx)
apr_status_t h2_proxy_fifo_create(h2_proxy_fifo **pfifo, apr_pool_t *pool, int capacity)
#define H2_LIT_ARGS(a)
static void iq_swap(h2_proxy_iqueue *q, int i, int j)
int h2_proxy_iq_cmp(int i1, int i2, void *ctx)
int h2_proxy_ihash_iter_t(void *ctx, void *val)
CORE HTTP Daemon.
Apache Logging library.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
Proxy Extension Module for Apache.
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
h2_proxy_ihash_t * ih
apr_pool_t * pool
apr_table_t * headers
apr_thread_mutex_t * lock
apr_thread_cond_t * not_empty
apr_thread_cond_t * not_full
apr_hash_t * hash
apr_pool_t * pool
const char * scheme
const char * authority
apr_table_t * headers
apr_time_t request_time
const char * path
const char * method
h2_proxy_ihash_t * ih
h2_proxy_ihash_iter_t * iter
const char * name
size_t len
A structure that represents the current request.
Definition httpd.h:845
apr_pool_t * pool
Definition httpd.h:847
int proxyreq
Definition httpd.h:873
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray
#define ns(x)
Definition xmltok.c:1644