Index: modules/protocols
===================================================================
--- modules/protocols	(revision 0)
+++ modules/protocols	(working copy)

Property changes on: modules/protocols
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,23 ##
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.x
+Debug
+Release
+*.plg
+*.aps
+*.dep
+*.mak
+*.rc
+BuildLog.htm
+*.stc
+*.stt
+*.sto
+*.vcproj
+*.vcproj.*
+
Index: modules/protocols/Makefile.in
===================================================================
--- modules/protocols/Makefile.in	(revision 0)
+++ modules/protocols/Makefile.in	(working copy)
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
Index: modules/protocols/config.m4
===================================================================
--- modules/protocols/config.m4	(revision 0)
+++ modules/protocols/config.m4	(working copy)
@@ -0,0 +1,8 @@
+dnl modules enabled in this directory by default
+
+APACHE_MODPATH_INIT(protocols)
+
+APACHE_MODULE(tcp, TCP protocol support, , , most)
+
+APACHE_MODPATH_FINISH
+
Index: modules/protocols/mod_tcp.c
===================================================================
--- modules/protocols/mod_tcp.c	(revision 0)
+++ modules/protocols/mod_tcp.c	(working copy)
@@ -0,0 +1,368 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 "httpd.h"
+#include "http_connection.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "ap_mpm.h"
+
+module AP_MODULE_DECLARE_DATA tcp_module;
+
+/*
+ * mod_tcp.c
+ *
+ * A module that accepts raw TCP connections, in the expectation that the
+ * connection will be handled by mod_proxy_tcp.
+ *
+ * When SNI is available, the target host can be used to select the given
+ * backend.
+ *
+ *  Created on: Feb 26, 2016
+ *      Author: minfrin
+ */
+
+typedef struct {
+    unsigned int is_enabled_set:1;
+    int is_enabled;
+    int is_sni;
+} tcp_server_conf;
+
+typedef enum {
+    TCP_STATE_INIT_REQUEST = 0,
+    TCP_STATE_READ_BLANK_LINE,
+    TCP_STATE_POST_READ_REQUEST,
+    TCP_STATE_PROCESS_REQUEST,
+    TCP_STATE_COMPLETION,
+} tcp_state_e;
+
+typedef struct {
+    request_rec *r;
+    apr_bucket_brigade *bb;
+    tcp_state_e state;
+} tcp_conn_conf;
+
+static int tcp_method = M_INVALID;
+
+/**
+ * Frontend Handling
+ * -----------------
+ *
+ * We handle connections from the frontend to the proxy, consisting
+ * of a simple tunnel.
+ */
+
+static tcp_conn_conf *tcp_init_connection_ctx(conn_rec *c)
+{
+    tcp_conn_conf *cconf = ap_get_module_config(c->conn_config, &tcp_module);
+
+    if (cconf) {
+        return cconf;
+    }
+
+    cconf = apr_pcalloc(c->pool, sizeof(*cconf));
+
+    ap_set_module_config(c->conn_config, &tcp_module, cconf);
+
+    return cconf;
+}
+
+static int tcp_pre_connection(conn_rec *c, void *csd)
+{
+
+    tcp_server_conf *tconf = ap_get_module_config(c->base_server->module_config,
+            &tcp_module);
+
+    /* are we tcp? */
+    if (!tconf->is_enabled) {
+        return DECLINED;
+    }
+
+    tcp_init_connection_ctx(c);
+
+    apr_socket_timeout_set(csd, c->base_server->keep_alive_timeout);
+
+    return OK;
+}
+
+static int tcp_process_connection(conn_rec* conn)
+{
+
+    tcp_server_conf *tconf =
+            ap_get_module_config(conn->base_server->module_config,
+                    &tcp_module);
+
+    /* are we tcp? */
+    if (!tconf->is_enabled) {
+        return DECLINED;
+    }
+
+    else {
+        request_rec *r;
+        apr_status_t rv;
+        int access_status;
+
+        tcp_conn_conf *cconf =
+                ap_get_module_config(conn->conn_config, &tcp_module);
+        r = cconf->r;
+
+        /*
+         * tcp_process_connection() is designed to be reentrant and work
+         * correctly in an asynchronous mode of operation.
+         */
+
+        switch (cconf->state) {
+
+        /* initialise our request */
+        case TCP_STATE_INIT_REQUEST:
+
+            conn->keepalive = AP_CONN_CLOSE;
+
+            if (tconf->is_sni) {
+                if (!ap_add_input_filter("SSL_TCP_SNI", NULL, r, conn)) {
+                    ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, APLOGNO()
+                                  "TCP with ServerName/ServerAlias enabled, but mod_ssl_tcp not present");
+
+                    conn->aborted = 1;
+
+                    return OK;
+                }
+            }
+
+            r = cconf->r = ap_create_request(conn);
+
+            /*
+             * Workaround: the http_create_request() function doesn't check
+             * the protocol, and adds HTTP filters in all cases incorrectly.
+             * Until this is fixed, work around the problem by actively
+             * removing the HTTP filters.
+             */
+            ap_remove_output_filter_byhandle(r->output_filters, "HTTP_IN");
+            ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
+            ap_remove_output_filter_byhandle(r->output_filters, "CHUNK");
+            ap_remove_output_filter_byhandle(r->output_filters,
+                    "HTTP_OUTERROR");
+            ap_remove_output_filter_byhandle(r->output_filters, "BYTERANGE");
+
+            ap_run_pre_read_request(r, conn);
+
+            /* set the request line */
+            r->the_request = "TCP /";
+            r->request_time = apr_time_now();
+            r->protocol = "TCP";
+            r->method = "TCP";
+            r->method_number = tcp_method;
+            ap_parse_uri(r, "/");
+
+            /* prepare for reading the blank line */
+            cconf->bb = apr_brigade_create(conn->pool, conn->bucket_alloc);
+            cconf->state = TCP_STATE_READ_BLANK_LINE;
+
+        /* read the blank line (no break) */
+        case TCP_STATE_READ_BLANK_LINE:
+
+            /* read a blank line to trigger SSL */
+
+            /* Note: we're forced to do a blocking read here because the
+             * core presently suppresses APR_EAGAIN, and as a result we
+             * can't tell the difference between "call me again, mod_ssl
+             * wants to handshake" or "nothing to see here".
+             *
+             * When APR_EAGAIN is supported we can be fully async here.
+             */
+
+            ap_update_child_status(conn->sbh, SERVER_BUSY_READ, r);
+
+            rv = ap_get_brigade(r->input_filters, cconf->bb, AP_MODE_INIT,
+                                APR_BLOCK_READ, 0);
+            if (APR_STATUS_IS_EAGAIN(rv)) {
+                /* coming around again */
+                return OK;
+            }
+            else if (APR_SUCCESS != rv) {
+
+                if (APR_STATUS_IS_TIMEUP(rv)) {
+                    access_status = HTTP_REQUEST_TIME_OUT;
+                }
+                else {
+                    access_status = HTTP_BAD_REQUEST;
+                }
+
+                ap_finalize_request_protocol(r);
+                ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+                ap_run_log_transaction(r);
+                cconf->r = NULL;
+
+                return OK;
+            }
+
+            /* prepare for running post read request */
+            apr_brigade_destroy(cconf->bb);
+            cconf->state = TCP_STATE_POST_READ_REQUEST;
+
+        /* after reading the request (no break) */
+        case TCP_STATE_POST_READ_REQUEST:
+
+            /* we may have switched to another server */
+            r->server = conn->base_server;
+            r->per_dir_config = r->server->lookup_defaults;
+
+            if ((access_status = ap_run_post_read_request(r))) {
+                ap_finalize_request_protocol(r);
+                ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+                ap_run_log_transaction(r);
+                r = NULL;
+
+                return OK;
+            }
+
+            /* prepare for running process request */
+            cconf->state = TCP_STATE_PROCESS_REQUEST;
+
+        /* process the request (no break) */
+        case TCP_STATE_PROCESS_REQUEST:
+
+            ap_update_child_status(conn->sbh, SERVER_BUSY_WRITE, r);
+
+            /* FIXME: ap_process_request() is void and can only be run once
+             * Make a ap_process_request_ex() that has the ability to be
+             * called again.
+             */
+            ap_process_request(r);
+
+            /* indicate that we're complete at this point */
+            cconf->state = TCP_STATE_COMPLETION;
+            return OK;
+
+        /* if we're done, we leave (no break) */
+        case TCP_STATE_COMPLETION:
+
+            /* if we're still being called at this point, our connection is done*/
+            conn->aborted = 1;
+
+        /* drop through (no break) */
+        default: {
+            break;
+        }
+        }
+
+    }
+
+    return OK;
+}
+
+
+/**
+ * Configuration
+ * -------------
+ *
+ */
+
+static void *create_tcp_config(apr_pool_t *p, server_rec *s)
+{
+    tcp_server_conf *new = apr_pcalloc(p, sizeof(tcp_server_conf));
+
+    return new;
+}
+
+static void *merge_tcp_config(apr_pool_t *p, void *basev, void *overridesv)
+{
+    tcp_server_conf *ps = apr_pcalloc(p, sizeof(tcp_server_conf));
+    tcp_server_conf *base = (tcp_server_conf *) basev;
+    tcp_server_conf *overrides = (tcp_server_conf *) overridesv;
+
+    ps->is_enabled =
+            (overrides->is_enabled_set == 0) ? base->is_enabled :
+                    overrides->is_enabled;
+    ps->is_enabled_set = overrides->is_enabled_set || base->is_enabled_set;
+
+    return ps;
+}
+
+static int tcp_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
+                       server_rec *base_server)
+{
+    server_rec *s;
+
+    tcp_server_conf *bconf = ap_get_module_config(base_server->module_config,
+            &tcp_module);
+
+    for (s = base_server; s; s = s->next) {
+        const char *protocol;
+
+        tcp_server_conf *conf = ap_get_module_config(s->module_config,
+                &tcp_module);
+
+        /* Default to enabled if Protocol is tcp */
+        if ((protocol = ap_get_server_protocol(s))) {
+
+            if (!strcmp("tcp", protocol)) {
+                conf->is_enabled = 1;
+            }
+            else if (!strcmp("tlsext", protocol)) {
+
+                /* Turn on the SNI filter if TCP and ServerName/ServerAlias */
+                if (s->server_hostname || s->names->nelts
+                        || s->wild_names->nelts) {
+                    conf->is_enabled = 1;
+                    bconf->is_sni = 1;
+                }
+                else {
+                    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO()
+                                 "mod_tcp: Protocol 'tlsext' specified in "
+                                 "VirtualHost, but ServerName/ServerAlias not present");
+                    return HTTP_INTERNAL_SERVER_ERROR;
+                }
+            }
+
+        }
+
+    }
+
+    tcp_method = ap_method_register(p, "TCP");
+
+    return OK;
+}
+
+static const command_rec tcp_cmds[] =
+{
+    {NULL}
+};
+
+static void ap_tcp_register_hook(apr_pool_t *p)
+{
+    static const char *const aszSucc[] = { "mod_ssl.c", NULL};
+
+    ap_hook_post_config(tcp_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+
+    ap_hook_pre_connection(tcp_pre_connection,NULL,NULL, APR_HOOK_MIDDLE);
+    /* after mod_ssl, but before http */
+    ap_hook_process_connection(tcp_process_connection,
+                               NULL, aszSucc, APR_HOOK_MIDDLE);
+
+}
+
+AP_DECLARE_MODULE(tcp) = {
+    STANDARD20_MODULE_STUFF,
+    NULL,                       /* create per-directory config structure */
+    NULL,                       /* merge per-directory config structures */
+    create_tcp_config,          /* create per-server config structure */
+    merge_tcp_config,           /* merge per-server config structures */
+    tcp_cmds,                   /* command apr_table_t */
+    ap_tcp_register_hook        /* register hooks */
+};
Index: modules/proxy/config.m4
===================================================================
--- modules/proxy/config.m4	(revision 1735175)
+++ modules/proxy/config.m4	(working copy)
@@ -25,6 +25,7 @@
 proxy_fdpass_objs="mod_proxy_fdpass.lo"
 proxy_ajp_objs="mod_proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo ajp_utils.lo"
 proxy_wstunnel_objs="mod_proxy_wstunnel.lo"
+proxy_tcp_objs="mod_proxy_tcp.lo"
 proxy_balancer_objs="mod_proxy_balancer.lo"
 
 case "$host" in
@@ -39,6 +40,7 @@
     proxy_fdpass_objs="$proxy_fdpass_objs mod_proxy.la"
     proxy_ajp_objs="$proxy_ajp_objs mod_proxy.la"
     proxy_wstunnel_objs="$proxy_wstunnel_objs mod_proxy.la"
+    proxy_tcp_objs="$proxy_tcp_objs mod_proxy.la"
     proxy_balancer_objs="$proxy_balancer_objs mod_proxy.la"
     ;;
 esac
@@ -58,6 +60,7 @@
     enable_proxy_fdpass=no
   fi
 ],proxy)
