Instantly share code, notes, and snippets.

@vitkin /README.md
Last active Aug 8, 2018

Embed
What would you like to do?
Backport WebSocket to Apache 2.2 that doesn't modify the 'mod_utils.c' and the 'mod_proxy.h'. See the 'README.md' for details and instructions.

Backport WebSocket to Apache 2.2

This is my variation from the original patch and that also includes the suggested correction for the bug with secure websocket 'wss://'.

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:

  1. Checkout apache source (Replace 'x' with the right number.):

    svn checkout \
        http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.x/ \
        httpd-2.2.x
  2. Get the patch and apply it

    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:

    autoconf
    ./configure --enable-so --enable-proxy --enable-proxy_wstunnel=shared
    make
  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.

Notes:

  • Don't forget to check that the configuration is OK by running the command:

    apachectl configtest
  • You may encounter some compilation error with version 2.2.22 and later similar to:

    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. A workaround consist in adding --with-included-apr to the configure options:

    ./configure --enable-so --enable-proxy --enable-proxy-wstunnel=shared --with-included-apr

References:

diff -Naur a/modules/proxy/config.m4 b/modules/proxy/config.m4
--- 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"
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
+++ 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
+ * 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
+++ b/modules/proxy/mod_proxy_wstunnel.c
@@ -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.
+ * 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;
+ 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";
+ }
+ 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 = is_ssl;
+ 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
+++ b/modules/proxy/mod_proxy_wstunnel.dsp
@@ -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 **
+
+# 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=.\mod_proxy_wstunnel.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
@zebby76

This comment has been minimized.

zebby76 commented Oct 18, 2013

Hi, thanks for your patch ! But, in instructions bloc on this page, the command

./configure --enable-so --enable-proxy --enable-proxy-wstunnel=shared

shoud be

./configure --enable-so --enable-proxy --enable-proxy_wstunnel=shared

no ?

@vitkin

This comment has been minimized.

Owner

vitkin commented Nov 6, 2013

@zebby76: Thanks, indeed it looks like a typo but actually it appears to work just fine with ./configure --enable-so --enable-proxy --enable-proxy-wstunnel=shared!

Nevertheless not only I've replaced --enable-proxy-wstunnel with --enable-proxy_wstunnel in the instructions but I've also modified the patch to incorporate the suggested correction for the bug with secure websocket 'wss://' and updated the README.md. ;-)

Enjoy!

@KenYN

This comment has been minimized.

KenYN commented Mar 24, 2014

Great, thanks! However, the apr versions are now too far ahead and the apr-util is now empty, so for Apache v2.2.15 I used:

svn co http://svn.apache.org/repos/asf/apr/apr/tags/1.4.2 srclib/apr
svn co http://svn.apache.org/repos/asf/apr/apr-util/tags/1.3.9 srclib/apr-util

Furthermore, ./buildconf (no parameters necessary) is needed before running ./configure.

@pikeymick

This comment has been minimized.

pikeymick commented Apr 28, 2014

For oracle linux with Apache v2.2.15 I had to tweak the above as such:

svn co http://svn.apache.org/repos/asf/apr/apr/tags/1.4.2 srclib/apr
svn co http://svn.apache.org/repos/asf/apr/apr-util/tags/1.5.3 srclib/apr-util

Furthermore, ./buildconf (no parameters necessary) is needed before running ./configure.

@pfee

This comment has been minimized.

pfee commented Oct 28, 2014

Which version of Apache 2.4 does this backported mod_proxy_wstunnel correspond to?

There are some mod_proxy_wstunnel bug fixes in httpd 2.4.10 (released 2014-07-21). My guess is that those changes aren't in this patch. If that's the case, any plans to refresh this 2.2 backport?

Thanks.

@pfee

This comment has been minimized.

pfee commented Oct 29, 2014

I created a fork of this page and rebased the patch against httpd 2.4.10.
https://gist.github.com/pfee/e07f02d668a809139ea0

Feedback welcome.

@emmanuel-deloget

This comment has been minimized.

emmanuel-deloget commented Oct 29, 2014

Hello,

Just wanted you to know that if you remove the config.m4 hunks, you can use this patch to create an out-of-tree module which will be successfully built using apxs. This solution is of interest to those who do not want to build apache again (there are many situations where this is not easy - or even possible).

Basically, this is:

$ tail -n +28 apache-2.2-wstunnel.patch > modified-wstunnel-patch.patch
$ patch -p2 < modified-wstunnel-patch.patch
$ cd proxy
$ apxs -c mod_proxy_wstunnel.c
$ apxs -i -a -n proxy_wstunnel mod_proxy_wstunnel.la

