]> granicus.if.org Git - apache/commitdiff
Merge of r1767936,1768160,1769192,1769550 from trunk:
authorStefan Eissing <icing@apache.org>
Mon, 14 Nov 2016 11:15:08 +0000 (11:15 +0000)
committerStefan Eissing <icing@apache.org>
Mon, 14 Nov 2016 11:15:08 +0000 (11:15 +0000)
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.

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.
     adding support for newly proposed 103 status code.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1769595 13f79535-47bb-0310-9956-ffa450edef68

21 files changed:
CHANGES
docs/manual/mod/mod_http2.xml
modules/http/http_filters.c
modules/http2/h2.h
modules/http2/h2_config.c
modules/http2/h2_config.h
modules/http2/h2_from_h1.c
modules/http2/h2_h2.c
modules/http2/h2_mplx.c
modules/http2/h2_proxy_session.c
modules/http2/h2_proxy_util.c
modules/http2/h2_proxy_util.h
modules/http2/h2_push.c
modules/http2/h2_push.h
modules/http2/h2_session.c
modules/http2/h2_stream.h
modules/http2/h2_version.h
modules/http2/mod_http2.c
modules/metadata/mod_headers.c
modules/session/mod_session.c
server/util_cookies.c

diff --git a/CHANGES b/CHANGES
index 042fb0dea7fc570aa5e2ce2b41b08fe78fe44be7..04d511c1b66f9fa3651b9dd083e195db8c2f109c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,27 @@
 
 Changes with Apache 2.4.24
 
+  *) 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]
+     
   *) mpm_unix: Apache fails to start if previously crashed then restarted with
      the same PID (e.g. in container).  PR 60261.
      [Val <valentin.bremond gmail.com>, Yann Ylavic]
index f05dd4dae7634afc17c3729b69b27a3afaf37724..02134bbe9f7a6911693eaf86bb406312482de770 100644 (file)
@@ -933,5 +933,43 @@ H2TLSCoolDownSecs 0
         </usage>
     </directivesynopsis>
     
+    <directivesynopsis>
+        <name>H2PushResource</name>
+        <description>Declares resources for early pushing to the client</description>
+        <syntax>H2PushResource [add] path [critical]</syntax>
+        <contextlist>
+            <context>server config</context>
+            <context>virtual host</context>
+            <context>directory</context>
+            <context>.htaccess</context>
+        </contextlist>
+        <compatibility>Available in version 2.4.24 and later.</compatibility>
+        
+        <usage>
+            <p>
+                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.
+            </p>
+            <p>
+                This directive pushes resources much earlier than adding 
+                <code>Link</code> headers via <module>mod_headers</module>.
+                <module>mod_http2</module> announces these resources in a
+                <code>103 Early Hints</code> interim response to the client.
+                That means that clients not supporting PUSH will still get
+                early preload hints.
+            </p>
+            <p>
+                In contrast to setting <code>Link</code> response headers 
+                via <module>mod_headers</module>, this directive will only
+                take effect on HTTP/2 connections. 
+            </p>
+            <p>
+                By adding <code>critical</code> 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.
+            </p>
+        </usage>
+    </directivesynopsis>
     
 </modulesynopsis>
index 2bc1fc9f1f8fc067d750d197875307ba87cf7dd0..b4ca437edd6267f05374954722e99831466e4463 100644 (file)
@@ -776,8 +776,7 @@ static void fixup_vary(request_rec *r)
      * its comma-separated fieldname values, and then add them to varies
      * if not already present in the array.
      */
-    apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
-                 (void *) varies, r->headers_out, "Vary", NULL);
+    apr_table_do(uniq_field_values, varies, r->headers_out, "Vary", NULL);
 
     /* If we found any, replace old Vary fields with unique-ified value */
 
index 59719ad8c7f02f1c6e094af89e93d436fc5a76df..2b80e1bfba083c2872639fb829f5ebcfd3c32b65 100644 (file)
@@ -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]))
index 5613e8a479e8187dd85d300eafbe2112d974396f..fe076370043368cbf1eb18f486cb83a5f70e53a3 100644 (file)
@@ -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
 };
 
index 4dc5f6c9b974bd509b0bd4557b2f4a2cf99e2af3..08bde3b74012c14ea997d781a18e8a90d50b5e0b 100644 (file)
@@ -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[];
 
index cdb444650b718ef8444b5829778a5eeba4b3f45a..372e0bfc780dc5174a2423a8ba8321012792069b 100644 (file)
@@ -103,8 +103,7 @@ static void fix_vary(request_rec *r)
      * its comma-separated fieldname values, and then add them to varies
      * if not already present in the array.
      */
