]> granicus.if.org Git - apache/commitdiff
merge of current 2.4.x and change 1702919
authorStefan Eissing <icing@apache.org>
Mon, 14 Sep 2015 12:38:53 +0000 (12:38 +0000)
committerStefan Eissing <icing@apache.org>
Mon, 14 Sep 2015 12:38:53 +0000 (12:38 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.17-protocols-http2@1702925 13f79535-47bb-0310-9956-ffa450edef68

1  2 
docs/manual/mod/quickreference.html.en
modules/http2/h2_config.c
modules/http2/h2_ctx.c
modules/http2/h2_ctx.h
modules/http2/h2_switch.c
modules/http2/h2_task.c
modules/http2/h2_task_input.c

index 883f273a38fe6c14d48b395ed4c5faeafdd93dbb,0000000000000000000000000000000000000000..a370bd620c3ec39b9e1d4951b975d7ca36d82cf6
mode 100644,000000..100644
--- /dev/null
@@@ -1,416 -1,0 +1,386 @@@
-     if (!ctx->server && ctx->hostname) {
-         /* We have a host agreed upon via TLS SNI, but no request yet.
-          * The sni host was accepted and therefore does match a server record
-          * (vhost) for it. But we need to know which one.
-          * Normally, it is enough to be set on the initial request on a
-          * connection, but we need it earlier. Simulate a request and call
-          * the vhost matching stuff.
-          */
-         apr_uri_t uri;
-         request_rec r;
-         memset(&uri, 0, sizeof(uri));
-         uri.scheme = (char*)"https";
-         uri.hostinfo = (char*)ctx->hostname;
-         uri.hostname = (char*)ctx->hostname;
-         uri.port_str = (char*)"";
-         uri.port = c->local_addr->port;
-         uri.path = (char*)"/";
-         
-         memset(&r, 0, sizeof(r));
-         r.uri = (char*)"/";
-         r.connection = c;
-         r.pool = c->pool;
-         r.hostname = ctx->hostname;
-         r.headers_in = apr_table_make(c->pool, 1);
-         r.parsed_uri = uri;
-         r.status = HTTP_OK;
-         r.server = r.connection->base_server;
-         ap_update_vhost_from_headers(&r);
-         ctx->server = r.server;
-     }
-     
-     if (ctx->server) {
 +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + 
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <assert.h>
 +
 +#include <httpd.h>
 +#include <http_core.h>
 +#include <http_config.h>
 +#include <http_log.h>
 +#include <http_vhost.h>
 +
 +#include <ap_mpm.h>
 +
 +#include <apr_strings.h>
 +
 +#include "h2_alt_svc.h"
 +#include "h2_ctx.h"
 +#include "h2_conn.h"
 +#include "h2_config.h"
 +#include "h2_private.h"
 +
 +#define DEF_VAL     (-1)
 +
 +#define H2_CONFIG_GET(a, b, n) \
 +    (((a)->n == DEF_VAL)? (b) : (a))->n
 +
 +static h2_config defconf = {
 +    "default",
 +    100,              /* max_streams */
 +    64 * 1024,        /* window_size */
 +    -1,               /* min workers */
 +    -1,               /* max workers */
 +    10 * 60,          /* max workers idle secs */
 +    64 * 1024,        /* stream max mem size */
 +    NULL,             /* no alt-svcs */
 +    -1,               /* alt-svc max age */
 +    0,                /* serialize headers */
 +    -1,               /* h2 direct mode */
 +    -1,               /* # session extra files */
 +};
 +
 +static int files_per_session = 0;
 +
 +void h2_config_init(apr_pool_t *pool) {
 +    /* Determine a good default for this platform and mpm?
 +     * TODO: not sure how APR wants to hand out this piece of 
 +     * information.
 +     */
 +    int max_files = 256;
 +    int conn_threads = 1;
 +    int tx_files = max_files / 4;
 +    
 +    (void)pool;
 +    ap_mpm_query(AP_MPMQ_MAX_THREADS, &conn_threads);
 +    switch (h2_conn_mpm_type()) {
 +        case H2_MPM_PREFORK:
 +        case H2_MPM_WORKER:
 +        case H2_MPM_EVENT:
 +            /* allow that many transfer open files per mplx */
 +            files_per_session = (tx_files / conn_threads);
 +            break;
 +        default:
 +            /* don't know anything about it, stay safe */
 +            break;
 +    }
 +}
 +
 +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_pcalloc(pool, strlen(prefix) + strlen(s) + 20);
 +    strcpy(name, prefix);
 +    strcat(name, "[");
 +    strcat(name, s);
 +    strcat(name, "]");
 +    
 +    conf->name                 = name;
 +    conf->h2_max_streams       = DEF_VAL;
 +    conf->h2_window_size       = DEF_VAL;
 +    conf->min_workers          = DEF_VAL;
 +    conf->max_workers          = DEF_VAL;
 +    conf->max_worker_idle_secs = DEF_VAL;
 +    conf->stream_max_mem_size  = DEF_VAL;
 +    conf->alt_svc_max_age      = DEF_VAL;
 +    conf->serialize_headers    = DEF_VAL;
 +    conf->h2_direct            = DEF_VAL;
 +    conf->session_extra_files  = DEF_VAL;
 +    return conf;
 +}
 +
 +void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
 +{
 +    return h2_config_create(pool, "srv", s->defn_name);
 +}
 +
 +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)
 +{
 +    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_pcalloc(pool, 20 + strlen(add->name) + strlen(base->name));
 +    strcpy(name, "merged[");
 +    strcat(name, add->name);
 +    strcat(name, ", ");
 +    strcat(name, base->name);
 +    strcat(name, "]");
 +    n->name = name;
 +
 +    n->h2_max_streams = H2_CONFIG_GET(add, base, h2_max_streams);
 +    n->h2_window_size = H2_CONFIG_GET(add, base, h2_window_size);
 +    n->min_workers    = H2_CONFIG_GET(add, base, min_workers);
 +    n->max_workers    = H2_CONFIG_GET(add, base, max_workers);
 +    n->max_worker_idle_secs = H2_CONFIG_GET(add, base, max_worker_idle_secs);
 +    n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size);
 +    n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs;
 +    n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age);
 +    n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers);
 +    n->h2_direct      = H2_CONFIG_GET(add, base, h2_direct);
 +    n->session_extra_files = H2_CONFIG_GET(add, base, session_extra_files);
 +    
 +    return n;
 +}
 +
 +int h2_config_geti(h2_config *conf, h2_config_var_t var)
 +{
 +    int n;
 +    switch(var) {
 +        case H2_CONF_MAX_STREAMS:
 +            return H2_CONFIG_GET(conf, &defconf, h2_max_streams);
 +        case H2_CONF_WIN_SIZE:
 +            return H2_CONFIG_GET(conf, &defconf, h2_window_size);
 +        case H2_CONF_MIN_WORKERS:
 +            return H2_CONFIG_GET(conf, &defconf, min_workers);
 +        case H2_CONF_MAX_WORKERS:
 +            return H2_CONFIG_GET(conf, &defconf, max_workers);
 +        case H2_CONF_MAX_WORKER_IDLE_SECS:
 +            return H2_CONFIG_GET(conf, &defconf, max_worker_idle_secs);
 +        case H2_CONF_STREAM_MAX_MEM:
 +            return H2_CONFIG_GET(conf, &defconf, stream_max_mem_size);
 +        case H2_CONF_ALT_SVC_MAX_AGE:
 +            return H2_CONFIG_GET(conf, &defconf, alt_svc_max_age);
 +        case H2_CONF_SER_HEADERS:
 +            return H2_CONFIG_GET(conf, &defconf, serialize_headers);
 +        case H2_CONF_DIRECT:
 +            return H2_CONFIG_GET(conf, &defconf, h2_direct);
 +        case H2_CONF_SESSION_FILES:
 +            n = H2_CONFIG_GET(conf, &defconf, session_extra_files);
 +            if (n < 0) {
 +                n = files_per_session;
 +            }
 +            return n;
 +        default:
 +            return DEF_VAL;
 +    }
 +}
 +
 +h2_config *h2_config_sget(server_rec *s)
 +{
 +    h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, 
 +                                                       &h2_module);
 +    AP_DEBUG_ASSERT(cfg);
 +    return cfg;
 +}
 +
 +
 +static const char *h2_conf_set_max_streams(cmd_parms *parms,
 +                                           void *arg, const char *value)
 +{
 +    h2_config *cfg = h2_config_sget(parms->server);
 +    cfg->h2_max_streams = (int)apr_atoi64(value);
 +    (void)arg;
 +    if (cfg->h2_max_streams < 1) {
 +        return "value must be > 0";
 +    }
 +    return NULL;
 +}
 +
 +static const char *h2_conf_set_window_size(cmd_parms *parms,
 +                                           void *arg, const char *value)
 +{
 +    h2_config *cfg = h2_config_sget(parms->server);
 +    cfg->h2_window_size = (int)apr_atoi64(value);
 +    (void)arg;
 +    if (cfg->h2_window_size < 1024) {
 +        return "value must be > 1k";
 +    }
 +    return NULL;
 +}
 +
 +static const char *h2_conf_set_min_workers(cmd_parms *parms,
 +                                           void *arg, const char *value)
 +{
 +    h2_config *cfg = h2_config_sget(parms->server);
 +    cfg->min_workers = (int)apr_atoi64(value);
 +    (void)arg;
 +    if (cfg->min_workers < 1) {
 +        return "value must be > 1";
 +    }
 +    return NULL;
 +}
 +
 +static const char *h2_conf_set_max_workers(cmd_parms *parms,
 +                                           void *arg, const char *value)
 +{
 +    h2_config *cfg = h2_config_sget(parms->server);
 +    cfg->max_workers = (int)apr_atoi64(value);
 +    (void)arg;
 +    if (cfg->max_workers < 1) {
 +        return "value must be > 1";
 +    }
 +    return NULL;
 +}
 +
 +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);
 +    cfg->max_worker_idle_secs = (int)apr_atoi64(value);
 +    (void)arg;
 +    if (cfg->max_worker_idle_secs < 1) {
 +        return "value must be > 1";
 +    }
 +    return NULL;
 +}
 +
 +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);
 +    
 +    
 +    cfg->stream_max_mem_size = (int)apr_atoi64(value);
 +    (void)arg;
 +    if (cfg->stream_max_mem_size < 1024) {
 +        return "value must be > 1k";
 +    }
 +    return NULL;
 +}
 +
 +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_alt_svc *as = h2_alt_svc_parse(value, parms->pool);
 +        if (!as) {
 +            return "unable to parse alt-svc specifier";
 +        }
 +        if (!cfg->alt_svcs) {
 +            cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*));
 +        }
 +        APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
 +    }
 +    (void)arg;
 +    return NULL;
 +}
 +
 +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);
 +    cfg->alt_svc_max_age = (int)apr_atoi64(value);
 +    (void)arg;
 +    return NULL;
 +}
 +
 +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);
 +    apr_int64_t max = (int)apr_atoi64(value);
 +    if (max <= 0) {
 +        return "value must be a positive number";
 +    }
 +    cfg->session_extra_files = (int)max;
 +    (void)arg;
 +    return NULL;
 +}
 +
 +static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
 +                                                 void *arg, const char *value)
 +{
 +    h2_config *cfg = h2_config_sget(parms->server);
 +    if (!strcasecmp(value, "On")) {
 +        cfg->serialize_headers = 1;
 +        return NULL;
 +    }
 +    else if (!strcasecmp(value, "Off")) {
 +        cfg->serialize_headers = 0;
 +        return NULL;
 +    }
 +    
 +    (void)arg;
 +    return "value must be On or Off";
 +}
 +
 +static const char *h2_conf_set_direct(cmd_parms *parms,
 +                                      void *arg, const char *value)
 +{
 +    h2_config *cfg = h2_config_sget(parms->server);
 +    if (!strcasecmp(value, "On")) {
 +        cfg->h2_direct = 1;
 +        return NULL;
 +    }
 +    else if (!strcasecmp(value, "Off")) {
 +        cfg->h2_direct = 0;
 +        return NULL;
 +    }
 +    
 +    (void)arg;
 +    return "value must be On or Off";
 +}
 +
 +#define AP_END_CMD     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
 +
 +
 +const command_rec h2_cmds[] = {
 +    AP_INIT_TAKE1("H2MaxSessionStreams", h2_conf_set_max_streams, NULL,
 +                  RSRC_CONF, "maximum number of open streams per session"),
 +    AP_INIT_TAKE1("H2WindowSize", h2_conf_set_window_size, NULL,
 +                  RSRC_CONF, "window size on client DATA"),
 +    AP_INIT_TAKE1("H2MinWorkers", h2_conf_set_min_workers, NULL,
 +                  RSRC_CONF, "minimum number of worker threads per child"),
 +    AP_INIT_TAKE1("H2MaxWorkers", h2_conf_set_max_workers, NULL,
 +                  RSRC_CONF, "maximum number of worker threads per child"),
 +    AP_INIT_TAKE1("H2MaxWorkerIdleSeconds", h2_conf_set_max_worker_idle_secs, NULL,
 +                  RSRC_CONF, "maximum number of idle seconds before a worker shuts down"),
 +    AP_INIT_TAKE1("H2StreamMaxMemSize", h2_conf_set_stream_max_mem_size, NULL,
 +                  RSRC_CONF, "maximum number of bytes buffered in memory for a stream"),
 +    AP_INIT_TAKE1("H2AltSvc", h2_add_alt_svc, NULL,
 +                  RSRC_CONF, "adds an Alt-Svc for this server"),
 +    AP_INIT_TAKE1("H2AltSvcMaxAge", h2_conf_set_alt_svc_max_age, NULL,
 +                  RSRC_CONF, "set the maximum age (in seconds) that client can rely on alt-svc information"),
 +    AP_INIT_TAKE1("H2SerializeHeaders", h2_conf_set_serialize_headers, NULL,
 +                  RSRC_CONF, "on to enable header serialization for compatibility"),
 +    AP_INIT_TAKE1("H2Direct", h2_conf_set_direct, NULL,
 +                  RSRC_CONF, "on to enable direct HTTP/2 mode"),
 +    AP_INIT_TAKE1("H2SessionExtraFiles", h2_conf_set_session_extra_files, NULL,
 +                  RSRC_CONF, "number of extra file a session might keep open"),
 +    AP_END_CMD
 +};
 +
 +
 +h2_config *h2_config_rget(request_rec *r)
 +{
 +    h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, 
 +                                                       &h2_module);
 +    return cfg? cfg : h2_config_sget(r->server); 
 +}
 +
 +h2_config *h2_config_get(conn_rec *c)
 +{
 +    h2_ctx *ctx = h2_ctx_get(c);
++    
 +    if (ctx->config) {
 +        return ctx->config;
 +    }
++    else if (ctx->server) {
 +        ctx->config = h2_config_sget(ctx->server);
 +        return ctx->config;
 +    }
 +    
 +    return h2_config_sget(c->base_server);
 +}
 +