(use sudo if you shall install the module as root)

I used this a few days ago to prepare the module for apache 2.2.29 - it worked like a charm.

@redhs

This comment has been minimized.

redhs commented Mar 3, 2015

Hello,

While trying to apply the patch on httpd-2.2.15 version , i get the following error :

[user@local httpd-2.2.15]# patch -p1 -i ../apache-2.2-wstunnel.patch
patching file modules/proxy/config.m4
Hunk #1 FAILED at 18.
Hunk #2 FAILED at 29.
Hunk #3 FAILED at 37.
3 out of 3 hunks FAILED -- saving rejects to file modules/proxy/config.m4.rej
patching file modules/proxy/mod_proxy_wstunnel.h
patching file modules/proxy/mod_proxy_wstunnel.c
patching file modules/proxy/mod_proxy_wstunnel.dsp

i followed the instructions but i can't patch my httpd-2.2.15 .

Could you please share your advice regarding this patch application ?
Thanks in advance.

@waflessnet

This comment has been minimized.

waflessnet commented Jun 9, 2015

" @pikeymick,
For oracle linux with Apache v2.2.15 I had to tweak the above as such:

svn co http://svn.apache.org/repos/asf/apr/apr/tags/1.4.2 srclib/apr
svn co http://svn.apache.org/repos/asf/apr/apr-util/tags/1.5.3 srclib/apr-util
Furthermore, ./buildconf (no parameters necessary) is needed before running ./configure. "

Thanks !! ran perfect on 'Centos 4.0' apache 2.2.15

@opsun

This comment has been minimized.

opsun commented Sep 22, 2015

I found sometimes the module dose not work,the websocket response (Opcode -1)

@dalesupa22

This comment has been minimized.

dalesupa22 commented Oct 23, 2015

Hi I am running Apache 2.2.21.
I tried to do @emmanuel-deloget solution but It didnt work forme when I execute apxs -c mod_proxy_wstunnel.c. It fails...The mod_proxy_wstunnel.so is never generated.

@mordillo123

This comment has been minimized.

mordillo123 commented Nov 19, 2015

No one have tried with an old version of Apache2? Maybe 2.2.3?
I get an error:
mod_proxy_wstunnel.c: In function 'ap_proxy_create_hdrbrgd':
mod_proxy_wstunnel.c:83: error: 'proxy_worker' has no member named 'ping_timeout_set'

@fastesol

This comment has been minimized.

fastesol commented Jan 6, 2016

shall it work on Apache 2.4.16? If not, please suggest the changes.

I'm getting following error

autoconf

configure.in💯 error: possibly undefined macro: AC_MSG_WARN
If this token and others are legitimate, please use m4_pattern_allow.
See the Autoconf documentation.

Thank you!

@soitech

This comment has been minimized.

soitech commented Sep 7, 2016

With Apache 2.2.31 on Ubuntu 12.04 this fixed
error: undefined symbol: ap_proxy_pass_brigade
but now I get
error: undefined symbol: ap_proxy_canonenc
haven't been able to find any solution to this problem.

@Maelfyn

This comment has been minimized.

Maelfyn commented Nov 2, 2016

I also had to do a ./buildconf before doing ./configure. I also had to svn down two more files because buildconf said they were missing, but once I got those downloaded I was able to ./buildconf, ./configure and make. Btw, I am a bit confused. Do I have to run all of this in a specific directory?

@renton-

This comment has been minimized.

renton- commented Mar 22, 2017

Hi,

I noticed this backport doesn't send Content-Length header to backend application.
Apache vhost configuration:


ProxyPass ws://127.0.0.1:10064/
ProxyPassReverse ws://127.0.0.1:10064/


Httpd Apache listens to port 80 and Tomcat listens to 10064.

There is example of session from client to apache:
POST /myapp HTTP/1.1
Host: myapp.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: gzip, deflate
Cookie: checkCookies=1; JSESSIONID=191B6F3DA9C371E370FB1D7D1E144B7F
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 31

and from Apache to Tomcat:
POST /myapp HTTP/1.1
Host: myapp.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: gzip, deflate
Cookie: checkCookies=1; JSESSIONID=191B6F3DA9C371E370FB1D7D1E144B7F
DNT: 1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 11.111.111.111
X-Forwarded-Host: myapp.com
X-Forwarded-Server: myapp.com
Upgrade: WebSocket
Connection: Upgrade

Content-Length disappeared somewhere.

@renton-

This comment has been minimized.

renton- commented Mar 27, 2017

This patch cafarelli.fr/gentoo/apache-2.2.31-wstunnel.patch fixes bag about content-length.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment