Commit 64956fd8 authored by Stefan Eissing's avatar Stefan Eissing
Browse files

merged mod_http2 1.0.8 from trunk

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1716493 13f79535-47bb-0310-9956-ffa450edef68
parent cce000b2
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -2,6 +2,10 @@

Changes with Apache 2.4.18

  *) mod_http2: new directive 'H2PushPriority' to allow priority specifications
     on server pushed streams according to their content-type. 
     [Stefan Eissing]
     
  *) mod_http2: fixes crash on connection abort for a busy connection.
     fixes crash on a request that did not produce any response.
     [Stefan Eissing]
+138 −2
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@
            <context>server config</context>
            <context>virtual host</context>
        </contextlist>
        <compatibility>Available in version 2.4.18 and later.</compatibility>
        
        <usage>
            <p>
@@ -166,11 +167,143 @@
            <p>
                Last but not least, pushes happen only when the client signals
                its willingness to accept those. Most browsers do, some, like Safari 9,
                do not.
                do not. Also, pushes also only happen for resources from the same
                <em>authority</em> as the original response is for.
            </p>
        </usage>
    </directivesynopsis>

    <directivesynopsis>
        <name>H2PushPriority</name>
        <description>H2 Server Push Priority</description>
        <syntax>H2PushPriority mime-type [after|before|interleaved] [weight]</syntax>
        <default>H2PushPriority * After 16</default>
        <contextlist>
            <context>server config</context>
            <context>virtual host</context>
        </contextlist>
        <compatibility>Available in version 2.4.18 and later. For having an
        effect, a nghttp2 library version newer than 1.4.0 is necessary.</compatibility>
        
        <usage>
            <p>
                This directive defines the priority handling of pushed responses
                based on the content-type of the response. This is usually defined
                per server config, but may also appear in a virtual host. 
            </p>
            <p>
                HTTP/2 server pushes are always related to a client request. Each
                such request/response pairs, or <em>streams</em> have a dependency
                and a weight, together defining the <em>priority</em> of a stream. 
            </p>
            <p>
                When a stream <em>depends</em> on another, say X depends on Y,
                then Y gets all bandwidth before X gets any. Note that this
                does not men that Y will block X. If Y has no data to send, 
                all bandwidth allocated to Y can be used by X.
            </p>
            <p>
                When a stream has more than one dependant, say X1 and X2 both
                depend on Y, the <em>weight</em> determines the bandwidth
                allocation. If X1 and X2 have the same weight, they both get
                half of the available bandwdith. If the weight of X1 is twice
                as large as that for X2, X1 gets twice the bandwidth of X2.
            </p>
            <p> 
                Ultimately, every stream depends on the <em>root</em> stream which
                gets all the bandwidht available, but never sends anything. So all
                its bandwidth is distributed by weight among its children. Which
                either have data to send or distribute the bandwidth to their
                own children. And so on. If none of the children have data
                to send, that bandwidth get distributed somewhere else according
                to the same rules.
            </p>
            <p> 
                The purpose of this priority system is to always make use of
                available bandwidth while allowing precedence and weight
                to be given to specific streams. Since, normally, all streams
                are initiated by the client, it is also the one that sets
                these priorities.
            </p>
            <p>
                Only when such a stream results in a PUSH, gets the server to
                decide what the <em>initial</em> priority of such a pushed
                stream is. In the examples below, X is the client stream. It
                depends on Y and the server decides to PUSH streams P1 and P2
                onto X.
            </p>
            <p>
                The default priority rule is:
            </p>
            <example><title>Default Priority Rule</title>
                <highlight language="config">
                    H2PushPriority * After 16
                </highlight>
            </example>
            <p>
                which reads as 'Send a pushed stream of any content-type
                depending on the client stream with weight 16'. And so P1
                and P2 will be send after X and, as they have equal weight,
                share bandwidth equally among themselves.
            </p>
            <example><title>Interleaved Priority Rule</title>
                <highlight language="config">
                    H2PushPriority text/css Interleaved 256
                </highlight>
            </example>
            <p>
                which reads as 'Send any CSS resource on the same dependency and
                weight as the client stream'. If P1 has content-type 'text/css',
                it will depend on Y (as does X) and its effective weight will be
                calculated as <code>P1ew = Xw * (P1w / 256)</code>. With P1w being 
                256, this will make the effective weight the same as the weight
                of X. If both X and P1 have data to send, bandwidth will be allocated
                to both equally.
            </p>
            <p>
                With Pw specified as 512, a pushed, interleaved stream would
                get double the weight of X. With 128 only half as much. Note that
                effective weights are always capped at 256.
            </p>
            <example><title>Before Priority Rule</title>
                <highlight language="config">
                    H2PushPriority application/json Before 256
                </highlight>
            </example>
            <p>
                This says that any pushed stream of content type 'application/json'
                should be send out <em>before</em> X. This makes P1 dependant
                on Y and X dependant on P1. So, X will be stalled as long as
                P1 has data to send. The effective weight is calculated as
                in the interleaved case.
            </p>
            <p>
                Be aware that the effect of priority specifications is limited
                by the available server resources. If a server does not have
                workers available for pushed streams, the data for the stream
                may only ever arrive when other streams have been finished.
            </p>
            <p>
                Last, but not least, there are some specifics of the syntax
                to be used in this directive.
                <ol>
                    <li>'*' is the only special content-type that matches all oither. 
                    'image/*' will not work.</li>
                    <li>The default dependency is 'After'. </li>
                    <li>There are also default weights: for 'After' it is 16, otherwise 256. 
                    </li>
                </ol>
            </p>
            <example><title>Shorter Priority Rules</title>
                <highlight language="config">
H2PushPriority application/json 32         # an After rule
H2PushPriority image/jpeg before           # weight 256 default
H2PushPriority text/css   interleaved      # weight 256 default
                </highlight>
            </example>
        </usage>
    </directivesynopsis>

    <directivesynopsis>
        <name>H2Upgrade</name>
        <description>H2 Upgrade Protocol Switch</description>
@@ -435,6 +568,7 @@
            <context>server config</context>
            <context>virtual host</context>
        </contextlist>
        <compatibility>Available in version 2.4.18 and later.</compatibility>

        <usage>
            <p>
@@ -482,6 +616,7 @@
            <context>server config</context>
            <context>virtual host</context>
        </contextlist>
        <compatibility>Available in version 2.4.18 and later.</compatibility>
        
        <usage>
            <p>
@@ -535,6 +670,7 @@
            <context>server config</context>
            <context>virtual host</context>
        </contextlist>
        <compatibility>Available in version 2.4.18 and later.</compatibility>
        
        <usage>
            <p>
+1 −1
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool) {
static int h2_alt_svc_handler(request_rec *r)
{
    h2_ctx *ctx;
    h2_config *cfg;
    const h2_config *cfg;
    int i;
    
    if (r->connection->keepalives > 0) {
+99 −22
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@

#include <assert.h>

#include <apr_hash.h>
#include <apr_lib.h>

#include <httpd.h>
#include <http_core.h>
#include <http_config.h>
@@ -43,7 +46,7 @@ static h2_config defconf = {
    H2_INITIAL_WINDOW_SIZE, /* window_size */
    -1,                     /* min workers */
    -1,                     /* max workers */
    10,                     /* max workers idle secs */
    10 * 60,                /* max workers idle secs */
    64 * 1024,              /* stream max mem size */
    NULL,                   /* no alt-svcs */
    -1,                     /* alt-svc max age */
@@ -55,6 +58,7 @@ static h2_config defconf = {
    1024*1024,              /* TLS warmup size */
    1,                      /* TLS cooldown secs */
    1,                      /* HTTP/2 server push enabled */
    NULL,                   /* map of content-type to priorities */
};

static int files_per_session = 0;
@@ -111,6 +115,7 @@ static void *h2_config_create(apr_pool_t *pool,
    conf->tls_warmup_size      = DEF_VAL;
    conf->tls_cooldown_secs    = DEF_VAL;
    conf->h2_push              = DEF_VAL;
    conf->priorities           = NULL;
    
    return conf;
}
@@ -155,16 +160,22 @@ void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
    n->tls_warmup_size      = H2_CONFIG_GET(add, base, tls_warmup_size);
    n->tls_cooldown_secs    = H2_CONFIG_GET(add, base, tls_cooldown_secs);
    n->h2_push              = H2_CONFIG_GET(add, base, h2_push);
    if (add->priorities && base->priorities) {
        n->priorities       = apr_hash_overlay(pool, add->priorities, base->priorities);
    }
    else {
        n->priorities       = add->priorities? add->priorities : base->priorities;
    }
    
    return n;
}

int h2_config_geti(h2_config *conf, h2_config_var_t var)
int h2_config_geti(const h2_config *conf, h2_config_var_t var)
{
    return (int)h2_config_geti64(conf, var);
}

apr_int64_t h2_config_geti64(h2_config *conf, h2_config_var_t var)
apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var)
{
    int n;
    switch(var) {
@@ -207,7 +218,7 @@ apr_int64_t h2_config_geti64(h2_config *conf, h2_config_var_t var)
    }
}

h2_config *h2_config_sget(server_rec *s)
const h2_config *h2_config_sget(server_rec *s)
{
    h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, 
                                                       &http2_module);
@@ -215,11 +226,21 @@ h2_config *h2_config_sget(server_rec *s)
    return cfg;
}

const struct h2_priority *h2_config_get_priority(const h2_config *conf, 
                                                 const char *content_type)
{
    if (content_type && conf->priorities) {
        size_t len = strcspn(content_type, "; \t");
        h2_priority *prio = apr_hash_get(conf->priorities, content_type, len);
        return prio? prio : apr_hash_get(conf->priorities, "*", 1);
    }
    return NULL;
}

static const char *h2_conf_set_max_streams(cmd_parms *parms,
                                           void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->h2_max_streams = (int)apr_atoi64(value);
    (void)arg;
    if (cfg->h2_max_streams < 1) {
@@ -231,7 +252,7 @@ static const char *h2_conf_set_max_streams(cmd_parms *parms,
static const char *h2_conf_set_window_size(cmd_parms *parms,
                                           void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->h2_window_size = (int)apr_atoi64(value);
    (void)arg;
    if (cfg->h2_window_size < 1024) {
@@ -243,7 +264,7 @@ static const char *h2_conf_set_window_size(cmd_parms *parms,
static const char *h2_conf_set_min_workers(cmd_parms *parms,
                                           void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->min_workers = (int)apr_atoi64(value);
    (void)arg;
    if (cfg->min_workers < 1) {
@@ -255,7 +276,7 @@ static const char *h2_conf_set_min_workers(cmd_parms *parms,
static const char *h2_conf_set_max_workers(cmd_parms *parms,
                                           void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->max_workers = (int)apr_atoi64(value);
    (void)arg;
    if (cfg->max_workers < 1) {
@@ -267,7 +288,7 @@ static const char *h2_conf_set_max_workers(cmd_parms *parms,
static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms,
                                                    void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->max_worker_idle_secs = (int)apr_atoi64(value);
    (void)arg;
    if (cfg->max_worker_idle_secs < 1) {
@@ -279,7 +300,7 @@ static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms,
static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms,
                                                   void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    
    
    cfg->stream_max_mem_size = (int)apr_atoi64(value);
@@ -294,7 +315,7 @@ static const char *h2_add_alt_svc(cmd_parms *parms,
                                  void *arg, const char *value)
{
    if (value && strlen(value)) {
        h2_config *cfg = h2_config_sget(parms->server);
        h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
        h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool);
        if (!as) {
            return "unable to parse alt-svc specifier";
@@ -311,7 +332,7 @@ static const char *h2_add_alt_svc(cmd_parms *parms,
static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms,
                                               void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->alt_svc_max_age = (int)apr_atoi64(value);
    (void)arg;
    return NULL;
@@ -320,7 +341,7 @@ static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms,
static const char *h2_conf_set_session_extra_files(cmd_parms *parms,
                                                   void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    apr_int64_t max = (int)apr_atoi64(value);
    if (max < 0) {
        return "value must be a non-negative number";
@@ -333,7 +354,7 @@ static const char *h2_conf_set_session_extra_files(cmd_parms *parms,
static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
                                                 void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    if (!strcasecmp(value, "On")) {
        cfg->serialize_headers = 1;
        return NULL;
@@ -350,7 +371,7 @@ static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
static const char *h2_conf_set_direct(cmd_parms *parms,
                                      void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    if (!strcasecmp(value, "On")) {
        cfg->h2_direct = 1;
        return NULL;
@@ -367,7 +388,7 @@ static const char *h2_conf_set_direct(cmd_parms *parms,
static const char *h2_conf_set_push(cmd_parms *parms,
                                    void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    if (!strcasecmp(value, "On")) {
        cfg->h2_push = 1;
        return NULL;
@@ -381,10 +402,64 @@ static const char *h2_conf_set_push(cmd_parms *parms,
    return "value must be On or Off";
}

static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg,
                                             const char *ctype, const char *sdependency,
                                             const char *sweight)
{
    h2_config *cfg = (h2_config *)h2_config_sget(cmd->server);
    const char *sdefweight = "16";         /* default AFTER weight */
    h2_dependency dependency;
    h2_priority *priority;
    int weight;
    
    if (!strlen(ctype)) {
        return "1st argument must be a mime-type, like 'text/css' or '*'";
    }
    
    if (!sweight) {
        /* 2 args only, but which one? */
        if (apr_isdigit(sdependency[0])) {
            sweight = sdependency;
            sdependency = "AFTER";        /* default dependency */
        }
    }
    
    if (!strcasecmp("AFTER", sdependency)) {
        dependency = H2_DEPENDANT_AFTER;
    } 
    else if (!strcasecmp("BEFORE", sdependency)) {
        dependency = H2_DEPENDANT_BEFORE;
        sdefweight = "256";        /* default BEFORE weight */
    } 
    else if (!strcasecmp("INTERLEAVED", sdependency)) {
        dependency = H2_DEPENDANT_INTERLEAVED;
        sdefweight = "256";        /* default INTERLEAVED weight */
    }
    else {
        return "dependency must be one of 'After', 'Before' or 'Interleaved'";
    }
    
    weight = (int)apr_atoi64(sweight? sweight : sdefweight);
    if (weight < NGHTTP2_MIN_WEIGHT) {
        return apr_psprintf(cmd->pool, "weight must be a number >= %d",
                            NGHTTP2_MIN_WEIGHT);
    }
    
    priority = apr_pcalloc(cmd->pool, sizeof(*priority));
    priority->dependency = dependency;
    priority->weight = weight;
    
    if (!cfg->priorities) {
        cfg->priorities = apr_hash_make(cmd->pool);
    }
    apr_hash_set(cfg->priorities, ctype, strlen(ctype), priority);
    return NULL;
}

static const char *h2_conf_set_modern_tls_only(cmd_parms *parms,
                                               void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    if (!strcasecmp(value, "On")) {
        cfg->modern_tls_only = 1;
        return NULL;
@@ -401,7 +476,7 @@ static const char *h2_conf_set_modern_tls_only(cmd_parms *parms,
static const char *h2_conf_set_upgrade(cmd_parms *parms,
                                       void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    if (!strcasecmp(value, "On")) {
        cfg->h2_upgrade = 1;
        return NULL;
@@ -418,7 +493,7 @@ static const char *h2_conf_set_upgrade(cmd_parms *parms,
static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms,
                                               void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->tls_warmup_size = apr_atoi64(value);
    (void)arg;
    return NULL;
@@ -427,7 +502,7 @@ static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms,
static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *parms,
                                                 void *arg, const char *value)
{
    h2_config *cfg = h2_config_sget(parms->server);
    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
    cfg->tls_cooldown_secs = (int)apr_atoi64(value);
    (void)arg;
    return NULL;
@@ -469,18 +544,20 @@ const command_rec h2_cmds[] = {
                  RSRC_CONF, "seconds of idle time on TLS before shrinking writes"),
    AP_INIT_TAKE1("H2Push", h2_conf_set_push, NULL,
                  RSRC_CONF, "off to disable HTTP/2 server push"),
    AP_INIT_TAKE23("H2PushPriority", h2_conf_add_push_priority, NULL,
                  RSRC_CONF, "define priority of PUSHed resources per content type"),
    AP_END_CMD
};


h2_config *h2_config_rget(request_rec *r)
const h2_config *h2_config_rget(request_rec *r)
{
    h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, 
                                                       &http2_module);
    return cfg? cfg : h2_config_sget(r->server); 
}

h2_config *h2_config_get(conn_rec *c)
const h2_config *h2_config_get(conn_rec *c)
{
    h2_ctx *ctx = h2_ctx_get(c);
    
+13 −6
Original line number Diff line number Diff line
@@ -41,6 +41,9 @@ typedef enum {
    H2_CONF_PUSH,
} h2_config_var_t;

struct apr_hash_t;
struct h2_priority;

/* Apache httpd module configuration for h2. */
typedef struct h2_config {
    const char *name;
@@ -61,6 +64,7 @@ typedef struct h2_config {
    apr_int64_t tls_warmup_size;  /* Amount of TLS data to send before going full write size */
    int tls_cooldown_secs;        /* Seconds of idle time before going back to small TLS records */
    int h2_push;                  /* if HTTP/2 server push is enabled */
    struct apr_hash_t *priorities;/* map of content-type to h2_priority records */
} h2_config;


@@ -68,18 +72,21 @@ void *h2_config_create_dir(apr_pool_t *pool, char *x);
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(h2_config *config, request_rec *r);
apr_status_t h2_config_apply_header(const h2_config *config, request_rec *r);

extern const command_rec h2_cmds[];

h2_config *h2_config_get(conn_rec *c);
h2_config *h2_config_sget(server_rec *s);
h2_config *h2_config_rget(request_rec *r);
const h2_config *h2_config_get(conn_rec *c);
const h2_config *h2_config_sget(server_rec *s);
const h2_config *h2_config_rget(request_rec *r);

int h2_config_geti(h2_config *conf, h2_config_var_t var);
apr_int64_t h2_config_geti64(h2_config *conf, h2_config_var_t var);
int h2_config_geti(const h2_config *conf, h2_config_var_t var);
apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var);

void h2_config_init(apr_pool_t *pool);

const struct h2_priority *h2_config_get_priority(const h2_config *conf, 
                                                 const char *content_type);
                                                 
#endif /* __mod_h2__h2_config_h__ */
Loading