index 0e198456f230c597a336a5b75d13a4c61e18ba7a,0000000000000000000000000000000000000000..f37a1474a1e7f1cd2af381a2bc346bdb79d75fe6
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,90 @@@
 +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + 
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <assert.h>
 +
 +#include <httpd.h>
 +#include <http_core.h>
 +#include <http_config.h>
 +
 +#include "h2_private.h"
 +#include "h2_task.h"
 +#include "h2_ctx.h"
 +#include "h2_private.h"
 +
 +static h2_ctx *h2_ctx_create(const conn_rec *c)
 +{
 +    h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx));
 +    AP_DEBUG_ASSERT(ctx);
 +    ap_set_module_config(c->conn_config, &h2_module, ctx);
 +    return ctx;
 +}
 +
 +h2_ctx *h2_ctx_create_for(const conn_rec *c, h2_task_env *env)
 +{
 +    h2_ctx *ctx = h2_ctx_create(c);
 +    if (ctx) {
 +        ctx->task_env = env;
 +    }
 +    return ctx;
 +}
 +
 +h2_ctx *h2_ctx_get(const conn_rec *c)
 +{
 +    h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &h2_module);
 +    if (ctx == NULL) {
 +        ctx = h2_ctx_create(c);
 +    }
 +    return ctx;
 +}
 +
 +h2_ctx *h2_ctx_rget(const request_rec *r)
 +{
 +    return h2_ctx_get(r->connection);
 +}
 +
 +const char *h2_ctx_protocol_get(const conn_rec *c)
 +{
 +    h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &h2_module);
 +    return ctx? ctx->protocol : NULL;
 +}
 +
 +h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto)
 +{
 +    ctx->protocol = proto;
 +    ctx->is_h2 = (proto != NULL);
 +    return ctx;
 +}
 +
++h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s)
++{
++    ctx->server = s;
++    return ctx;
++}
++
 +int h2_ctx_is_task(h2_ctx *ctx)
 +{
 +    return ctx && !!ctx->task_env;
 +}
 +
 +int h2_ctx_is_active(h2_ctx *ctx)
 +{
 +    return ctx && ctx->is_h2;
 +}
 +
 +struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx)
 +{
 +    return ctx->task_env;
 +}
index f9bc8278296769b996f002e4fab32722ac98ad76,0000000000000000000000000000000000000000..86c59206eda4f6c14374e70473f221c437330df3
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,63 @@@
 +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + 
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#ifndef __mod_h2__h2_ctx__
 +#define __mod_h2__h2_ctx__
 +
 +struct h2_task_env;
 +struct h2_config;
 +
 +/**
 + * The h2 module context associated with a connection. 
 + *
 + * It keeps track of the different types of connections:
 + * - those from clients that use HTTP/2 protocol
 + * - those from clients that do not use HTTP/2
 + * - those created by ourself to perform work on HTTP/2 streams
 + */
 +typedef struct h2_ctx {
 +    int is_h2;                    /* h2 engine is used */
 +    const char *protocol;         /* the protocol negotiated */
 +    struct h2_task_env *task_env; /* the h2_task environment or NULL */
 +    const char *hostname;         /* hostname negotiated via SNI, optional */
 +    server_rec *server;           /* httpd server config selected. */
 +    struct h2_config *config;     /* effective config in this context */
 +} h2_ctx;
 +
 +h2_ctx *h2_ctx_get(const conn_rec *c);
 +h2_ctx *h2_ctx_rget(const request_rec *r);
 +h2_ctx *h2_ctx_create_for(const conn_rec *c, struct h2_task_env *env);
 +
 +
 +/* Set the h2 protocol established on this connection context or
 + * NULL when other protocols are in place.
 + */
 +h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto);
 +
