httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject svn commit: r1688474 [7/21] - in /httpd/httpd/trunk/modules/http2: ./ m4/ mod-h2.xcodeproj/ mod-h2.xcodeproj/project.xcworkspace/ mod-h2.xcodeproj/project.xcworkspace/xcshareddata/ mod-h2.xcodeproj/xcuserdata/ mod-h2.xcodeproj/xcuserdata/sei.xcuserdata...
Date Tue, 30 Jun 2015 15:26:19 GMT
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_stream_set.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_stream_set.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_stream_set.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_stream_set.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,52 @@
+/* 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_stream_set__
+#define __mod_h2__h2_stream_set__
+
+/**
+ * A set of h2_stream instances. Allows lookup by stream id
+ * and other criteria.
+ */
+
+typedef h2_stream *h2_stream_set_match_fn(void *ctx, h2_stream *stream);
+typedef int h2_stream_set_iter_fn(void *ctx, h2_stream *stream);
+
+typedef struct h2_stream_set h2_stream_set;
+
+
+h2_stream_set *h2_stream_set_create(apr_pool_t *pool);
+
+void h2_stream_set_destroy(h2_stream_set *sp);
+
+apr_status_t h2_stream_set_add(h2_stream_set *sp, h2_stream *stream);
+
+h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id);
+
+h2_stream *h2_stream_set_remove(h2_stream_set *sp,h2_stream *stream);
+
+void h2_stream_set_remove_all(h2_stream_set *sp);
+
+int h2_stream_set_is_empty(h2_stream_set *sp);
+
+apr_size_t h2_stream_set_size(h2_stream_set *sp);
+
+h2_stream *h2_stream_set_find(h2_stream_set *sp,
+                              h2_stream_set_match_fn *match, void *ctx);
+
+void h2_stream_set_iter(h2_stream_set *sp,
+                        h2_stream_set_iter_fn *iter, void *ctx);
+
+#endif /* defined(__mod_h2__h2_stream_set__) */

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,464 @@
+/* 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(task);
+    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(task);
+    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(task);
+    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)
+{
+    (void)arg;
+    
+    h2_ctx *ctx = h2_ctx_get(c);
+    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,
+                      "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 *path, 
+                         const char *authority, apr_table_t *headers, int eos)
+{
+    task->method = method;
+    task->path = path;
+    task->authority = authority;
+    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;
+    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.path      = apr_pstrdup(env.pool, task->path);
+    env.authority = apr_pstrdup(env.pool, task->authority);
+    env.headers   = apr_table_clone(env.pool, task->headers);
+    
+    /* Setup the pseudo connection to use our own pool and bucket_alloc */
+    if (task->c) {
+        env.c = *task->c;
+        task->c = NULL;
+        status = h2_conn_setup(&env, worker);
+    }
+    else {
+        status = h2_conn_init(&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,
+                      "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/1.1";
+    r->proto_num = HTTP_VERSION(1, 1);
+    
+    r->hostname = env->authority;
+    
+    /* update what we think the virtual host is based on the headers we've
+     * now read. may update status.
+     */
+    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;
+}
+
+
+
+

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,181 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_task__
+#define __mod_h2__h2_task__
+
+/**
+ * A h2_task fakes a HTTP/1.1 request from the data in a HTTP/2 stream 
+ * (HEADER+CONT.+DATA) the module recieves.
+ *
+ * In order to answer a HTTP/2 stream, we want all Apache httpd infrastructure
+ * to be involved as usual, as if this stream can as a separate HTTP/1.1
+ * request. The basic trickery to do so was derived from google's mod_spdy
+ * source. Basically, we fake a new conn_rec object, even with its own
+ * socket and give it to ap_process_connection().
+ *
+ * Since h2_task instances are executed in separate threads, we may have
+ * different lifetimes than our h2_stream or h2_session instances. Basically,
+ * we would like to be as standalone as possible.
+ *
+ * Finally, to keep certain connection level filters, such as ourselves and
+ * especially mod_ssl ones, from messing with our data, we need a filter
+ * of our own to disble those.
+ */
+
+struct apr_thread_cond_t;
+struct h2_conn;
+struct h2_mplx;
+struct h2_task;
+struct h2_resp_head;
+struct h2_worker;
+
+typedef struct h2_task h2_task;
+
+struct h2_task {
+    /** Links to the rest of the tasks */
+    APR_RING_ENTRY(h2_task) link;
+
+    const char *id;
+    int stream_id;
+    struct h2_mplx *mplx;
+    
+    volatile apr_uint32_t has_started;
+    volatile apr_uint32_t has_finished;
+    
+    const char *method;
+    const char *path;
+    const char *authority;
+    apr_table_t *headers;
+    int input_eos;
+
+    struct conn_rec *c;
+};
+
+typedef struct h2_task_env h2_task_env;
+
+struct h2_task_env {
+    const char *id;
+    int stream_id;
+    struct h2_mplx *mplx;
+    
+    apr_pool_t *pool;              /* pool for task lifetime things */
+    apr_bucket_alloc_t *bucket_alloc;
+    
+    const char *method;
+    const char *path;
+    const char *authority;
+    apr_table_t *headers;
+    int input_eos;
+    
+    int serialize_headers;
+
+    struct conn_rec c;
+    struct h2_task_input *input;
+    struct h2_task_output *output;
+    
+    struct apr_thread_cond_t *io;   /* used to wait for events on */
+};
+
+/**
+ * The magic pointer value that indicates the head of a h2_task list
+ * @param  b The task list
+ * @return The magic pointer value
+ */
+#define H2_TASK_LIST_SENTINEL(b)	APR_RING_SENTINEL((b), h2_task, link)
+
+/**
+ * Determine if the task list is empty
+ * @param b The list to check
+ * @return true or false
+ */
+#define H2_TASK_LIST_EMPTY(b)	APR_RING_EMPTY((b), h2_task, link)
+
+/**
+ * Return the first task in a list
+ * @param b The list to query
+ * @return The first task in the list
+ */
+#define H2_TASK_LIST_FIRST(b)	APR_RING_FIRST(b)
+
+/**
+ * Return the last task in a list
+ * @param b The list to query
+ * @return The last task int he list
+ */
+#define H2_TASK_LIST_LAST(b)	APR_RING_LAST(b)
+
+/**
+ * Insert a single task at the front of a list
+ * @param b The list to add to
+ * @param e The task to insert
+ */
+#define H2_TASK_LIST_INSERT_HEAD(b, e) do {				\
+    h2_task *ap__b = (e);                                        \
+    APR_RING_INSERT_HEAD((b), ap__b, h2_task, link);	\
+} while (0)
+
+/**
+ * Insert a single task at the end of a list
+ * @param b The list to add to
+ * @param e The task to insert
+ */
+#define H2_TASK_LIST_INSERT_TAIL(b, e) do {				\
+    h2_task *ap__b = (e);					\
+    APR_RING_INSERT_TAIL((b), ap__b, h2_task, link);	\
+} while (0)
+
+/**
+ * Get the next task in the list
+ * @param e The current task
+ * @return The next task
+ */
+#define H2_TASK_NEXT(e)	APR_RING_NEXT((e), link)
+/**
+ * Get the previous task in the list
+ * @param e The current task
+ * @return The previous task
+ */
+#define H2_TASK_PREV(e)	APR_RING_PREV((e), link)
+
+/**
+ * Remove a task from its list
+ * @param e The task to remove
+ */
+#define H2_TASK_REMOVE(e)	APR_RING_REMOVE((e), link)
+
+
+h2_task *h2_task_create(long session_id, int stream_id, 
+                        apr_pool_t *pool, struct h2_mplx *mplx,
+                        conn_rec *c);
+
+apr_status_t h2_task_destroy(h2_task *task);
+
+void h2_task_set_request(h2_task *task, const char *method, const char *path, 
+                         const char *authority, apr_table_t *headers, int eos);
+
+
+apr_status_t h2_task_do(h2_task *task, struct h2_worker *worker);
+apr_status_t h2_task_process_request(h2_task_env *env);
+
+int h2_task_has_started(h2_task *task);
+void h2_task_set_started(h2_task *task);
+int h2_task_has_finished(h2_task *task);
+void h2_task_set_finished(h2_task *task);
+
+void h2_task_register_hooks(void);
+void h2_task_die(h2_task_env *env, int status, request_rec *r);
+
+#endif /* defined(__mod_h2__h2_task__) */

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,216 @@
+/* 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) {
+            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);
+            }
+            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,
+                          "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, 
+                                NULL, "task_input_read(exhaustive)");
+        }
+        else if (mode == AP_MODE_READBYTES) {
+            return h2_util_move(bb, input->bb, readbytes, 0, 
+                                NULL, "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,
+                          "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;
+}
+

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task_input.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,46 @@
+/* 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_task_input__
+#define __mod_h2__h2_task_input__
+
+/* h2_task_input places the HEADER+DATA, formatted in HTTP/1.1, into
+ * a bucket brigade. The brigade is setup as the input brigade for our
+ * pseudo httpd conn_rec that is handling a specific h2_task.
+ */
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task_env;
+
+typedef struct h2_task_input h2_task_input;
+struct h2_task_input {
+    struct h2_task_env *env;
+    apr_bucket_brigade *bb;
+};
+
+
+h2_task_input *h2_task_input_create(struct h2_task_env *env, apr_pool_t *pool,
+                                    apr_bucket_alloc_t *bucket_alloc);
+
+void h2_task_input_destroy(h2_task_input *input);
+
+apr_status_t h2_task_input_read(h2_task_input *input,
+                                  ap_filter_t* filter,
+                                  apr_bucket_brigade* brigade,
+                                  ap_input_mode_t mode,
+                                  apr_read_type_e block,
+                                  apr_off_t readbytes);
+
+#endif /* defined(__mod_h2__h2_task_input__) */

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,133 @@
+/* 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_thread_cond.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_from_h1.h"
+#include "h2_response.h"
+#include "h2_task_output.h"
+#include "h2_task.h"
+#include "h2_util.h"
+
+
+h2_task_output *h2_task_output_create(h2_task_env *env, apr_pool_t *pool,
+                                      apr_bucket_alloc_t *bucket_alloc)
+{
+    (void)bucket_alloc;
+    h2_task_output *output = apr_pcalloc(pool, sizeof(h2_task_output));
+    if (output) {
+        output->env = env;
+        output->state = H2_TASK_OUT_INIT;
+        output->from_h1 = h2_from_h1_create(env->stream_id, pool);
+        if (!output->from_h1) {
+            return NULL;
+        }
+    }
+    return output;
+}
+
+void h2_task_output_destroy(h2_task_output *output)
+{
+    if (output->from_h1) {
+        h2_from_h1_destroy(output->from_h1);
+        output->from_h1 = NULL;
+    }
+}
+
+static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f,
+                                   apr_bucket_brigade *bb)
+{
+    if (output->state == H2_TASK_OUT_INIT) {
+        output->state = H2_TASK_OUT_STARTED;
+        h2_response *response = h2_from_h1_get_response(output->from_h1);
+        if (!response) {
+            if (f) {
+                /* This happens currently when ap_die(status, r) is invoked
+                 * by a read request filter.
+                 */
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
+                              "h2_task_output(%s): write without response "
+                              "for %s %s %s",
+                              output->env->id, output->env->method, 
+                              output->env->authority, output->env->path);
+                f->c->aborted = 1;
+            }
+            if (output->env->io) {
+                apr_thread_cond_broadcast(output->env->io);
+            }
+            return APR_ECONNABORTED;
+        }
+        
+        return h2_mplx_out_open(output->env->mplx, output->env->stream_id, 
+                                response, f, bb, output->env->io);
+    }
+    return APR_EOF;
+}
+
+void h2_task_output_close(h2_task_output *output)
+{
+    open_if_needed(output, NULL, NULL);
+    if (output->state != H2_TASK_OUT_DONE) {
+        h2_mplx_out_close(output->env->mplx, output->env->stream_id);
+        output->state = H2_TASK_OUT_DONE;
+    }
+}
+
+int h2_task_output_has_started(h2_task_output *output)
+{
+    return output->state >= H2_TASK_OUT_STARTED;
+}
+
+/* Bring the data from the brigade (which represents the result of the
+ * request_rec out filter chain) into the h2_mplx for further sending
+ * on the master connection. 
+ */
+apr_status_t h2_task_output_write(h2_task_output *output,
+                                  ap_filter_t* f, apr_bucket_brigade* bb)
+{
+    if (APR_BRIGADE_EMPTY(bb)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                      "h2_task_output(%s): empty write", output->env->id);
+        return APR_SUCCESS;
+    }
+    
+    apr_status_t status = open_if_needed(output, f, bb);
+    if (status != APR_EOF) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                      "h2_task_output(%s): opened and passed brigade", 
+                      output->env->id);
+        return status;
+    }
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                  "h2_task_output(%s): write brigade", output->env->id);
+    return h2_mplx_out_write(output->env->mplx, output->env->stream_id, 
+                             f, bb, output->env->io);
+}
+
+void h2_task_output_die(h2_task_output *output, int status, request_rec *r)
+{
+    h2_from_h1_die(output->from_h1, status, r);
+}

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task_output.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,58 @@
+/* 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_task_output__
+#define __mod_h2__h2_task_output__
+
+/* h2_task_output reads a HTTP/1 response from the brigade and applies
+ * them to a h2_output_converter. The brigade is setup as the output brigade
+ * for our pseudo httpd conn_rec that is handling a specific h2_task.
+ * 
+ */
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task_env;
+struct h2_from_h1;
+
+typedef enum {
+    H2_TASK_OUT_INIT,
+    H2_TASK_OUT_STARTED,
+    H2_TASK_OUT_DONE,
+} h2_task_output_state_t;
+
+typedef struct h2_task_output h2_task_output;
+
+struct h2_task_output {
+    struct h2_task_env *env;
+    h2_task_output_state_t state;
+    struct h2_from_h1 *from_h1;
+};
+
+h2_task_output *h2_task_output_create(struct h2_task_env *env, apr_pool_t *pool,
+                                      apr_bucket_alloc_t *bucket_alloc);
+
+void h2_task_output_destroy(h2_task_output *output);
+
+apr_status_t h2_task_output_write(h2_task_output *output,
+                                  ap_filter_t* filter,
+                                  apr_bucket_brigade* brigade);
+
+void h2_task_output_close(h2_task_output *output);
+
+int h2_task_output_has_started(h2_task_output *output);
+
+void h2_task_output_die(h2_task_output *output, int status, request_rec *r);
+
+#endif /* defined(__mod_h2__h2_task_output__) */

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,88 @@
+/* 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 <httpd.h>
+#include <http_core.h>
+
+#include "h2_task.h"
+#include "h2_task_queue.h"
+
+
+h2_task_queue *h2_tq_create(long id, apr_pool_t *pool)
+{
+    h2_task_queue *q = apr_pcalloc(pool, sizeof(h2_task_queue));
+    if (q) {
+        q->id = id;
+        APR_RING_ELEM_INIT(q, link);
+        APR_RING_INIT(&q->tasks, h2_task, link);
+    }
+    return q;
+}
+
+void h2_tq_destroy(h2_task_queue *q)
+{
+    while (!H2_TASK_LIST_EMPTY(&q->tasks)) {
+        h2_task *task = H2_TASK_LIST_FIRST(&q->tasks);
+        H2_TASK_REMOVE(task);
+    }
+}
+
+static int in_list(h2_task_queue *q, h2_task *task)
+{
+    h2_task *e;
+    for (e = H2_TASK_LIST_FIRST(&q->tasks); 
+         e != H2_TASK_LIST_SENTINEL(&q->tasks);
+         e = H2_TASK_NEXT(e)) {
+        if (e == task) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int h2_tq_empty(h2_task_queue *q)
+{
+    return H2_TASK_LIST_EMPTY(&q->tasks);
+}
+
+void h2_tq_append(h2_task_queue *q, struct h2_task *task)
+{
+    H2_TASK_LIST_INSERT_TAIL(&q->tasks, task);
+}
+
+apr_status_t h2_tq_remove(h2_task_queue *q, struct h2_task *task)
+{
+    if (in_list(q, task)) {
+        H2_TASK_REMOVE(task);
+        return APR_SUCCESS;
+    }
+    return APR_NOTFOUND;
+}
+
+h2_task *h2_tq_pop_first(h2_task_queue *q)
+{
+    if (!H2_TASK_LIST_EMPTY(&q->tasks)) {
+        h2_task *task = H2_TASK_LIST_FIRST(&q->tasks);
+        H2_TASK_REMOVE(task);
+        return task;
+    }
+    return NULL;
+}
+
+
+

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_task_queue.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,148 @@
+/* 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_task_queue__
+#define __mod_h2__h2_task_queue__
+
+struct h2_task;
+
+/**
+ * A simple ring of rings that keeps a list of h2_tasks and can
+ * be ringed itself, using the APR RING macros.
+ */
+typedef struct h2_task_queue h2_task_queue;
+
+struct h2_task_queue {
+    APR_RING_ENTRY(h2_task_queue) link;
+    APR_RING_HEAD(h2_tasks, h2_task) tasks;
+    long id;
+};
+
+/**
+ * Allocate a new queue from the pool and initialize.
+ * @param id the identifier of the queue
+ * @param pool the memory pool
+ */
+h2_task_queue *h2_tq_create(long id, apr_pool_t *pool);
+
+/**
+ * Release all queue tasks.
+ * @param q the queue to destroy
+ */
+void h2_tq_destroy(h2_task_queue *q);
+
+/**
+ * Return != 0 iff there are no tasks in the queue.
+ * @param q the queue to check
+ */
+int h2_tq_empty(h2_task_queue *q);
+
+/**
+ * Append the task to the end of the queue.
+ * @param q the queue to append the task to
+ * @param task the task to append
+  */
+void h2_tq_append(h2_task_queue *q, struct h2_task *task);
+
+/**
+ * Remove a task from the queue. Return APR_SUCCESS if the task
+ * was indeed queued, APR_NOTFOUND otherwise.
+ * @param q the queue to remove from
+ * @param task the task to remove
+ */
+apr_status_t h2_tq_remove(h2_task_queue *q, struct h2_task *task);
+
+/**
+ * Get the first task from the queue or NULL if the queue is empty. The
+ * task will be removed.
+ * @param q the queue to pop the first task from
+ */
+h2_task *h2_tq_pop_first(h2_task_queue *q);
+
+/*******************************************************************************
+ * Queue Manipulation.
+ ******************************************************************************/
+
+/**
+ * The magic pointer value that indicates the head of a h2_task_queue list
+ * @param  b The queue list
+ * @return The magic pointer value
+ */
+#define H2_TQ_LIST_SENTINEL(b)	APR_RING_SENTINEL((b), h2_task_queue, link)
+
+/**
+ * Determine if the queue list is empty
+ * @param b The list to check
+ * @return true or false
+ */
+#define H2_TQ_LIST_EMPTY(b)	APR_RING_EMPTY((b), h2_task_queue, link)
+
+/**
+ * Return the first queue in a list
+ * @param b The list to query
+ * @return The first queue in the list
+ */
+#define H2_TQ_LIST_FIRST(b)	APR_RING_FIRST(b)
+
+/**
+ * Return the last queue in a list
+ * @param b The list to query
+ * @return The last queue int he list
+ */
+#define H2_TQ_LIST_LAST(b)	APR_RING_LAST(b)
+
+/**
+ * Insert a single queue at the front of a list
+ * @param b The list to add to
+ * @param e The queue to insert
+ */
+#define H2_TQ_LIST_INSERT_HEAD(b, e) do {				\
+h2_task_queue *ap__b = (e);                                        \
+APR_RING_INSERT_HEAD((b), ap__b, h2_task_queue, link);	\
+} while (0)
+
+/**
+ * Insert a single queue at the end of a list
+ * @param b The list to add to
+ * @param e The queue to insert
+ */
+#define H2_TQ_LIST_INSERT_TAIL(b, e) do {				\
+h2_task_queue *ap__b = (e);					\
+APR_RING_INSERT_TAIL((b), ap__b, h2_task_queue, link);	\
+} while (0)
+
+/**
+ * Get the next queue in the list
+ * @param e The current queue
+ * @return The next queue
+ */
+#define H2_TQ_NEXT(e)	APR_RING_NEXT((e), link)
+/**
+ * Get the previous queue in the list
+ * @param e The current queue
+ * @return The previous queue
+ */
+#define H2_TQ_PREV(e)	APR_RING_PREV((e), link)
+
+/**
+ * Remove a queue from its list
+ * @param e The queue to remove
+ */
+#define H2_TQ_REMOVE(e)	APR_RING_REMOVE((e), link)
+
+
+#define H2_TQ_EMPTY(e)	H2_TASK_LIST_EMPTY(&(e)->tasks)
+
+#endif /* defined(__mod_h2__h2_task_queue__) */

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,288 @@
+/* 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 <stdio.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+
+#include "h2_private.h"
+#include "h2_mplx.h"
+#include "h2_response.h"
+#include "h2_task.h"
+#include "h2_to_h1.h"
+#include "h2_util.h"
+
+
+h2_to_h1 *h2_to_h1_create(int stream_id, apr_pool_t *pool, 
+                          apr_bucket_alloc_t *bucket_alloc, 
+                          const char *method, const char *path,
+                          const char *authority, struct h2_mplx *m)
+{
+    if (!method) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
+                      "h2_to_h1: header start but :method missing");
+        return NULL;
+    }
+    if (!path) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
+                      "h2_to_h1: header start but :path missing");
+        return NULL;
+    }
+    
+    h2_to_h1 *to_h1 = apr_pcalloc(pool, sizeof(h2_to_h1));
+    if (to_h1) {
+        to_h1->stream_id = stream_id;
+        to_h1->pool = pool;
+        to_h1->method = method;
+        to_h1->path = path;
+        to_h1->authority = authority;
+        to_h1->m = m;
+        to_h1->headers = apr_table_make(to_h1->pool, 10);
+        to_h1->bb = apr_brigade_create(pool, bucket_alloc);
+        to_h1->chunked = 0; /* until we see a content-type and no length */
+        to_h1->content_len = -1;
+    }
+    return to_h1;
+}
+
+void h2_to_h1_destroy(h2_to_h1 *to_h1)
+{
+    to_h1->bb = NULL;
+}
+
+apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1,
+                                 const char *name, size_t nlen,
+                                 const char *value, size_t vlen)
+{
+    if (H2_HD_MATCH_LIT("transfer-encoding", name, nlen)) {
+        if (!apr_strnatcasecmp("chunked", value)) {
+            /* This should never arrive here in a HTTP/2 request */
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_BADARG, to_h1->m->c,
+                          "h2_to_h1: 'transfer-encoding: chunked' received");
+            return APR_BADARG;
+        }
+    }
+    else if (H2_HD_MATCH_LIT("content-length", name, nlen)) {
+        char *end;
+        to_h1->content_len = apr_strtoi64(value, &end, 10);
+        if (value == end) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, to_h1->m->c,
+                          "h2_request(%d): content-length value not parsed: %s",
+                          to_h1->stream_id, value);
+            return APR_EINVAL;
+        }
+        to_h1->remain_len = to_h1->content_len;
+        to_h1->chunked = 0;
+    }
+    else if (H2_HD_MATCH_LIT("content-type", name, nlen)) {
+        /* If we see a content-type and have no length (yet),
+         * we need to chunk. */
+        to_h1->chunked = (to_h1->content_len == -1);
+    }
+    else if ((to_h1->seen_host && H2_HD_MATCH_LIT("host", name, nlen))
+             || H2_HD_MATCH_LIT("expect", name, nlen)
+             || H2_HD_MATCH_LIT("upgrade", name, nlen)
+             || H2_HD_MATCH_LIT("connection", name, nlen)
+             || H2_HD_MATCH_LIT("proxy-connection", name, nlen)
+             || H2_HD_MATCH_LIT("keep-alive", name, nlen)
+             || H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
+        // ignore these.
+        return APR_SUCCESS;
+    }
+    else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
+        const char *existing = apr_table_get(to_h1->headers, "cookie");
+        if (existing) {
+            /* Cookie headers come separately in HTTP/2, but need
+             * to be merged by "; " (instead of default ", ")
+             */
+            char *hvalue = apr_pstrndup(to_h1->pool, value, vlen);
+            char *nval = apr_psprintf(to_h1->pool, "%s; %s", existing, hvalue);
+            apr_table_setn(to_h1->headers, "Cookie", nval);
+            return APR_SUCCESS;
+        }
+    }
+    else if (H2_HD_MATCH_LIT("host", name, nlen)) {
+        to_h1->seen_host = 1;
+    }
+    
+    char *hname = apr_pstrndup(to_h1->pool, name, nlen);
+    char *hvalue = apr_pstrndup(to_h1->pool, value, vlen);
+    h2_util_camel_case_header(hname, nlen);
+    apr_table_mergen(to_h1->headers, hname, hvalue);
+    
+    return APR_SUCCESS;
+}
+
+static int set_header(void *ctx, const char *key, const char *value)
+{
+    h2_to_h1 *to_h1 = (h2_to_h1*)ctx;
+    h2_to_h1_add_header(to_h1, key, strlen(key), value, strlen(value));
+    return 1;
+}
+
+apr_status_t h2_to_h1_add_headers(h2_to_h1 *to_h1, apr_table_t *headers)
+{
+    apr_table_do(set_header, to_h1, headers, NULL);
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_to_h1_end_headers(h2_to_h1 *to_h1, h2_task *task, int eos)
+{
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, to_h1->m->c,
+                  "h2_to_h1(%ld-%d): end headers", 
+                  to_h1->m->id, to_h1->stream_id);
+    
+    if (to_h1->eoh) {
+        return APR_EINVAL;
+    }
+    
+    if (!to_h1->seen_host) {
+        /* Need to add a "Host" header if not already there to
+         * make virtual hosts work correctly. */
+        if (!to_h1->authority) {
+            return APR_BADARG;
+        }
+        apr_table_set(to_h1->headers, "Host", to_h1->authority);
+    }
+
+    if (eos && to_h1->chunked) {
+        /* We had chunking figured out, but the EOS is already there.
+         * unmark chunking and set a definitive content-length.
+         */
+        to_h1->chunked = 0;
+        apr_table_setn(to_h1->headers, "Content-Length", "0");
+    }
+    else if (to_h1->chunked) {
+        /* We have not seen a content-length. We therefore must
+         * pass any request content in chunked form.
+         */
+        apr_table_mergen(to_h1->headers, "Transfer-Encoding", "chunked");
+    }
+    
+    h2_task_set_request(task, to_h1->method, to_h1->path, 
+                        to_h1->authority, to_h1->headers, eos);
+    to_h1->eoh = 1;
+    
+    if (eos) {
+        apr_status_t status = h2_to_h1_close(to_h1);
+        if (status != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, to_h1->m->c,
+                          "h2_to_h1(%ld-%d): end headers, eos=%d", 
+                          to_h1->m->id, to_h1->stream_id, eos);
+        }
+        return status;
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t flush(apr_bucket_brigade *bb, void *ctx) 
+{
+    (void)bb;
+    return h2_to_h1_flush((h2_to_h1*)ctx);
+}
+
+static apr_status_t h2_to_h1_add_data_raw(h2_to_h1 *to_h1,
+                                          const char *data, size_t len)
+{
+    apr_status_t status = APR_SUCCESS;
+
+    if (to_h1->eos || !to_h1->eoh) {
+        return APR_EINVAL;
+    }
+    
+    status = apr_brigade_write(to_h1->bb, flush, to_h1, data, len);
+    if (status == APR_SUCCESS) {
+        status = h2_to_h1_flush(to_h1);
+    }
+    return status;
+}
+
+
+apr_status_t h2_to_h1_add_data(h2_to_h1 *to_h1,
+                               const char *data, size_t len)
+{
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, to_h1->m->c,
+                  "h2_to_h1(%ld-%d): add %ld data bytes", 
+                  to_h1->m->id, to_h1->stream_id, (long)len);
+    
+    if (to_h1->chunked) {
+        /* if input may have a body and we have not seen any
+         * content-length header, we need to chunk the input data.
+         */
+        apr_status_t status = apr_brigade_printf(to_h1->bb, NULL, NULL,
+                                                 "%lx\r\n", (unsigned long)len);
+        if (status == APR_SUCCESS) {
+            status = h2_to_h1_add_data_raw(to_h1, data, len);
+            if (status == APR_SUCCESS) {
+                status = apr_brigade_puts(to_h1->bb, NULL, NULL, "\r\n");
+            }
+        }
+        return status;
+    }
+    else {
+        to_h1->remain_len -= len;
+        if (to_h1->remain_len < 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, to_h1->m->c,
+                          "h2_to_h1(%ld-%d): got %ld more content bytes than announced "
+                          "in content-length header: %ld", 
+                          to_h1->m->id, to_h1->stream_id, 
+                          (long)to_h1->content_len, -(long)to_h1->remain_len);
+        }
+        return h2_to_h1_add_data_raw(to_h1, data, len);
+    }
+}
+
+apr_status_t h2_to_h1_flush(h2_to_h1 *to_h1)
+{
+    apr_status_t status = APR_SUCCESS;
+    if (!APR_BRIGADE_EMPTY(to_h1->bb)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, to_h1->m->c,
+                      "h2_to_h1(%ld-%d): flush request bytes", 
+                      to_h1->m->id, to_h1->stream_id);
+        
+        status = h2_mplx_in_write(to_h1->m, to_h1->stream_id, to_h1->bb);
+        if (status != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, status, to_h1->m->c,
+                          "h2_request(%d): pushing request data",
+                          to_h1->stream_id);
+        }
+    }
+    return status;
+}
+
+apr_status_t h2_to_h1_close(h2_to_h1 *to_h1)
+{
+    apr_status_t status = APR_SUCCESS;
+    if (!to_h1->eos) {
+        if (to_h1->chunked) {
+            status = h2_to_h1_add_data_raw(to_h1, "0\r\n\r\n", 5);
+        }
+        to_h1->eos = 1;
+        status = h2_to_h1_flush(to_h1);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, to_h1->m->c,
+                      "h2_to_h1(%d): close", to_h1->stream_id);
+        
+        status = h2_mplx_in_close(to_h1->m, to_h1->stream_id);
+    }
+    return status;
+}
+
+

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_to_h1.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,83 @@
+/* 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_to_h1__
+#define __mod_h2__h2_to_h1__
+
+struct h2_mplx;
+struct h2_task;
+typedef struct h2_to_h1 h2_to_h1;
+
+struct h2_to_h1 {
+    int stream_id;
+    apr_pool_t *pool;
+    h2_mplx *m;
+
+    const char *method;
+    const char *path;
+    const char *authority;
+    
+    int chunked;
+    int eoh;
+    int eos;
+    int flushed;
+    int seen_host;
+    
+    apr_off_t content_len;
+    apr_off_t remain_len;
+    apr_table_t *headers;
+    apr_bucket_brigade *bb;
+};
+
+/* Create a converter from a HTTP/2 request to a serialzation in
+ * HTTP/1.1 format. The serialized data will be written onto the
+ * given h2_mplx instance.
+ */
+h2_to_h1 *h2_to_h1_create(int stream_id, apr_pool_t *pool, 
+                          apr_bucket_alloc_t *bucket_alloc, 
+                          const char *method, const char *path,
+                          const char *authority, struct h2_mplx *m);
+
+/* Destroy the converter and free resources. */
+void h2_to_h1_destroy(h2_to_h1 *to_h1);
+
+/* Add a header to the serialization. Only valid to call after start
+ * and before end_headers.
+ */
+apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1,
+                                 const char *name, size_t nlen,
+                                 const char *value, size_t vlen);
+
+apr_status_t h2_to_h1_add_headers(h2_to_h1 *to_h1, apr_table_t *headers);
+
+/** End the request headers.
+ */
+apr_status_t h2_to_h1_end_headers(h2_to_h1 *to_h1, 
+                                  struct h2_task *task, int eos);
+
+/* Add request body data.
+ */
+apr_status_t h2_to_h1_add_data(h2_to_h1 *to_h1,
+                               const char *data, size_t len);
+
+/* Flush the converted data onto the h2_mplx instance.
+ */
+apr_status_t h2_to_h1_flush(h2_to_h1 *to_h1);
+
+/* Close the request, flushed automatically.
+ */
+apr_status_t h2_to_h1_close(h2_to_h1 *to_h1);
+
+#endif /* defined(__mod_h2__h2_to_h1__) */

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,199 @@
+/* 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_optional.h>
+#include <apr_optional_hooks.h>
+
+#include <ap_mpm.h>
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_connection.h>
+#include <http_log.h>
+#include <http_protocol.h>
+#include <http_request.h>
+
+#include "h2_private.h"
+#include "h2_conn.h"
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_h2.h"
+#include "h2_upgrade.h"
+#include "h2_util.h"
+
+static int h2_upgrade_request_handler(request_rec *r);
+static const char *h2_get_upgrade_proto(request_rec *r);
+static int h2_upgrade_to(request_rec *r, const char *proto);
+static int h2_upgrade_options(request_rec *r);
+
+void h2_upgrade_register_hooks(void)
+{
+    ap_hook_handler(h2_upgrade_request_handler, NULL, NULL, APR_HOOK_FIRST - 1);
+    ap_hook_map_to_storage(h2_upgrade_options, NULL, NULL, APR_HOOK_FIRST);
+}
+
+static int h2_upgrade_options(request_rec *r)
+{
+    if ((r->method_number == M_OPTIONS) && r->uri && (r->uri[0] == '*') &&
+        (r->uri[1] == '\0')) {
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+                      "h2c: request OPTIONS * seen");
+        return h2_upgrade_request_handler(r);
+    }
+    return DECLINED;
+}
+
+static int h2_upgrade_request_handler(request_rec *r)
+{
+    h2_ctx *ctx = h2_ctx_rget(r);
+    h2_config *cfg = h2_config_rget(r);
+    int enabled_for_request = h2_config_geti(cfg, H2_CONF_ENABLED);
+    
+    if (h2_ctx_is_task(ctx) || h2_ctx_is_active(ctx)) {
+        /* talking h2 already, either task for main conn */
+        if (!enabled_for_request) {
+            /* we have a request for a server (vhost) where h2 is
+             * not enabled. This happened over a connection on which
+             * we talk h2.
+             * Tell the client, she should open a new connection to that
+             * vhost to get fresh protocol negotiations.
+             */
+            r->status = 421;
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r->status, r,
+                          "421-ing h2 request to host %s", r->hostname);
+            return DONE;
+        }
+        return DECLINED;
+    }
+    
+    /* not talking h2 (yet) */
+    if (enabled_for_request) {
+        /* Check for the start of an h2c Upgrade dance. */
+        const char *proto = h2_get_upgrade_proto(r);
+        if (proto) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "seeing %s upgrade invitation", proto);
+            /* We do not handle upgradeable requests with a body.
+             * The reason being that we would need to read the body in full
+             * before we ca use HTTP2 frames on the wire.
+             * 
+             * This seems to be consensus among server implemntations and
+             * clients are advised to use an "OPTIONS *" before a POST.
+             */
+            const char *clen = apr_table_get(r->headers_in, "Content-Length");
+            if (clen && strcmp(clen, "0")) {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                              "upgrade with content-length: %s, declined", clen);
+                return DECLINED;
+            }
+            return h2_upgrade_to(r, proto);
+        }
+    }
+    
+    return DECLINED;
+}
+
+static const char *h2_get_upgrade_proto(request_rec *r)
+{
+    const char *proto, *conn;
+    const char *upgrade = apr_table_get(r->headers_in, "Upgrade");
+    
+    if (upgrade && *upgrade) {
+        
+        conn = apr_table_get(r->headers_in, "Connection");
+        if (h2_util_contains_token(r->pool, conn, "Upgrade")
+            && apr_table_get(r->headers_in, "HTTP2-Settings")) {
+            
+            /* HTTP/1 Upgrade: is just another mechanism to switch
+             * protocols on a connection, same as ALPN or NPN.
+             * Security desirability aside, the bit protocol spoken
+             * afterwards is the same. Why require different identifier?
+             *
+             * We allow the same tokens as in ALPN negotiation, plus the
+             * special 'c' variants that RFC 7540 defines. We just do not
+             * care about the transport here.
+             */
+            proto = h2_util_first_token_match(r->pool, upgrade, 
+                                              h2_alpn_protos, 
+                                              h2_alpn_protos_len);
+            if (!proto) {
+                proto = h2_util_first_token_match(r->pool, upgrade, 
+                                                  h2_upgrade_protos, 
+                                                  h2_upgrade_protos_len);
+            }
+            
+            if (proto) {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                              "suiteable upgrade detected: %s %s, "
+                              "Upgrade: %s", r->method, r->uri, upgrade);
+                return proto;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static int h2_upgrade_to(request_rec *r, const char *proto)
+{
+    conn_rec *c = r->connection;
+    h2_ctx *ctx = h2_ctx_rget(r);
+    
+    h2_ctx_pnego_set_done(ctx, proto);
+    
+    /* Let the client know what we are upgrading to. */
+    apr_table_clear(r->headers_out);
+    apr_table_setn(r->headers_out, "Upgrade", proto);
+    apr_table_setn(r->headers_out, "Connection", "Upgrade");
+    
+    r->status = HTTP_SWITCHING_PROTOCOLS;
+    r->status_line = ap_get_status_line(r->status);
+    ap_send_interim_response(r, 1);
+    
+    /* Make sure the core filter that parses http1 requests does
+     * not mess with our http2 frames. */
+    if (APLOGrtrace2(r)) {
+        ap_filter_t *filter = r->input_filters;
+        while (filter) {
+            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+                          "h2_conn(%ld), has request filter %s",
+                          r->connection->id, filter->frec->name);
+            filter = filter->next;
+        }
+    }
+    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. */
+    apr_status_t 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");
+    }
+    
+    /* make sure httpd closes the connection after this */
+    c->keepalive = AP_CONN_CLOSE;
+    ap_lingering_close(c);
+    
+    if (c->sbh) {
+        ap_update_child_status_from_conn(c->sbh, SERVER_CLOSING, c);
+    }
+
+    return DONE;
+}
+

Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_upgrade.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,24 @@
+/* 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_upgrade__
+#define __mod_h2__h2_upgrade__
+
+/* Specific function to HTTP/1 connection Upgradeds.
+ */
+void h2_upgrade_register_hooks(void);
+
+
+#endif /* defined(__mod_h2__h2_upgrade__) */



Mime
View raw message