Skip to content

Instantly share code, notes, and snippets.

@vitkin
Last active August 24, 2024 03:12
Show Gist options
  • Select an option

  • Save vitkin/6661683 to your computer and use it in GitHub Desktop.

Select an option

Save vitkin/6661683 to your computer and use it in GitHub Desktop.

Revisions

  1. vitkin revised this gist Nov 6, 2013. 1 changed file with 0 additions and 4 deletions.
    4 changes: 0 additions & 4 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -28,18 +28,14 @@ httpd -l | grep mod_proxy
    2. Get the patch and apply it
    ```bash
    wget https://gist.github.com/vitkin/6661683/raw/873dd8b4de4ad1ff69757ffe48fc574374aedc57/apache-2.2-wstunnel.patch
    cd httpd-2.2.x
    patch -p1 -i ../apache-2.2-wstunnel.patch
    ```

    3. Build _Apache_:
    ```bash
    autoconf
    ./configure --enable-so --enable-proxy --enable-proxy_wstunnel=shared
    make
    ```

  2. vitkin revised this gist Nov 6, 2013. 1 changed file with 17 additions and 5 deletions.
    22 changes: 17 additions & 5 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -45,12 +45,24 @@ httpd -l | grep mod_proxy

    4. Just copy the resulting file **modules/proxy/.libs/mod_proxy_wstunnel.so** to your Apache modules folder and follow the instructions from any of the hereunder references.

    > **Note:**
    > **Notes:**
    >
    > Don't forget to check that the configuration is OK by running the command:
    > ```bash
    > apachectl configtest
    > ```
    > - Don't forget to check that the configuration is OK by running the command:
    > ```bash
    > apachectl configtest
    > ```
    >
    > - You may encounter some compilation error with version 2.2.22 and later similar to:
    > ```bash
    > modules/http/.libs/libmod_http.a(byterange_filter.o): In function `ap_set_byterange':
    > byterange_filter.c:(.text+0x130d): undefined reference to `apr_array_clear'
    > collect2: ld returned 1 exit status
    > ```
    > This is a [known bug](https://issues.apache.org/bugzilla/show_bug.cgi?id=53162).
    > A workaround consist in adding `--with-included-apr` to the configure options:
    > ```bash
    > ./configure --enable-so --enable-proxy --enable-proxy-wstunnel=shared --with-included-apr
    > ```
    ## References:
  3. vitkin revised this gist Nov 6, 2013. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -23,12 +23,14 @@ httpd -l | grep mod_proxy
    svn checkout \
    http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.x/ \
    httpd-2.2.x

    cd httpd-2.2.x
    ```

    2. Get the patch and apply it
    ```bash
    wget https://gist.github.com/vitkin/6661683/raw/873dd8b4de4ad1ff69757ffe48fc574374aedc57/apache-2.2-wstunnel.patch
    cd httpd-2.2.x
    patch -p1 -i ../apache-2.2-wstunnel.patch
    ```

  4. vitkin revised this gist Nov 6, 2013. 2 changed files with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # Backport WebSocket to Apache 2.2.2x
    # Backport WebSocket to Apache 2.2

    This is my variation from the
    [original patch](http://cafarelli.fr/gentoo/apache-2.2.24-wstunnel.patch)
    @@ -21,15 +21,15 @@ httpd -l | grep mod_proxy
    1. Checkout apache source (Replace 'x' with the right number.):
    ```bash
    svn checkout \
    http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.2x/ \
    httpd-2.2.2x
    http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.x/ \
    httpd-2.2.x

    cd httpd-2.2.2x
    cd httpd-2.2.x
    ```

    2. Get the patch and apply it
    ```bash
    patch -p1 -i ../apache-2.2.2x-wstunnel.patch
    patch -p1 -i ../apache-2.2-wstunnel.patch
    ```

    3. Build _Apache_:
    File renamed without changes.
  5. vitkin revised this gist Nov 6, 2013. 2 changed files with 48 additions and 18 deletions.
    59 changes: 43 additions & 16 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,32 +1,59 @@
    # Backport WebSocket to Apache 2.2.2x

    This is my variation from the original patch (http://cafarelli.fr/gentoo/apache-2.2.24-wstunnel.patch).
    The difference is that I avoid modifying the `mod_utils.c` and the `mod_proxy.h` so that if the `mod_proxy` module has been embedded in the main 'httpd' binary then it will work and you won't get the `error: undefined symbol: ap_proxy_pass_brigade`.
    Indeed you can check that the module `mod_proxy` is part of Apache main binary by running the command:
    httpd -l | grep mod_proxy
    This is my variation from the
    [original patch](http://cafarelli.fr/gentoo/apache-2.2.24-wstunnel.patch)
    and that also includes the suggested correction for the
    [bug with secure websocket 'wss://'](https://issues.apache.org/bugzilla/show_bug.cgi?id=55320).

    The difference is that I avoid modifying the **mod_utils.c** and the
    **mod_proxy.h** so that if the **mod_proxy** module has been embedded in the
    main **httpd** binary then it will work and you won't get the
    `error: undefined symbol: ap_proxy_pass_brigade`.

    Indeed you can check that the module **mod_proxy** is part of _Apache_ main
    binary by running the command:
    ```bash
    httpd -l | grep mod_proxy
    ```

    ## Instructions:

    # Checkout apache source ( Replace 'x' with the right number.)
    svn checkout http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.2x/ httpd-2.2.2x
    1. Checkout apache source (Replace 'x' with the right number.):
    ```bash
    svn checkout \
    http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.2x/ \
    httpd-2.2.2x

    cd httpd-2.2.2x
    ```

    # Get patch and apply it

    2. Get the patch and apply it
    ```bash
    patch -p1 -i ../apache-2.2.2x-wstunnel.patch
    ```

    # Build Apache
    3. Build _Apache_:
    ```bash
    autoconf
    ./configure --enable-so --enable-proxy --enable-proxy-wstunnel=shared
    ./configure --enable-so --enable-proxy --enable-proxy_wstunnel=shared
    make
    ```

    Just copy the resulting file `modules/proxy/.libs/mod_proxy_wstunnel.so` to your Apache modules folder and follow the instructions from any of the hereunder references.
    4. Just copy the resulting file **modules/proxy/.libs/mod_proxy_wstunnel.so** to your Apache modules folder and follow the instructions from any of the hereunder references.

    Don't forget to check that the configuration is OK by running:
    apachectl configtest
    > **Note:**
    >
    > Don't forget to check that the configuration is OK by running the command:
    > ```bash
    > apachectl configtest
    > ```
    ## References:
    * http://www.amoss.me.uk/2013/06/apache-2-2-websocket-proxying-ubuntu-mod_proxy_wstunnel/
    * http://blog.cafarelli.fr/post/2013/04/26/Backporting-Apache-support-for-websockets-reverse-proxy-%28aka-getting-GateOne-to-work-behind-Apache%29
    * https://i.rationa.li/mark/note/co97okgZSC-bIvONa5ai6Q
    * [_Apache 2.2 Websocket_ Proxying on Ubuntu with **mod_proxy_wstunnel**](http://www.amoss.me.uk/2013/06/apache-2-2-websocket-proxying-ubuntu-mod_proxy_wstunnel/)
    * [Backporting _Apache_ support for _websockets_ reverse proxy](http://blog.cafarelli.fr/post/2013/04/26/Backporting-Apache-support-for-websockets-reverse-proxy-%28aka-getting-GateOne-to-work-behind-Apache%29)
    * [How to use _Voyageur's Apache websocket_ backport with _pump.io_](https://i.rationa.li/mark/note/co97okgZSC-bIvONa5ai6Q)
    7 changes: 5 additions & 2 deletions apache-2.2.2x-wstunnel.patch
    Original file line number Diff line number Diff line change
    @@ -24,6 +24,7 @@ diff -Naur a/modules/proxy/config.m4 b/modules/proxy/config.m4
    +APACHE_MODULE(proxy_wstunnel, Apache proxy Websocket Tunnel module, $proxy_wstunnel_objs, , $proxy_mods_enable)
    APACHE_MODULE(proxy_ajp, Apache proxy AJP module, $proxy_ajp_objs, , $proxy_mods_enable)
    APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module, $proxy_balancer_objs, , $proxy_mods_enable)

    diff -Naur a/modules/proxy/mod_proxy_wstunnel.h b/modules/proxy/mod_proxy_wstunnel.h
    --- a/modules/proxy/mod_proxy_wstunnel.h
    +++ b/modules/proxy/mod_proxy_wstunnel.h
    @@ -119,7 +120,7 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.h b/modules/proxy/mod_proxy_wstunn
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c
    --- a/modules/proxy/mod_proxy_wstunnel.c
    +++ b/modules/proxy/mod_proxy_wstunnel.c
    @@ -0,0 +1,726 @@
    @@ -0,0 +1,728 @@
    +/* 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.
    @@ -760,12 +761,14 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunn
    + proxy_conn_rec *backend = NULL;
    + char *scheme;
    + int retry;
    + int is_ssl = 0;
    + conn_rec *c = r->connection;
    + apr_pool_t *p = r->pool;
    + apr_uri_t *uri;
    +
    + if (strncasecmp(url, "wss:", 4) == 0) {
    + scheme = "WSS";
    + is_ssl = 1;
    + }
    + else if (strncasecmp(url, "ws:", 3) == 0) {
    + scheme = "WS";
    @@ -789,7 +792,7 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunn
    + return status;
    + }
    +
    + backend->is_ssl = 0;
    + backend->is_ssl = is_ssl;
    + backend->close = 0;
    +
    + retry = 0;
  6. vitkin revised this gist Sep 22, 2013. 1 changed file with 6 additions and 266 deletions.
    272 changes: 6 additions & 266 deletions apache-2.2.2x-wstunnel.patch
    Original file line number Diff line number Diff line change
    @@ -849,7 +849,7 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunn
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.dsp b/modules/proxy/mod_proxy_wstunnel.dsp
    --- a/modules/proxy/mod_proxy_wstunnel.dsp
    +++ b/modules/proxy/mod_proxy_wstunnel.dsp
    @@ -0,0 +1,123 @@
    @@ -0,0 +1,128 @@
    +# Microsoft Developer Studio Project File - Name="mod_proxy_wstunnel" - Package Owner=<4>
    +# Microsoft Developer Studio Generated Build File, Format Version 6.00
    +# ** DO NOT EDIT **
    @@ -969,272 +969,12 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.dsp b/modules/proxy/mod_proxy_wstu
    +# End Group
    +# Begin Source File
    +
    +SOURCE=.\mod_proxy_wstunnel.h
    +# End Source File
    +# End Group
    +# Begin Source File
    +
    +SOURCE=..\..\build\win32\httpd.rc
    +# End Source File
    +# End Target
    +# End Project
    diff -Naur a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile
    --- a/modules/proxy/NWGNUmakefile 2013-04-25 21:08:56.417441322 +0200
    +++ b/modules/proxy/NWGNUmakefile 2013-04-25 21:08:58.480811797 +0200
    @@ -159,6 +159,7 @@
    $(OBJDIR)/proxybalancer.nlm \
    $(OBJDIR)/proxyajp.nlm \
    $(OBJDIR)/proxyscgi.nlm \
    + $(OBJDIR)/proxywstunnel.nlm \
    $(EOLIST)

    #
    diff -Naur a/modules/proxy/NWGNUproxywstunnel b/modules/proxy/NWGNUproxywstunnel
    --- a/modules/proxy/NWGNUproxywstunnel 1970-01-01 01:00:00.000000000 +0100
    +++ b/modules/proxy/NWGNUproxywstunnel 2013-04-25 21:08:58.480811797 +0200
    @@ -0,0 +1,250 @@
    +#
    +# Make sure all needed macro's are defined
    +#
    +
    +#
    +# Get the 'head' of the build environment if necessary. This includes default
    +# targets and paths to tools
    +#
    +
    +ifndef EnvironmentDefined
    +include $(AP_WORK)/build/NWGNUhead.inc
    +endif
    +
    +#
    +# These directories will be at the beginning of the include list, followed by
    +# INCDIRS
    +#
    +XINCDIRS += \
    + $(APR)/include \
    + $(APRUTIL)/include \
    + $(SRC)/include \
    + $(STDMOD)/http \
    + $(STDMOD)/proxy \
    + $(NWOS) \
    + $(EOLIST)
    +
    +#
    +# These flags will come after CFLAGS
    +#
    +XCFLAGS += \
    + $(EOLIST)
    +
    +#
    +# These defines will come after DEFINES
    +#
    +XDEFINES += \
    + $(EOLIST)
    +
    +#
    +# These flags will be added to the link.opt file
    +#
    +XLFLAGS += \
    + $(EOLIST)
    +
    +#
    +# These values will be appended to the correct variables based on the value of
    +# RELEASE
    +#
    +ifeq "$(RELEASE)" "debug"
    +XINCDIRS += \
    + $(EOLIST)
    +
    +XCFLAGS += \
    + $(EOLIST)
    +
    +XDEFINES += \
    + $(EOLIST)
    +
    +XLFLAGS += \
    + $(EOLIST)
    +endif
    +
    +ifeq "$(RELEASE)" "noopt"
    +XINCDIRS += \
    + $(EOLIST)
    +
    +XCFLAGS += \
    + $(EOLIST)
    +
    +XDEFINES += \
    + $(EOLIST)
    +
    +XLFLAGS += \
    + $(EOLIST)
    +endif
    +
    +ifeq "$(RELEASE)" "release"
    +XINCDIRS += \
    + $(EOLIST)
    +
    +XCFLAGS += \
    + $(EOLIST)
    +
    +XDEFINES += \
    + $(EOLIST)
    +
    +XLFLAGS += \
    + $(EOLIST)
    +endif
    +
    +#
    +# These are used by the link target if an NLM is being generated
    +# This is used by the link 'name' directive to name the nlm. If left blank
    +# TARGET_nlm (see below) will be used.
    +#
    +NLM_NAME = proxywstunnel
    +
    +#
    +# This is used by the link '-desc ' directive.
    +# If left blank, NLM_NAME will be used.
    +#
    +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Web Socket Tunnel Module
    +
    +#
    +# This is used by the '-threadname' directive. If left blank,
    +# NLM_NAME Thread will be used.
    +#
    +NLM_THREAD_NAME = Prxy WbSkt Module
    +
    +#
    +# If this is specified, it will override VERSION value in
    +# $(AP_WORK)/build/NWGNUenvironment.inc
    +#
    +NLM_VERSION =
    +
    +#
    +# If this is specified, it will override the default of 64K
    +#
    +NLM_STACK_SIZE = 8192
    +
    +
    +#
    +# If this is specified it will be used by the link '-entry' directive
    +#
    +NLM_ENTRY_SYM =
    +
    +#
    +# If this is specified it will be used by the link '-exit' directive
    +#
    +NLM_EXIT_SYM =
    +
    +#
    +# If this is specified it will be used by the link '-check' directive
    +#
    +NLM_CHECK_SYM =
    +
    +#
    +# If these are specified it will be used by the link '-flags' directive
    +#
    +NLM_FLAGS =
    +
    +#
    +# If this is specified it will be linked in with the XDCData option in the def
    +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
    +# by setting APACHE_UNIPROC in the environment
    +#
    +XDCDATA =
    +
    +#
    +# If there is an NLM target, put it here
    +#
    +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm
    +
    +#
    +# If there is an LIB target, put it here
    +#
    +TARGET_lib =
    +
    +#
    +# These are the OBJ files needed to create the NLM target above.
    +# Paths must all use the '/' character
    +#
    +FILES_nlm_objs = \
    + $(OBJDIR)/mod_proxy_wstunnel.o \
    + $(EOLIST)
    +
    +#
    +# These are the LIB files needed to create the NLM target above.
    +# These will be added as a library command in the link.opt file.
    +#
    +FILES_nlm_libs = \
    + $(PRELUDE) \
    + $(EOLIST)
    +
    +#
    +# These are the modules that the above NLM target depends on to load.
    +# These will be added as a module command in the link.opt file.
    +#
    +FILES_nlm_modules = \
    + libc \
    + aprlib \
    + proxy \
    + $(EOLIST)
    +
    +#
    +# If the nlm has a msg file, put it's path here
    +#
    +FILE_nlm_msg =
    +
    +#
    +# If the nlm has a hlp file put it's path here
    +#
    +FILE_nlm_hlp =
    +
    +#
    +# If this is specified, it will override $(NWOS)\copyright.txt.
    +#
    +FILE_nlm_copyright =
    +
    +#
    +# Any additional imports go here
    +#
    +FILES_nlm_Ximports = \
    + @libc.imp \
    + @aprlib.imp \
    + @httpd.imp \
    + @$(OBJDIR)/mod_proxy.imp \
    + $(EOLIST)
    +
    +#
    +# Any symbols exported to here
    +#
    +FILES_nlm_exports = \
    + proxy_wstunnel_module \
    + $(EOLIST)
    +
    +#
    +# These are the OBJ files needed to create the LIB target above.
    +# Paths must all use the '/' character
    +#
    +FILES_lib_objs = \
    + $(EOLIST)
    +
    +#
    +# implement targets and dependancies (leave this section alone)
    +#
    +
    +libs :: $(OBJDIR) $(TARGET_lib)
    +
    +nlms :: libs $(TARGET_nlm)
    +
    +#
    +# Updated this target to create necessary directories and copy files to the
    +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
    +#
    +install :: nlms FORCE
    +
    +#
    +# Any specialized rules here
    +#
    +
    +vpath %.c balancers
    +#
    +# Include the 'tail' makefile that has targets that depend on variables defined
    +# in this makefile
    +#
    +
    +include $(APBUILD)/NWGNUtail.inc
    +
    +
  7. vitkin renamed this gist Sep 22, 2013. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  8. vitkin revised this gist Sep 22, 2013. 1 changed file with 9 additions and 342 deletions.
    351 changes: 9 additions & 342 deletions apache-2.2.2x-wstunnel.patch
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    diff -Naur a/modules/proxy/config.m4 b/modules/proxy/config.m4
    --- a/modules/proxy/config.m4 Sun Sep 22 19:01:23 2013
    +++ b/modules/proxy/config.m4 Sun Sep 22 19:00:58 2013
    --- a/modules/proxy/config.m4
    +++ b/modules/proxy/config.m4
    @@ -18,6 +18,7 @@
    proxy_http_objs="mod_proxy_http.lo"
    proxy_scgi_objs="mod_proxy_scgi.lo"
    @@ -25,8 +25,8 @@ diff -Naur a/modules/proxy/config.m4 b/modules/proxy/config.m4
    APACHE_MODULE(proxy_ajp, Apache proxy AJP module, $proxy_ajp_objs, , $proxy_mods_enable)
    APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module, $proxy_balancer_objs, , $proxy_mods_enable)
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.h b/modules/proxy/mod_proxy_wstunnel.h
    --- a/modules/proxy/mod_proxy_wstunnel.h Sun Sep 22 19:11:28 2013
    +++ b/modules/proxy/mod_proxy_wstunnel.h Sun Sep 22 19:00:58 2013
    --- a/modules/proxy/mod_proxy_wstunnel.h
    +++ b/modules/proxy/mod_proxy_wstunnel.h
    @@ -0,0 +1,88 @@
    +/* Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements. See the NOTICE file distributed with
    @@ -117,8 +117,8 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.h b/modules/proxy/mod_proxy_wstunn
    +#endif /*MOD_PROXY_WSTUNNEL_H*/
    +/** @} */
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c
    --- a/modules/proxy/mod_proxy_wstunnel.c Sun Sep 22 19:11:33 2013
    +++ b/modules/proxy/mod_proxy_wstunnel.c Sun Sep 22 19:00:58 2013
    --- a/modules/proxy/mod_proxy_wstunnel.c
    +++ b/modules/proxy/mod_proxy_wstunnel.c
    @@ -0,0 +1,726 @@
    +/* Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements. See the NOTICE file distributed with
    @@ -847,8 +847,8 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunn
    + ap_proxy_http_register_hook /* register hooks */
    +};
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.dsp b/modules/proxy/mod_proxy_wstunnel.dsp
    --- a/modules/proxy/mod_proxy_wstunnel.dsp Sun Sep 22 19:36:10 2013
    +++ b/modules/proxy/mod_proxy_wstunnel.dsp Sun Sep 22 19:00:58 2013
    --- a/modules/proxy/mod_proxy_wstunnel.dsp
    +++ b/modules/proxy/mod_proxy_wstunnel.dsp
    @@ -0,0 +1,123 @@
    +# Microsoft Developer Studio Project File - Name="mod_proxy_wstunnel" - Package Owner=<4>
    +# Microsoft Developer Studio Generated Build File, Format Version 6.00
    @@ -974,7 +974,7 @@ diff -Naur a/modules/proxy/mod_proxy_wstunnel.dsp b/modules/proxy/mod_proxy_wstu
    +# End Target
    +# End Project
    diff -Naur a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile
    --- a/modules/proxy/NWGNUmakefile 2013-04-25 21:08:56.417441322 +0200
    --- a/modules/proxy/NWGNUmakefile 2013-04-25 21:08:56.417441322 +0200
    +++ b/modules/proxy/NWGNUmakefile 2013-04-25 21:08:58.480811797 +0200
    @@ -159,6 +159,7 @@
    $(OBJDIR)/proxybalancer.nlm \
    @@ -1238,336 +1238,3 @@ diff -Naur a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile
    +include $(APBUILD)/NWGNUtail.inc
    +
    +
    diff -Naur a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
    --- a/modules/proxy/proxy_util.c 2013-04-25 21:08:56.417441322 +0200
    +++ b/modules/proxy/proxy_util.c 2013-04-25 21:08:58.484145190 +0200
    @@ -2680,3 +2680,329 @@
    }
    return rv;
    }
    +
    +/* Clear all connection-based headers from the incoming headers table */
    +typedef struct header_dptr {
    + apr_pool_t *pool;
    + apr_table_t *table;
    + apr_time_t time;
    +} header_dptr;
    +
    +static int clear_conn_headers(void *data, const char *key, const char *val)
    +{
    + apr_table_t *headers = ((header_dptr*)data)->table;
    + apr_pool_t *pool = ((header_dptr*)data)->pool;
    + const char *name;
    + char *next = apr_pstrdup(pool, val);
    + while (*next) {
    + name = next;
    + while (*next && !apr_isspace(*next) && (*next != ',')) {
    + ++next;
    + }
    + while (*next && (apr_isspace(*next) || (*next == ','))) {
    + *next++ = '\0';
    + }
    + apr_table_unset(headers, name);
    + }
    + return 1;
    +}
    +
    +static void proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
    +{
    + header_dptr x;
    + x.pool = p;
    + x.table = headers;
    + apr_table_unset(headers, "Proxy-Connection");
    + apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL);
    + apr_table_unset(headers, "Connection");
    +}
    +
    +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
    + apr_bucket_brigade *header_brigade,
    + request_rec *r,
    + proxy_conn_rec *p_conn,
    + proxy_worker *worker,
    + proxy_server_conf *conf,
    + apr_uri_t *uri,
    + char *url, char *server_portstr,
    + char **old_cl_val,
    + char **old_te_val)
    +{
    + conn_rec *c = r->connection;
    + int counter;
    + char *buf;
    + const apr_array_header_t *headers_in_array;
    + const apr_table_entry_t *headers_in;
    + apr_table_t *headers_in_copy;
    + apr_bucket *e;
    + int do_100_continue;
    + conn_rec *origin = p_conn->connection;
    + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
    +
    + /*
    + * To be compliant, we only use 100-Continue for requests with bodies.
    + * We also make sure we won't be talking HTTP/1.0 as well.
    + */
    + do_100_continue = (worker->ping_timeout_set
    + && !r->header_only
    + && (apr_table_get(r->headers_in, "Content-Length")
    + || apr_table_get(r->headers_in, "Transfer-Encoding"))
    + && (PROXYREQ_REVERSE == r->proxyreq)
    + && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0")));
    +
    + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
    + /*
    + * According to RFC 2616 8.2.3 we are not allowed to forward an
    + * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
    + * a HTTP_EXPECTATION_FAILED
    + */
    + if (r->expecting_100) {
    + return HTTP_EXPECTATION_FAILED;
    + }
    + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
    + p_conn->close = 1;
    + } else {
    + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
    + }
    + if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
    + origin->keepalive = AP_CONN_CLOSE;
    + p_conn->close = 1;
    + }
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    + if (conf->preserve_host == 0) {
    + if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
    + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
    + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:",
    + uri->port_str, CRLF, NULL);
    + } else {
    + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL);
    + }
    + } else {
    + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
    + buf = apr_pstrcat(p, "Host: ", uri->hostname, ":",
    + uri->port_str, CRLF, NULL);
    + } else {
    + buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
    + }
    + }
    + }
    + else {
    + /* don't want to use r->hostname, as the incoming header might have a
    + * port attached
    + */
    + const char* hostname = apr_table_get(r->headers_in,"Host");
    + if (!hostname) {
    + hostname = r->server->server_hostname;
    + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AH01092: "
    + "no HTTP 0.9 request (with no host line) "
    + "on incoming request and preserve host set "
    + "forcing hostname to be %s for uri %s",
    + hostname, r->uri);
    + }
    + buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
    + }
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    +
    + /* handle Via */
    + if (conf->viaopt == via_block) {
    + /* Block all outgoing Via: headers */
    + apr_table_unset(r->headers_in, "Via");
    + } else if (conf->viaopt != via_off) {
    + const char *server_name = ap_get_server_name(r);
    + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
    + * then the server name returned by ap_get_server_name() is the
    + * origin server name (which does make too much sense with Via: headers)
    + * so we use the proxy vhost's name instead.
    + */
    + if (server_name == r->hostname)
    + server_name = r->server->server_hostname;
    + /* Create a "Via:" request header entry and merge it */
    + /* Generate outgoing Via: header with/without server comment: */
    + apr_table_mergen(r->headers_in, "Via",
    + (conf->viaopt == via_full)
    + ? apr_psprintf(p, "%d.%d %s%s (%s)",
    + HTTP_VERSION_MAJOR(r->proto_num),
    + HTTP_VERSION_MINOR(r->proto_num),
    + server_name, server_portstr,
    + AP_SERVER_BASEVERSION)
    + : apr_psprintf(p, "%d.%d %s%s",
    + HTTP_VERSION_MAJOR(r->proto_num),
    + HTTP_VERSION_MINOR(r->proto_num),
    + server_name, server_portstr)
    + );
    + }
    +
    + /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
    + * to backend
    + */
    + if (do_100_continue) {
    + apr_table_mergen(r->headers_in, "Expect", "100-Continue");
    + r->expecting_100 = 1;
    + }
    +
    + /* X-Forwarded-*: handling
    + *
    + * XXX Privacy Note:
    + * -----------------
    + *
    + * These request headers are only really useful when the mod_proxy
    + * is used in a reverse proxy configuration, so that useful info
    + * about the client can be passed through the reverse proxy and on
    + * to the backend server, which may require the information to
    + * function properly.
    + *
    + * In a forward proxy situation, these options are a potential
    + * privacy violation, as information about clients behind the proxy
    + * are revealed to arbitrary servers out there on the internet.
    + *
    + * The HTTP/1.1 Via: header is designed for passing client
    + * information through proxies to a server, and should be used in
    + * a forward proxy configuation instead of X-Forwarded-*. See the
    + * ProxyVia option for details.
    + */
    + if (PROXYREQ_REVERSE == r->proxyreq) {
    + const char *buf;
    +
    + /* Add X-Forwarded-For: so that the upstream has a chance to
    + * determine, where the original request came from.
    + */
    + apr_table_mergen(r->headers_in, "X-Forwarded-For",
    + c->remote_ip);
    +
    + /* Add X-Forwarded-Host: so that upstream knows what the
    + * original request hostname was.
    + */
    + if ((buf = apr_table_get(r->headers_in, "Host"))) {
    + apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
    + }
    +
    + /* Add X-Forwarded-Server: so that upstream knows what the
    + * name of this proxy server is (if there are more than one)
    + * XXX: This duplicates Via: - do we strictly need it?
    + */
    + apr_table_mergen(r->headers_in, "X-Forwarded-Server",
    + r->server->server_hostname);
    + }
    +
    + proxy_run_fixups(r);
    + /*
    + * Make a copy of the headers_in table before clearing the connection
    + * headers as we need the connection headers later in the http output
    + * filter to prepare the correct response headers.
    + *
    + * Note: We need to take r->pool for apr_table_copy as the key / value
    + * pairs in r->headers_in have been created out of r->pool and
    + * p might be (and actually is) a longer living pool.
    + * This would trigger the bad pool ancestry abort in apr_table_copy if
    + * apr is compiled with APR_POOL_DEBUG.
    + */
    + headers_in_copy = apr_table_copy(r->pool, r->headers_in);
    + proxy_clear_connection(p, headers_in_copy);
    + /* send request headers */
    + headers_in_array = apr_table_elts(headers_in_copy);
    + headers_in = (const apr_table_entry_t *) headers_in_array->elts;
    + for (counter = 0; counter < headers_in_array->nelts; counter++) {
    + if (headers_in[counter].key == NULL
    + || headers_in[counter].val == NULL
    +
    + /* Already sent */
    + || !strcasecmp(headers_in[counter].key, "Host")
    +
    + /* Clear out hop-by-hop request headers not to send
    + * RFC2616 13.5.1 says we should strip these headers
    + */
    + || !strcasecmp(headers_in[counter].key, "Keep-Alive")
    + || !strcasecmp(headers_in[counter].key, "TE")
    + || !strcasecmp(headers_in[counter].key, "Trailer")
    + || !strcasecmp(headers_in[counter].key, "Upgrade")
    +
    + ) {
    + continue;
    + }
    + /* Do we want to strip Proxy-Authorization ?
    + * If we haven't used it, then NO
    + * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
    + * So let's make it configurable by env.
    + */
    + if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) {
    + if (r->user != NULL) { /* we've authenticated */
    + if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
    + continue;
    + }
    + }
    + }
    +
    + /* Skip Transfer-Encoding and Content-Length for now.
    + */
    + if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) {
    + *old_te_val = headers_in[counter].val;
    + continue;
    + }
    + if (!strcasecmp(headers_in[counter].key, "Content-Length")) {
    + *old_cl_val = headers_in[counter].val;
    + continue;
    + }
    +
    + /* for sub-requests, ignore freshness/expiry headers */
    + if (r->main) {
    + if ( !strcasecmp(headers_in[counter].key, "If-Match")
    + || !strcasecmp(headers_in[counter].key, "If-Modified-Since")
    + || !strcasecmp(headers_in[counter].key, "If-Range")
    + || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since")
    + || !strcasecmp(headers_in[counter].key, "If-None-Match")) {
    + continue;
    + }
    + }
    +
    + buf = apr_pstrcat(p, headers_in[counter].key, ": ",
    + headers_in[counter].val, CRLF,
    + NULL);
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    + }
    + return OK;
    +}
    +
    +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
    + request_rec *r, proxy_conn_rec *p_conn,
    + conn_rec *origin, apr_bucket_brigade *bb,
    + int flush)
    +{
    + apr_status_t status;
    + apr_off_t transferred;
    +
    + if (flush) {
    + apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(bb, e);
    + }
    + apr_brigade_length(bb, 0, &transferred);
    + if (transferred != -1)
    + p_conn->worker->s->transferred += transferred;
    + status = ap_pass_brigade(origin->output_filters, bb);
    + if (status != APR_SUCCESS) {
    + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "AH01084: "
    + "pass request body failed to %pI (%s)",
    + p_conn->addr, p_conn->hostname);
    + if (origin->aborted) {
    + const char *ssl_note;
    +
    + if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv"))
    + != NULL) && (strcmp(ssl_note, "err") == 0)) {
    + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
    + "Error during SSL Handshake with"
    + " remote server");
    + }
    + return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY;
    + }
    + else {
    + return HTTP_BAD_REQUEST;
    + }
    + }
    + apr_brigade_cleanup(bb);
    + return OK;
    +}
  9. vitkin revised this gist Sep 22, 2013. 1 changed file with 32 additions and 0 deletions.
    32 changes: 32 additions & 0 deletions README
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,32 @@
    # Backport WebSocket to Apache 2.2.2x

    This is my variation from the original patch (http://cafarelli.fr/gentoo/apache-2.2.24-wstunnel.patch).
    The difference is that I avoid modifying the `mod_utils.c` and the `mod_proxy.h` so that if the `mod_proxy` module has been embedded in the main 'httpd' binary then it will work and you won't get the `error: undefined symbol: ap_proxy_pass_brigade`.
    Indeed you can check that the module `mod_proxy` is part of Apache main binary by running the command:
    httpd -l | grep mod_proxy

    ## Instructions:

    # Checkout apache source ( Replace 'x' with the right number.)
    svn checkout http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.2x/ httpd-2.2.2x
    cd httpd-2.2.2x

    # Get patch and apply it

    patch -p1 -i ../apache-2.2.2x-wstunnel.patch

    # Build Apache
    autoconf
    ./configure --enable-so --enable-proxy --enable-proxy-wstunnel=shared
    make

    Just copy the resulting file `modules/proxy/.libs/mod_proxy_wstunnel.so` to your Apache modules folder and follow the instructions from any of the hereunder references.

    Don't forget to check that the configuration is OK by running:
    apachectl configtest

    ## References:

    * http://www.amoss.me.uk/2013/06/apache-2-2-websocket-proxying-ubuntu-mod_proxy_wstunnel/
    * http://blog.cafarelli.fr/post/2013/04/26/Backporting-Apache-support-for-websockets-reverse-proxy-%28aka-getting-GateOne-to-work-behind-Apache%29
    * https://i.rationa.li/mark/note/co97okgZSC-bIvONa5ai6Q
  10. vitkin created this gist Sep 22, 2013.
    1,573 changes: 1,573 additions & 0 deletions apache-2.2.2x-wstunnel.patch
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1573 @@
    diff -Naur a/modules/proxy/config.m4 b/modules/proxy/config.m4
    --- a/modules/proxy/config.m4 Sun Sep 22 19:01:23 2013
    +++ b/modules/proxy/config.m4 Sun Sep 22 19:00:58 2013
    @@ -18,6 +18,7 @@
    proxy_http_objs="mod_proxy_http.lo"
    proxy_scgi_objs="mod_proxy_scgi.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_balancer_objs="mod_proxy_balancer.lo"

    case "$host" in
    @@ -29,6 +30,7 @@
    proxy_http_objs="$proxy_http_objs mod_proxy.la"
    proxy_scgi_objs="$proxy_scgi_objs mod_proxy.la"
    proxy_ajp_objs="$proxy_ajp_objs mod_proxy.la"
    + proxy_wstunnel_objs="$proxy_wstunnel_objs mod_proxy.la"
    proxy_balancer_objs="$proxy_balancer_objs mod_proxy.la"
    ;;
    esac
    @@ -37,6 +39,7 @@
    APACHE_MODULE(proxy_ftp, Apache proxy FTP module, $proxy_ftp_objs, , $proxy_mods_enable)
    APACHE_MODULE(proxy_http, Apache proxy HTTP module, $proxy_http_objs, , $proxy_mods_enable)
    APACHE_MODULE(proxy_scgi, Apache proxy SCGI module, $proxy_scgi_objs, , $proxy_mods_enable)
    +APACHE_MODULE(proxy_wstunnel, Apache proxy Websocket Tunnel module, $proxy_wstunnel_objs, , $proxy_mods_enable)
    APACHE_MODULE(proxy_ajp, Apache proxy AJP module, $proxy_ajp_objs, , $proxy_mods_enable)
    APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module, $proxy_balancer_objs, , $proxy_mods_enable)
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.h b/modules/proxy/mod_proxy_wstunnel.h
    --- a/modules/proxy/mod_proxy_wstunnel.h Sun Sep 22 19:11:28 2013
    +++ b/modules/proxy/mod_proxy_wstunnel.h Sun Sep 22 19:00:58 2013
    @@ -0,0 +1,88 @@
    +/* 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.
    + */
    +
    +#ifndef MOD_PROXY_WSTUNNEL_H
    +#define MOD_PROXY_WSTUNNEL_H
    +
    +/**
    + * @file mod_proxy_wstunnel.h
    + * @brief Proxy Extension Module for Apache
    + *
    + * @defgroup MOD_PROXY_WSTUNNEL mod_proxy_wstunnel
    + * @ingroup APACHE_MODS
    + * @{
    + */
    +
    +/*
    +
    + Also note numerous FIXMEs and CHECKMEs which should be eliminated.
    +
    + This code is once again experimental!
    +
    + Things to do:
    +
    + 1. Make it completely work (for FTP too)
    +
    + 2. HTTP/1.1
    +
    + Chuck Murcko <chuck@topsail.org> 02-06-01
    +
    + */
    +
    +#include "mod_proxy.h"
    +
    +/**
    + * Create a HTTP request header brigade, old_cl_val and old_te_val as required.
    + * @parama p pool
    + * @param header_brigade header brigade to use/fill
    + * @param r request
    + * @param p_conn proxy connection rec
    + * @param worker selected worker
    + * @param conf per-server proxy config
    + * @param uri uri
    + * @param url url
    + * @param server_portstr port as string
    + * @param old_cl_val stored old content-len val
    + * @param old_te_val stored old TE val
    + * @return OK or HTTP_EXPECTATION_FAILED
    + */
    +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
    + apr_bucket_brigade *header_brigade,
    + request_rec *r,
    + proxy_conn_rec *p_conn,
    + proxy_worker *worker,
    + proxy_server_conf *conf,
    + apr_uri_t *uri,
    + char *url, char *server_portstr,
    + char **old_cl_val,
    + char **old_te_val);
    +
    +/**
    + * @param bucket_alloc bucket allocator
    + * @param r request
    + * @param p_conn proxy connection
    + * @param origin connection rec of origin
    + * @param bb brigade to send to origin
    + * @param flush flush
    + * @return status (OK)
    + */
    +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
    + request_rec *r, proxy_conn_rec *p_conn,
    + conn_rec *origin, apr_bucket_brigade *bb,
    + int flush);
    +
    +#endif /*MOD_PROXY_WSTUNNEL_H*/
    +/** @} */
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c
    --- a/modules/proxy/mod_proxy_wstunnel.c Sun Sep 22 19:11:33 2013
    +++ b/modules/proxy/mod_proxy_wstunnel.c Sun Sep 22 19:00:58 2013
    @@ -0,0 +1,726 @@
    +/* 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_wstunnel.h"
    +
    +module AP_MODULE_DECLARE_DATA proxy_wstunnel_module;
    +
    +/* Clear all connection-based headers from the incoming headers table */
    +typedef struct header_dptr {
    + apr_pool_t *pool;
    + apr_table_t *table;
    + apr_time_t time;
    +} header_dptr;
    +
    +static int clear_conn_headers(void *data, const char *key, const char *val)
    +{
    + apr_table_t *headers = ((header_dptr*)data)->table;
    + apr_pool_t *pool = ((header_dptr*)data)->pool;
    + const char *name;
    + char *next = apr_pstrdup(pool, val);
    + while (*next) {
    + name = next;
    + while (*next && !apr_isspace(*next) && (*next != ',')) {
    + ++next;
    + }
    + while (*next && (apr_isspace(*next) || (*next == ','))) {
    + *next++ = '\0';
    + }
    + apr_table_unset(headers, name);
    + }
    + return 1;
    +}
    +
    +static void proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
    +{
    + header_dptr x;
    + x.pool = p;
    + x.table = headers;
    + apr_table_unset(headers, "Proxy-Connection");
    + apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL);
    + apr_table_unset(headers, "Connection");
    +}
    +
    +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
    + apr_bucket_brigade *header_brigade,
    + request_rec *r,
    + proxy_conn_rec *p_conn,
    + proxy_worker *worker,
    + proxy_server_conf *conf,
    + apr_uri_t *uri,
    + char *url, char *server_portstr,
    + char **old_cl_val,
    + char **old_te_val)
    +{
    + conn_rec *c = r->connection;
    + int counter;
    + char *buf;
    + const apr_array_header_t *headers_in_array;
    + const apr_table_entry_t *headers_in;
    + apr_table_t *headers_in_copy;
    + apr_bucket *e;
    + int do_100_continue;
    + conn_rec *origin = p_conn->connection;
    + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
    +
    + /*
    + * To be compliant, we only use 100-Continue for requests with bodies.
    + * We also make sure we won't be talking HTTP/1.0 as well.
    + */
    + do_100_continue = (worker->ping_timeout_set
    + && !r->header_only
    + && (apr_table_get(r->headers_in, "Content-Length")
    + || apr_table_get(r->headers_in, "Transfer-Encoding"))
    + && (PROXYREQ_REVERSE == r->proxyreq)
    + && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0")));
    +
    + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
    + /*
    + * According to RFC 2616 8.2.3 we are not allowed to forward an
    + * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
    + * a HTTP_EXPECTATION_FAILED
    + */
    + if (r->expecting_100) {
    + return HTTP_EXPECTATION_FAILED;
    + }
    + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
    + p_conn->close = 1;
    + } else {
    + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
    + }
    + if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
    + origin->keepalive = AP_CONN_CLOSE;
    + p_conn->close = 1;
    + }
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    + if (conf->preserve_host == 0) {
    + if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
    + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
    + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:",
    + uri->port_str, CRLF, NULL);
    + } else {
    + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL);
    + }
    + } else {
    + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
    + buf = apr_pstrcat(p, "Host: ", uri->hostname, ":",
    + uri->port_str, CRLF, NULL);
    + } else {
    + buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
    + }
    + }
    + }
    + else {
    + /* don't want to use r->hostname, as the incoming header might have a
    + * port attached
    + */
    + const char* hostname = apr_table_get(r->headers_in,"Host");
    + if (!hostname) {
    + hostname = r->server->server_hostname;
    + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AH01092: "
    + "no HTTP 0.9 request (with no host line) "
    + "on incoming request and preserve host set "
    + "forcing hostname to be %s for uri %s",
    + hostname, r->uri);
    + }
    + buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
    + }
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    +
    + /* handle Via */
    + if (conf->viaopt == via_block) {
    + /* Block all outgoing Via: headers */
    + apr_table_unset(r->headers_in, "Via");
    + } else if (conf->viaopt != via_off) {
    + const char *server_name = ap_get_server_name(r);
    + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
    + * then the server name returned by ap_get_server_name() is the
    + * origin server name (which does make too much sense with Via: headers)
    + * so we use the proxy vhost's name instead.
    + */
    + if (server_name == r->hostname)
    + server_name = r->server->server_hostname;
    + /* Create a "Via:" request header entry and merge it */
    + /* Generate outgoing Via: header with/without server comment: */
    + apr_table_mergen(r->headers_in, "Via",
    + (conf->viaopt == via_full)
    + ? apr_psprintf(p, "%d.%d %s%s (%s)",
    + HTTP_VERSION_MAJOR(r->proto_num),
    + HTTP_VERSION_MINOR(r->proto_num),
    + server_name, server_portstr,
    + AP_SERVER_BASEVERSION)
    + : apr_psprintf(p, "%d.%d %s%s",
    + HTTP_VERSION_MAJOR(r->proto_num),
    + HTTP_VERSION_MINOR(r->proto_num),
    + server_name, server_portstr)
    + );
    + }
    +
    + /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
    + * to backend
    + */
    + if (do_100_continue) {
    + apr_table_mergen(r->headers_in, "Expect", "100-Continue");
    + r->expecting_100 = 1;
    + }
    +
    + /* X-Forwarded-*: handling
    + *
    + * XXX Privacy Note:
    + * -----------------
    + *
    + * These request headers are only really useful when the mod_proxy
    + * is used in a reverse proxy configuration, so that useful info
    + * about the client can be passed through the reverse proxy and on
    + * to the backend server, which may require the information to
    + * function properly.
    + *
    + * In a forward proxy situation, these options are a potential
    + * privacy violation, as information about clients behind the proxy
    + * are revealed to arbitrary servers out there on the internet.
    + *
    + * The HTTP/1.1 Via: header is designed for passing client
    + * information through proxies to a server, and should be used in
    + * a forward proxy configuation instead of X-Forwarded-*. See the
    + * ProxyVia option for details.
    + */
    + if (PROXYREQ_REVERSE == r->proxyreq) {
    + const char *buf;
    +
    + /* Add X-Forwarded-For: so that the upstream has a chance to
    + * determine, where the original request came from.
    + */
    + apr_table_mergen(r->headers_in, "X-Forwarded-For",
    + c->remote_ip);
    +
    + /* Add X-Forwarded-Host: so that upstream knows what the
    + * original request hostname was.
    + */
    + if ((buf = apr_table_get(r->headers_in, "Host"))) {
    + apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
    + }
    +
    + /* Add X-Forwarded-Server: so that upstream knows what the
    + * name of this proxy server is (if there are more than one)
    + * XXX: This duplicates Via: - do we strictly need it?
    + */
    + apr_table_mergen(r->headers_in, "X-Forwarded-Server",
    + r->server->server_hostname);
    + }
    +
    + proxy_run_fixups(r);
    + /*
    + * Make a copy of the headers_in table before clearing the connection
    + * headers as we need the connection headers later in the http output
    + * filter to prepare the correct response headers.
    + *
    + * Note: We need to take r->pool for apr_table_copy as the key / value
    + * pairs in r->headers_in have been created out of r->pool and
    + * p might be (and actually is) a longer living pool.
    + * This would trigger the bad pool ancestry abort in apr_table_copy if
    + * apr is compiled with APR_POOL_DEBUG.
    + */
    + headers_in_copy = apr_table_copy(r->pool, r->headers_in);
    + proxy_clear_connection(p, headers_in_copy);
    + /* send request headers */
    + headers_in_array = apr_table_elts(headers_in_copy);
    + headers_in = (const apr_table_entry_t *) headers_in_array->elts;
    + for (counter = 0; counter < headers_in_array->nelts; counter++) {
    + if (headers_in[counter].key == NULL
    + || headers_in[counter].val == NULL
    +
    + /* Already sent */
    + || !strcasecmp(headers_in[counter].key, "Host")
    +
    + /* Clear out hop-by-hop request headers not to send
    + * RFC2616 13.5.1 says we should strip these headers
    + */
    + || !strcasecmp(headers_in[counter].key, "Keep-Alive")
    + || !strcasecmp(headers_in[counter].key, "TE")
    + || !strcasecmp(headers_in[counter].key, "Trailer")
    + || !strcasecmp(headers_in[counter].key, "Upgrade")
    +
    + ) {
    + continue;
    + }
    + /* Do we want to strip Proxy-Authorization ?
    + * If we haven't used it, then NO
    + * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
    + * So let's make it configurable by env.
    + */
    + if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) {
    + if (r->user != NULL) { /* we've authenticated */
    + if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
    + continue;
    + }
    + }
    + }
    +
    + /* Skip Transfer-Encoding and Content-Length for now.
    + */
    + if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) {
    + *old_te_val = headers_in[counter].val;
    + continue;
    + }
    + if (!strcasecmp(headers_in[counter].key, "Content-Length")) {
    + *old_cl_val = headers_in[counter].val;
    + continue;
    + }
    +
    + /* for sub-requests, ignore freshness/expiry headers */
    + if (r->main) {
    + if ( !strcasecmp(headers_in[counter].key, "If-Match")
    + || !strcasecmp(headers_in[counter].key, "If-Modified-Since")
    + || !strcasecmp(headers_in[counter].key, "If-Range")
    + || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since")
    + || !strcasecmp(headers_in[counter].key, "If-None-Match")) {
    + continue;
    + }
    + }
    +
    + buf = apr_pstrcat(p, headers_in[counter].key, ": ",
    + headers_in[counter].val, CRLF,
    + NULL);
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    + }
    + return OK;
    +}
    +
    +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
    + request_rec *r, proxy_conn_rec *p_conn,
    + conn_rec *origin, apr_bucket_brigade *bb,
    + int flush)
    +{
    + apr_status_t status;
    + apr_off_t transferred;
    +
    + if (flush) {
    + apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(bb, e);
    + }
    + apr_brigade_length(bb, 0, &transferred);
    + if (transferred != -1)
    + p_conn->worker->s->transferred += transferred;
    + status = ap_pass_brigade(origin->output_filters, bb);
    + if (status != APR_SUCCESS) {
    + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "AH01084: "
    + "pass request body failed to %pI (%s)",
    + p_conn->addr, p_conn->hostname);
    + if (origin->aborted) {
    + const char *ssl_note;
    +
    + if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv"))
    + != NULL) && (strcmp(ssl_note, "err") == 0)) {
    + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
    + "Error during SSL Handshake with"
    + " remote server");
    + }
    + return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY;
    + }
    + else {
    + return HTTP_BAD_REQUEST;
    + }
    + }
    + apr_brigade_cleanup(bb);
    + return OK;
    +}
    +
    +/*
    + * Canonicalise http-like URLs.
    + * scheme is the scheme for the URL
    + * url is the URL starting with the first '/'
    + * def_port is the default port for this scheme.
    + */
    +static int proxy_wstunnel_canon(request_rec *r, char *url)
    +{
    + char *host, *path, sport[7];
    + char *search = NULL;
    + const char *err;
    + char *scheme;
    + apr_port_t port, def_port;
    +
    + /* ap_port_of_scheme() */
    + if (strncasecmp(url, "ws:", 3) == 0) {
    + url += 3;
    + scheme = "ws:";
    + def_port = apr_uri_port_of_scheme("http");
    + }
    + else if (strncasecmp(url, "wss:", 4) == 0) {
    + url += 4;
    + scheme = "wss:";
    + def_port = apr_uri_port_of_scheme("https");
    + }
    + else {
    + return DECLINED;
    + }
    +
    + port = def_port;
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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, "AH02439: " "error parsing URL %s: %s",
    + url, err);
    + return HTTP_BAD_REQUEST;
    + }
    +
    + /*
    + * now parse path/search args, according to rfc1738:
    + * process the path. With proxy-nocanon set (by
    + * mod_proxy) we use the raw, unparsed uri
    + */
    + if (apr_table_get(r->notes, "proxy-nocanon")) {
    + path = url; /* this is the raw path */
    + }
    + else {
    + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
    + r->proxyreq);
    + search = r->args;
    + }
    + if (path == NULL)
    + 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,
    + "/", path, (search) ? "?" : "",
    + (search) ? search : "", NULL);
    + return OK;
    +}
    +
    +
    +static int proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
    + apr_bucket_brigade *bb, char *name)
    +{
    + int rv;
    +#ifdef DEBUGGING
    + apr_off_t len;
    +#endif
    +
    + do {
    + apr_brigade_cleanup(bb);
    + rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES,
    + APR_NONBLOCK_READ, AP_IOBUFSIZE);
    + if (rv == APR_SUCCESS) {
    + if (c_o->aborted)
    + return APR_EPIPE;
    + if (APR_BRIGADE_EMPTY(bb))
    + break;
    +#ifdef DEBUGGING
    + len = -1;
    + apr_brigade_length(bb, 0, &len);
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "AH02440: "
    + "read %" APR_OFF_T_FMT
    + " bytes from %s", len, name);
    +#endif
    + rv = ap_pass_brigade(c_o->output_filters, bb);
    + if (rv == APR_SUCCESS) {
    + ap_fflush(c_o->output_filters, bb);
    + }
    + else {
    + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "AH02441: "
    + "error on %s - ap_pass_brigade",
    + name);
    + }
    + } else if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "AH02442: "
    + "error on %s - ap_get_brigade",
    + name);
    + }
    + } while (rv == APR_SUCCESS);
    +
    + if (APR_STATUS_IS_EAGAIN(rv)) {
    + rv = APR_SUCCESS;
    + }
    + return rv;
    +}
    +
    +/* Search thru the input filters and remove the reqtimeout one */
    +static void remove_reqtimeout(ap_filter_t *next)
    +{
    + ap_filter_t *reqto = NULL;
    + ap_filter_rec_t *filter;
    +
    + filter = ap_get_input_filter_handle("reqtimeout");
    + if (!filter) {
    + return;
    + }
    +
    + while (next) {
    + if (next->frec == filter) {
    + reqto = next;
    + break;
    + }
    + next = next->next;
    + }
    + if (reqto) {
    + ap_remove_input_filter(reqto);
    + }
    +}
    +
    +/*
    + * process the request and write the response.
    + */
    +static int ap_proxy_wstunnel_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)
    +{
    + apr_status_t rv = APR_SUCCESS;
    + apr_pollset_t *pollset;
    + apr_pollfd_t pollfd;
    + const apr_pollfd_t *signalled;
    + apr_int32_t pollcnt, pi;
    + apr_int16_t pollevent;
    + conn_rec *c = r->connection;
    + apr_socket_t *sock = conn->sock;
    + conn_rec *backconn = conn->connection;
    + int client_error = 0;
    + char *buf;
    + apr_bucket_brigade *header_brigade;
    + apr_bucket *e;
    + char *old_cl_val = NULL;
    + char *old_te_val = NULL;
    + apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
    + apr_socket_t *client_socket = ap_get_module_config(c->conn_config, &core_module);
    +
    + header_brigade = apr_brigade_create(p, backconn->bucket_alloc);
    +
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "sending request");
    +
    + rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, conn,
    + worker, conf, uri, url, server_portstr,
    + &old_cl_val, &old_te_val);
    + if (rv != OK) {
    + return rv;
    + }
    +
    + buf = apr_pstrcat(p, "Upgrade: WebSocket", CRLF, "Connection: Upgrade", CRLF, CRLF, NULL);
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    +
    + if ((rv = ap_proxy_pass_brigade(c->bucket_alloc, r, conn, backconn,
    + header_brigade, 1)) != OK)
    + return rv;
    +
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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, "AH02443: "
    + "error apr_pollset_create()");
    + return HTTP_INTERNAL_SERVER_ERROR;
    + }
    +
    +#if 0
    + apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1);
    + apr_socket_opt_set(sock, APR_SO_KEEPALIVE, 1);
    + apr_socket_opt_set(client_socket, APR_SO_NONBLOCK, 1);
    + apr_socket_opt_set(client_socket, APR_SO_KEEPALIVE, 1);
    +#endif
    +
    + pollfd.p = p;
    + pollfd.desc_type = APR_POLL_SOCKET;
    + pollfd.reqevents = APR_POLLIN;
    + pollfd.desc.s = sock;
    + pollfd.client_data = NULL;
    + apr_pollset_add(pollset, &pollfd);
    +
    + pollfd.desc.s = client_socket;
    + apr_pollset_add(pollset, &pollfd);
    +
    +
    + r->output_filters = c->output_filters;
    + r->proto_output_filters = c->output_filters;
    + r->input_filters = c->input_filters;
    + r->proto_input_filters = c->input_filters;
    +
    + remove_reqtimeout(r->input_filters);
    +
    + while (1) { /* Infinite loop until error (one side closes the connection) */
    + if ((rv = apr_pollset_poll(pollset, -1, &pollcnt, &signalled))
    + != APR_SUCCESS) {
    + if (APR_STATUS_IS_EINTR(rv)) {
    + continue;
    + }
    + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "AH02444: " "error apr_poll()");
    + return HTTP_INTERNAL_SERVER_ERROR;
    + }
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "AH02445: "
    + "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) {
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "AH02446: "
    + "sock was readable");
    + rv = proxy_wstunnel_transfer(r, backconn, c, bb, "sock");
    + }
    + else if ((pollevent & APR_POLLERR)
    + || (pollevent & APR_POLLHUP)) {
    + rv = APR_EPIPE;
    + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "AH02447: "
    + "err/hup on backconn");
    + }
    + if (rv != APR_SUCCESS)
    + client_error = 1;
    + }
    + else if (cur->desc.s == client_socket) {
    + pollevent = cur->rtnevents;
    + if (pollevent & APR_POLLIN) {
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "AH02448: "
    + "client was readable");
    + rv = proxy_wstunnel_transfer(r, c, backconn, bb, "client");
    + }
    + }
    + else {
    + rv = APR_EBADF;
    + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "AH02449: "
    + "unknown socket in pollset");
    + }
    +
    + }
    + if (rv != APR_SUCCESS) {
    + break;
    + }
    + }
    +
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
    + "finished with poll() - cleaning up");
    +
    + if (client_error) {
    + return HTTP_INTERNAL_SERVER_ERROR;
    + }
    + return OK;
    +}
    +
    +/*
    + */
    +static int proxy_wstunnel_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;
    + int retry;
    + conn_rec *c = r->connection;
    + apr_pool_t *p = r->pool;
    + apr_uri_t *uri;
    +
    + if (strncasecmp(url, "wss:", 4) == 0) {
    + scheme = "WSS";
    + }
    + else if (strncasecmp(url, "ws:", 3) == 0) {
    + scheme = "WS";
    + }
    + else {
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "AH02450: " "declining URL %s", url);
    + return DECLINED;
    + }
    +
    + uri = apr_palloc(p, sizeof(*uri));
    + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "AH02451: " "serving URL %s", url);
    +
    + /* create space for state information */
    + status = ap_proxy_acquire_connection(scheme, &backend, worker,
    + r->server);
    + if (status != OK) {
    + if (backend) {
    + backend->close = 1;
    + ap_proxy_release_connection(scheme, backend, r->server);
    + }
    + return status;
    + }
    +
    + backend->is_ssl = 0;
    + backend->close = 0;
    +
    + retry = 0;
    + while (retry < 2) {
    + char *locurl = url;
    + /* 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)
    + break;
    +
    + /* Step Two: Make the Connection */
    + if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
    + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "AH02452: "
    + "failed to make connection to backend: %s",
    + backend->hostname);
    + status = HTTP_SERVICE_UNAVAILABLE;
    + break;
    + }
    + /* Step Three: Create conn_rec */
    + if (!backend->connection) {
    + if ((status = ap_proxy_connection_create(scheme, backend,
    + c, r->server)) != OK)
    + break;
    + }
    +
    + /* Step Three: Process the Request */
    + status = ap_proxy_wstunnel_request(p, r, backend, worker, conf, uri, locurl,
    + server_portstr);
    + break;
    + }
    +
    + /* Do not close the socket */
    + ap_proxy_release_connection(scheme, backend, r->server);
    + return status;
    +}
    +
    +static void ap_proxy_http_register_hook(apr_pool_t *p)
    +{
    + proxy_hook_scheme_handler(proxy_wstunnel_handler, NULL, NULL, APR_HOOK_FIRST);
    + proxy_hook_canon_handler(proxy_wstunnel_canon, NULL, NULL, APR_HOOK_FIRST);
    +}
    +
    +APLOG_USE_MODULE(proxy_wstunnel);
    +module AP_MODULE_DECLARE_DATA proxy_wstunnel_module = {
    + 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 */
    + NULL, /* command apr_table_t */
    + ap_proxy_http_register_hook /* register hooks */
    +};
    diff -Naur a/modules/proxy/mod_proxy_wstunnel.dsp b/modules/proxy/mod_proxy_wstunnel.dsp
    --- a/modules/proxy/mod_proxy_wstunnel.dsp Sun Sep 22 19:36:10 2013
    +++ b/modules/proxy/mod_proxy_wstunnel.dsp Sun Sep 22 19:00:58 2013
    @@ -0,0 +1,123 @@
    +# Microsoft Developer Studio Project File - Name="mod_proxy_wstunnel" - Package Owner=<4>
    +# Microsoft Developer Studio Generated Build File, Format Version 6.00
    +# ** DO NOT EDIT **
    +
    +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
    +
    +CFG=mod_proxy_wstunnel - Win32 Release
    +!MESSAGE This is not a valid makefile. To build this project using NMAKE,
    +!MESSAGE use the Export Makefile command and run
    +!MESSAGE
    +!MESSAGE NMAKE /f "mod_proxy_wstunnel.mak".
    +!MESSAGE
    +!MESSAGE You can specify a configuration when running NMAKE
    +!MESSAGE by defining the macro CFG on the command line. For example:
    +!MESSAGE
    +!MESSAGE NMAKE /f "mod_proxy_wstunnel.mak" CFG="mod_proxy_wstunnel - Win32 Release"
    +!MESSAGE
    +!MESSAGE Possible choices for configuration are:
    +!MESSAGE
    +!MESSAGE "mod_proxy_wstunnel - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
    +!MESSAGE "mod_proxy_wstunnel - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
    +!MESSAGE
    +
    +# Begin Project
    +# PROP AllowPerConfigDependencies 0
    +# PROP Scc_ProjName ""
    +# PROP Scc_LocalPath ""
    +CPP=cl.exe
    +MTL=midl.exe
    +RSC=rc.exe
    +
    +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release"
    +
    +# PROP BASE Use_MFC 0
    +# PROP BASE Use_Debug_Libraries 0
    +# PROP BASE Output_Dir "Release"
    +# PROP BASE Intermediate_Dir "Release"
    +# PROP BASE Target_Dir ""
    +# PROP Use_MFC 0
    +# PROP Use_Debug_Libraries 0
    +# PROP Output_Dir "Release"
    +# PROP Intermediate_Dir "Release"
    +# PROP Ignore_Export_Lib 0
    +# PROP Target_Dir ""
    +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
    +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_wstunnel_src" /FD /c
    +# ADD BASE MTL /nologo /D "NDEBUG" /win32
    +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
    +# ADD BASE RSC /l 0x809 /d "NDEBUG"
    +# ADD RSC /l 0x409 /fo"Release/mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache"
    +BSC32=bscmake.exe
    +# ADD BASE BSC32 /nologo
    +# ADD BSC32 /nologo
    +LINK32=link.exe
    +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so
    +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so /opt:ref
    +# Begin Special Build Tool
    +TargetPath=.\Release\mod_proxy_wstunnel.so
    +SOURCE="$(InputPath)"
    +PostBuild_Desc=Embed .manifest
    +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
    +# End Special Build Tool
    +
    +!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug"
    +
    +# PROP BASE Use_MFC 0
    +# PROP BASE Use_Debug_Libraries 1
    +# PROP BASE Output_Dir "Debug"
    +# PROP BASE Intermediate_Dir "Debug"
    +# PROP BASE Target_Dir ""
    +# PROP Use_MFC 0
    +# PROP Use_Debug_Libraries 1
    +# PROP Output_Dir "Debug"
    +# PROP Intermediate_Dir "Debug"
    +# PROP Ignore_Export_Lib 0
    +# PROP Target_Dir ""
    +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
    +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_wstunnel_src" /FD /c
    +# ADD BASE MTL /nologo /D "_DEBUG" /win32
    +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
    +# ADD BASE RSC /l 0x809 /d "_DEBUG"
    +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache"
    +BSC32=bscmake.exe
    +# ADD BASE BSC32 /nologo
    +# ADD BSC32 /nologo
    +LINK32=link.exe
    +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so
    +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so
    +# Begin Special Build Tool
    +TargetPath=.\Debug\mod_proxy_wstunnel.so
    +SOURCE="$(InputPath)"
    +PostBuild_Desc=Embed .manifest
    +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
    +# End Special Build Tool
    +
    +!ENDIF
    +
    +# Begin Target
    +
    +# Name "mod_proxy_wstunnel - Win32 Release"
    +# Name "mod_proxy_wstunnel - Win32 Debug"
    +# Begin Group "Source Files"
    +
    +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
    +# Begin Source File
    +
    +SOURCE=.\mod_proxy_wstunnel.c
    +# End Source File
    +# End Group
    +# Begin Group "Header Files"
    +
    +# PROP Default_Filter ".h"
    +# Begin Source File
    +
    +SOURCE=.\mod_proxy.h
    +# End Source File
    +# End Group
    +# Begin Source File
    +
    +SOURCE=..\..\build\win32\httpd.rc
    +# End Source File
    +# End Target
    +# End Project
    diff -Naur a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile
    --- a/modules/proxy/NWGNUmakefile 2013-04-25 21:08:56.417441322 +0200
    +++ b/modules/proxy/NWGNUmakefile 2013-04-25 21:08:58.480811797 +0200
    @@ -159,6 +159,7 @@
    $(OBJDIR)/proxybalancer.nlm \
    $(OBJDIR)/proxyajp.nlm \
    $(OBJDIR)/proxyscgi.nlm \
    + $(OBJDIR)/proxywstunnel.nlm \
    $(EOLIST)

    #
    diff -Naur a/modules/proxy/NWGNUproxywstunnel b/modules/proxy/NWGNUproxywstunnel
    --- a/modules/proxy/NWGNUproxywstunnel 1970-01-01 01:00:00.000000000 +0100
    +++ b/modules/proxy/NWGNUproxywstunnel 2013-04-25 21:08:58.480811797 +0200
    @@ -0,0 +1,250 @@
    +#
    +# Make sure all needed macro's are defined
    +#
    +
    +#
    +# Get the 'head' of the build environment if necessary. This includes default
    +# targets and paths to tools
    +#
    +
    +ifndef EnvironmentDefined
    +include $(AP_WORK)/build/NWGNUhead.inc
    +endif
    +
    +#
    +# These directories will be at the beginning of the include list, followed by
    +# INCDIRS
    +#
    +XINCDIRS += \
    + $(APR)/include \
    + $(APRUTIL)/include \
    + $(SRC)/include \
    + $(STDMOD)/http \
    + $(STDMOD)/proxy \
    + $(NWOS) \
    + $(EOLIST)
    +
    +#
    +# These flags will come after CFLAGS
    +#
    +XCFLAGS += \
    + $(EOLIST)
    +
    +#
    +# These defines will come after DEFINES
    +#
    +XDEFINES += \
    + $(EOLIST)
    +
    +#
    +# These flags will be added to the link.opt file
    +#
    +XLFLAGS += \
    + $(EOLIST)
    +
    +#
    +# These values will be appended to the correct variables based on the value of
    +# RELEASE
    +#
    +ifeq "$(RELEASE)" "debug"
    +XINCDIRS += \
    + $(EOLIST)
    +
    +XCFLAGS += \
    + $(EOLIST)
    +
    +XDEFINES += \
    + $(EOLIST)
    +
    +XLFLAGS += \
    + $(EOLIST)
    +endif
    +
    +ifeq "$(RELEASE)" "noopt"
    +XINCDIRS += \
    + $(EOLIST)
    +
    +XCFLAGS += \
    + $(EOLIST)
    +
    +XDEFINES += \
    + $(EOLIST)
    +
    +XLFLAGS += \
    + $(EOLIST)
    +endif
    +
    +ifeq "$(RELEASE)" "release"
    +XINCDIRS += \
    + $(EOLIST)
    +
    +XCFLAGS += \
    + $(EOLIST)
    +
    +XDEFINES += \
    + $(EOLIST)
    +
    +XLFLAGS += \
    + $(EOLIST)
    +endif
    +
    +#
    +# These are used by the link target if an NLM is being generated
    +# This is used by the link 'name' directive to name the nlm. If left blank
    +# TARGET_nlm (see below) will be used.
    +#
    +NLM_NAME = proxywstunnel
    +
    +#
    +# This is used by the link '-desc ' directive.
    +# If left blank, NLM_NAME will be used.
    +#
    +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Web Socket Tunnel Module
    +
    +#
    +# This is used by the '-threadname' directive. If left blank,
    +# NLM_NAME Thread will be used.
    +#
    +NLM_THREAD_NAME = Prxy WbSkt Module
    +
    +#
    +# If this is specified, it will override VERSION value in
    +# $(AP_WORK)/build/NWGNUenvironment.inc
    +#
    +NLM_VERSION =
    +
    +#
    +# If this is specified, it will override the default of 64K
    +#
    +NLM_STACK_SIZE = 8192
    +
    +
    +#
    +# If this is specified it will be used by the link '-entry' directive
    +#
    +NLM_ENTRY_SYM =
    +
    +#
    +# If this is specified it will be used by the link '-exit' directive
    +#
    +NLM_EXIT_SYM =
    +
    +#
    +# If this is specified it will be used by the link '-check' directive
    +#
    +NLM_CHECK_SYM =
    +
    +#
    +# If these are specified it will be used by the link '-flags' directive
    +#
    +NLM_FLAGS =
    +
    +#
    +# If this is specified it will be linked in with the XDCData option in the def
    +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
    +# by setting APACHE_UNIPROC in the environment
    +#
    +XDCDATA =
    +
    +#
    +# If there is an NLM target, put it here
    +#
    +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm
    +
    +#
    +# If there is an LIB target, put it here
    +#
    +TARGET_lib =
    +
    +#
    +# These are the OBJ files needed to create the NLM target above.
    +# Paths must all use the '/' character
    +#
    +FILES_nlm_objs = \
    + $(OBJDIR)/mod_proxy_wstunnel.o \
    + $(EOLIST)
    +
    +#
    +# These are the LIB files needed to create the NLM target above.
    +# These will be added as a library command in the link.opt file.
    +#
    +FILES_nlm_libs = \
    + $(PRELUDE) \
    + $(EOLIST)
    +
    +#
    +# These are the modules that the above NLM target depends on to load.
    +# These will be added as a module command in the link.opt file.
    +#
    +FILES_nlm_modules = \
    + libc \
    + aprlib \
    + proxy \
    + $(EOLIST)
    +
    +#
    +# If the nlm has a msg file, put it's path here
    +#
    +FILE_nlm_msg =
    +
    +#
    +# If the nlm has a hlp file put it's path here
    +#
    +FILE_nlm_hlp =
    +
    +#
    +# If this is specified, it will override $(NWOS)\copyright.txt.
    +#
    +FILE_nlm_copyright =
    +
    +#
    +# Any additional imports go here
    +#
    +FILES_nlm_Ximports = \
    + @libc.imp \
    + @aprlib.imp \
    + @httpd.imp \
    + @$(OBJDIR)/mod_proxy.imp \
    + $(EOLIST)
    +
    +#
    +# Any symbols exported to here
    +#
    +FILES_nlm_exports = \
    + proxy_wstunnel_module \
    + $(EOLIST)
    +
    +#
    +# These are the OBJ files needed to create the LIB target above.
    +# Paths must all use the '/' character
    +#
    +FILES_lib_objs = \
    + $(EOLIST)
    +
    +#
    +# implement targets and dependancies (leave this section alone)
    +#
    +
    +libs :: $(OBJDIR) $(TARGET_lib)
    +
    +nlms :: libs $(TARGET_nlm)
    +
    +#
    +# Updated this target to create necessary directories and copy files to the
    +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
    +#
    +install :: nlms FORCE
    +
    +#
    +# Any specialized rules here
    +#
    +
    +vpath %.c balancers
    +#
    +# Include the 'tail' makefile that has targets that depend on variables defined
    +# in this makefile
    +#
    +
    +include $(APBUILD)/NWGNUtail.inc
    +
    +
    diff -Naur a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
    --- a/modules/proxy/proxy_util.c 2013-04-25 21:08:56.417441322 +0200
    +++ b/modules/proxy/proxy_util.c 2013-04-25 21:08:58.484145190 +0200
    @@ -2680,3 +2680,329 @@
    }
    return rv;
    }
    +
    +/* Clear all connection-based headers from the incoming headers table */
    +typedef struct header_dptr {
    + apr_pool_t *pool;
    + apr_table_t *table;
    + apr_time_t time;
    +} header_dptr;
    +
    +static int clear_conn_headers(void *data, const char *key, const char *val)
    +{
    + apr_table_t *headers = ((header_dptr*)data)->table;
    + apr_pool_t *pool = ((header_dptr*)data)->pool;
    + const char *name;
    + char *next = apr_pstrdup(pool, val);
    + while (*next) {
    + name = next;
    + while (*next && !apr_isspace(*next) && (*next != ',')) {
    + ++next;
    + }
    + while (*next && (apr_isspace(*next) || (*next == ','))) {
    + *next++ = '\0';
    + }
    + apr_table_unset(headers, name);
    + }
    + return 1;
    +}
    +
    +static void proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
    +{
    + header_dptr x;
    + x.pool = p;
    + x.table = headers;
    + apr_table_unset(headers, "Proxy-Connection");
    + apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL);
    + apr_table_unset(headers, "Connection");
    +}
    +
    +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
    + apr_bucket_brigade *header_brigade,
    + request_rec *r,
    + proxy_conn_rec *p_conn,
    + proxy_worker *worker,
    + proxy_server_conf *conf,
    + apr_uri_t *uri,
    + char *url, char *server_portstr,
    + char **old_cl_val,
    + char **old_te_val)
    +{
    + conn_rec *c = r->connection;
    + int counter;
    + char *buf;
    + const apr_array_header_t *headers_in_array;
    + const apr_table_entry_t *headers_in;
    + apr_table_t *headers_in_copy;
    + apr_bucket *e;
    + int do_100_continue;
    + conn_rec *origin = p_conn->connection;
    + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
    +
    + /*
    + * To be compliant, we only use 100-Continue for requests with bodies.
    + * We also make sure we won't be talking HTTP/1.0 as well.
    + */
    + do_100_continue = (worker->ping_timeout_set
    + && !r->header_only
    + && (apr_table_get(r->headers_in, "Content-Length")
    + || apr_table_get(r->headers_in, "Transfer-Encoding"))
    + && (PROXYREQ_REVERSE == r->proxyreq)
    + && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0")));
    +
    + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
    + /*
    + * According to RFC 2616 8.2.3 we are not allowed to forward an
    + * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
    + * a HTTP_EXPECTATION_FAILED
    + */
    + if (r->expecting_100) {
    + return HTTP_EXPECTATION_FAILED;
    + }
    + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
    + p_conn->close = 1;
    + } else {
    + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
    + }
    + if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
    + origin->keepalive = AP_CONN_CLOSE;
    + p_conn->close = 1;
    + }
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    + if (conf->preserve_host == 0) {
    + if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
    + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
    + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:",
    + uri->port_str, CRLF, NULL);
    + } else {
    + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL);
    + }
    + } else {
    + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
    + buf = apr_pstrcat(p, "Host: ", uri->hostname, ":",
    + uri->port_str, CRLF, NULL);
    + } else {
    + buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
    + }
    + }
    + }
    + else {
    + /* don't want to use r->hostname, as the incoming header might have a
    + * port attached
    + */
    + const char* hostname = apr_table_get(r->headers_in,"Host");
    + if (!hostname) {
    + hostname = r->server->server_hostname;
    + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AH01092: "
    + "no HTTP 0.9 request (with no host line) "
    + "on incoming request and preserve host set "
    + "forcing hostname to be %s for uri %s",
    + hostname, r->uri);
    + }
    + buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
    + }
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    +
    + /* handle Via */
    + if (conf->viaopt == via_block) {
    + /* Block all outgoing Via: headers */
    + apr_table_unset(r->headers_in, "Via");
    + } else if (conf->viaopt != via_off) {
    + const char *server_name = ap_get_server_name(r);
    + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
    + * then the server name returned by ap_get_server_name() is the
    + * origin server name (which does make too much sense with Via: headers)
    + * so we use the proxy vhost's name instead.
    + */
    + if (server_name == r->hostname)
    + server_name = r->server->server_hostname;
    + /* Create a "Via:" request header entry and merge it */
    + /* Generate outgoing Via: header with/without server comment: */
    + apr_table_mergen(r->headers_in, "Via",
    + (conf->viaopt == via_full)
    + ? apr_psprintf(p, "%d.%d %s%s (%s)",
    + HTTP_VERSION_MAJOR(r->proto_num),
    + HTTP_VERSION_MINOR(r->proto_num),
    + server_name, server_portstr,
    + AP_SERVER_BASEVERSION)
    + : apr_psprintf(p, "%d.%d %s%s",
    + HTTP_VERSION_MAJOR(r->proto_num),
    + HTTP_VERSION_MINOR(r->proto_num),
    + server_name, server_portstr)
    + );
    + }
    +
    + /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
    + * to backend
    + */
    + if (do_100_continue) {
    + apr_table_mergen(r->headers_in, "Expect", "100-Continue");
    + r->expecting_100 = 1;
    + }
    +
    + /* X-Forwarded-*: handling
    + *
    + * XXX Privacy Note:
    + * -----------------
    + *
    + * These request headers are only really useful when the mod_proxy
    + * is used in a reverse proxy configuration, so that useful info
    + * about the client can be passed through the reverse proxy and on
    + * to the backend server, which may require the information to
    + * function properly.
    + *
    + * In a forward proxy situation, these options are a potential
    + * privacy violation, as information about clients behind the proxy
    + * are revealed to arbitrary servers out there on the internet.
    + *
    + * The HTTP/1.1 Via: header is designed for passing client
    + * information through proxies to a server, and should be used in
    + * a forward proxy configuation instead of X-Forwarded-*. See the
    + * ProxyVia option for details.
    + */
    + if (PROXYREQ_REVERSE == r->proxyreq) {
    + const char *buf;
    +
    + /* Add X-Forwarded-For: so that the upstream has a chance to
    + * determine, where the original request came from.
    + */
    + apr_table_mergen(r->headers_in, "X-Forwarded-For",
    + c->remote_ip);
    +
    + /* Add X-Forwarded-Host: so that upstream knows what the
    + * original request hostname was.
    + */
    + if ((buf = apr_table_get(r->headers_in, "Host"))) {
    + apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
    + }
    +
    + /* Add X-Forwarded-Server: so that upstream knows what the
    + * name of this proxy server is (if there are more than one)
    + * XXX: This duplicates Via: - do we strictly need it?
    + */
    + apr_table_mergen(r->headers_in, "X-Forwarded-Server",
    + r->server->server_hostname);
    + }
    +
    + proxy_run_fixups(r);
    + /*
    + * Make a copy of the headers_in table before clearing the connection
    + * headers as we need the connection headers later in the http output
    + * filter to prepare the correct response headers.
    + *
    + * Note: We need to take r->pool for apr_table_copy as the key / value
    + * pairs in r->headers_in have been created out of r->pool and
    + * p might be (and actually is) a longer living pool.
    + * This would trigger the bad pool ancestry abort in apr_table_copy if
    + * apr is compiled with APR_POOL_DEBUG.
    + */
    + headers_in_copy = apr_table_copy(r->pool, r->headers_in);
    + proxy_clear_connection(p, headers_in_copy);
    + /* send request headers */
    + headers_in_array = apr_table_elts(headers_in_copy);
    + headers_in = (const apr_table_entry_t *) headers_in_array->elts;
    + for (counter = 0; counter < headers_in_array->nelts; counter++) {
    + if (headers_in[counter].key == NULL
    + || headers_in[counter].val == NULL
    +
    + /* Already sent */
    + || !strcasecmp(headers_in[counter].key, "Host")
    +
    + /* Clear out hop-by-hop request headers not to send
    + * RFC2616 13.5.1 says we should strip these headers
    + */
    + || !strcasecmp(headers_in[counter].key, "Keep-Alive")
    + || !strcasecmp(headers_in[counter].key, "TE")
    + || !strcasecmp(headers_in[counter].key, "Trailer")
    + || !strcasecmp(headers_in[counter].key, "Upgrade")
    +
    + ) {
    + continue;
    + }
    + /* Do we want to strip Proxy-Authorization ?
    + * If we haven't used it, then NO
    + * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
    + * So let's make it configurable by env.
    + */
    + if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) {
    + if (r->user != NULL) { /* we've authenticated */
    + if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
    + continue;
    + }
    + }
    + }
    +
    + /* Skip Transfer-Encoding and Content-Length for now.
    + */
    + if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) {
    + *old_te_val = headers_in[counter].val;
    + continue;
    + }
    + if (!strcasecmp(headers_in[counter].key, "Content-Length")) {
    + *old_cl_val = headers_in[counter].val;
    + continue;
    + }
    +
    + /* for sub-requests, ignore freshness/expiry headers */
    + if (r->main) {
    + if ( !strcasecmp(headers_in[counter].key, "If-Match")
    + || !strcasecmp(headers_in[counter].key, "If-Modified-Since")
    + || !strcasecmp(headers_in[counter].key, "If-Range")
    + || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since")
    + || !strcasecmp(headers_in[counter].key, "If-None-Match")) {
    + continue;
    + }
    + }
    +
    + buf = apr_pstrcat(p, headers_in[counter].key, ": ",
    + headers_in[counter].val, CRLF,
    + NULL);
    + ap_xlate_proto_to_ascii(buf, strlen(buf));
    + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(header_brigade, e);
    + }
    + return OK;
    +}
    +
    +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
    + request_rec *r, proxy_conn_rec *p_conn,
    + conn_rec *origin, apr_bucket_brigade *bb,
    + int flush)
    +{
    + apr_status_t status;
    + apr_off_t transferred;
    +
    + if (flush) {
    + apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
    + APR_BRIGADE_INSERT_TAIL(bb, e);
    + }
    + apr_brigade_length(bb, 0, &transferred);
    + if (transferred != -1)
    + p_conn->worker->s->transferred += transferred;
    + status = ap_pass_brigade(origin->output_filters, bb);
    + if (status != APR_SUCCESS) {
    + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "AH01084: "
    + "pass request body failed to %pI (%s)",
    + p_conn->addr, p_conn->hostname);
    + if (origin->aborted) {
    + const char *ssl_note;
    +
    + if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv"))
    + != NULL) && (strcmp(ssl_note, "err") == 0)) {
    + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
    + "Error during SSL Handshake with"
    + " remote server");
    + }
    + return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY;
    + }
    + else {
    + return HTTP_BAD_REQUEST;
    + }
    + }
    + apr_brigade_cleanup(bb);
    + return OK;
    +}