++/* Set the server_rec relevant for this context.
++ */
++h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s);
++
 +/**
 + * Get the h2 protocol negotiated for this connection, or NULL.
 + */
 +const char *h2_ctx_protocol_get(const conn_rec *c);
 +
 +int h2_ctx_is_task(h2_ctx *ctx);
 +int h2_ctx_is_active(h2_ctx *ctx);
 +
 +struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx);
 +
 +#endif /* defined(__mod_h2__h2_ctx__) */
index 4cdc4118b0a114546e663055f572cd98f418fd71,0000000000000000000000000000000000000000..23c34490eaa27c4414ee30a9e3111c30df9f9f55
mode 100644,000000..100644
--- /dev/null
@@@ -1,180 -1,0 +1,181 @@@
 +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + 
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <assert.h>
 +
 +#include <apr_strings.h>
 +#include <apr_optional.h>
 +#include <apr_optional_hooks.h>
 +
 +#include <httpd.h>
 +#include <http_core.h>
 +#include <http_config.h>
 +#include <http_connection.h>
 +#include <http_protocol.h>
 +#include <http_log.h>
 +
 +#include "h2_private.h"
 +
 +#include "h2_config.h"
 +#include "h2_ctx.h"
 +#include "h2_conn.h"
 +#include "h2_h2.h"
 +#include "h2_switch.h"
 +
 +/*******************************************************************************
 + * SSL var lookup
 + */
 +APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
 +                        (apr_pool_t *, server_rec *,
 +                         conn_rec *, request_rec *,
 +                         char *));
 +static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *,
 +                                   conn_rec *, request_rec *,
 +                                   char *);
 +
 +/*******************************************************************************
 + * Once per lifetime init, retrieve optional functions
 + */
 +apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s)
 +{
 +    (void)pool;
 +    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_switch init");
 +    opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
 +
 +    return APR_SUCCESS;
 +}
 +
 +static int h2_protocol_propose(conn_rec *c, request_rec *r,
 +                               server_rec *s,
 +                               const apr_array_header_t *offers,
 +                               apr_array_header_t *proposals)
 +{
 +    int proposed = 0;
 +    const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
 +    
 +    (void)s;
 +    if (strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) {
 +        /* We do not know how to switch from anything else but http/1.1.
 +         */
 +        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
 +                      "protocol switch: current proto != http/1.1, declined");
 +        return DECLINED;
 +    }
 +    
 +    if (r) {
 +        const char *p;
 +        /* So far, this indicates an HTTP/1 Upgrade header initiated
 +         * protocol switch. For that, the HTTP2-Settings header needs
 +         * to be present and valid for the connection.
 +         */
 +        p = apr_table_get(r->headers_in, "HTTP2-Settings");
 +        if (!p) {
 +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
 +                          "upgrade without HTTP2-Settings declined");
 +            return DECLINED;
 +        }
 +        
 +        p = apr_table_get(r->headers_in, "Connection");
 +        if (!ap_find_token(r->pool, p, "http2-settings")) {
 +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
 +                          "upgrade without HTTP2-Settings declined");
 +            return DECLINED;
 +        }
 +        
 +        /* We also allow switching only for requests that have no body.
 +         */
 +        p = apr_table_get(r->headers_in, "Content-Length");
 +        if (p && strcmp(p, "0")) {
 +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
 +                          "upgrade with content-length: %s, declined", p);
 +            return DECLINED;
 +        }
 +    }
 +    
 +    while (*protos) {
 +        /* Add all protocols we know (tls or clear) and that
 +         * are part of the offerings (if there have been any). 
 +         */
 +        if (!offers || ap_array_str_contains(offers, *protos)) {
 +            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
 +                          "proposing protocol '%s'", *protos);
 +            APR_ARRAY_PUSH(proposals, const char*) = *protos;
 +            proposed = 1;
 +        }
 +        ++protos;
 +    }
 +    return proposed? DECLINED : OK;
 +}
 +
 +static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
 +                              const char *protocol)
 +{
 +    int found = 0;
 +    const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos;
 +    const char **p = protos;
 +    
 +    (void)s;
 +    while (*p) {
 +        if (!strcmp(*p, protocol)) {
 +            found = 1;
 +            break;
 +        }
 +        p++;
 +    }
 +    
 +    if (found) {
 +        h2_ctx *ctx = h2_ctx_get(c);
 +        
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
 +                      "switching protocol to '%s'", protocol);
 +        h2_ctx_protocol_set(ctx, protocol);
++        h2_ctx_server_set(ctx, s);
 +        
 +        if (r != NULL) {
 +            apr_status_t status;
 +            /* Switching in the middle of a request means that
 +             * we have to send out the response to this one in h2
 +             * format. So we need to take over the connection
 +             * right away.
 +             */
 +            ap_remove_input_filter_byhandle(r->input_filters, "http_in");
 +            ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
 +            
 +            /* Ok, start an h2_conn on this one. */
 +            status = h2_conn_rprocess(r);
 +            if (status != DONE) {
 +                /* Nothing really to do about this. */
 +                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
 +                              "session proessed, unexpected status");
 +            }
 +        }
 +        return DONE;
 +    }
 +    
 +    return DECLINED;
 +}
 +
 +static const char *h2_protocol_get(const conn_rec *c)
 +{
 +    return h2_ctx_protocol_get(c);
 +}
 +
 +void h2_switch_register_hooks(void)
 +{
 +    ap_hook_protocol_propose(h2_protocol_propose, NULL, NULL, APR_HOOK_MIDDLE);
 +    ap_hook_protocol_switch(h2_protocol_switch, NULL, NULL, APR_HOOK_MIDDLE);
 +    ap_hook_protocol_get(h2_protocol_get, NULL, NULL, APR_HOOK_MIDDLE);
 +}
 +
