From d300afee341908cd90a78f48e362690ad3f3a8eb Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sun, 13 Nov 2016 21:30:40 +0000 Subject: [PATCH] mod_http2: H2PushResource directive for early pushing mod_proxy_http2: Link header uris are reverse mapped git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1769550 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 18 ++ docs/log-message-tags/next-number | 2 +- docs/manual/mod/mod_http2.xml | 38 ++++ modules/http2/h2.h | 2 - modules/http2/h2_config.c | 80 ++++++- modules/http2/h2_config.h | 12 +- modules/http2/h2_from_h1.c | 5 +- modules/http2/h2_h2.c | 29 ++- modules/http2/h2_proxy_session.c | 18 +- modules/http2/h2_proxy_util.c | 352 ++++++++++++++++++++++++++++++ modules/http2/h2_proxy_util.h | 9 +- modules/http2/h2_push.c | 6 +- modules/http2/h2_push.h | 1 + modules/http2/h2_session.c | 23 +- modules/http2/h2_stream.h | 1 + modules/http2/h2_version.h | 4 +- modules/http2/mod_http2.c | 4 +- 17 files changed, 573 insertions(+), 31 deletions(-) diff --git a/CHANGES b/CHANGES index fcbc8d5aac..737bf1e74b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,24 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_http2: new directive 'H2PushResource' to enable early pushes before + processing of the main request starts. Resources are announced to the + client in Link headers on a 103 early hint response. + All responses with status code <400 are inspected for Link header and + trigger pushes accordingly. 304 still does prevent pushes. + 'H2PushResource' can mark resources as 'critical' which gives them higher + priority than the main resource. This leads to preferred scheduling for + processing and, when content is available, will send it first. 'critical' + is also recognized on Link headers. [Stefan Eissing] + + *) mod_proxy_http2: uris in Link headers are now mapped back to a suitable + local url when available. Relative uris with an absolute path are mapped + as well. This makes reverse proxy mapping available for resources + announced in this header. + With 103 interim responses being forwarded to the main client connection, + this effectively allows early pushing of resources by a reverse proxied + backend server. [Stefan Eissing] + *) mod_proxy_http2: adding support for newly proposed 103 status code. [Stefan Eissing] diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 6d716864b3..2422ca54d4 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -3482 +3483 diff --git a/docs/manual/mod/mod_http2.xml b/docs/manual/mod/mod_http2.xml index f05dd4dae7..02134bbe9f 100644 --- a/docs/manual/mod/mod_http2.xml +++ b/docs/manual/mod/mod_http2.xml @@ -933,5 +933,43 @@ H2TLSCoolDownSecs 0 + + H2PushResource + Declares resources for early pushing to the client + H2PushResource [add] path [critical] + + server config + virtual host + directory + .htaccess + + Available in version 2.4.24 and later. + + +

+ When added to a directory/location HTTP/2 PUSHes will be attempted + for all paths added via this directive. This directive can be used + several times for the same location. +

+

+ This directive pushes resources much earlier than adding + Link headers via mod_headers. + mod_http2 announces these resources in a + 103 Early Hints interim response to the client. + That means that clients not supporting PUSH will still get + early preload hints. +

+

+ In contrast to setting Link response headers + via mod_headers, this directive will only + take effect on HTTP/2 connections. +

+

+ By adding critical to such a resource, the server + will give processing it more preference and send its data, once + available, before the data from the main request. +