+APACHE_MODULE(proxy_tcp, Apache proxy TCP Tunnel module.  Requires and is enabled by --enable-proxy., $proxy_tcp_objs, , $proxy_mods_enable,, proxy)
 APACHE_MODULE(proxy_wstunnel, Apache proxy Websocket Tunnel module.  Requires and is enabled by --enable-proxy., $proxy_wstunnel_objs, , $proxy_mods_enable,, proxy)
 APACHE_MODULE(proxy_ajp, Apache proxy AJP module.  Requires and is enabled by --enable-proxy., $proxy_ajp_objs, , $proxy_mods_enable,, proxy)
 APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module.  Requires and is enabled by --enable-proxy., $proxy_balancer_objs, , $proxy_mods_enable,, proxy)
Index: modules/proxy/mod_proxy_tcp.c
===================================================================
--- modules/proxy/mod_proxy_tcp.c	(revision 0)
+++ modules/proxy/mod_proxy_tcp.c	(working copy)
@@ -0,0 +1,592 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 "mod_proxy.h"
+#include "ap_mpm.h"
+
+module AP_MODULE_DECLARE_DATA proxy_tcp_module;
+
+/*
+ * mod_proxy_tcp.c
+ *
+ * A module for mod_proxy that creates a TCP tunnel towards a backend endpoint,
+ * with or without SSL.
+ *
+ * When SNI is available, the target host can be used to select the given
+ * backend.
+ *
+ *  Created on: Feb 26, 2016
+ *      Author: minfrin
+ */
+
+typedef struct {
+    unsigned int idle_timeout_set:1;
+    unsigned int async_delay_set:1;
+    int can_poll;
+    apr_time_t idle_timeout;
+    apr_time_t async_delay;
+} proxy_tcp_server_conf;
+
+typedef struct tcp_baton_t {
+    request_rec *r;
+    proxy_conn_rec *proxy_connrec;
+    apr_socket_t *server_soc;
+    apr_socket_t *client_soc;
+    apr_pollset_t *pollset;
+    apr_bucket_brigade *bb_i;
+    apr_bucket_brigade *bb_o;
+    apr_pool_t *subpool;        /* cleared before each suspend, destroyed when request ends */
+    char *scheme;               /* required to release the proxy connection */
+} tcp_baton_t;
+
+static ap_filter_rec_t *proxy_tcp_output_filter_handle;
+
+
+/**
+ * Backend Handling
+ * ----------------
+ *
+ * We handle connections from the proxy to the backend, consisting
+ * of a simple tunnel.
+ */
+
+static void proxy_tcp_callback(void *b);
+
+static apr_status_t proxy_tcp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+
+    return APR_SUCCESS;
+}
+
+static int proxy_tcp_pump(tcp_baton_t *baton, apr_time_t timeout, int try_async) {
+    request_rec *r = baton->r;
+    conn_rec *c = r->connection;
+    proxy_conn_rec *conn = baton->proxy_connrec;
+    apr_socket_t *sock = conn->sock;
+    conn_rec *backconn = conn->connection;
+    const apr_pollfd_t *signalled;
+    apr_int32_t pollcnt, pi;
+    apr_int16_t pollevent;
+    apr_pollset_t *pollset = baton->pollset;
+    apr_socket_t *client_socket = baton->client_soc;
+    apr_status_t rv;
+    apr_bucket_brigade *bb_i = baton->bb_i;
+    apr_bucket_brigade *bb_o = baton->bb_o;
+    int done = 0, replied = 0;
+
+    do {
+        int loop = 0;
+
+        /* first, do we have any pending data? read it before trying to block */
+        if (ap_filter_input_pending(backconn) == OK) {
+            done |= ap_proxy_transfer_between_connections(r, backconn, c, bb_i,
+                    bb_o, "sock", NULL, AP_IOBUFSIZE, 0) != APR_SUCCESS;
+            loop = 1;
+        }
+        if (ap_filter_input_pending(c) == OK) {
+            done |= ap_proxy_transfer_between_connections(r, c, backconn, bb_i,
+                    bb_o, "client", NULL, AP_IOBUFSIZE, 0) != APR_SUCCESS;
+            loop = 1;
+        }
+        if (loop) {
+            continue;
+        }
+
+        /* poll for read in either direction */
+        rv = apr_pollset_poll(pollset, timeout, &pollcnt, &signalled);
+        if (rv != APR_SUCCESS) {
+            if (APR_STATUS_IS_EINTR(rv)) {
+                continue;
+            }
+            else if (APR_STATUS_IS_TIMEUP(rv)) {
+                if (try_async) {
+                    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO() "Attempting to go async");
+                    return SUSPENDED;
+                }
+                else {
+                    return HTTP_REQUEST_TIME_OUT;
+                }
+            }
+            else {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO() "error apr_poll()");
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+        }
+
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO()
+                "woke from poll(), i=%d", pollcnt);
+
+        for (pi = 0; pi < pollcnt; pi++) {
+            const apr_pollfd_t *cur = &signalled[pi];
+
+            if (cur->desc.s == sock) {
+                pollevent = cur->rtnevents;
+                if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
+                    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02446)
+                            "sock was readable");
+                    done |= ap_proxy_transfer_between_connections(r, backconn,
+                            c, bb_i, bb_o, "sock", NULL, AP_IOBUFSIZE, 0)
+                            != APR_SUCCESS;
+                }
+                else if (pollevent & APR_POLLERR) {
+                    ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447)
+                            "error on backconn");
+                    backconn->aborted = 1;
+                    done = 1;
+                }
+                else {
+                    ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02605)
+                            "unknown event on backconn %d", pollevent);
+                    done = 1;
+                }
+            }
+            else if (cur->desc.s == client_socket) {
+                pollevent = cur->rtnevents;
+                if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
+                    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02448)
+                            "client was readable");
+                    done |= ap_proxy_transfer_between_connections(r, c,
+                            backconn, bb_o, bb_i, "client", &replied,
+                            AP_IOBUFSIZE, 0) != APR_SUCCESS;
+                }
+                else if (pollevent & APR_POLLERR) {
+                    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02607)
+                            "error on client conn");
+                    c->aborted = 1;
+                    done = 1;
+                }
+                else {
+                    ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02606)
+                            "unknown event on client conn %d", pollevent);
+                    done = 1;
+                }
+            }
+            else {
+                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02449)
+                        "unknown socket in pollset");
+                done = 1;
+            }
+
+        }
+    } while (!done);
+
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+            "finished with poll() - cleaning up");
+
+    if (!replied) {
+        return HTTP_BAD_GATEWAY;
+    }
+    else {
+        return OK;
+    }
+}
+
+static void proxy_tcp_finish(tcp_baton_t *baton) {
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, baton->r, "proxy_tcp_finish");
+    baton->proxy_connrec->close = 1; /* new handshake expected on each back-conn */
+    baton->r->connection->keepalive = AP_CONN_CLOSE;
+    ap_proxy_release_connection(baton->scheme, baton->proxy_connrec, baton->r->server);
+    ap_finalize_request_protocol(baton->r);
+    ap_lingering_close(baton->r->connection);
+    apr_socket_close(baton->client_soc);
+    ap_mpm_resume_suspended(baton->r->connection);
+    ap_process_request_after_handler(baton->r); /* don't touch baton or r after here */
+}
+
+/* If neither socket becomes readable in the specified timeout,
+ * this callback will kill the request.  We do not have to worry about
+ * having a cancel and a IO both queued.
+ */
+static void proxy_tcp_cancel_callback(void *b)
+{
+    tcp_baton_t *baton = (tcp_baton_t*)b;
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, baton->r, "proxy_tcp_cancel_callback, IO timed out");
+    proxy_tcp_finish(baton);
+    return;
+}
+
+/* Invoked by the event loop when data is ready on either end.
+ *  Pump both ends until they'd block and then start over again
+ *  We don't need the invoke_mtx, since we never put multiple callback events
+ *  in the queue.
+ */
+static void proxy_tcp_callback(void *b) {
+    int status;
+    tcp_baton_t *baton = (tcp_baton_t*)b;
+
+    proxy_tcp_server_conf *tconf =
+            ap_get_module_config(baton->r->connection->base_server->module_config,
+                    &proxy_tcp_module);
+
+    apr_pool_clear(baton->subpool);
+
+    status = proxy_tcp_pump(baton, tconf->async_delay, tconf->can_poll);
+    if (status == SUSPENDED) {
+        apr_pollfd_t *pfd;
+
+        apr_array_header_t *pfds = apr_array_make(baton->subpool, 2, sizeof(apr_pollfd_t));
+
+        pfd = apr_array_push(pfds);
+        pfd->desc_type = APR_POLL_SOCKET;
+        pfd->reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
+        pfd->desc.s = baton->client_soc;
+        pfd->p = baton->subpool;
+
+        pfd = apr_array_push(pfds);
+        pfd->desc_type = APR_POLL_SOCKET;
+        pfd->reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
+        pfd->desc.s = baton->server_soc;
+        pfd->p = baton->subpool;
+
+        ap_mpm_register_poll_callback_timeout(pfds,
+            proxy_tcp_callback,
+            proxy_tcp_cancel_callback,
+            baton,
+            tconf->idle_timeout);
+
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, baton->r, "proxy_tcp_callback suspend");
+    }
+    else {
+        proxy_tcp_finish(baton);
+    }
+
+}
+
+/*
+ * process the request and write the response.
+ */
+static int proxy_tcp_request(apr_pool_t *p, request_rec *r,
+                             proxy_conn_rec *conn,
+                             proxy_worker *worker,
+                             proxy_server_conf *conf,
+                             apr_uri_t *uri,
+                             char *url, char *server_portstr, char *scheme)
+{
+    apr_status_t rv;
+    apr_pollset_t *pollset;
+    apr_pollfd_t pollfd;
+    conn_rec *c = r->connection;
+    apr_socket_t *sock = conn->sock;
+    apr_bucket_brigade *bb_in = apr_brigade_create(p, conn->connection->bucket_alloc);
+    apr_bucket_brigade *bb_out = apr_brigade_create(p, c->bucket_alloc);
+    apr_socket_t *client_socket = ap_get_conn_socket(c);
+    tcp_baton_t *baton = apr_pcalloc(r->pool, sizeof(tcp_baton_t));
+    int status;
+
+    proxy_tcp_server_conf *tconf = ap_get_module_config(c->base_server->module_config,
+            &proxy_tcp_module);
+
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "setting up poll()");
+
+    if ((rv = apr_pollset_create(&pollset, 2, p, 0)) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO()
+                      "error apr_pollset_create()");
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* CHECKME: This probably doesn't work with mod_ssl WANTS_WRITE. */
+
+    pollfd.p = p;
+    pollfd.desc_type = APR_POLL_SOCKET;
+    pollfd.reqevents = APR_POLLIN | APR_POLLHUP;
+    pollfd.desc.s = sock;
+    pollfd.client_data = NULL;
+    apr_pollset_add(pollset, &pollfd);
+
+    pollfd.desc.s = client_socket;
+    apr_pollset_add(pollset, &pollfd);
+
+    ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout");
+
+    /* This handler should take care of the entire connection; make it so that
+     * nothing else is attempted on the connection after returning. */
+    c->keepalive = AP_CONN_CLOSE;
+
+    baton->r = r;
+    baton->pollset = pollset;
+    baton->client_soc = client_socket;
+    baton->server_soc = sock;
+    baton->proxy_connrec = conn;
+    baton->bb_o = bb_out;
+    baton->bb_i = bb_in;
+    baton->scheme = scheme;
+    apr_pool_create(&baton->subpool, r->pool);
+
+    if (!tconf->can_poll) {
+        status = proxy_tcp_pump(baton, tconf->idle_timeout, tconf->can_poll);
+    }
+    else {
+        status = proxy_tcp_pump(baton, tconf->async_delay, tconf->can_poll);
+        apr_pool_clear(baton->subpool);
+        if (status == SUSPENDED) {
+            apr_pollfd_t *pfd;
+
+            apr_array_header_t *pfds = apr_array_make(baton->subpool, 2, sizeof(apr_pollfd_t));
+
+            pfd = apr_array_push(pfds);
+            pfd->desc_type = APR_POLL_SOCKET;
+            pfd->reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
+            pfd->desc.s = baton->client_soc;
+            pfd->p = baton->subpool;
+
+            pfd = apr_array_push(pfds);
+            pfd->desc_type = APR_POLL_SOCKET;
+            pfd->reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
+            pfd->desc.s = baton->server_soc;
+            pfd->p = baton->subpool;
+
+            rv = ap_mpm_register_poll_callback_timeout(pfds,
+                         proxy_tcp_callback,
+                         proxy_tcp_cancel_callback,
+                         baton,
+                         tconf->idle_timeout);
+            if (rv == APR_SUCCESS) {
+                return SUSPENDED;
+            }
+            else if (APR_STATUS_IS_ENOTIMPL(rv)) {
+                ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO() "No async support");
+                status = proxy_tcp_pump(baton, tconf->idle_timeout, 0); /* force no async */
+            }
+            else {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                              APLOGNO() "error creating tcp tunnel");
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+        }
+    }
+
+    if (status != OK) {
+        /* Avoid sending error pages down an upgraded connection */
+        if (status != HTTP_REQUEST_TIME_OUT) {
+            r->status = status;
+        }
+        status = OK;
+    }
+    return status;
+}
+
+/**
+ * Canonicalize the URL, ignoring the path components which make
+ * no sense for a raw tcp or tls connection.
+ */
+static int proxy_tcp_canon(request_rec *r, char *url)
+{
+    char *host, sport[7];
+    const char *err;
+    char *scheme;
+    apr_port_t port, def_port;
+
+    /* ap_port_of_scheme() */
+    if (ap_casecmpstrn(url, "tcp:", 4) == 0) {
+        url += 4;
+        scheme = "tcp:";
+        def_port = apr_uri_port_of_scheme("http");
+    }
+    else if (ap_casecmpstrn(url, "tls:", 4) == 0) {
+        url += 4;
+        scheme = "tls:";
+        def_port = apr_uri_port_of_scheme("https");
+    }
+    else {
+        return DECLINED;
+    }
+
+    port = def_port;
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url);
+
+    /*
+     * do syntactic check.
+     * We break the URL into host, port, path, search
+     */
+    err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
+    if (err) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02439) "error parsing URL %s: %s",
+                      url, err);
+        return HTTP_BAD_REQUEST;
+    }
+
+    apr_snprintf(sport, sizeof(sport), ":%d", port);
+
+    if (ap_strchr_c(host, ':')) {
+        /* if literal IPv6 address */
+        host = apr_pstrcat(r->pool, "[", host, "]", NULL);
+    }
+    r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "//", host, sport,
+            NULL);
+
+    return OK;
+}
+
+static int proxy_tcp_handler(request_rec *r, proxy_worker *worker,
+                             proxy_server_conf *conf,
+                             char *url, const char *proxyname,
+                             apr_port_t proxyport)
+{
+    int status;
+    char server_portstr[32];
+    proxy_conn_rec *backend = NULL;
+    char *scheme;
+    conn_rec *c = r->connection;
+    apr_pool_t *p = r->pool;
+    char *locurl = url;
+    apr_uri_t *uri;
+    int is_ssl = 0;
+
+    if (ap_casecmpstrn(url, "tls:", 4) == 0) {
+        scheme = "tls";
+        is_ssl = 1;
+    }
+    else if (ap_casecmpstrn(url, "tcp:", 4) == 0) {
+        scheme = "tcp";
+    }
+    else {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() "declining URL %s", url);
+        return DECLINED;
+    }
+
+    // FIXME: looks generic below this point
+
+    uri = apr_palloc(p, sizeof(*uri));
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() "serving URL %s", url);
+
+    /* create space for state information */
+    status = ap_proxy_acquire_connection(scheme, &backend, worker, r->server);
+    if (status != OK) {
+        goto cleanup;
+    }
+
+    backend->is_ssl = is_ssl;
+    backend->close = 0;
+
+    /* Step One: Determine Who To Connect To */
+    status = ap_proxy_determine_connection(p, r, conf, worker, backend,
+                                           uri, &locurl, proxyname, proxyport,
+                                           server_portstr,
+                                           sizeof(server_portstr));
+    if (status != OK) {
+        goto cleanup;
+    }
+
+    /* Step Two: Make the Connection */
+    if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02452)
+                      "failed to make connection to backend: %s",
+                      backend->hostname);
+        status = HTTP_SERVICE_UNAVAILABLE;
+        goto cleanup;
+    }
+
+    /* Step Three: Create conn_rec */
+    if (!backend->connection) {
+        status = ap_proxy_connection_create(scheme, backend, c, r->server);
+        if (status  != OK) {
+            goto cleanup;
+        }
+    }
+
+    /* Step Three: Process the Request */
+    status = proxy_tcp_request(p, r, backend, worker, conf, uri, locurl,
+                                  server_portstr, scheme);
+
+cleanup:
+    /* Do not close the socket */
+    if (backend && status != SUSPENDED) {
+        backend->close = 1;
+        ap_proxy_release_connection(scheme, backend, r->server);
+    }
+    return status;
+}
+
+/**
+ * Configuration
+ * -------------
+ *
+ */
+
+static void * create_proxy_tcp_config(apr_pool_t *p, server_rec *s)
+{
+    proxy_tcp_server_conf *new = apr_pcalloc(p, sizeof(proxy_tcp_server_conf));
+
+    new->idle_timeout = -1; /* no timeout */
+
+    return new;
+}
+
+static void * merge_proxy_tcp_config(apr_pool_t *p, void *basev, void *overridesv)
+{
+    proxy_tcp_server_conf *ps = apr_pcalloc(p, sizeof(proxy_tcp_server_conf));
+    proxy_tcp_server_conf *base = (proxy_tcp_server_conf *) basev;
+    proxy_tcp_server_conf *overrides = (proxy_tcp_server_conf *) overridesv;
+
+    ps->idle_timeout =
+            (overrides->idle_timeout_set == 0) ? base->idle_timeout :
+                    overrides->idle_timeout;
+    ps->idle_timeout_set = overrides->idle_timeout_set || base->idle_timeout_set;
+
+    ps->async_delay =
+            (overrides->async_delay_set == 0) ? base->async_delay :
+                    overrides->async_delay;
+    ps->async_delay_set = overrides->async_delay_set || base->async_delay_set;
+
+    return ps;
+}
+
+static int proxy_tcp_post_config(apr_pool_t *p, apr_pool_t *plog,
+        apr_pool_t *ptemp, server_rec *base_server)
+{
+    server_rec *s;
+    int mpm_can_poll = 0;
+
+    if (ap_mpm_query(AP_MPMQ_CAN_POLL, &mpm_can_poll) != APR_SUCCESS) {
+        mpm_can_poll = 0;
+    }
+
+    for (s = base_server; s; s = s->next) {
+        proxy_tcp_server_conf *conf = ap_get_module_config(s->module_config,
+                &proxy_tcp_module);
+
+        conf->can_poll = mpm_can_poll;
+    }
+
+    return OK;
+}
+
+static const command_rec proxy_tcp_cmds[] =
+{
+    {NULL}
+};
+
+static void ap_proxy_tcp_register_hook(apr_pool_t *p)
+{
+    ap_hook_post_config(proxy_tcp_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+
+    proxy_hook_scheme_handler(proxy_tcp_handler, NULL, NULL, APR_HOOK_FIRST);
+    proxy_hook_canon_handler(proxy_tcp_canon, NULL, NULL, APR_HOOK_FIRST);
+
+    proxy_tcp_output_filter_handle = ap_register_output_filter("PROXY_TCP_OUT",
+            proxy_tcp_output_filter, NULL, AP_FTYPE_PROTOCOL);
+
+}
+
+AP_DECLARE_MODULE(proxy_tcp) = {
+    STANDARD20_MODULE_STUFF,
+    NULL,                       /* create per-directory config structure */
+    NULL,                       /* merge per-directory config structures */
+    create_proxy_tcp_config,    /* create per-server config structure */
+    merge_proxy_tcp_config,     /* merge per-server config structures */
+    proxy_tcp_cmds,             /* command apr_table_t */
+    ap_proxy_tcp_register_hook  /* register hooks */
+};
Index: modules/ssl/config.m4
===================================================================
--- modules/ssl/config.m4	(revision 1735175)
+++ modules/ssl/config.m4	(working copy)
@@ -60,6 +60,13 @@
     fi
 ])
 
+APACHE_MODULE(ssl_tcp, [Support for detecting SNI during TCP proxying], , , most, [
+    dnl TODO: Check for OpenSSL >= 1.0.2
+    if test "$enable_ssl" = "no"; then
+        AC_MSG_ERROR([mod_ssl_tcp is dependent on mod_ssl, which is not enabled.])
+    fi
+])
+
 dnl #  end of module specific part
 APACHE_MODPATH_FINISH
 
Index: modules/ssl/mod_ssl_tcp.c
===================================================================
--- modules/ssl/mod_ssl_tcp.c	(revision 0)
+++ modules/ssl/mod_ssl_tcp.c	(working copy)
@@ -0,0 +1,435 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 "ap_mpm.h"
+#include "httpd.h"
+#include "http_log.h"
+#include "http_vhost.h"
+#include "util_filter.h"
+
+/* OpenSSL headers */
+#include <openssl/opensslv.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+/* must be defined before including ssl.h */
+#define OPENSSL_NO_SSL_INTERN
+#endif
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/ocsp.h>
+
+/**
+  * The following features all depend on TLS extension support.
+  * Within this block, check again for features (not version numbers).
+  */
+#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name)
+
+#define HAVE_TLSEXT
+
+/* TLS session tickets */
+#if defined(SSL_CTX_set_tlsext_ticket_key_cb)
+#define HAVE_TLS_SESSION_TICKETS
+#define TLSEXT_TICKET_KEY_LEN 48
+#ifndef tlsext_tick_md
+#ifdef OPENSSL_NO_SHA256
+#define tlsext_tick_md EVP_sha1
+#else
+#define tlsext_tick_md EVP_sha256
+#endif
+#endif
+#endif
+
+/* ALPN Protocol Negotiation */
+#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
+#define HAVE_TLS_ALPN
+#endif
+
+#endif /* !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name) */
+
+module AP_MODULE_DECLARE_DATA ssl_tcp_module;
+
+/*
+ * mod_ssl_tcp.c
+ *
+ *  Created on: Mar 6, 2016
+ *      Author: minfrin
+ */
+
+/*
+ * This input filter parses the initial client hello, and extracts the
+ * SNI information and sets the virtual host as detected. This filter
+ * is designed to be used when httpd must react to the SNI request, but
+ * not terminate TLS itself.
+ */
+
+static ap_filter_rec_t *ssl_tcp_output_filter_handle;
+
+#ifdef HAVE_TLSEXT
+
+typedef struct {
+    apr_pool_t *pool;
+    apr_bucket_brigade *tmpbb;
+    SSL_CTX *ssl_ctx;
+    SSL *ssl;
+    BIO* in_bio;
+    BIO* out_bio;
+    int done;
+} filter_sni_ctx_t;
+
+apr_status_t ssl_tcp_cleanup(void *data)
+{
+    filter_sni_ctx_t *ctx = data;
+
+    SSL_free(ctx->ssl);
+    SSL_CTX_free(ctx->ssl_ctx);
+
+    return APR_SUCCESS;
+}
+
+/*
+ * Return TRUE iff the given servername matches the server record when
+ * selecting virtual hosts.
+ */
+int sni_vhost_matches(const char *servername, server_rec *s)
+{
+    apr_array_header_t *names;
+    int i;
+
+    /* check ServerName */
+    if (!strcasecmp(servername, s->server_hostname)) {
+        return TRUE;
+    }
+
+    /*
+     * if not matched yet, check ServerAlias entries
+     * (adapted from vhost.c:matches_aliases())
+     */
+    names = s->names;
+    if (names) {
+        char **name = (char **)names->elts;
+        for (i = 0; i < names->nelts; ++i) {
+            if (!name[i])
+                continue;
+            if (!strcasecmp(servername, name[i])) {
+                return TRUE;
+            }
+        }
+    }
+
+    /* if still no match, check ServerAlias entries with wildcards */
+    names = s->wild_names;
+    if (names) {
+        char **name = (char **)names->elts;
+        for (i = 0; i < names->nelts; ++i) {
+            if (!name[i])
+                continue;
+            if (!ap_strcasecmp_match(servername, name[i])) {
+                return TRUE;
+            }
+        }
+    }
+
+    return FALSE;
+}
+
+static int sni_find_vhost(void *servername, conn_rec *c, server_rec *s)
+{
+    int found;
+
+    found = sni_vhost_matches(servername, s);
+
+    /* set SSL_CTX (if matched) */
+    if (found) {
+
+        c->base_server = s;
+
+        return 1;
+    }
+
+    return 0;
+}
+
+#endif
+
+static apr_status_t ssl_tcp_filter(ap_filter_t *f,
+                                      apr_bucket_brigade *b,
+                                      ap_input_mode_t mode,
+                                      apr_read_type_e block,
+                                      apr_off_t readbytes)
+{
+#ifdef HAVE_TLSEXT
+
+    apr_status_t rv;
+    filter_sni_ctx_t *ctx = f->ctx;
+    const char *str;
+    apr_size_t len;
+    apr_off_t extra = 0;
+    const char *servername = NULL;
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+                  "read of TLS client hello to parse SNI, mode %d, "
+                  "%" APR_OFF_T_FMT " bytes",
+                  mode, readbytes);
+
+    if (!ctx) {
+        apr_pool_t *pool;
+
+        /* we want to cleanup as early as possible so we don't
+         * hang around for the duration of the request.
+         */
+        apr_pool_create(&pool, f->c->pool);
+
+        f->ctx = ctx = apr_palloc(pool, sizeof(*ctx));
+        ctx->pool = pool;
+        ap_filter_prepare_brigade(f, NULL);
+        ctx->tmpbb = apr_brigade_create(pool, f->c->bucket_alloc);
+        ctx->done = 0;
+
+        ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method());
+        ctx->ssl = SSL_new(ctx->ssl_ctx);
+        SSL_set_cipher_list(ctx->ssl, "ALL");
+        ctx->in_bio = BIO_new(BIO_s_mem());
+        ctx->out_bio = BIO_new(BIO_s_mem());
+        SSL_set_bio(ctx->ssl, ctx->in_bio, ctx->out_bio);
+        SSL_set_accept_state(ctx->ssl);
+
+        apr_pool_cleanup_register(pool, ctx,
+                                  ssl_tcp_cleanup,
+                                  apr_pool_cleanup_null);
+
+    }
+    else if (ctx->done && APR_BRIGADE_EMPTY(f->bb)) {
+
+        /* we're done, clean up early */
+        ap_remove_input_filter(f);
+        apr_pool_destroy(ctx->pool);
+        f->ctx = NULL;
+
+        return ap_get_brigade(f->next, b, mode, block, readbytes);
+    }
+
+    if (mode == AP_MODE_INIT) {
+
+        /*
+         * if we attempt to initialise, turn this into a READBYTES
+         * mode and attempt to read at least one SSL handshake worth
+         * of data.
+         */
+        mode = AP_MODE_READBYTES;
+        extra = HUGE_STRING_LEN;
+    }
+
+    if (mode == AP_MODE_GETLINE) {
+        return apr_brigade_split_line(b, f->bb, block, HUGE_STRING_LEN);
+    }
+
+    if (mode == AP_MODE_EATCRLF) {
+        apr_bucket *e;
+        const char *c;
+
+        while (1) {
+            if (APR_BRIGADE_EMPTY(f->bb)) {
+                return ap_get_brigade(f->next, b, mode, block, readbytes);
+            }
+
+            e = APR_BRIGADE_FIRST(f->bb);
+
+            rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ);
+
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+
+            c = str;
+            while (c < str + len) {
+                if (*c == APR_ASCII_LF) {
+                    c++;
+                }
+                else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF) {
+                    c += 2;
+                }
+                else {
+                    return APR_SUCCESS;
+                }
+            }
+
+            apr_bucket_delete(e);
+        }
+        return APR_SUCCESS;
+    }
+
+    if (mode == AP_MODE_EXHAUSTIVE) {
+
+        APR_BRIGADE_CONCAT(b, f->bb);
+
+        return ap_get_brigade(f->next, b, mode, block, readbytes);
+    }
+
+    if (mode == AP_MODE_SPECULATIVE) {
+        return ap_get_brigade(f->next, b, mode, block, readbytes);
+    }
+
+    if (mode == AP_MODE_READBYTES) {
+        apr_bucket *e;
+
+        rv = ap_get_brigade(f->next, f->bb, mode, block, readbytes + extra);
+
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+
+        e = APR_BRIGADE_FIRST(f->bb);
+        while (!ctx->done && e != APR_BRIGADE_SENTINEL(f->bb)) {
+
+            const char *str;
+            apr_size_t len;
+
+            rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
+            if (rv == APR_SUCCESS) {
+                int rc;
+
+                BIO_write(ctx->in_bio, str, (int) len);
+
+                if ((rc = SSL_do_handshake(ctx->ssl)) <= 0) {
+                    int ssl_err = SSL_get_error(ctx->ssl, rc);
+                    if (ssl_err == SSL_ERROR_WANT_READ) {
+                        /* handshake fine so far, give us more
+                         * data.
+                         */
+
+                    }
+                    else {
+                        /* either the first part of the handshake is done and we
+                         * want to write, or some part of the handshake has
+                         * failed. It doesn't matter at this point, we only care
+                         * about the servername.
+                         */
+
+                        /* not SSL, step out of the way */
+                        servername = SSL_get_servername(ctx->ssl, TLSEXT_NAMETYPE_host_name);
+                        ctx->done = 1;
+                    }
+                }
+
+            }
+
+            e = APR_BUCKET_NEXT(e);
+        }
+
+        rv = apr_brigade_partition(f->bb, readbytes, &e);
+        if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
+            return rv;
+        }
+
+        ctx->tmpbb = apr_brigade_split_ex(f->bb, e, ctx->tmpbb);
+
+        APR_BRIGADE_CONCAT(b, f->bb);
+        APR_BRIGADE_CONCAT(f->bb, ctx->tmpbb);
+
+    }
+
+    /* did we detect a valid handshake and a valid servername? */
+    if (servername) {
+        if (ap_vhost_iterate_given_conn(f->c, sni_find_vhost,
+                                        (void *)servername)) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO()
+                          "TCP+SNI virtual host for servername %s found",
+                          servername);
+            return APR_SUCCESS;
+        }
+        else {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO()
+                          "No matching TCP+SNI virtual host for servername "
+                          "%s found (using default/first virtual host)",
+                          servername);
+        }
+    }
+    else {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO()
+                      "TCP+SNI servername not provided via TLS extension "
+                      "(using default/first virtual host)");
+    }
+
+    return APR_SUCCESS;
+
+#else
+
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
+                  "no support for TLSEXT, SSL_TCP_SNI disabled, mode %d, "
+                  "%" APR_OFF_T_FMT " bytes",
+                  mode, readbytes);
+
+    ap_remove_input_filter(f);
+    return ap_get_brigade(f->next, b, mode, block, readbytes);
+
+#endif
+
+}
+
+static apr_status_t ssl_tcp_cleanup_pre_config(void *data)
+{
+    /* Corresponds to SSL_library_init: */
+    EVP_cleanup();
+
+    return APR_SUCCESS;
+}
+
+static int ssl_tcp_hook_pre_config(apr_pool_t *pconf,
+                               apr_pool_t *plog,
+                               apr_pool_t *ptemp)
+{
+
+    SSL_library_init();
+
+    /*
+     * Let us cleanup the ssl library when the module is unloaded
+     */
+    apr_pool_cleanup_register(pconf, NULL, ssl_tcp_cleanup_pre_config,
+                                           apr_pool_cleanup_null);
+
+    return OK;
+}
+
+static const command_rec ssl_tcp_cmds[] =
+{
+    {NULL}
+};
+
+static void ap_ssl_tcp_register_hook(apr_pool_t *p)
+{
+
+    ap_hook_pre_config(ssl_tcp_hook_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+
+    ssl_tcp_output_filter_handle = ap_register_input_filter("SSL_TCP_SNI",
+            ssl_tcp_filter, NULL, AP_FTYPE_CONNECTION + 6);
+
+}
+
+AP_DECLARE_MODULE(ssl_tcp) = {
+    STANDARD20_MODULE_STUFF,
+    NULL,                       /* create per-directory config structure */
+    NULL,                       /* merge per-directory config structures */
+    NULL,                       /* create per-server config structure */
+    NULL,                       /* merge per-server config structures */
+    ssl_tcp_cmds,             /* command apr_table_t */
+    ap_ssl_tcp_register_hook  /* register hooks */
+};