index 26b8c6e40e84ac5e80a410a7a99d912c8789f74a,0000000000000000000000000000000000000000..bbea7b20f8aa1ae48309e38cd9a04368b5d703ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,467 -1,0 +1,467 @@@
-     env.serialize_headers = !!h2_config_geti(cfg, H2_CONF_SER_HEADERS);
 +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + 
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <assert.h>
 +#include <stddef.h>
 +
 +#include <apr_atomic.h>
 +#include <apr_thread_cond.h>
 +#include <apr_strings.h>
 +
 +#include <httpd.h>
 +#include <http_core.h>
 +#include <http_connection.h>
 +#include <http_protocol.h>
 +#include <http_request.h>
 +#include <http_log.h>
 +#include <http_vhost.h>
 +#include <util_filter.h>
 +#include <ap_mpm.h>
 +#include <mod_core.h>
 +#include <scoreboard.h>
 +
 +#include "h2_private.h"
 +#include "h2_conn.h"
 +#include "h2_config.h"
 +#include "h2_from_h1.h"
 +#include "h2_h2.h"
 +#include "h2_mplx.h"
 +#include "h2_session.h"
 +#include "h2_stream.h"
 +#include "h2_task_input.h"
 +#include "h2_task_output.h"
 +#include "h2_task.h"
 +#include "h2_ctx.h"
 +#include "h2_worker.h"
 +
 +
 +static apr_status_t h2_filter_stream_input(ap_filter_t* filter,
 +                                           apr_bucket_brigade* brigade,
 +                                           ap_input_mode_t mode,
 +                                           apr_read_type_e block,
 +                                           apr_off_t readbytes) {
 +    h2_task_env *env = filter->ctx;
 +    AP_DEBUG_ASSERT(env);
 +    if (!env->input) {
 +        return APR_ECONNABORTED;
 +    }
 +    return h2_task_input_read(env->input, filter, brigade,
 +                              mode, block, readbytes);
 +}
 +
 +static apr_status_t h2_filter_stream_output(ap_filter_t* filter,
 +                                            apr_bucket_brigade* brigade) {
 +    h2_task_env *env = filter->ctx;
 +    AP_DEBUG_ASSERT(env);
 +    if (!env->output) {
 +        return APR_ECONNABORTED;
 +    }
 +    return h2_task_output_write(env->output, filter, brigade);
 +}
 +
 +static apr_status_t h2_filter_read_response(ap_filter_t* f,
 +                                            apr_bucket_brigade* bb) {
 +    h2_task_env *env = f->ctx;
 +    AP_DEBUG_ASSERT(env);
 +    if (!env->output || !env->output->from_h1) {
 +        return APR_ECONNABORTED;
 +    }
 +    return h2_from_h1_read_response(env->output->from_h1, f, bb);
 +}
 +
 +/*******************************************************************************
 + * Register various hooks
 + */
 +static const char *const mod_ssl[]        = { "mod_ssl.c", NULL};
 +static int h2_task_pre_conn(conn_rec* c, void *arg);
 +static int h2_task_process_conn(conn_rec* c);
 +
 +void h2_task_register_hooks(void)
 +{
 +    /* This hook runs on new connections before mod_ssl has a say.
 +     * Its purpose is to prevent mod_ssl from touching our pseudo-connections
 +     * for streams.
 +     */
 +    ap_hook_pre_connection(h2_task_pre_conn,
 +                           NULL, mod_ssl, APR_HOOK_FIRST);
 +    /* When the connection processing actually starts, we might to
 +     * take over, if the connection is for a task.
 +     */
 +    ap_hook_process_connection(h2_task_process_conn, 
 +                               NULL, NULL, APR_HOOK_FIRST);
 +
 +    ap_register_output_filter("H2_RESPONSE", h2_response_output_filter,
 +                              NULL, AP_FTYPE_PROTOCOL);
 +    ap_register_input_filter("H2_TO_H1", h2_filter_stream_input,
 +                             NULL, AP_FTYPE_NETWORK);
 +    ap_register_output_filter("H1_TO_H2", h2_filter_stream_output,
 +                              NULL, AP_FTYPE_NETWORK);
 +    ap_register_output_filter("H1_TO_H2_RESP", h2_filter_read_response,
 +                              NULL, AP_FTYPE_PROTOCOL);
 +}
 +
 +static int h2_task_pre_conn(conn_rec* c, void *arg)
 +{
 +    
 +    h2_ctx *ctx = h2_ctx_get(c);
 +    
 +    (void)arg;
 +    if (h2_ctx_is_task(ctx)) {
 +        h2_task_env *env = h2_ctx_get_task(ctx);
 +        
 +        /* This connection is a pseudo-connection used for a h2_task.
 +         * Since we read/write directly from it ourselves, we need
 +         * to disable a possible ssl connection filter.
 +         */
 +        h2_tls_disable(c);
 +        
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
 +                      "h2_h2, pre_connection, found stream task");
 +        
 +        /* Add our own, network level in- and output filters.
 +         */
 +        ap_add_input_filter("H2_TO_H1", env, NULL, c);
 +        ap_add_output_filter("H1_TO_H2", env, NULL, c);
 +    }
 +    return OK;
 +}
 +
 +static int h2_task_process_conn(conn_rec* c)
 +{
 +    h2_ctx *ctx = h2_ctx_get(c);
 +    
 +    if (h2_ctx_is_task(ctx)) {
 +        if (!ctx->task_env->serialize_headers) {
 +            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
 +                          "h2_h2, processing request directly");
 +            h2_task_process_request(ctx->task_env);
 +            return DONE;
 +        }
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
 +                      "h2_task(%s), serialized handling", ctx->task_env->id);
 +    }
 +    return DECLINED;
 +}
 +
 +
 +h2_task *h2_task_create(long session_id,
 +                        int stream_id,
 +                        apr_pool_t *stream_pool,
 +                        h2_mplx *mplx, conn_rec *c)
 +{
 +    h2_task *task = apr_pcalloc(stream_pool, sizeof(h2_task));
 +    if (task == NULL) {
 +        ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, stream_pool,
 +                      APLOGNO(02941) "h2_task(%ld-%d): create stream task", 
 +                      session_id, stream_id);
 +        h2_mplx_out_close(mplx, stream_id);
 +        return NULL;
 +    }
 +    
 +    APR_RING_ELEM_INIT(task, link);
 +
 +    task->id = apr_psprintf(stream_pool, "%ld-%d", session_id, stream_id);
 +    task->stream_id = stream_id;
 +    task->mplx = mplx;
 +    
 +    task->c = c;
 +    
 +    ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, stream_pool,
 +                  "h2_task(%s): created", task->id);
 +    return task;
 +}
 +
 +void h2_task_set_request(h2_task *task, 
 +                         const char *method, 
 +                         const char *scheme, 
 +                         const char *authority, 
 +                         const char *path, 
 +                         apr_table_t *headers, int eos)
 +{
 +    task->method = method;
 +    task->scheme = scheme;
 +    task->authority = authority;
 +    task->path = path;
 +    task->headers = headers;
 +    task->input_eos = eos;
 +}
 +
 +apr_status_t h2_task_destroy(h2_task *task)
 +{
 +    (void)task;
 +    return APR_SUCCESS;
 +}
 +
 +apr_status_t h2_task_do(h2_task *task, h2_worker *worker)
 +{
 +    apr_status_t status = APR_SUCCESS;
 +    h2_config *cfg = h2_config_get(task->mplx->c);
 +    h2_task_env env; 
 +    
 +    AP_DEBUG_ASSERT(task);
 +    
 +    memset(&env, 0, sizeof(env));
 +    
 +    env.id = task->id;
 +    env.stream_id = task->stream_id;
 +    env.mplx = task->mplx;
 +    task->mplx = NULL;
 +    
 +    env.input_eos = task->input_eos;
-     r->protocol = (char*)"HTTP/1.1";
-     r->proto_num = HTTP_VERSION(1, 1);
++    env.serialize_headers = h2_config_geti(cfg, H2_CONF_SER_HEADERS);
 +    
 +    /* Create a subpool from the worker one to be used for all things
 +     * with life-time of this task_env execution.
 +     */
 +    apr_pool_create(&env.pool, h2_worker_get_pool(worker));
 +
 +    /* Link the env to the worker which provides useful things such
 +     * as mutex, a socket etc. */
 +    env.io = h2_worker_get_cond(worker);
 +    
 +    /* Clone fields, so that lifetimes become (more) independent. */
 +    env.method    = apr_pstrdup(env.pool, task->method);
 +    env.scheme    = apr_pstrdup(env.pool, task->scheme);
 +    env.authority = apr_pstrdup(env.pool, task->authority);
 +    env.path      = apr_pstrdup(env.pool, task->path);
 +    env.headers   = apr_table_clone(env.pool, task->headers);
 +    
 +    /* Setup the pseudo connection to use our own pool and bucket_alloc */
 +    env.c = *task->c;
 +    task->c = NULL;
 +    status = h2_conn_setup(&env, worker);
 +    
 +    /* save in connection that this one is a pseudo connection, prevents
 +     * other hooks from messing with it. */
 +    h2_ctx_create_for(&env.c, &env);
 +
 +    if (status == APR_SUCCESS) {
 +        env.input = h2_task_input_create(&env, env.pool, 
 +                                         env.c.bucket_alloc);
 +        env.output = h2_task_output_create(&env, env.pool,
 +                                           env.c.bucket_alloc);
 +        status = h2_conn_process(&env.c, h2_worker_get_socket(worker));
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, &env.c,
 +                      "h2_task(%s): processing done", env.id);
 +    }
 +    else {
 +        ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, &env.c,
 +                      APLOGNO(02957) "h2_task(%s): error setting up h2_task_env", 
 +                      env.id);
 +    }
 +    
 +    if (env.input) {
 +        h2_task_input_destroy(env.input);
 +        env.input = NULL;
 +    }
 +    
 +    if (env.output) {
 +        h2_task_output_close(env.output);
 +        h2_task_output_destroy(env.output);
 +        env.output = NULL;
 +    }
 +
 +    h2_task_set_finished(task);
 +    if (env.io) {
 +        apr_thread_cond_signal(env.io);
 +    }
 +    
 +    if (env.pool) {
 +        apr_pool_destroy(env.pool);
 +        env.pool = NULL;
 +    }
 +    
 +    if (env.c.id) {
 +        h2_conn_post(&env.c, worker);
 +    }
 +    
 +    h2_mplx_task_done(env.mplx, env.stream_id);
 +    
 +    return status;
 +}
 +
 +int h2_task_has_started(h2_task *task)
 +{
 +    AP_DEBUG_ASSERT(task);
 +    return apr_atomic_read32(&task->has_started);
 +}
 +
 +void h2_task_set_started(h2_task *task)
 +{
 +    AP_DEBUG_ASSERT(task);
 +    apr_atomic_set32(&task->has_started, 1);
 +}
 +
 +int h2_task_has_finished(h2_task *task)
 +{
 +    return apr_atomic_read32(&task->has_finished);
 +}
 +
 +void h2_task_set_finished(h2_task *task)
 +{
 +    apr_atomic_set32(&task->has_finished, 1);
 +}
 +
 +void h2_task_die(h2_task_env *env, int status, request_rec *r)
 +{
 +    (void)env;
 +    ap_die(status, r);
 +}
 +
 +static request_rec *h2_task_create_request(h2_task_env *env)
 +{
 +    conn_rec *conn = &env->c;
 +    request_rec *r;
 +    apr_pool_t *p;
 +    int access_status = HTTP_OK;    
 +    
 +    apr_pool_create(&p, conn->pool);
 +    apr_pool_tag(p, "request");
 +    r = apr_pcalloc(p, sizeof(request_rec));
 +    AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
 +    r->pool            = p;
 +    r->connection      = conn;
 +    r->server          = conn->base_server;
 +    
 +    r->user            = NULL;
 +    r->ap_auth_type    = NULL;
 +    
 +    r->allowed_methods = ap_make_method_list(p, 2);
 +    
 +    r->headers_in = apr_table_copy(r->pool, env->headers);
 +    r->trailers_in     = apr_table_make(r->pool, 5);
 +    r->subprocess_env  = apr_table_make(r->pool, 25);
 +    r->headers_out     = apr_table_make(r->pool, 12);
 +    r->err_headers_out = apr_table_make(r->pool, 5);
 +    r->trailers_out    = apr_table_make(r->pool, 5);
 +    r->notes           = apr_table_make(r->pool, 5);
 +    
 +    r->request_config  = ap_create_request_config(r->pool);
 +    /* Must be set before we run create request hook */
 +    
 +    r->proto_output_filters = conn->output_filters;
 +    r->output_filters  = r->proto_output_filters;
 +    r->proto_input_filters = conn->input_filters;
 +    r->input_filters   = r->proto_input_filters;
 +    ap_run_create_request(r);
 +    r->per_dir_config  = r->server->lookup_defaults;
 +    
 +    r->sent_bodyct     = 0;                      /* bytect isn't for body */
 +    
 +    r->read_length     = 0;
 +    r->read_body       = REQUEST_NO_BODY;
 +    
 +    r->status          = HTTP_OK;  /* Until further notice */
 +    r->header_only     = 0;
 +    r->the_request     = NULL;
 +    
 +    /* Begin by presuming any module can make its own path_info assumptions,
 +     * until some module interjects and changes the value.
 +     */
 +    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
 +    
 +    r->useragent_addr = conn->client_addr;
 +    r->useragent_ip = conn->client_ip;
 +    
 +    ap_run_pre_read_request(r, conn);
 +    
 +    /* Time to populate r with the data we have. */
 +    r->request_time = apr_time_now();
 +    r->the_request = apr_psprintf(r->pool, "%s %s HTTP/1.1", 
 +                                  env->method, env->path);
 +    r->method = env->method;
 +    /* Provide quick information about the request method as soon as known */
 +    r->method_number = ap_method_number_of(r->method);
 +    if (r->method_number == M_GET && r->method[0] == 'H') {
 +        r->header_only = 1;
 +    }
 +
 +    ap_parse_uri(r, env->path);
++    r->protocol = (char*)"HTTP/2";
++    r->proto_num = HTTP_VERSION(2, 0);
 +    
 +    /* update what we think the virtual host is based on the headers we've
 +     * now read. may update status.
 +     * Leave r->hostname empty, vhost will parse if form our Host: header,
 +     * otherwise we get complains about port numbers.
 +     */
 +    r->hostname = NULL;
 +    ap_update_vhost_from_headers(r);
 +    
 +    /* we may have switched to another server */
 +    r->per_dir_config = r->server->lookup_defaults;
 +    
 +    /*
 +     * Add the HTTP_IN filter here to ensure that ap_discard_request_body
 +     * called by ap_die and by ap_send_error_response works correctly on
 +     * status codes that do not cause the connection to be dropped and
 +     * in situations where the connection should be kept alive.
 +     */
 +    ap_add_input_filter_handle(ap_http_input_filter_handle,
 +                               NULL, r, r->connection);
 +    
 +    if (access_status != HTTP_OK
 +        || (access_status = ap_run_post_read_request(r))) {
 +        /* Request check post hooks failed. An example of this would be a
 +         * request for a vhost where h2 is disabled --> 421.
 +         */
 +        h2_task_die(env, access_status, r);
 +        ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
 +        ap_run_log_transaction(r);
 +        r = NULL;
 +        goto traceout;
 +    }
 +    
 +    AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, 
 +                            (char *)r->uri, (char *)r->server->defn_name, 
 +                            r->status);
 +    return r;
 +traceout:
 +    AP_READ_REQUEST_FAILURE((uintptr_t)r);
 +    return r;
 +}
 +
 +
 +apr_status_t h2_task_process_request(h2_task_env *env)
 +{
 +    conn_rec *c = &env->c;
 +    request_rec *r;
 +    conn_state_t *cs = c->cs;
 +
 +    r = h2_task_create_request(env);
 +    if (r && (r->status == HTTP_OK)) {
 +        ap_update_child_status(c->sbh, SERVER_BUSY_READ, r);
 +        
 +        if (cs)
 +            cs->state = CONN_STATE_HANDLER;
 +        ap_process_request(r);
 +        /* After the call to ap_process_request, the
 +         * request pool will have been deleted.  We set
 +         * r=NULL here to ensure that any dereference
 +         * of r that might be added later in this function
 +         * will result in a segfault immediately instead
 +         * of nondeterministic failures later.
 +         */
 +        r = NULL;
 +    }
 +    ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, NULL);
 +    c->sbh = NULL;
 +
 +    return APR_SUCCESS;
 +}
 +
 +
 +
 +