-    apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
-                 (void *) varies, r->headers_out, "Vary", NULL);
+    apr_table_do(uniq_field_values, varies, r->headers_out, "Vary", NULL);
     
     /* If we found any, replace old Vary fields with unique-ified value */
     
@@ -275,8 +274,7 @@ static h2_headers *create_response(h2_task *task, request_rec *r)
     
     set_basic_http_header(headers, r, r->pool);
     if (r->status == HTTP_NOT_MODIFIED) {
-        apr_table_do((int (*)(void *, const char *, const char *)) copy_header,
-                     (void *) headers, r->headers_out,
+        apr_table_do(copy_header, headers, r->headers_out,
                      "ETag",
                      "Content-Location",
                      "Expires",
@@ -290,8 +288,7 @@ static h2_headers *create_response(h2_task *task, request_rec *r)
                      NULL);
     }
     else {
-        apr_table_do((int (*)(void *, const char *, const char *)) copy_header,
-                     (void *) headers, r->headers_out, NULL);
+        apr_table_do(copy_header, headers, r->headers_out, NULL);
     }
     
     return h2_headers_rcreate(r, r->status, headers, r->pool);
@@ -491,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;
index f810014638ce59a34d2b9546cab1225352dab538..8ca8ccb55f01404febcded9e15021b4618dc50ae 100644 (file)
@@ -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;
index 60f4f29adbf927ed378a7ef93ceb62e6cbd143c0..7150058558051282df92baa4476872955ebb30bf 100644 (file)
@@ -512,10 +512,6 @@ static int task_print(void *ctx, void *val)
                       (stream? 0 : 1), task->worker_started, 
                       task->worker_done, task->frozen);
     }
-    else if (task) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */
-                      "->03198: h2_stream(%ld-%d): NULL", m->id, task->stream_id);
-    }
     else {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */
                       "->03198: h2_stream(%ld-NULL): NULL", m->id);
index 59ae9d48e2941295e3144668265138cd76a388a0..9624bd1f2b10fc7b867d16a3f9369a9e1f62cab3 100644 (file)
@@ -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;
@@ -161,7 +163,17 @@ static int on_frame_recv(nghttp2_session *ngh2, const nghttp2_frame *frame,
                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 
                               "h2_proxy_session(%s): got interim HEADERS, status=%d",
                               session->id, r->status);
-                r->status_line = ap_get_status_line(r->status);
+                switch(r->status) {
+                    case 103:
+                        /* workaround until we get this into http protocol base
+                         * parts. without this, unknown codes are converted to
+                         * 500... */
+                        r->status_line = "103 Early Hints";
+                        break;
+                    default:
+                        r->status_line = ap_get_status_line(r->status);
+                        break;
+                }
                 ap_send_interim_response(r, 1);
             }
             stream->waiting_on_100 = 0;
@@ -216,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;
@@ -240,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);
 }
 
@@ -274,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;
 }
@@ -689,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);
index 8089dde5e531fe5b0f6ab6b6807989b3a51810c7..8e1231c64251f1f1779c7a5ecaf53b21858cd4ec 100644 (file)
  */
 
 #include <assert.h>
+#include <apr_lib.h>
 #include <apr_strings.h>
 
 #include <httpd.h>
 #include <http_core.h>
 #include <http_log.h>
 #include <http_request.h>
+#include <mod_proxy.h>
 
 #include <nghttp2/nghttp2.h>
 
 #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 <https://tools.ietf.org/html/rfc5988#section-6.2.1>
+  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 <https://tools.ietf.org/html/rfc5987>
+  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;
+}
index 06185e4d7de9f1aa97228bd626e3f37a1b626d6a..f90d14951bc3b10455c138c7c56513c4688e516b 100644 (file)
@@ -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__) */
index 455b7f060021870d11be27a9d599970e5531b5f7..4330d7a4dc62133a298cd0fe43e128267f47b9ba 100644 (file)
@@ -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*));
                 }
index d4c8a7a012eac4ce8b20dc3dd4c02c51c34656b3..eb122ebf864bd18d7d0ba2a6f8dec35212161a26 100644 (file)
@@ -25,6 +25,7 @@ struct h2_stream;
 
 typedef struct h2_push {
     const struct h2_request *req;
+    h2_priority *priority;
 } h2_push;
 
 typedef enum {
index f743f4814ccae4c11ccf3cfbebfad802ce49d6c6..5cb63d00b4dabed0564227f0220fe22b96ca66ae 100644 (file)
@@ -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);
index 57fdbba04c9d1945a8dd052a13f298aee3a98a7b..adb419e5165e50cfee0618435dfdf77e8f452bc7 100644 (file)
@@ -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 */
index 1b81038aa7ed79ca351f076e458aeda5444cc63d..ef185663a42baa6088a93cc1d70b7e2ed536683e 100644 (file)
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.7.9"
+#define MOD_HTTP2_VERSION "1.8.0"
 
 /**
  * @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 0x010709
+#define MOD_HTTP2_VERSION_NUM 0x010800
 
 
 #endif /* mod_h2_h2_version_h */
