From 9929fb783b5cfbefe8890cb7edc35a8a337e6e7e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 14 Jun 2016 08:51:31 +0000 Subject: [PATCH] Merge of r1747550 from trunk: mod_proxy_http2 no longer shares objects with mod_http2 h2_casecmpstr eliminated, uses backported ap_cstr_casecmp* git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1748359 13f79535-47bb-0310-9956-ffa450edef68 --- CMakeLists.txt | 2 +- modules/http2/NWGNUmod_http2 | 1 - modules/http2/config2.m4 | 2 +- modules/http2/h2_mplx.h | 1 - modules/http2/h2_proxy_session.c | 4 +- modules/http2/h2_proxy_util.c | 705 ++++++++++++++++++++++++++++++ modules/http2/h2_proxy_util.h | 181 ++++++++ modules/http2/h2_util.c | 245 ----------- modules/http2/h2_util.h | 31 -- modules/http2/mod_proxy_http2.c | 6 +- modules/http2/mod_proxy_http2.dsp | 2 +- 11 files changed, 894 insertions(+), 286 deletions(-) create mode 100644 modules/http2/h2_proxy_util.c create mode 100644 modules/http2/h2_proxy_util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b5da0b6166..3f4b6d57e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -436,7 +436,7 @@ SET(mod_proxy_http2_extra_defines ssize_t=long) SET(mod_proxy_http2_extra_includes ${NGHTTP2_INCLUDE_DIR}) SET(mod_proxy_http2_extra_libs ${NGHTTP2_LIBRARIES} mod_proxy) SET(mod_proxy_http2_extra_sources - modules/http2/h2_proxy_session.c modules/http2/h2_util.c + modules/http2/h2_proxy_session.c modules/http2/h2_proxy_util.c ) SET(mod_ratelimit_extra_defines AP_RL_DECLARE_EXPORT) SET(mod_sed_extra_sources diff --git a/modules/http2/NWGNUmod_http2 b/modules/http2/NWGNUmod_http2 index 755537cd85..e9c48a4009 100644 --- a/modules/http2/NWGNUmod_http2 +++ b/modules/http2/NWGNUmod_http2 @@ -355,7 +355,6 @@ $(OBJDIR)/mod_http2.imp : NWGNUmod_http2 @echo $(DL)GEN $@$(DL) @echo $(DL) (HTTP2)$(DL) > $@ @echo $(DL) http2_module,$(DL) >> $@ - @echo $(DL) h2_casecmpstrn,$(DL) >> $@ @echo $(DL) h2_ihash_add,$(DL) >> $@ @echo $(DL) h2_ihash_clear,$(DL) >> $@ @echo $(DL) h2_ihash_count,$(DL) >> $@ diff --git a/modules/http2/config2.m4 b/modules/http2/config2.m4 index b17387c9ed..a77ad80899 100644 --- a/modules/http2/config2.m4 +++ b/modules/http2/config2.m4 @@ -201,7 +201,7 @@ dnl # list of module object files proxy_http2_objs="dnl mod_proxy_http2.lo dnl h2_proxy_session.lo dnl -h2_util.lo dnl +h2_proxy_util.lo dnl " dnl # hook module into the Autoconf mechanism (--enable-proxy_http2) diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index f2f60fc9b3..821e6d65df 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -40,7 +40,6 @@ struct apr_thread_cond_t; struct h2_bucket_beam; struct h2_config; struct h2_ihash_t; -struct h2_ilist_t; struct h2_response; struct h2_task; struct h2_stream; diff --git a/modules/http2/h2_proxy_session.c b/modules/http2/h2_proxy_session.c index 919fe4a4c3..b7dadca147 100644 --- a/modules/http2/h2_proxy_session.c +++ b/modules/http2/h2_proxy_session.c @@ -23,7 +23,7 @@ #include "mod_http2.h" #include "h2.h" -#include "h2_util.h" +#include "h2_proxy_util.h" #include "h2_proxy_session.h" APLOG_USE_MODULE(proxy_http2); @@ -211,7 +211,7 @@ static void process_proxy_header(request_rec *r, const char *n, const char *v) int i; for (i = 0; transform_hdrs[i].name; ++i) { - if (!h2_casecmpstr(transform_hdrs[i].name, n)) { + if (!ap_cstr_casecmp(transform_hdrs[i].name, n)) { dconf = ap_get_module_config(r->per_dir_config, &proxy_module); apr_table_add(r->headers_out, n, (*transform_hdrs[i].func)(r, dconf, v)); diff --git a/modules/http2/h2_proxy_util.c b/modules/http2/h2_proxy_util.c new file mode 100644 index 0000000000..839f4a5a94 --- /dev/null +++ b/modules/http2/h2_proxy_util.c @@ -0,0 +1,705 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include "h2.h" +#include "h2_proxy_util.h" + +/* h2_log2(n) iff n is a power of 2 */ +unsigned char h2_log2(apr_uint32_t n) +{ + int lz = 0; + if (!n) { + return 0; + } + if (!(n & 0xffff0000u)) { + lz += 16; + n = (n << 16); + } + if (!(n & 0xff000000u)) { + lz += 8; + n = (n << 8); + } + if (!(n & 0xf0000000u)) { + lz += 4; + n = (n << 4); + } + if (!(n & 0xc0000000u)) { + lz += 2; + n = (n << 2); + } + if (!(n & 0x80000000u)) { + lz += 1; + } + + return 31 - lz; +} + +/******************************************************************************* + * ihash - hash for structs with int identifier + ******************************************************************************/ +struct h2_ihash_t { + apr_hash_t *hash; + size_t ioff; +}; + +static unsigned int ihash(const char *key, apr_ssize_t *klen) +{ + return (unsigned int)(*((int*)key)); +} + +h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int) +{ + h2_ihash_t *ih = apr_pcalloc(pool, sizeof(h2_ihash_t)); + ih->hash = apr_hash_make_custom(pool, ihash); + ih->ioff = offset_of_int; + return ih; +} + +size_t h2_ihash_count(h2_ihash_t *ih) +{ + return apr_hash_count(ih->hash); +} + +int h2_ihash_empty(h2_ihash_t *ih) +{ + return apr_hash_count(ih->hash) == 0; +} + +void *h2_ihash_get(h2_ihash_t *ih, int id) +{ + return apr_hash_get(ih->hash, &id, sizeof(id)); +} + +typedef struct { + h2_ihash_iter_t *iter; + void *ctx; +} iter_ctx; + +static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen, + const void *val) +{ + iter_ctx *ictx = ctx; + return ictx->iter(ictx->ctx, (void*)val); /* why is this passed const?*/ +} + +int h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx) +{ + iter_ctx ictx; + ictx.iter = fn; + ictx.ctx = ctx; + return apr_hash_do(ihash_iter, &ictx, ih->hash); +} + +void h2_ihash_add(h2_ihash_t *ih, void *val) +{ + apr_hash_set(ih->hash, ((char *)val + ih->ioff), sizeof(int), val); +} + +void h2_ihash_remove(h2_ihash_t *ih, int id) +{ + apr_hash_set(ih->hash, &id, sizeof(id), NULL); +} + +void h2_ihash_remove_val(h2_ihash_t *ih, void *val) +{ + int id = *((int*)((char *)val + ih->ioff)); + apr_hash_set(ih->hash, &id, sizeof(id), NULL); +} + + +void h2_ihash_clear(h2_ihash_t *ih) +{ + apr_hash_clear(ih->hash); +} + +typedef struct { + h2_ihash_t *ih; + void **buffer; + size_t max; + size_t len; +} collect_ctx; + +static int collect_iter(void *x, void *val) +{ + collect_ctx *ctx = x; + if (ctx->len < ctx->max) { + ctx->buffer[ctx->len++] = val; + return 1; + } + return 0; +} + +size_t h2_ihash_shift(h2_ihash_t *ih, void **buffer, size_t max) +{ + collect_ctx ctx; + size_t i; + + ctx.ih = ih; + ctx.buffer = buffer; + ctx.max = max; + ctx.len = 0; + h2_ihash_iter(ih, collect_iter, &ctx); + for (i = 0; i < ctx.len; ++i) { + h2_ihash_remove_val(ih, buffer[i]); + } + return ctx.len; +} + +typedef struct { + h2_ihash_t *ih; + int *buffer; + size_t max; + size_t len; +} icollect_ctx; + +static int icollect_iter(void *x, void *val) +{ + icollect_ctx *ctx = x; + if (ctx->len < ctx->max) { + ctx->buffer[ctx->len++] = *((int*)((char *)val + ctx->ih->ioff)); + return 1; + } + return 0; +} + +size_t h2_ihash_ishift(h2_ihash_t *ih, int *buffer, size_t max) +{ + icollect_ctx ctx; + size_t i; + + ctx.ih = ih; + ctx.buffer = buffer; + ctx.max = max; + ctx.len = 0; + h2_ihash_iter(ih, icollect_iter, &ctx); + for (i = 0; i < ctx.len; ++i) { + h2_ihash_remove(ih, buffer[i]); + } + return ctx.len; +} + +/******************************************************************************* + * iqueue - sorted list of int + ******************************************************************************/ + +static void iq_grow(h2_iqueue *q, int nlen); +static void iq_swap(h2_iqueue *q, int i, int j); +static int iq_bubble_up(h2_iqueue *q, int i, int top, + h2_iq_cmp *cmp, void *ctx); +static int iq_bubble_down(h2_iqueue *q, int i, int bottom, + h2_iq_cmp *cmp, void *ctx); + +h2_iqueue *h2_iq_create(apr_pool_t *pool, int capacity) +{ + h2_iqueue *q = apr_pcalloc(pool, sizeof(h2_iqueue)); + if (q) { + q->pool = pool; + iq_grow(q, capacity); + q->nelts = 0; + } + return q; +} + +int h2_iq_empty(h2_iqueue *q) +{ + return q->nelts == 0; +} + +int h2_iq_count(h2_iqueue *q) +{ + return q->nelts; +} + + +void h2_iq_add(h2_iqueue *q, int sid, h2_iq_cmp *cmp, void *ctx) +{ + int i; + + if (q->nelts >= q->nalloc) { + iq_grow(q, q->nalloc * 2); + } + + i = (q->head + q->nelts) % q->nalloc; + q->elts[i] = sid; + ++q->nelts; + + if (cmp) { + /* bubble it to the front of the queue */ + iq_bubble_up(q, i, q->head, cmp, ctx); + } +} + +int h2_iq_remove(h2_iqueue *q, int sid) +{ + int i; + for (i = 0; i < q->nelts; ++i) { + if (sid == q->elts[(q->head + i) % q->nalloc]) { + break; + } + } + + if (i < q->nelts) { + ++i; + for (; i < q->nelts; ++i) { + q->elts[(q->head+i-1)%q->nalloc] = q->elts[(q->head+i)%q->nalloc]; + } + --q->nelts; + return 1; + } + return 0; +} + +void h2_iq_clear(h2_iqueue *q) +{ + q->nelts = 0; +} + +void h2_iq_sort(h2_iqueue *q, h2_iq_cmp *cmp, void *ctx) +{ + /* Assume that changes in ordering are minimal. This needs, + * best case, q->nelts - 1 comparisions to check that nothing + * changed. + */ + if (q->nelts > 0) { + int i, ni, prev, last; + + /* Start at the end of the queue and create a tail of sorted + * entries. Make that tail one element longer in each iteration. + */ + last = i = (q->head + q->nelts - 1) % q->nalloc; + while (i != q->head) { + prev = (q->nalloc + i - 1) % q->nalloc; + + ni = iq_bubble_up(q, i, prev, cmp, ctx); + if (ni == prev) { + /* i bubbled one up, bubble the new i down, which + * keeps all tasks below i sorted. */ + iq_bubble_down(q, i, last, cmp, ctx); + } + i = prev; + }; + } +} + + +int h2_iq_shift(h2_iqueue *q) +{ + int sid; + + if (q->nelts <= 0) { + return 0; + } + + sid = q->elts[q->head]; + q->head = (q->head + 1) % q->nalloc; + q->nelts--; + + return sid; +} + +static void iq_grow(h2_iqueue *q, int nlen) +{ + if (nlen > q->nalloc) { + int *nq = apr_pcalloc(q->pool, sizeof(int) * nlen); + if (q->nelts > 0) { + int l = ((q->head + q->nelts) % q->nalloc) - q->head; + + memmove(nq, q->elts + q->head, sizeof(int) * l); + if (l < q->nelts) { + /* elts wrapped, append elts in [0, remain] to nq */ + int remain = q->nelts - l; + memmove(nq + l, q->elts, sizeof(int) * remain); + } + } + q->elts = nq; + q->nalloc = nlen; + q->head = 0; + } +} + +static void iq_swap(h2_iqueue *q, int i, int j) +{ + int x = q->elts[i]; + q->elts[i] = q->elts[j]; + q->elts[j] = x; +} + +static int iq_bubble_up(h2_iqueue *q, int i, int top, + h2_iq_cmp *cmp, void *ctx) +{ + int prev; + while (((prev = (q->nalloc + i - 1) % q->nalloc), i != top) + && (*cmp)(q->elts[i], q->elts[prev], ctx) < 0) { + iq_swap(q, prev, i); + i = prev; + } + return i; +} + +static int iq_bubble_down(h2_iqueue *q, int i, int bottom, + h2_iq_cmp *cmp, void *ctx) +{ + int next; + while (((next = (q->nalloc + i + 1) % q->nalloc), i != bottom) + && (*cmp)(q->elts[i], q->elts[next], ctx) > 0) { + iq_swap(q, next, i); + i = next; + } + return i; +} + +/******************************************************************************* + * h2_ngheader + ******************************************************************************/ +#define H2_HD_MATCH_LIT_CS(l, name) \ + ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name)) + +static int h2_util_ignore_header(const char *name) +{ + /* never forward, ch. 8.1.2.2 */ + return (H2_HD_MATCH_LIT_CS("connection", name) + || H2_HD_MATCH_LIT_CS("proxy-connection", name) + || H2_HD_MATCH_LIT_CS("upgrade", name) + || H2_HD_MATCH_LIT_CS("keep-alive", name) + || H2_HD_MATCH_LIT_CS("transfer-encoding", name)); +} + +static int count_header(void *ctx, const char *key, const char *value) +{ + if (!h2_util_ignore_header(key)) { + (*((size_t*)ctx))++; + } + return 1; +} + +#define NV_ADD_LIT_CS(nv, k, v) add_header(nv, k, sizeof(k) - 1, v, strlen(v)) +#define NV_ADD_CS_CS(nv, k, v) add_header(nv, k, strlen(k), v, strlen(v)) + +static int add_header(h2_ngheader *ngh, + const char *key, size_t key_len, + const char *value, size_t val_len) +{ + nghttp2_nv *nv = &ngh->nv[ngh->nvlen++]; + + nv->name = (uint8_t*)key; + nv->namelen = key_len; + nv->value = (uint8_t*)value; + nv->valuelen = val_len; + return 1; +} + +static int add_table_header(void *ctx, const char *key, const char *value) +{ + if (!h2_util_ignore_header(key)) { + add_header(ctx, key, strlen(key), value, strlen(value)); + } + return 1; +} + +h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, + const struct h2_request *req) +{ + + h2_ngheader *ngh; + size_t n; + + AP_DEBUG_ASSERT(req); + AP_DEBUG_ASSERT(req->scheme); + AP_DEBUG_ASSERT(req->authority); + AP_DEBUG_ASSERT(req->path); + AP_DEBUG_ASSERT(req->method); + + n = 4; + apr_table_do(count_header, &n, req->headers, NULL); + + ngh = apr_pcalloc(p, sizeof(h2_ngheader)); + ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv)); + NV_ADD_LIT_CS(ngh, ":scheme", req->scheme); + NV_ADD_LIT_CS(ngh, ":authority", req->authority); + NV_ADD_LIT_CS(ngh, ":path", req->path); + NV_ADD_LIT_CS(ngh, ":method", req->method); + apr_table_do(add_table_header, ngh, req->headers, NULL); + + return ngh; +} + +/******************************************************************************* + * header HTTP/1 <-> HTTP/2 conversions + ******************************************************************************/ + +typedef struct { + const char *name; + size_t len; +} literal; + +#define H2_DEF_LITERAL(n) { (n), (sizeof(n)-1) } +#define H2_LIT_ARGS(a) (a),H2_ALEN(a) + +static literal IgnoredRequestHeaders[] = { + H2_DEF_LITERAL("expect"), + H2_DEF_LITERAL("upgrade"), + H2_DEF_LITERAL("connection"), + H2_DEF_LITERAL("keep-alive"), + H2_DEF_LITERAL("http2-settings"), + H2_DEF_LITERAL("proxy-connection"), + H2_DEF_LITERAL("transfer-encoding"), +}; +static literal IgnoredProxyRespHds[] = { + H2_DEF_LITERAL("alt-svc"), +}; + +static int ignore_header(const literal *lits, size_t llen, + const char *name, size_t nlen) +{ + const literal *lit; + int i; + + for (i = 0; i < llen; ++i) { + lit = &lits[i]; + if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) { + return 1; + } + } + return 0; +} + +static int h2_req_ignore_header(const char *name, size_t len) +{ + return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len); +} + +int h2_proxy_res_ignore_header(const char *name, size_t len) +{ + return (h2_req_ignore_header(name, len) + || ignore_header(H2_LIT_ARGS(IgnoredProxyRespHds), name, len)); +} + +void h2_util_camel_case_header(char *s, size_t len) +{ + size_t start = 1; + size_t i; + for (i = 0; i < len; ++i) { + if (start) { + if (s[i] >= 'a' && s[i] <= 'z') { + s[i] -= 'a' - 'A'; + } + + start = 0; + } + else if (s[i] == '-') { + start = 1; + } + } +} + +/******************************************************************************* + * h2 request handling + ******************************************************************************/ + +/** Match a header value against a string constance, case insensitive */ +#define H2_HD_MATCH_LIT(l, name, nlen) \ + ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name)) + +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) +{ + char *hname, *hvalue; + + if (h2_req_ignore_header(name, nlen)) { + return APR_SUCCESS; + } + else if (H2_HD_MATCH_LIT("cookie", name, nlen)) { + const char *existing = apr_table_get(headers, "cookie"); + if (existing) { + char *nval; + + /* Cookie header come separately in HTTP/2, but need + * to be merged by "; " (instead of default ", ") + */ + hvalue = apr_pstrndup(pool, value, vlen); + nval = apr_psprintf(pool, "%s; %s", existing, hvalue); + apr_table_setn(headers, "Cookie", nval); + return APR_SUCCESS; + } + } + else if (H2_HD_MATCH_LIT("host", name, nlen)) { + if (apr_table_get(headers, "Host")) { + return APR_SUCCESS; /* ignore duplicate */ + } + } + + hname = apr_pstrndup(pool, name, nlen); + hvalue = apr_pstrndup(pool, value, vlen); + h2_util_camel_case_header(hname, nlen); + apr_table_mergen(headers, hname, hvalue); + + return APR_SUCCESS; +} + +static h2_request *h2_req_createn(int id, apr_pool_t *pool, const char *method, + const char *scheme, const char *authority, + const char *path, apr_table_t *header, + int serialize) +{ + h2_request *req = apr_pcalloc(pool, sizeof(h2_request)); + + req->id = id; + req->method = method; + req->scheme = scheme; + req->authority = authority; + req->path = path; + req->headers = header? header : apr_table_make(pool, 10); + req->request_time = apr_time_now(); + req->serialize = serialize; + + return req; +} + +h2_request *h2_req_create(int id, apr_pool_t *pool, int serialize) +{ + return h2_req_createn(id, pool, NULL, NULL, NULL, NULL, NULL, serialize); +} + +typedef struct { + apr_table_t *headers; + apr_pool_t *pool; +} h1_ctx; + +static int set_h1_header(void *ctx, const char *key, const char *value) +{ + h1_ctx *x = ctx; + size_t klen = strlen(key); + if (!h2_req_ignore_header(key, klen)) { + h2_headers_add_h1(x->headers, x->pool, key, klen, value, strlen(value)); + } + return 1; +} + +apr_status_t h2_req_make(h2_request *req, apr_pool_t *pool, + const char *method, const char *scheme, + const char *authority, const char *path, + apr_table_t *headers) +{ + h1_ctx x; + + req->method = method; + req->scheme = scheme; + req->authority = authority; + req->path = path; + + AP_DEBUG_ASSERT(req->scheme); + AP_DEBUG_ASSERT(req->authority); + AP_DEBUG_ASSERT(req->path); + AP_DEBUG_ASSERT(req->method); + + x.pool = pool; + x.headers = req->headers; + apr_table_do(set_h1_header, &x, headers, NULL); + return APR_SUCCESS; +} + +/******************************************************************************* + * frame logging + ******************************************************************************/ + +int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen) +{ + char scratch[128]; + size_t s_len = sizeof(scratch)/sizeof(scratch[0]); + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + return apr_snprintf(buffer, maxlen, + "DATA[length=%d, flags=%d, stream=%d, padlen=%d]", + (int)frame->hd.length, frame->hd.flags, + frame->hd.stream_id, (int)frame->data.padlen); + } + case NGHTTP2_HEADERS: { + return apr_snprintf(buffer, maxlen, + "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + frame->hd.stream_id, + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); + } + case NGHTTP2_PRIORITY: { + return apr_snprintf(buffer, maxlen, + "PRIORITY[length=%d, flags=%d, stream=%d]", + (int)frame->hd.length, + frame->hd.flags, frame->hd.stream_id); + } + case NGHTTP2_RST_STREAM: { + return apr_snprintf(buffer, maxlen, + "RST_STREAM[length=%d, flags=%d, stream=%d]", + (int)frame->hd.length, + frame->hd.flags, frame->hd.stream_id); + } + case NGHTTP2_SETTINGS: { + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + return apr_snprintf(buffer, maxlen, + "SETTINGS[ack=1, stream=%d]", + frame->hd.stream_id); + } + return apr_snprintf(buffer, maxlen, + "SETTINGS[length=%d, stream=%d]", + (int)frame->hd.length, frame->hd.stream_id); + } + case NGHTTP2_PUSH_PROMISE: { + return apr_snprintf(buffer, maxlen, + "PUSH_PROMISE[length=%d, hend=%d, stream=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + frame->hd.stream_id); + } + case NGHTTP2_PING: { + return apr_snprintf(buffer, maxlen, + "PING[length=%d, ack=%d, stream=%d]", + (int)frame->hd.length, + frame->hd.flags&NGHTTP2_FLAG_ACK, + frame->hd.stream_id); + } + case NGHTTP2_GOAWAY: { + size_t len = (frame->goaway.opaque_data_len < s_len)? + frame->goaway.opaque_data_len : s_len-1; + memcpy(scratch, frame->goaway.opaque_data, len); + scratch[len] = '\0'; + return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s', " + "last_stream=%d]", frame->goaway.error_code, + scratch, frame->goaway.last_stream_id); + } + case NGHTTP2_WINDOW_UPDATE: { + return apr_snprintf(buffer, maxlen, + "WINDOW_UPDATE[stream=%d, incr=%d]", + frame->hd.stream_id, + frame->window_update.window_size_increment); + } + default: + return apr_snprintf(buffer, maxlen, + "type=%d[length=%d, flags=%d, stream=%d]", + frame->hd.type, (int)frame->hd.length, + frame->hd.flags, frame->hd.stream_id); + } +} diff --git a/modules/http2/h2_proxy_util.h b/modules/http2/h2_proxy_util.h new file mode 100644 index 0000000000..98f297fab4 --- /dev/null +++ b/modules/http2/h2_proxy_util.h @@ -0,0 +1,181 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __mod_h2__h2_proxy_util__ +#define __mod_h2__h2_proxy_util__ + +/******************************************************************************* + * some debugging/format helpers + ******************************************************************************/ +struct h2_request; +struct nghttp2_frame; + +int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen); + +/******************************************************************************* + * ihash - hash for structs with int identifier + ******************************************************************************/ +typedef struct h2_ihash_t h2_ihash_t; +typedef int h2_ihash_iter_t(void *ctx, void *val); + +/** + * Create a hash for structures that have an identifying int member. + * @param pool the pool to use + * @param offset_of_int the offsetof() the int member in the struct + */ +h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int); + +size_t h2_ihash_count(h2_ihash_t *ih); +int h2_ihash_empty(h2_ihash_t *ih); +void *h2_ihash_get(h2_ihash_t *ih, int id); + +/** + * Iterate over the hash members (without defined order) and invoke + * fn for each member until 0 is returned. + * @param ih the hash to iterate over + * @param fn the function to invoke on each member + * @param ctx user supplied data passed into each iteration call + * @return 0 if one iteration returned 0, otherwise != 0 + */ +int h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx); + +void h2_ihash_add(h2_ihash_t *ih, void *val); +void h2_ihash_remove(h2_ihash_t *ih, int id); +void h2_ihash_remove_val(h2_ihash_t *ih, void *val); +void h2_ihash_clear(h2_ihash_t *ih); + +size_t h2_ihash_shift(h2_ihash_t *ih, void **buffer, size_t max); +size_t h2_ihash_ishift(h2_ihash_t *ih, int *buffer, size_t max); + +/******************************************************************************* + * iqueue - sorted list of int with user defined ordering + ******************************************************************************/ +typedef struct h2_iqueue { + int *elts; + int head; + int nelts; + int nalloc; + apr_pool_t *pool; +} h2_iqueue; + +/** + * Comparator for two int to determine their order. + * + * @param i1 first int to compare + * @param i2 second int to compare + * @param ctx provided user data + * @return value is the same as for strcmp() and has the effect: + * == 0: s1 and s2 are treated equal in ordering + * < 0: s1 should be sorted before s2 + * > 0: s2 should be sorted before s1 + */ +typedef int h2_iq_cmp(int i1, int i2, void *ctx); + +/** + * Allocate a new queue from the pool and initialize. + * @param id the identifier of the queue + * @param pool the memory pool + */ +h2_iqueue *h2_iq_create(apr_pool_t *pool, int capacity); + +/** + * Return != 0 iff there are no tasks in the queue. + * @param q the queue to check + */ +int h2_iq_empty(h2_iqueue *q); + +/** + * Return the number of int in the queue. + * @param q the queue to get size on + */ +int h2_iq_count(h2_iqueue *q); + +/** + * Add a stream id to the queue. + * + * @param q the queue to append the task to + * @param sid the stream id to add + * @param cmp the comparator for sorting + * @param ctx user data for comparator + */ +void h2_iq_add(h2_iqueue *q, int sid, h2_iq_cmp *cmp, void *ctx); + +/** + * Remove the stream id from the queue. Return != 0 iff task + * was found in queue. + * @param q the task queue + * @param sid the stream id to remove + * @return != 0 iff task was found in queue + */ +int h2_iq_remove(h2_iqueue *q, int sid); + +/** + * Remove all entries in the queue. + */ +void h2_iq_clear(h2_iqueue *q); + +/** + * Sort the stream idqueue again. Call if the task ordering + * has changed. + * + * @param q the queue to sort + * @param cmp the comparator for sorting + * @param ctx user data for the comparator + */ +void h2_iq_sort(h2_iqueue *q, h2_iq_cmp *cmp, void *ctx); + +/** + * Get the first stream id from the queue or NULL if the queue is empty. + * The task will be removed. + * + * @param q the queue to get the first task from + * @return the first stream id of the queue, 0 if empty + */ +int h2_iq_shift(h2_iqueue *q); + +/******************************************************************************* + * common helpers + ******************************************************************************/ +/* h2_log2(n) iff n is a power of 2 */ +unsigned char h2_log2(apr_uint32_t n); + +/******************************************************************************* + * HTTP/2 header helpers + ******************************************************************************/ +void h2_util_camel_case_header(char *s, size_t len); +int h2_proxy_res_ignore_header(const char *name, size_t len); + +/******************************************************************************* + * nghttp2 helpers + ******************************************************************************/ +typedef struct h2_ngheader { + nghttp2_nv *nv; + apr_size_t nvlen; +} h2_ngheader; +h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, + const struct h2_request *req); + +/******************************************************************************* + * h2_request helpers + ******************************************************************************/ +struct h2_request *h2_req_create(int id, apr_pool_t *pool, int serialize); +apr_status_t h2_req_make(struct h2_request *req, apr_pool_t *pool, + const char *method, const char *scheme, + const char *authority, const char *path, + apr_table_t *headers); + + + +#endif /* defined(__mod_h2__h2_proxy_util__) */ diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index f8575fa7e1..8d1060e579 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -403,131 +403,6 @@ size_t h2_ihash_ishift(h2_ihash_t *ih, int *buffer, size_t max) return ctx.len; } -/******************************************************************************* - * ilist - sorted list for structs with int identifier - ******************************************************************************/ - -#define h2_ilist_IDX(list, i) ((int**)(list)->elts)[i] - -struct h2_ilist_t { - apr_array_header_t *l; -}; - -h2_ilist_t *h2_ilist_create(apr_pool_t *pool) -{ - h2_ilist_t *list = apr_pcalloc(pool, sizeof(h2_ilist_t)); - if (list) { - list->l = apr_array_make(pool, 100, sizeof(int*)); - if (!list->l) { - return NULL; - } - } - return list; -} - -static int h2_ilist_cmp(const void *s1, const void *s2) -{ - int **pi1 = (int **)s1; - int **pi2 = (int **)s2; - return *(*pi1) - *(*pi2); -} - -void *h2_ilist_get(h2_ilist_t *list, int id) -{ - /* we keep the array sorted by id, so lookup can be done - * by bsearch. - */ - int **pi; - int *pkey = &id; - - pi = bsearch(&pkey, list->l->elts, list->l->nelts, - list->l->elt_size, h2_ilist_cmp); - return pi? *pi : NULL; -} - -static void h2_ilist_sort(h2_ilist_t *list) -{ - qsort(list->l->elts, list->l->nelts, list->l->elt_size, h2_ilist_cmp); -} - -apr_status_t h2_ilist_add(h2_ilist_t *list, void *val) -{ - int *pi = val; - void *existing = h2_ilist_get(list, *pi); - if (!existing) { - int last; - APR_ARRAY_PUSH(list->l, void*) = val; - /* Often, values get added in ascending order of id. We - * keep the array sorted, so we just need to check if the newly - * appended stream has a lower id than the last one. if not, - * sorting is not necessary. - */ - last = list->l->nelts - 1; - if (last > 0 - && *h2_ilist_IDX(list->l, last) < *h2_ilist_IDX(list->l, last-1)) { - h2_ilist_sort(list); - } - } - return APR_SUCCESS; -} - -static void remove_idx(h2_ilist_t *list, int idx) -{ - int n; - --list->l->nelts; - n = list->l->nelts - idx; - if (n > 0) { - /* There are n h2_io* behind idx. Move the rest down */ - int **selts = (int**)list->l->elts; - memmove(selts + idx, selts + idx + 1, n * sizeof(int*)); - } -} - -void *h2_ilist_remove(h2_ilist_t *list, int id) -{ - int i; - for (i = 0; i < list->l->nelts; ++i) { - int *e = h2_ilist_IDX(list->l, i); - if (id == *e) { - remove_idx(list, i); - return e; - } - } - return NULL; -} - -void *h2_ilist_shift(h2_ilist_t *list) -{ - if (list->l->nelts > 0) { - int *pi = h2_ilist_IDX(list->l, 0); - remove_idx(list, 0); - return pi; - } - return NULL; -} - -int h2_ilist_empty(h2_ilist_t *list) -{ - return list->l->nelts == 0; -} - -int h2_ilist_iter(h2_ilist_t *list, h2_ilist_iter_t *iter, void *ctx) -{ - int i; - for (i = 0; i < list->l->nelts; ++i) { - int *pi = h2_ilist_IDX(list->l, i); - if (!iter(ctx, pi)) { - return 0; - } - } - return 1; -} - -apr_size_t h2_ilist_count(h2_ilist_t *list) -{ - return list->l->nelts; -} - /******************************************************************************* * iqueue - sorted list of int ******************************************************************************/ @@ -1578,123 +1453,3 @@ void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_en req->push_policy = policy; } -/******************************************************************************* - * ap_casecmpstr, when will it be backported? - ******************************************************************************/ -#if !APR_CHARSET_EBCDIC -/* - * Provide our own known-fast implementation of str[n]casecmp() - * NOTE: Only ASCII alpha characters 41-5A are folded to 61-7A, - * other 8-bit latin alphabetics are never case-folded! - */ -static const unsigned char ucharmap[] = { - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', - 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', - 'x', 'y', 'z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff -}; - -#else /* APR_CHARSET_EBCDIC */ -/* Derived from apr-iconv/ccs/cp037.c for EBCDIC case comparison, - provides unique identity of every char value (strict ISO-646 - conformance, arbitrary election of an ISO-8859-1 ordering, and - very arbitrary control code assignments into C1 to achieve - identity and a reversible mapping of code points), - then folding the equivalences of ASCII 41-5A into 61-7A, - presenting comparison results in a somewhat ISO/IEC 10646 - (ASCII-like) order, depending on the EBCDIC code page in use. - */ -static const unsigned char ucharmap[] = { - 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, - 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, - 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, - 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, - 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, - 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, - 0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5, - 0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, - 0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, - 0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAC, - 0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5, - 0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, - 0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, - 0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, - 0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1, - 0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, - 0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4, - 0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0xDD, 0xDE, 0xAE, - 0x5E, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC, - 0xBD, 0xBE, 0x5B, 0x5D, 0xAF, 0xA8, 0xB4, 0xD7, - 0x7B, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5, - 0x7D, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, - 0x71, 0x72, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF, - 0x5C, 0xF7, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F -}; -#endif - -int h2_casecmpstr(const char *s1, const char *s2) -{ - const unsigned char *ps1 = (const unsigned char *) s1; - const unsigned char *ps2 = (const unsigned char *) s2; - - while (ucharmap[*ps1] == ucharmap[*ps2]) { - if (*ps1++ == '\0') { - return (0); - } - ps2++; - } - return (ucharmap[*ps1] - ucharmap[*ps2]); -} - -int h2_casecmpstrn(const char *s1, const char *s2, apr_size_t n) -{ - const unsigned char *ps1 = (const unsigned char *) s1; - const unsigned char *ps2 = (const unsigned char *) s2; - while (n--) { - if (ucharmap[*ps1] != ucharmap[*ps2]) { - return (ucharmap[*ps1] - ucharmap[*ps2]); - } - if (*ps1++ == '\0') { - break; - } - ps2++; - } - return (0); -} - diff --git a/modules/http2/h2_util.h b/modules/http2/h2_util.h index 61ffdbcbea..7cae0ac016 100644 --- a/modules/http2/h2_util.h +++ b/modules/http2/h2_util.h @@ -70,33 +70,6 @@ void h2_ihash_clear(h2_ihash_t *ih); size_t h2_ihash_shift(h2_ihash_t *ih, void **buffer, size_t max); size_t h2_ihash_ishift(h2_ihash_t *ih, int *buffer, size_t max); -/******************************************************************************* - * ilist - sorted list for structs with int identifier as first member - ******************************************************************************/ -typedef struct h2_ilist_t h2_ilist_t; -typedef int h2_ilist_iter_t(void *ctx, void *val); - -h2_ilist_t *h2_ilist_create(apr_pool_t *pool); - -apr_status_t h2_ilist_add(h2_ilist_t *list, void *val); -void *h2_ilist_get(h2_ilist_t *list, int id); -void *h2_ilist_shift(h2_ilist_t *list); -void *h2_ilist_remove(h2_ilist_t *list, int id); - -int h2_ilist_empty(h2_ilist_t *list); -apr_size_t h2_ilist_count(h2_ilist_t *list); - -/* Iterator over all h2_io* in the set or until a - * callback returns 0. It is not safe to add or remove - * set members during iteration. - * - * @param set the set of h2_io to iterate over - * @param iter the function to call for each io - * @param ctx user data for the callback - * @return 1 iff iteration completed for all members - */ -int h2_ilist_iter(h2_ilist_t *lis, h2_ilist_iter_t *iter, void *ctx); - /******************************************************************************* * iqueue - sorted list of int with user defined ordering ******************************************************************************/ @@ -413,8 +386,4 @@ apr_status_t h2_append_brigade(apr_bucket_brigade *to, */ apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb); -/* when will ap_casecmpstr() be backported finally? */ -int h2_casecmpstr(const char *s1, const char *s2); -int h2_casecmpstrn(const char *s1, const char *s2, apr_size_t n); - #endif /* defined(__mod_h2__h2_util__) */ diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c index 7dfee7083a..c0e4bf1765 100644 --- a/modules/http2/mod_proxy_http2.c +++ b/modules/http2/mod_proxy_http2.c @@ -22,7 +22,7 @@ #include "mod_proxy_http2.h" #include "h2_request.h" -#include "h2_util.h" +#include "h2_proxy_util.h" #include "h2_version.h" #include "h2_proxy_session.h" @@ -125,12 +125,12 @@ static int proxy_http2_canon(request_rec *r, char *url) apr_port_t port, def_port; /* ap_port_of_scheme() */ - if (h2_casecmpstrn(url, "h2c:", 4) == 0) { + if (ap_cstr_casecmpn(url, "h2c:", 4) == 0) { url += 4; scheme = "h2c"; http_scheme = "http"; } - else if (h2_casecmpstrn(url, "h2:", 3) == 0) { + else if (ap_cstr_casecmpn(url, "h2:", 3) == 0) { url += 3; scheme = "h2"; http_scheme = "https"; diff --git a/modules/http2/mod_proxy_http2.dsp b/modules/http2/mod_proxy_http2.dsp index 31b91b55e6..5d6305fdf7 100644 --- a/modules/http2/mod_proxy_http2.dsp +++ b/modules/http2/mod_proxy_http2.dsp @@ -105,7 +105,7 @@ SOURCE=./h2_proxy_session.c # End Source File # Begin Source File -SOURCE=./h2_util.c +SOURCE=./h2_proxy_util.c # End Source File # Begin Source File -- 2.40.0