index cc7d8503fd56475007b1d770e0b844f18d551d9c,0000000000000000000000000000000000000000..487f7e606920c26fce185bbdf2b357f52f885c5d
mode 100644,000000..100644
--- /dev/null
@@@ -1,219 -1,0 +1,222 @@@
 +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + 
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <assert.h>
 +
 +#include <httpd.h>
 +#include <http_core.h>
 +#include <http_log.h>
 +#include <http_connection.h>
 +
 +#include "h2_private.h"
 +#include "h2_conn.h"
 +#include "h2_mplx.h"
 +#include "h2_session.h"
 +#include "h2_stream.h"
 +#include "h2_task_input.h"
 +#include "h2_task.h"
 +#include "h2_util.h"
 +
 +
 +static int is_aborted(ap_filter_t *f)
 +{
 +    return (f->c->aborted);
 +}
 +
 +static int ser_header(void *ctx, const char *name, const char *value) 
 +{
 +    h2_task_input *input = (h2_task_input*)ctx;
 +    apr_brigade_printf(input->bb, NULL, NULL, "%s: %s\r\n", name, value);
 +    return 1;
 +}
 +
 +h2_task_input *h2_task_input_create(h2_task_env *env, apr_pool_t *pool, 
 +                                    apr_bucket_alloc_t *bucket_alloc)
 +{
 +    h2_task_input *input = apr_pcalloc(pool, sizeof(h2_task_input));
 +    if (input) {
 +        input->env = env;
 +        input->bb = NULL;
 +        
 +        if (env->serialize_headers) {
++            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, &env->c,
++                          "h2_task_input(%s): serialize request %s %s", 
++                          env->id, env->method, env->path);
 +            input->bb = apr_brigade_create(pool, bucket_alloc);
 +            apr_brigade_printf(input->bb, NULL, NULL, "%s %s HTTP/1.1\r\n", 
 +                               env->method, env->path);
 +            apr_table_do(ser_header, input, env->headers, NULL);
 +            apr_brigade_puts(input->bb, NULL, NULL, "\r\n");
 +            if (input->env->input_eos) {
 +                APR_BRIGADE_INSERT_TAIL(input->bb, apr_bucket_eos_create(bucket_alloc));
 +            }
 +        }
 +        else if (!input->env->input_eos) {
 +            input->bb = apr_brigade_create(pool, bucket_alloc);
 +        }
 +        else {
 +            /* We do not serialize and have eos already, no need to
 +             * create a bucket brigade. */
 +        }
 +        
 +        if (APLOGcdebug(&env->c)) {
 +            char buffer[1024];
 +            apr_size_t len = sizeof(buffer)-1;
 +            if (input->bb) {
 +                apr_brigade_flatten(input->bb, buffer, &len);
 +            }
 +            else {
 +                len = 0;
 +            }
 +            buffer[len] = 0;
 +            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, &env->c,
 +                          "h2_task_input(%s): request is: %s", 
 +                          env->id, buffer);
 +        }
 +    }
 +    return input;
 +}
 +
 +void h2_task_input_destroy(h2_task_input *input)
 +{
 +    input->bb = NULL;
 +}
 +
 +apr_status_t h2_task_input_read(h2_task_input *input,
 +                                ap_filter_t* f,
 +                                apr_bucket_brigade* bb,
 +                                ap_input_mode_t mode,
 +                                apr_read_type_e block,
 +                                apr_off_t readbytes)
 +{
 +    apr_status_t status = APR_SUCCESS;
 +    apr_off_t bblen = 0;
 +    
 +    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
 +                  "h2_task_input(%s): read, block=%d, mode=%d, readbytes=%ld", 
 +                  input->env->id, block, mode, (long)readbytes);
 +    
 +    if (is_aborted(f)) {
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
 +                      "h2_task_input(%s): is aborted", 
 +                      input->env->id);
 +        return APR_ECONNABORTED;
 +    }
 +    
 +    if (mode == AP_MODE_INIT) {
 +        return APR_SUCCESS;
 +    }
 +    
 +    if (input->bb) {
 +        status = apr_brigade_length(input->bb, 1, &bblen);
 +        if (status != APR_SUCCESS) {
 +            ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, f->c,
 +                          APLOGNO(02958) "h2_task_input(%s): brigade length fail", 
 +                          input->env->id);
 +            return status;
 +        }
 +    }
 +    
 +    if ((bblen == 0) && input->env->input_eos) {
 +        return APR_EOF;
 +    }
 +    
 +    while ((bblen == 0) || (mode == AP_MODE_READBYTES && bblen < readbytes)) {
 +        /* Get more data for our stream from mplx.
 +         */
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
 +                      "h2_task_input(%s): get more data from mplx, block=%d, "
 +                      "readbytes=%ld, queued=%ld",
 +                      input->env->id, block, 
 +                      (long)readbytes, (long)bblen);
 +        
 +        /* Although we sometimes get called with APR_NONBLOCK_READs, 
 +         we seem to  fill our buffer blocking. Otherwise we get EAGAIN,
 +         return that to our caller and everyone throws up their hands,
 +         never calling us again. */
 +        status = h2_mplx_in_read(input->env->mplx, APR_BLOCK_READ,
 +                                 input->env->stream_id, input->bb, 
 +                                 input->env->io);
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
 +                      "h2_task_input(%s): mplx in read returned",
 +                      input->env->id);
 +        if (status != APR_SUCCESS) {
 +            return status;
 +        }
 +        status = apr_brigade_length(input->bb, 1, &bblen);
 +        if (status != APR_SUCCESS) {
 +            return status;
 +        }
 +        if ((bblen == 0) && (block == APR_NONBLOCK_READ)) {
 +            return h2_util_has_eos(input->bb, 0)? APR_EOF : APR_EAGAIN;
 +        }
 +        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
 +                      "h2_task_input(%s): mplx in read, %ld bytes in brigade",
 +                      input->env->id, (long)bblen);
 +    }
 +    
 +    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
 +                  "h2_task_input(%s): read, mode=%d, block=%d, "
 +                  "readbytes=%ld, queued=%ld",
 +                  input->env->id, mode, block, 
 +                  (long)readbytes, (long)bblen);
 +           
 +    if (!APR_BRIGADE_EMPTY(input->bb)) {
 +        if (mode == AP_MODE_EXHAUSTIVE) {
 +            /* return all we have */
 +            return h2_util_move(bb, input->bb, readbytes, 0, 
 +                                "task_input_read(exhaustive)");
 +        }
 +        else if (mode == AP_MODE_READBYTES) {
 +            return h2_util_move(bb, input->bb, readbytes, 0, 
 +                                "task_input_read(readbytes)");
 +        }
 +        else if (mode == AP_MODE_SPECULATIVE) {
 +            /* return not more than was asked for */
 +            return h2_util_copy(bb, input->bb, readbytes,  
 +                                "task_input_read(speculative)");
 +        }
 +        else if (mode == AP_MODE_GETLINE) {
 +            /* we are reading a single LF line, e.g. the HTTP headers */
 +            status = apr_brigade_split_line(bb, input->bb, block, 
 +                                            HUGE_STRING_LEN);
 +            if (APLOGctrace1(f->c)) {
 +                char buffer[1024];
 +                apr_size_t len = sizeof(buffer)-1;
 +                apr_brigade_flatten(bb, buffer, &len);
 +                buffer[len] = 0;
 +                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
 +                              "h2_task_input(%s): getline: %s",
 +                              input->env->id, buffer);
 +            }
 +            return status;
 +        }
 +        else {
 +            /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
 +             * to support it. Seems to work. */
 +            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
 +                          APLOGNO(02942) 
 +                          "h2_task_input, unsupported READ mode %d", mode);
 +            return APR_ENOTIMPL;
 +        }
 +    }
 +    
 +    if (is_aborted(f)) {
 +        return APR_ECONNABORTED;
 +    }
 +    
 +    return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF;
 +}
 +