index 7452cd7c2bd073a104fdf54720fcc3ee25bc3f6c..e0a4b908832d8d0571cfd183e206b499fb33a368 100644 (file)
@@ -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
 };
index 210b2bbb5759f48ebbb8006e9701671b5db4147e..1ea970d78013853132f992054d70d84a67f90293 100644 (file)
@@ -666,13 +666,15 @@ static const char *process_regexp(header_entry *hdr, const char *value,
     return ret;
 }
 
-static int echo_header(echo_do *v, const char *key, const char *val)
+static int echo_header(void *v, const char *key, const char *val)
 {
+    edit_do *ed = v;
+
     /* If the input header (key) matches the regex, echo it intact to
      * r->headers_out.
      */
-    if (!ap_regexec(v->hdr->regex, key, 0, NULL, 0)) {
-        apr_table_add(v->r->headers_out, key, val);
+    if (!ap_regexec(ed->hdr->regex, key, 0, NULL, 0)) {
+        apr_table_add(ed->r->headers_out, key, val);
     }
 
     return 1;
@@ -808,8 +810,7 @@ static int do_headers_fixup(request_rec *r, apr_table_t *headers,
         case hdr_echo:
             v.r = r;
             v.hdr = hdr;
-            apr_table_do((int (*) (void *, const char *, const char *))
-                         echo_header, (void *) &v, r->headers_in, NULL);
+            apr_table_do(echo_header, &v, r->headers_in, NULL);
             break;
         case hdr_edit:
         case hdr_edit_r:
index 0b472a240e01c93985e128549ae32e3c6a3211d7..ff4c9a6f2dbeb8756fd1b58bb3398e911c09b05c 100644 (file)
@@ -299,15 +299,16 @@ static apr_status_t ap_session_set(request_rec * r, session_rec * z,
     return APR_SUCCESS;
 }
 
-static int identity_count(int *count, const char *key, const char *val)
+static int identity_count(void *v, const char *key, const char *val)
 {
+    int *count = v;
     *count += strlen(key) * 3 + strlen(val) * 3 + 1;
     return 1;
 }
 
-static int identity_concat(char *buffer, const char *key, const char *val)
+static int identity_concat(void *v, const char *key, const char *val)
 {
-    char *slider = buffer;
+    char *slider = v;
     int length = strlen(slider);
     slider += length;
     if (length) {
@@ -344,11 +345,9 @@ static apr_status_t session_identity_encode(request_rec * r, session_rec * z)
         char *expiry = apr_psprintf(z->pool, "%" APR_INT64_T_FMT, z->expiry);
         apr_table_setn(z->entries, SESSION_EXPIRY, expiry);
     }
-    apr_table_do((int (*) (void *, const char *, const char *))
-                 identity_count, &length, z->entries, NULL);
+    apr_table_do(identity_count, &length, z->entries, NULL);
     buffer = apr_pcalloc(r->pool, length + 1);
-    apr_table_do((int (*) (void *, const char *, const char *))
-                 identity_concat, buffer, z->entries, NULL);
+    apr_table_do(identity_concat, buffer, z->entries, NULL);
     z->encoded = buffer;
     return OK;
 
index 5139aecd4c48ec210025d8324647492fd78a80d0..82a514fcc66f13b7020c5df4d9442f5d6c6bd6d6 100644 (file)
@@ -174,8 +174,9 @@ AP_DECLARE(apr_status_t) ap_cookie_remove2(request_rec * r, const char *name2, c
  * $path or other attributes following our cookie if present. If we end
  * up with an empty cookie, remove the whole header.
  */
-static int extract_cookie_line(ap_cookie_do * v, const char *key, const char *val)
+static int extract_cookie_line(void *varg, const char *key, const char *val)
 {
+    ap_cookie_do *v = varg;
     char *last1, *last2;
     char *cookie = apr_pstrdup(v->r->pool, val);
     const char *name = apr_pstrcat(v->r->pool, v->name ? v->name : "", "=", NULL);
@@ -252,8 +253,7 @@ AP_DECLARE(apr_status_t) ap_cookie_read(request_rec * r, const char *name, const
     v.duplicated = 0;
     v.name = name;
 
-    apr_table_do((int (*) (void *, const char *, const char *))
-                 extract_cookie_line, (void *) &v, r->headers_in,
+    apr_table_do(extract_cookie_line, &v, r->headers_in,
                  "Cookie", "Cookie2", NULL);
     if (v.duplicated) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00011) LOG_PREFIX