+
+
diff --git a/modules/http2/h2.h b/modules/http2/h2.h index 59719ad8c7..2b80e1bfba 100644 --- a/modules/http2/h2.h +++ b/modules/http2/h2.h @@ -55,8 +55,6 @@ extern const char *H2_MAGIC_TOKEN; /* Initial default window size, RFC 7540 ch. 6.5.2 */ #define H2_INITIAL_WINDOW_SIZE ((64*1024)-1) -#define H2_HTTP_2XX(a) ((a) >= 200 && (a) < 300) - #define H2_STREAM_CLIENT_INITIATED(id) (id&0x01) #define H2_ALEN(a) (sizeof(a)/sizeof((a)[0])) diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c index 5613e8a479..fe07637004 100644 --- a/modules/http2/h2_config.c +++ b/modules/http2/h2_config.c @@ -73,7 +73,6 @@ static void *h2_config_create(apr_pool_t *pool, const char *prefix, const char *x) { h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config)); - const char *s = x? x : "unknown"; char *name = apr_pstrcat(pool, prefix, "[", s, "]", NULL); @@ -96,7 +95,7 @@ static void *h2_config_create(apr_pool_t *pool, conf->priorities = NULL; conf->push_diary_size = DEF_VAL; conf->copy_files = DEF_VAL; - + conf->push_list = NULL; return conf; } @@ -110,12 +109,11 @@ void *h2_config_create_dir(apr_pool_t *pool, char *x) return h2_config_create(pool, "dir", x); } -void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) +static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) { h2_config *base = (h2_config *)basev; h2_config *add = (h2_config *)addv; h2_config *n = (h2_config *)apr_pcalloc(pool, sizeof(h2_config)); - char *name = apr_pstrcat(pool, "merged[", add->name, ", ", base->name, "]", NULL); n->name = name; @@ -143,10 +141,25 @@ void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) } n->push_diary_size = H2_CONFIG_GET(add, base, push_diary_size); n->copy_files = H2_CONFIG_GET(add, base, copy_files); - + if (add->push_list && base->push_list) { + n->push_list = apr_array_append(pool, base->push_list, add->push_list); + } + else { + n->push_list = add->push_list? add->push_list : base->push_list; + } return n; } +void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv) +{ + return h2_config_merge(pool, basev, addv); +} + +void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) +{ + return h2_config_merge(pool, basev, addv); +} + int h2_config_geti(const h2_config *conf, h2_config_var_t var) { return (int)h2_config_geti64(conf, var); @@ -521,6 +534,59 @@ static const char *h2_conf_set_copy_files(cmd_parms *parms, return "value must be On or Off"; } +static void add_push(apr_pool_t *pool, h2_config *conf, h2_push_res *push) +{ + h2_push_res *new; + if (!conf->push_list) { + conf->push_list = apr_array_make(pool, 10, sizeof(*push)); + } + new = apr_array_push(conf->push_list); + new->uri_ref = push->uri_ref; + new->critical = push->critical; +} + +static const char *h2_conf_add_push_res(cmd_parms *cmd, void *dirconf, + const char *arg1, const char *arg2, + const char *arg3) +{ + h2_config *dconf = (h2_config*)dirconf ; + h2_config *sconf = (h2_config*)h2_config_sget(cmd->server); + h2_push_res push; + const char *last = arg3; + + memset(&push, 0, sizeof(push)); + if (!strcasecmp("add", arg1)) { + push.uri_ref = arg2; + } + else { + push.uri_ref = arg1; + last = arg2; + if (arg3) { + return "too many parameter"; + } + } + + if (last) { + if (!strcasecmp("critical", last)) { + push.critical = 1; + } + else { + return "unknown last parameter"; + } + } + + /* server command? set both */ + if (cmd->path == NULL) { + add_push(cmd->pool, sconf, &push); + add_push(cmd->pool, dconf, &push); + } + else { + add_push(cmd->pool, dconf, &push); + } + + return NULL; +} + #define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) const command_rec h2_cmds[] = { @@ -561,7 +627,9 @@ const command_rec h2_cmds[] = { AP_INIT_TAKE1("H2PushDiarySize", h2_conf_set_push_diary_size, NULL, RSRC_CONF, "size of push diary"), AP_INIT_TAKE1("H2CopyFiles", h2_conf_set_copy_files, NULL, - OR_ALL, "on to perform copy of file data"), + OR_FILEINFO, "on to perform copy of file data"), + AP_INIT_TAKE123("H2PushResource", h2_conf_add_push_res, NULL, + OR_FILEINFO, "add a resource to be pushed in this location/on this server."), AP_END_CMD }; diff --git a/modules/http2/h2_config.h b/modules/http2/h2_config.h index 4dc5f6c9b9..08bde3b740 100644 --- a/modules/http2/h2_config.h +++ b/modules/http2/h2_config.h @@ -45,6 +45,12 @@ typedef enum { struct apr_hash_t; struct h2_priority; +struct h2_push_res; + +typedef struct h2_push_res { + const char *uri_ref; + int critical; +} h2_push_res; /* Apache httpd module configuration for h2. */ typedef struct h2_config { @@ -70,14 +76,14 @@ typedef struct h2_config { int push_diary_size; /* # of entries in push diary */ int copy_files; /* if files shall be copied vs setaside on output */ + apr_array_header_t *push_list;/* list of h2_push_res configurations */ } h2_config; void *h2_config_create_dir(apr_pool_t *pool, char *x); +void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv); void *h2_config_create_svr(apr_pool_t *pool, server_rec *s); -void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv); - -apr_status_t h2_config_apply_header(const h2_config *config, request_rec *r); +void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv); extern const command_rec h2_cmds[]; diff --git a/modules/http2/h2_from_h1.c b/modules/http2/h2_from_h1.c index f1ea12de2f..372e0bfc78 100644 --- a/modules/http2/h2_from_h1.c +++ b/modules/http2/h2_from_h1.c @@ -488,10 +488,13 @@ apr_status_t h2_from_h1_parse_response(h2_task *task, ap_filter_t *f, } else if (line[0] == '\0') { /* end of headers, pass response onward */ - + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, + "h2_task(%s): end of response", task->id); return pass_response(task, f, parser); } else { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, + "h2_task(%s): response header %s", task->id, line); status = parse_header(parser, line); } break; diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c index f810014638..8ca8ccb55f 100644 --- a/modules/http2/h2_h2.c +++ b/modules/http2/h2_h2.c @@ -681,6 +681,31 @@ static int h2_h2_pre_close_conn(conn_rec *c) return DECLINED; } +static void check_push(request_rec *r, const char *tag) +{ + const h2_config *conf = h2_config_rget(r); + if (conf && conf->push_list && conf->push_list->nelts > 0) { + int i, old_status; + const char *old_line; + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "%s, early announcing %d resources for push", + tag, conf->push_list->nelts); + for (i = 0; i < conf->push_list->nelts; ++i) { + h2_push_res *push = &APR_ARRAY_IDX(conf->push_list, i, h2_push_res); + apr_table_addn(r->headers_out, "Link", + apr_psprintf(r->pool, "<%s>; rel=preload%s", + push->uri_ref, push->critical? "; critical" : "")); + } + old_status = r->status; + old_line = r->status_line; + r->status = 103; + r->status_line = "103 Early Hints"; + ap_send_interim_response(r, 1); + r->status = old_status; + r->status_line = old_line; + } +} + static int h2_h2_post_read_req(request_rec *r) { /* slave connection? */ @@ -691,7 +716,8 @@ static int h2_h2_post_read_req(request_rec *r) * that we manipulate filters only once. */ if (task && !task->filters_set) { ap_filter_t *f; - ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "adding request filters"); + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "h2_task(%s): adding request filters", task->id); /* setup the correct filters to process the request for h2 */ ap_add_input_filter("H2_REQUEST", task, r, r->connection); @@ -729,6 +755,7 @@ static int h2_h2_late_fixups(request_rec *r) "h2_slave_out(%s): copy_files on", task->id); h2_beam_on_file_beam(task->output.beam, h2_beam_no_files, NULL); } + check_push(r, "late_fixup"); } } return DECLINED; diff --git a/modules/http2/h2_proxy_session.c b/modules/http2/h2_proxy_session.c index a75e91a674..9624bd1f2b 100644 --- a/modules/http2/h2_proxy_session.c +++ b/modules/http2/h2_proxy_session.c @@ -36,6 +36,8 @@ typedef struct h2_proxy_stream { const char *url; request_rec *r; h2_proxy_request *req; + const char *real_server_uri; + const char *p_server_uri; int standalone; h2_stream_state_t state; @@ -226,8 +228,9 @@ static int add_header(void *table, const char *n, const char *v) return 1; } -static void process_proxy_header(request_rec *r, const char *n, const char *v) +static void process_proxy_header(h2_proxy_stream *stream, const char *n, const char *v) { + request_rec *r = stream->r; static const struct { const char *name; ap_proxy_header_reverse_map_fn func; @@ -250,6 +253,13 @@ static void process_proxy_header(request_rec *r, const char *n, const char *v) return; } } + if (!ap_cstr_casecmp("Link", n)) { + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + apr_table_add(r->headers_out, n, + h2_proxy_link_reverse_map(r, dconf, + stream->real_server_uri, stream->p_server_uri, v)); + return; + } apr_table_add(r->headers_out, n, v); } @@ -284,7 +294,7 @@ static apr_status_t h2_proxy_stream_add_header_out(h2_proxy_stream *stream, ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c, "h2_proxy_stream(%s-%d): got header %s: %s", stream->session->id, stream->id, hname, hvalue); - process_proxy_header(stream->r, hname, hvalue); + process_proxy_header(stream, hname, hvalue); } return APR_SUCCESS; } @@ -699,6 +709,10 @@ static apr_status_t open_stream(h2_proxy_session *session, const char *url, /* port info missing and port is not default for scheme: append */ authority = apr_psprintf(stream->pool, "%s:%d", authority, puri.port); } + /* we need this for mapping relative uris in headers ("Link") back + * to local uris */ + stream->real_server_uri = apr_psprintf(stream->pool, "%s://%s", scheme, authority); + stream->p_server_uri = apr_psprintf(stream->pool, "%s://%s", puri.scheme, authority); path = apr_uri_unparse(stream->pool, &puri, APR_URI_UNP_OMITSITEPART); h2_proxy_req_make(stream->req, stream->pool, r->method, scheme, authority, path, r->headers_in); diff --git a/modules/http2/h2_proxy_util.c b/modules/http2/h2_proxy_util.c index 8089dde5e5..8e1231c642 100644 --- a/modules/http2/h2_proxy_util.c +++ b/modules/http2/h2_proxy_util.c @@ -14,18 +14,22 @@ */ #include +#include #include #include #include #include #include +#include #include #include "h2.h" #include "h2_proxy_util.h" +APLOG_USE_MODULE(proxy_http2); + /* h2_log2(n) iff n is a power of 2 */ unsigned char h2_proxy_log2(int n) { @@ -701,3 +705,351 @@ int h2_proxy_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t m frame->hd.flags, frame->hd.stream_id); } } + +/******************************************************************************* + * link header handling + ******************************************************************************/ + +typedef struct { + apr_pool_t *pool; + request_rec *r; + proxy_dir_conf *conf; + const char *s; + int slen; + int i; + const char *server_uri; + int su_len; + const char *real_backend_uri; + int rbu_len; + const char *p_server_uri; + int psu_len; + int link_start; + int link_end; +} link_ctx; + +static int attr_char(char c) +{ + switch (c) { + case '!': + case '#': + case '$': + case '&': + case '+': + case '-': + case '.': + case '^': + case '_': + case '`': + case '|': + case '~': + return 1; + default: + return apr_isalnum(c); + } +} + +static int ptoken_char(char c) +{ + switch (c) { + case '!': + case '#': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case '-': + case '.': + case '/': + case ':': + case '<': + case '=': + case '>': + case '?': + case '@': + case '[': + case ']': + case '^': + case '_': + case '`': + case '{': + case '|': + case '}': + case '~': + return 1; + default: + return apr_isalnum(c); + } +} + +static int skip_ws(link_ctx *ctx) +{ + char c; + while (ctx->i < ctx->slen + && (((c = ctx->s[ctx->i]) == ' ') || (c == '\t'))) { + ++ctx->i; + } + return (ctx->i < ctx->slen); +} + +static int find_chr(link_ctx *ctx, char c, size_t *pidx) +{ + size_t j; + for (j = ctx->i; j < ctx->slen; ++j) { + if (ctx->s[j] == c) { + *pidx = j; + return 1; + } + } + return 0; +} + +static int read_chr(link_ctx *ctx, char c) +{ + if (ctx->i < ctx->slen && ctx->s[ctx->i] == c) { + ++ctx->i; + return 1; + } + return 0; +} + +static int skip_qstring(link_ctx *ctx) +{ + if (skip_ws(ctx) && read_chr(ctx, '\"')) { + size_t end; + if (find_chr(ctx, '\"', &end)) { + ctx->i = end + 1; + return 1; + } + } + return 0; +} + +static int skip_ptoken(link_ctx *ctx) +{ + if (skip_ws(ctx)) { + size_t i; + for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) { + /* nop */ + } + if (i > ctx->i) { + ctx->i = i; + return 1; + } + } + return 0; +} + + +static int read_link(link_ctx *ctx) +{ + ctx->link_start = ctx->link_end = 0; + if (skip_ws(ctx) && read_chr(ctx, '<')) { + size_t end; + if (find_chr(ctx, '>', &end)) { + ctx->link_start = ctx->i; + ctx->link_end = end; + ctx->i = end + 1; + return 1; + } + } + return 0; +} + +static int skip_pname(link_ctx *ctx) +{ + if (skip_ws(ctx)) { + int i; + for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) { + /* nop */ + } + if (i > ctx->i) { + ctx->i = i; + return 1; + } + } + return 0; +} + +static int skip_pvalue(link_ctx *ctx) +{ + if (skip_ws(ctx) && read_chr(ctx, '=')) { + if (skip_qstring(ctx) || skip_ptoken(ctx)) { + return 1; + } + } + return 0; +} + +static int skip_param(link_ctx *ctx) +{ + if (skip_ws(ctx) && read_chr(ctx, ';')) { + if (skip_pname(ctx)) { + skip_pvalue(ctx); /* value is optional */ + return 1; + } + } + return 0; +} + +static int read_sep(link_ctx *ctx) +{ + if (skip_ws(ctx) && read_chr(ctx, ',')) { + return 1; + } + return 0; +} + +static size_t subst_str(link_ctx *ctx, int start, int end, const char *ns) +{ + int olen, nlen, plen; + int delta; + char *p; + + olen = end - start; + nlen = strlen(ns); + delta = nlen - olen; + plen = ctx->slen + delta + 1; + p = apr_pcalloc(ctx->pool, plen); + strncpy(p, ctx->s, start); + strncpy(p + start, ns, nlen); + strcpy(p + start + nlen, ctx->s + end); + ctx->s = p; + ctx->slen = strlen(p); + if (ctx->i >= end) { + ctx->i += delta; + } + return nlen; +} + +static void map_link(link_ctx *ctx) +{ + if (ctx->link_start < ctx->link_end) { + char buffer[HUGE_STRING_LEN]; + int need_len, link_len, buffer_len, prepend_p_server; + const char *mapped; + + buffer[0] = '\0'; + buffer_len = 0; + link_len = ctx->link_end - ctx->link_start; + need_len = link_len + 1; + prepend_p_server = (ctx->s[ctx->link_start] == '/'); + if (prepend_p_server) { + /* common to use relative uris in link header, for mappings + * to work need to prefix the backend server uri */ + need_len += ctx->psu_len; + strncpy(buffer, ctx->p_server_uri, sizeof(buffer)); + buffer_len = ctx->psu_len; + } + if (need_len > sizeof(buffer)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ctx->r, APLOGNO(03482) + "link_reverse_map uri too long, skipped: %s", ctx->s); + return; + } + strncpy(buffer + buffer_len, ctx->s + ctx->link_start, link_len); + buffer_len += link_len; + buffer[buffer_len] = '\0'; + if (!prepend_p_server + && strcmp(ctx->real_backend_uri, ctx->p_server_uri) + && !strncmp(buffer, ctx->real_backend_uri, ctx->rbu_len)) { + /* the server uri and our local proxy uri we use differ, for mapping + * to work, we need to use the proxy uri */ + int path_start = ctx->link_start + ctx->rbu_len; + link_len -= ctx->rbu_len; + strcpy(buffer, ctx->p_server_uri); + strncpy(buffer + ctx->psu_len, ctx->s + path_start, link_len); + buffer_len = ctx->psu_len + link_len; + buffer[buffer_len] = '\0'; + } + mapped = ap_proxy_location_reverse_map(ctx->r, ctx->conf, buffer); + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, ctx->r, + "reverse_map[%s] %s --> %s", ctx->p_server_uri, buffer, mapped); + if (mapped != buffer) { + if (prepend_p_server) { + if (ctx->server_uri == NULL) { + ctx->server_uri = ap_construct_url(ctx->pool, "", ctx->r); + ctx->su_len = strlen(ctx->server_uri); + } + if (!strncmp(mapped, ctx->server_uri, ctx->su_len)) { + mapped += ctx->su_len; + } + } + subst_str(ctx, ctx->link_start, ctx->link_end, mapped); + } + } +} + +/* RFC 5988 + Link = "Link" ":" #link-value + link-value = "<" URI-Reference ">" *( ";" link-param ) + link-param = ( ( "rel" "=" relation-types ) + | ( "anchor" "=" <"> URI-Reference <"> ) + | ( "rev" "=" relation-types ) + | ( "hreflang" "=" Language-Tag ) + | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) ) + | ( "title" "=" quoted-string ) + | ( "title*" "=" ext-value ) + | ( "type" "=" ( media-type | quoted-mt ) ) + | ( link-extension ) ) + link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] ) + | ( ext-name-star "=" ext-value ) + ext-name-star = parmname "*" ; reserved for RFC2231-profiled + ; extensions. Whitespace NOT + ; allowed in between. + ptoken = 1*ptokenchar + ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "(" + | ")" | "*" | "+" | "-" | "." | "/" | DIGIT + | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA + | "[" | "]" | "^" | "_" | "`" | "{" | "|" + | "}" | "~" + media-type = type-name "/" subtype-name + quoted-mt = <"> media-type <"> + relation-types = relation-type + | <"> relation-type *( 1*SP relation-type ) <"> + relation-type = reg-rel-type | ext-rel-type + reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" ) + ext-rel-type = URI + + and from + parmname = 1*attr-char + attr-char = ALPHA / DIGIT + / "!" / "#" / "$" / "&" / "+" / "-" / "." + / "^" / "_" / "`" / "|" / "~" + */ + +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) +{ + link_ctx ctx; + + if (r->proxyreq != PROXYREQ_REVERSE) { + return s; + } + memset(&ctx, 0, sizeof(ctx)); + ctx.r = r; + ctx.pool = r->pool; + ctx.conf = conf; + ctx.real_backend_uri = real_backend_uri; + ctx.rbu_len = strlen(ctx.real_backend_uri); + ctx.p_server_uri = proxy_server_uri; + ctx.psu_len = strlen(ctx.p_server_uri); + ctx.s = s; + ctx.slen = strlen(s); + while (read_link(&ctx)) { + while (skip_param(&ctx)) { + /* nop */ + } + map_link(&ctx); + if (!read_sep(&ctx)) { + break; + } + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "link_reverse_map %s --> %s", s, ctx.s); + return ctx.s; +} diff --git a/modules/http2/h2_proxy_util.h b/modules/http2/h2_proxy_util.h index 06185e4d7d..f90d14951b 100644 --- a/modules/http2/h2_proxy_util.h +++ b/modules/http2/h2_proxy_util.h @@ -192,6 +192,13 @@ apr_status_t h2_proxy_req_make(h2_proxy_request *req, apr_pool_t *pool, const char *authority, const char *path, apr_table_t *headers); - +/******************************************************************************* + * reverse mapping for link headers + ******************************************************************************/ +const char *h2_proxy_link_reverse_map(request_rec *r, + proxy_dir_conf *conf, + const char *real_server_uri, + const char *proxy_server_uri, + const char *s); #endif /* defined(__mod_h2__h2_proxy_util__) */ diff --git a/modules/http2/h2_push.c b/modules/http2/h2_push.c index 455b7f0600..4330d7a4dc 100644 --- a/modules/http2/h2_push.c +++ b/modules/http2/h2_push.c @@ -353,7 +353,11 @@ static int add_push(link_ctx *ctx) /* atm, we do not push on pushes */ h2_request_end_headers(req, ctx->pool, 1); push->req = req; - + if (has_param(ctx, "critical")) { + h2_priority *prio = apr_pcalloc(ctx->pool, sizeof(*prio)); + prio->dependency = H2_DEPENDANT_BEFORE; + push->priority = prio; + } if (!ctx->pushes) { ctx->pushes = apr_array_make(ctx->pool, 5, sizeof(h2_push*)); } diff --git a/modules/http2/h2_push.h b/modules/http2/h2_push.h index d4c8a7a012..eb122ebf86 100644 --- a/modules/http2/h2_push.h +++ b/modules/http2/h2_push.h @@ -25,6 +25,7 @@ struct h2_stream; typedef struct h2_push { const struct h2_request *req; + h2_priority *priority; } h2_push; typedef enum { diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index f743f4814c..5cb63d00b4 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -1233,6 +1233,7 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is, stream = h2_session_open_stream(session, nid, is->id, push->req); if (stream) { + h2_session_set_prio(session, stream, push->priority); status = stream_schedule(session, stream, 1); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c, @@ -1271,6 +1272,10 @@ apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, #ifdef H2_NG2_CHANGE_PRIO nghttp2_stream *s_grandpa, *s_parent, *s; + if (prio == NULL) { + /* we treat this as a NOP */ + return APR_SUCCESS; + } s = nghttp2_session_find_stream(session->ngh2, stream->id); if (!s) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, @@ -1437,7 +1442,6 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream, nghttp2_data_provider provider, *pprovider = NULL; h2_ngheader *ngh; apr_table_t *hout; - const h2_priority *prio; const char *note; ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03073) @@ -1454,7 +1458,7 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream, /* If this stream is not a pushed one itself, * and HTTP/2 server push is enabled here, - * and the response is in the range 200-299 *), + * and the response HTTP status is not sth >= 400, * and the remote side has pushing enabled, * -> find and perform any pushes on this stream * *before* we submit the stream response itself. @@ -1462,23 +1466,24 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream, * headers that get pushed right afterwards. * * *) the response code is relevant, as we do not want to - * make pushes on 401 or 403 codes, neiterh on 301/302 - * and friends. And if we see a 304, we do not push either + * make pushes on 401 or 403 codes and friends. + * And if we see a 304, we do not push either * as the client, having this resource in its cache, might * also have the pushed ones as well. */ if (!stream->initiated_on - && h2_headers_are_response(headers) - && H2_HTTP_2XX(headers->status) + && !stream->has_response + && (headers->status < 400) + && (headers->status != 304) && h2_session_push_enabled(session)) { h2_stream_submit_pushes(stream, headers); } - prio = h2_stream_get_priority(stream, headers); - if (prio) { - h2_session_set_prio(session, stream, prio); + if (!stream->pref_priority) { + stream->pref_priority = h2_stream_get_priority(stream, headers); } + h2_session_set_prio(session, stream, stream->pref_priority); hout = headers->headers; note = apr_table_get(headers->notes, H2_FILTER_DEBUG_NOTE); diff --git a/modules/http2/h2_stream.h b/modules/http2/h2_stream.h index 57fdbba04c..adb419e516 100644 --- a/modules/http2/h2_stream.h +++ b/modules/http2/h2_stream.h @@ -67,6 +67,7 @@ struct h2_stream { unsigned int push_policy; /* which push policy to use for this request */ unsigned int can_be_cleaned : 1; /* stream pool can be cleaned */ + const h2_priority *pref_priority; /* preferred priority for this stream */ apr_off_t out_data_frames; /* # of DATA frames sent */ apr_off_t out_data_octets; /* # of DATA octets (payload) sent */ apr_off_t in_data_frames; /* # of DATA frames received */ diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index fcc8b70bfd..6b3594c991 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -26,7 +26,7 @@ * @macro * Version number of the http2 module as c string */ -#define MOD_HTTP2_VERSION "1.7.10-DEV" +#define MOD_HTTP2_VERSION "1.8.0-DEV" /** * @macro @@ -34,7 +34,7 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define MOD_HTTP2_VERSION_NUM 0x01070A +#define MOD_HTTP2_VERSION_NUM 0x010800 #endif /* mod_h2_h2_version_h */ diff --git a/modules/http2/mod_http2.c b/modules/http2/mod_http2.c index 7452cd7c2b..e0a4b90883 100644 --- a/modules/http2/mod_http2.c +++ b/modules/http2/mod_http2.c @@ -48,9 +48,9 @@ static void h2_hooks(apr_pool_t *pool); AP_DECLARE_MODULE(http2) = { STANDARD20_MODULE_STUFF, h2_config_create_dir, /* func to create per dir config */ - h2_config_merge, + h2_config_merge_dir, /* func to merge per dir config */ h2_config_create_svr, /* func to create per server config */ - h2_config_merge, /* func to merge per server config */ + h2_config_merge_svr, /* func to merge per server config */ h2_cmds, /* command handlers */ h2_hooks }; -- 2.50.1