]> granicus.if.org Git - apache/commitdiff
Introduce proxy_ajp.c : use --enable-proxy-ajp with configuring Apache.
authorWilliam A. Rowe Jr <wrowe@apache.org>
Wed, 11 Aug 2004 20:29:46 +0000 (20:29 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Wed, 11 Aug 2004 20:29:46 +0000 (20:29 +0000)
To load it:
+++
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
+++

And to use it something like:
+++
<Location /examples/>
ProxyPass ajp://backend.example.net:8009/examples/
</Location>
+++

proxy_ajp will not be enabled by default due to ./ajp dependencies

Build magic improvements: mturk, wrowe

Submitted by:  jfclere

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@104556 13f79535-47bb-0310-9956-ffa450edef68

modules/proxy/config.m4
modules/proxy/mod_proxy_ajp.dsp [new file with mode: 0644]
modules/proxy/proxy_ajp.c [new file with mode: 0644]

index d33683e93451572195de31c23793ea7d93ea6900..5592290a30cfe674bd2f154853f89bd63feda0cc 100644 (file)
@@ -16,6 +16,7 @@ APACHE_MODULE(proxy, Apache proxy module, $proxy_objs, , $proxy_mods_enable)
 proxy_connect_objs="proxy_connect.lo"
 proxy_ftp_objs="proxy_ftp.lo"
 proxy_http_objs="proxy_http.lo"
+proxy_ajp_objs="proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo"
 
 case "$host" in
   *os2*)
@@ -30,5 +31,10 @@ esac
 APACHE_MODULE(proxy_connect, Apache proxy CONNECT module, $proxy_connect_objs, , $proxy_mods_enable)
 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_ajp, Apache proxy AJP module, $proxy_ajp_objs, , no)
+
+if test "$proxy_ajp_enable" != "no" -o "$enable_proxy_ajp" != "no"; then
+  APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current/ajp])
+fi
 
 APACHE_MODPATH_FINISH
diff --git a/modules/proxy/mod_proxy_ajp.dsp b/modules/proxy/mod_proxy_ajp.dsp
new file mode 100644 (file)
index 0000000..f3e56da
--- /dev/null
@@ -0,0 +1,168 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy_ajp" - 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_ajp - 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_ajp.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_ajp.mak" CFG="mod_proxy_ajp - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_proxy_ajp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_ajp - 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_ajp - 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 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_ajp_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 0x809 /d "NDEBUG"
+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 /machine:I386 /out:"Release/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"Release/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so /opt:ref
+
+!ELSEIF  "$(CFG)" == "mod_proxy_ajp - 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 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_ajp_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 0x809 /d "_DEBUG"
+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 /machine:I386 /out:"Debug/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_proxy_ajp - Win32 Release"
+# Name "mod_proxy_ajp - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\proxy_ajp.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 Group "Ajp Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\ajp\ajp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_header.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_header.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_logon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_msg.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_proxy_ajp - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_ajp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_proxy_ajp.so "proxy_ajp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ajp.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_proxy_ajp - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_ajp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_proxy_ajp.so "proxy_ajp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ajp.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/modules/proxy/proxy_ajp.c b/modules/proxy/proxy_ajp.c
new file mode 100644 (file)
index 0000000..577a6f8
--- /dev/null
@@ -0,0 +1,906 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* HTTP routines for Apache proxy */
+
+#include "mod_proxy.h"
+
+module AP_MODULE_DECLARE_DATA proxy_ajp_module;
+
+int ap_proxy_ajp_canon(request_rec *r, char *url);
+int ap_proxy_ajp_handler(request_rec *r, proxy_server_conf *conf,
+                          char *url, const char *proxyname, 
+                          apr_port_t proxyport);
+
+typedef struct {
+    const char     *name;
+    apr_port_t      port;
+    apr_sockaddr_t *addr;
+    apr_socket_t   *sock;
+    int             close;
+    void           *data;  /* To store ajp data */
+} proxy_ajp_conn_t;
+
+static apr_status_t ap_proxy_http_cleanup(request_rec *r,
+                                          proxy_ajp_conn_t *p_conn,
+                                          proxy_conn_rec *backend);
+
+/*
+ * 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.
+ */
+int ap_proxy_ajp_canon(request_rec *r, char *url)
+{
+    char *host, *path, *search, sport[7];
+    const char *err;
+    const char *scheme;
+    apr_port_t port, def_port;
+
+    ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+             "proxy: HTTP: canonicalising URL %s", url);
+
+    /* ap_port_of_scheme() */
+    if (strncasecmp(url, "http:", 5) == 0) {
+        url += 5;
+        scheme = "http";
+    }
+    else if (strncasecmp(url, "https:", 6) == 0) {
+        url += 6;
+        scheme = "https";
+    }
+    else {
+        return DECLINED;
+    }
+    def_port = apr_uri_port_of_scheme(scheme);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy: HTTP: canonicalising URL %s", url);
+
+    /* do syntatic check.
+     * We break the URL into host, port, path, search
+     */
+    port = def_port;
+    err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
+    if (err) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "error parsing URL %s: %s",
+                      url, err);
+        return HTTP_BAD_REQUEST;
+    }
+
+    /* now parse path/search args, according to rfc1738 */
+    /* N.B. if this isn't a true proxy request, then the URL _path_
+     * has already been decoded.  True proxy requests have r->uri
+     * == r->unparsed_uri, and no others have that property.
+     */
+    if (r->uri == r->unparsed_uri) {
+        search = strchr(url, '?');
+        if (search != NULL)
+            *(search++) = '\0';
+    }
+    else
+        search = r->args;
+
+    /* process path */
+    path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
+    if (path == NULL)
+        return HTTP_BAD_REQUEST;
+
+    if (port != def_port)
+        apr_snprintf(sport, sizeof(sport), ":%d", port);
+    else
+        sport[0] = '\0';
+
+    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 const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
+{
+    struct proxy_alias *ent;
+    int i, l1, l2;
+    char *u;
+
+    /* XXX FIXME: Make sure this handled the ambiguous case of the :80
+     * after the hostname */
+
+    l1 = strlen(url);
+    ent = (struct proxy_alias *)conf->raliases->elts;
+    for (i = 0; i < conf->raliases->nelts; i++) {
+        l2 = strlen(ent[i].real);
+        if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) {
+            u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
+            return ap_construct_url(r->pool, u, r);
+        }
+    }
+    return url;
+}
+/* cookies are a bit trickier to match: we've got two substrings to worry
+ * about, and we can't just find them with strstr 'cos of case.  Regexp
+ * matching would be an easy fix, but for better consistency with all the
+ * other matches we'll refrain and use apr_strmatch to find path=/domain=
+ * and stick to plain strings for the config values.
+ */
+static const char *proxy_cookie_reverse_map(request_rec *r,
+                          proxy_server_conf *conf, const char *str)
+{
+    struct proxy_alias *ent;
+    size_t len = strlen(str);
+    const char* newpath = NULL ;
+    const char* newdomain = NULL ;
+    const char* pathp ;
+    const char* domainp ;
+    const char* pathe = NULL;
+    const char* domaine = NULL;
+    size_t l1, l2, i, poffs = 0, doffs = 0 ;
+    int ddiff = 0 ;
+    int pdiff = 0 ;
+    char* ret ;
+
+/* find the match and replacement, but save replacing until we've done
+   both path and domain so we know the new strlen
+*/
+    if ( pathp = apr_strmatch(conf->cookie_path_str, str, len) , pathp ) {
+        pathp += 5 ;
+        poffs = pathp - str ;
+        pathe = ap_strchr_c(pathp, ';') ;
+        l1 = pathe ? (pathe-pathp) : strlen(pathp) ;
+        pathe = pathp + l1 ;
+        ent = (struct proxy_alias *)conf->cookie_paths->elts;
+        for (i = 0; i < conf->cookie_paths->nelts; i++) {
+            l2 = strlen(ent[i].fake);
+            if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
+                newpath = ent[i].real ;
+                pdiff = strlen(newpath) - l1 ;
+                break ;
+            }
+        }
+    }
+    if ( domainp = apr_strmatch(conf->cookie_domain_str, str, len) , domainp ) {
+        domainp += 7 ;
+        doffs = domainp - str ;
+        domaine = ap_strchr_c(domainp, ';') ;
+        l1 = domaine ? (domaine-domainp) : strlen(domainp) ;
+        domaine = domainp + l1 ;
+        ent = (struct proxy_alias *)conf->cookie_domains->elts;
+        for (i = 0; i < conf->cookie_domains->nelts; i++) {
+            l2 = strlen(ent[i].fake);
+            if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
+                newdomain = ent[i].real ;
+                ddiff = strlen(newdomain) - l1 ;
+                break ;
+            }
+        }
+    }
+    if ( newpath ) {
+        ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ;
+        l1 = strlen(newpath) ;
+        if ( newdomain ) {
+            l2 = strlen(newdomain) ;
+            if ( doffs > poffs ) {
+                memcpy(ret, str, poffs) ;
+                memcpy(ret+poffs, newpath, l1) ;
+                memcpy(ret+poffs+l1, pathe, domainp-pathe) ;
+                memcpy(ret+doffs+pdiff, newdomain, l2) ;
+                strcpy(ret+doffs+pdiff+l2, domaine) ;
+            } else {
+                memcpy(ret, str, doffs) ;
+                memcpy(ret+doffs, newdomain, l2) ;
+                memcpy(ret+doffs+l2, domaine, pathp-domaine) ;
+                memcpy(ret+poffs+ddiff, newpath, l1) ;
+                strcpy(ret+poffs+ddiff+l1, pathe) ;
+            }
+        } else {
+            memcpy(ret, str, poffs) ;
+            memcpy(ret+poffs, newpath, l1) ;
+            strcpy(ret+poffs+l1, pathe) ;
+        }
+    } else {
+        if ( newdomain ) {
+            ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ;
+            l2 = strlen(newdomain) ;
+            memcpy(ret, str, doffs) ;
+            memcpy(ret+doffs, newdomain, l2) ;
+            strcpy(ret+doffs+l2, domaine) ;
+        } else {
+            ret = (char*) str ;        /* no change */
+        }
+    }
+    return ret ;
+}
+
+/* Clear all connection-based headers from the incoming headers table */
+static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
+{
+    const char *name;
+    char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
+
+    apr_table_unset(headers, "Proxy-Connection");
+    if (!next)
+        return;
+
+    while (*next) {
+        name = next;
+        while (*next && !apr_isspace(*next) && (*next != ',')) {
+            ++next;
+        }
+        while (*next && (apr_isspace(*next) || (*next == ','))) {
+            *next = '\0';
+            ++next;
+        }
+        apr_table_unset(headers, name);
+    }
+    apr_table_unset(headers, "Connection");
+}
+
+static
+apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r,
+                                                proxy_ajp_conn_t *p_conn,
+                                                conn_rec *c,
+                                                proxy_server_conf *conf,
+                                                apr_uri_t *uri,
+                                                char **url,
+                                                const char *proxyname,
+                                                apr_port_t proxyport,
+                                                char *server_portstr,
+                                                int server_portstr_size) {
+    int server_port;
+    apr_status_t err;
+    apr_sockaddr_t *uri_addr;
+    /*
+     * Break up the URL to determine the host to connect to
+     */
+
+    /* we break the URL into host, port, uri */
+    if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
+        return ap_proxyerror(r, HTTP_BAD_REQUEST,
+                             apr_pstrcat(p,"URI cannot be parsed: ", *url,
+                                         NULL));
+    }
+    if (!uri->port) {
+        uri->port = apr_uri_port_of_scheme(uri->scheme);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy: HTTP connecting %s to %s:%d", *url, uri->hostname,
+                 uri->port);
+
+    /* do a DNS lookup for the destination host */
+    /* see memory note above */
+    err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri->hostname),
+                                APR_UNSPEC, uri->port, 0, c->pool);
+
+    /* allocate these out of the connection pool - the check on
+     * r->connection->id makes sure that this string does not get accessed
+     * past the connection lifetime */
+    /* are we connecting directly, or via a proxy? */
+    if (proxyname) {
+        p_conn->name = apr_pstrdup(c->pool, proxyname);
+        p_conn->port = proxyport;
+        /* see memory note above */
+        err = apr_sockaddr_info_get(&p_conn->addr, p_conn->name, APR_UNSPEC,
+                                    p_conn->port, 0, c->pool);
+    } else {
+        p_conn->name = apr_pstrdup(c->pool, uri->hostname);
+        p_conn->port = uri->port;
+        p_conn->addr = uri_addr;
+        *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
+                           uri->query ? uri->query : "",
+                           uri->fragment ? "#" : "",
+                           uri->fragment ? uri->fragment : "", NULL);
+    }
+
+    if (err != APR_SUCCESS) {
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             apr_pstrcat(p, "DNS lookup failure for: ",
+                                         p_conn->name, NULL));
+    }
+
+    /* Get the server port for the Via headers */
+    {
+        server_port = ap_get_server_port(r);
+        if (ap_is_default_port(server_port, r)) {
+            strcpy(server_portstr,"");
+        } else {
+            apr_snprintf(server_portstr, server_portstr_size, ":%d",
+                         server_port);
+        }
+    }
+
+    /* check if ProxyBlock directive on this host */
+    if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) {
+        return ap_proxyerror(r, HTTP_FORBIDDEN,
+                             "Connect to remote machine blocked");
+    }
+    return OK;
+}
+
+static
+apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r,
+                                             proxy_ajp_conn_t *p_conn,
+                                             conn_rec *c, conn_rec **origin,
+                                             proxy_conn_rec *backend,
+                                             proxy_server_conf *conf,
+                                             const char *proxyname) {
+    int failed=0, new=0;
+    apr_socket_t *client_socket = NULL;
+
+    /* We have determined who to connect to. Now make the connection, supporting
+     * a KeepAlive connection.
+     */
+
+    /* get all the possible IP addresses for the destname and loop through them
+     * until we get a successful connection
+     */
+
+    /* if a keepalive socket is already open, check whether it must stay
+     * open, or whether it should be closed and a new socket created.
+     */
+    /* see memory note above */
+    if (backend->connection) {
+        client_socket = ap_get_module_config(backend->connection->conn_config, &core_module);
+        if ((backend->connection->id == c->id) &&
+            (backend->port == p_conn->port) &&
+            (backend->hostname) &&
+            (!apr_strnatcasecmp(backend->hostname, p_conn->name))) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "proxy: keepalive address match (keep original socket)");
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "proxy: keepalive address mismatch / connection has"
+                         " changed (close old socket (%s/%s, %d/%d))", 
+                         p_conn->name, backend->hostname, p_conn->port,
+                         backend->port);
+            apr_socket_close(client_socket);
+            backend->connection = NULL;
+        }
+    }
+
+    /* get a socket - either a keepalive one, or a new one */
+    new = 1;
+    if ((backend->connection) && (backend->connection->id == c->id)) {
+        apr_size_t buffer_len = 1;
+        char test_buffer[1]; 
+        apr_status_t socket_status;
+        apr_interval_time_t current_timeout;
+
+        /* use previous keepalive socket */
+        *origin = backend->connection;
+        p_conn->sock = client_socket;
+        new = 0;
+
+        /* save timeout */
+        apr_socket_timeout_get(p_conn->sock, &current_timeout);
+        /* set no timeout */
+        apr_socket_timeout_set(p_conn->sock, 0);
+        socket_status = apr_socket_recv(p_conn->sock, test_buffer, &buffer_len);
+        /* put back old timeout */
+        apr_socket_timeout_set(p_conn->sock, current_timeout);
+        if ( APR_STATUS_IS_EOF(socket_status) ) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                         "proxy: HTTP: previous connection is closed");
+            new = 1;
+        }
+    }
+    if (new) {
+
+        /* create a new socket */
+        backend->connection = NULL;
+
+        /*
+         * At this point we have a list of one or more IP addresses of
+         * the machine to connect to. If configured, reorder this
+         * list so that the "best candidate" is first try. "best
+         * candidate" could mean the least loaded server, the fastest
+         * responding server, whatever.
+         *
+         * For now we do nothing, ie we get DNS round robin.
+         * XXX FIXME
+         */
+        failed = ap_proxy_connect_to_backend(&p_conn->sock, "HTTP",
+                                             p_conn->addr, p_conn->name,
+                                             conf, r->server, c->pool);
+
+        /* handle a permanent error on the connect */
+        if (failed) {
+            if (proxyname) {
+                return DECLINED;
+            } else {
+                return HTTP_BAD_GATEWAY;
+            }
+        }
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: socket is connected");
+
+        /* the socket is now open, create a new backend server connection */
+        *origin = ap_run_create_connection(c->pool, r->server, p_conn->sock,
+                                           r->connection->id,
+                                           r->connection->sbh, c->bucket_alloc);
+        if (!*origin) {
+        /* the peer reset the connection already; ap_run_create_connection() 
+         * closed the socket
+         */
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                         r->server, "proxy: an error occurred creating a "
+                         "new connection to %pI (%s)", p_conn->addr,
+                         p_conn->name);
+            apr_socket_close(p_conn->sock);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        backend->connection = *origin;
+        backend->hostname = apr_pstrdup(c->pool, p_conn->name);
+        backend->port = p_conn->port;
+
+        if (backend->is_ssl) {
+            if (!ap_proxy_ssl_enable(backend->connection)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+                             r->server, "proxy: failed to enable ssl support "
+                             "for %pI (%s)", p_conn->addr, p_conn->name);
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+        }
+        else {
+            ap_proxy_ssl_disable(backend->connection);
+        }
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: connection complete to %pI (%s)",
+                     p_conn->addr, p_conn->name);
+
+        /* set up the connection filters */
+        ap_run_pre_connection(*origin, p_conn->sock);
+    }
+    return OK;
+}
+
+
+static
+apr_status_t ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
+                                   proxy_ajp_conn_t *p_conn, conn_rec *origin, 
+                                   proxy_server_conf *conf,
+                                   apr_uri_t *uri,
+                                   char *url, char *server_portstr)
+{
+    apr_status_t status;
+    int result;
+
+    /*
+     * Send the AJP request to the remote server
+     */
+
+    /* send request headers */
+    status = ajp_send_header(p_conn->sock,r);
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: request failed to %pI (%s)",
+                     p_conn->addr, p_conn->name);
+        return status;
+    }
+
+    /* read the response */
+    status = ajp_read_header(p_conn->sock,r,&(p_conn->data));
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: request failed to %pI (%s)",
+                     p_conn->addr, p_conn->name);
+        return status;
+    }
+
+    /* parse the reponse */
+    result = ajp_parse_type(r,p_conn->data);
+    if (result == 4) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: got response from %pI (%s)",
+                     p_conn->addr, p_conn->name);
+        return APR_SUCCESS;
+    }
+
+    /* send data via brigade or not??? */
+/*
+    status = ajp_send_data(p_conn->sock,r);
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: request failed to %pI (%s)",
+                     p_conn->addr, p_conn->name);
+        return status;
+    }
+ */
+
+    return APR_SUCCESS;
+}
+static void process_proxy_header(request_rec* r, proxy_server_conf* c,
+                      const char* key, const char* value)
+{
+    static const char* date_hdrs[]
+        = { "Date", "Expires", "Last-Modified", NULL } ;
+    static const struct {
+        const char* name ;
+        const char* (*func)(request_rec*, proxy_server_conf*, const char*) ;
+    } transform_hdrs[] = {
+        { "Location", ap_proxy_location_reverse_map } ,
+        { "Content-Location", ap_proxy_location_reverse_map } ,
+        { "URI", ap_proxy_location_reverse_map } ,
+        { "Set-Cookie", proxy_cookie_reverse_map } ,
+        { NULL, NULL }
+    } ;
+    int i ;
+    for ( i = 0 ; date_hdrs[i] ; ++i ) {
+        if ( !strcasecmp(date_hdrs[i], key) ) {
+            apr_table_add(r->headers_out, key,
+                ap_proxy_date_canon(r->pool, value)) ;
+            return ;
+        }
+    }
+    for ( i = 0 ; transform_hdrs[i].name ; ++i ) {
+        if ( !strcasecmp(transform_hdrs[i].name, key) ) {
+            apr_table_add(r->headers_out, key,
+                (*transform_hdrs[i].func)(r, c, value)) ;
+            return ;
+       }
+    }
+    apr_table_add(r->headers_out, key, value) ;
+    return ;
+}
+
+static void ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c)
+{
+    int len;
+    char *value, *end;
+    char field[MAX_STRING_LEN];
+    int saw_headers = 0;
+    void *sconf = r->server->module_config;
+    proxy_server_conf *psc;
+
+    psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+
+    r->headers_out = apr_table_make(r->pool, 20);
+
+    /*
+     * Read header lines until we get the empty separator line, a read error,
+     * the connection closes (EOF), or we timeout.
+     */
+    while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
+
+        if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
+
+            /* We may encounter invalid headers, usually from buggy
+             * MS IIS servers, so we need to determine just how to handle
+             * them. We can either ignore them, assume that they mark the
+             * start-of-body (eg: a missing CRLF) or (the default) mark
+             * the headers as totally bogus and return a 500. The sole
+             * exception is an extra "HTTP/1.0 200, OK" line sprinkled
+             * in between the usual MIME headers, which is a favorite
+             * IIS bug.
+             */
+             /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
+
+            if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
+                if (psc->badopt == bad_error) {
+                    /* Nope, it wasn't even an extra HTTP header. Give up. */
+                    return ;
+                }
+                else if (psc->badopt == bad_body) {
+                    /* if we've already started loading headers_out, then
+                     * return what we've accumulated so far, in the hopes
+                     * that they are useful. Otherwise, we completely bail.
+                     */
+                    /* FIXME: We've already scarfed the supposed 1st line of
+                     * the body, so the actual content may end up being bogus
+                     * as well. If the content is HTML, we may be lucky.
+                     */
+                    if (saw_headers) {
+                        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                         "proxy: Starting body due to bogus non-header in headers "
+                         "returned by %s (%s)", r->uri, r->method);
+                        return ;
+                    } else {
+                         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                         "proxy: No HTTP headers "
+                         "returned by %s (%s)", r->uri, r->method);
+                        return ;
+                    }
+                }
+            }
+            /* this is the psc->badopt == bad_ignore case */
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                         "proxy: Ignoring bogus HTTP header "
+                         "returned by %s (%s)", r->uri, r->method);
+            continue;
+        }
+
+        *value = '\0';
+        ++value;
+        /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
+         * wrong... and so are many others probably.
+         */
+        while (apr_isspace(*value))
+            ++value;            /* Skip to start of value   */
+
+        /* should strip trailing whitespace as well */
+        for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --
+end)
+            *end = '\0';
+
+        /* make sure we add so as not to destroy duplicated headers
+         * Modify headers requiring canonicalisation and/or affected
+         * by ProxyPassReverse and family with process_proxy_header
+         */
+        process_proxy_header(r, psc, buffer, value) ;
+        saw_headers = 1;
+
+        /* the header was too long; at the least we should skip extra data */
+        if (len >= size - 1) {
+            while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
+                    >= MAX_STRING_LEN - 1) {
+                /* soak up the extra data */
+            }
+            if (len == 0) /* time to exit the larger loop as well */
+                break;
+        }
+    }
+}
+
+
+
+static int addit_dammit(void *v, const char *key, const char *val)
+{
+    apr_table_addn(v, key, val);
+    return 1;
+}
+
+/*
+ * Process the AJP response, data already contains the first part of it.
+ */
+static
+apr_status_t ap_proxy_ajp_process_response(apr_pool_t * p, request_rec *r,
+                                            proxy_ajp_conn_t *p_conn,
+                                            conn_rec *origin,
+                                            proxy_conn_rec *backend,
+                                            proxy_server_conf *conf,
+                                            char *server_portstr) {
+    conn_rec *c = r->connection;
+    apr_bucket *e;
+    apr_bucket_brigade *bb;
+    int type;
+    apr_status_t status;
+
+    // bb = apr_brigade_create(p, c->bucket_alloc);
+    
+    type = ajp_parse_type(r, p_conn->data);
+    status = APR_SUCCESS;
+    while (type != 5) {
+        if (type == 4) {
+            /* AJP13_SEND_HEADERS: process them */
+            status = ajp_parse_headers(r, p_conn->data); 
+            if (status != APR_SUCCESS) {
+                break;
+            }
+        } else if  (type == 3) {
+            /* AJP13_SEND_BODY_CHUNK: piece of data */
+            apr_size_t size;
+            char *buff;
+
+            status = ajp_parse_data(r, p_conn->data, &size, &buff);
+            ap_rflush(r);
+            ap_rwrite(buff,size,r);
+            // e = apr_bucket_transient_create(buff, size, c->bucket_alloc);
+            // APR_BRIGADE_INSERT_TAIL(bb, e);
+        } else {
+            status = APR_EGENERAL;
+            break;
+        }
+        /* Read the next message */
+        status = ajp_read_header(p_conn->sock, r, &(p_conn->data));
+        if (status != APR_SUCCESS) {
+            break;
+        }
+        type = ajp_parse_type(r, p_conn->data);
+    }
+    if (status != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "proxy: error reading headers from remote "
+                      "server %s:%d", p_conn->name, p_conn->port);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
+    }
+
+    return ap_rflush(r);
+
+    /* The page is ready give it to the rest of the logic */
+    e = apr_bucket_eos_create(c->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(bb, e);
+    if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "proxy: error processing body");
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
+    } 
+
+    return OK;
+}
+
+static
+apr_status_t ap_proxy_http_cleanup(request_rec *r, proxy_ajp_conn_t *p_conn,
+                                   proxy_conn_rec *backend) {
+    /* If there are no KeepAlives, or if the connection has been signalled
+     * to close, close the socket and clean up
+     */
+
+    /* if the connection is < HTTP/1.1, or Connection: close,
+     * we close the socket, otherwise we leave it open for KeepAlive support
+     */
+    if (p_conn->close || (r->proto_num < HTTP_VERSION(1,1))) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "ap_proxy_http_cleanup closing %d %d %d %s",
+                       p_conn->sock, p_conn->close, r->proto_num, apr_table_get(r->headers_out, "Connection"));
+        if (p_conn->sock) {
+            apr_socket_close(p_conn->sock);
+            p_conn->sock = NULL;
+            backend->connection = NULL;
+        }
+    }
+    return OK;
+}
+
+/*
+ * This handles http:// URLs, and other URLs using a remote proxy over http
+ * If proxyhost is NULL, then contact the server directly, otherwise
+ * go via the proxy.
+ * Note that if a proxy is used, then URLs other than http: can be accessed,
+ * also, if we have trouble which is clearly specific to the proxy, then
+ * we return DECLINED so that we can try another proxy. (Or the direct
+ * route.)
+ */
+int ap_proxy_ajp_handler(request_rec *r, proxy_server_conf *conf,
+                          char *url, const char *proxyname, 
+                          apr_port_t proxyport)
+{
+    int status;
+    char server_portstr[32];
+    conn_rec *origin = NULL;
+    proxy_conn_rec *backend = NULL;
+    int is_ssl = 0;
+
+    /* Note: Memory pool allocation.
+     * A downstream keepalive connection is always connected to the existence
+     * (or not) of an upstream keepalive connection. If this is not done then
+     * load balancing against multiple backend servers breaks (one backend
+     * server ends up taking 100% of the load), and the risk is run of
+     * downstream keepalive connections being kept open unnecessarily. This
+     * keeps webservers busy and ties up resources.
+     *
+     * As a result, we allocate all sockets out of the upstream connection
+     * pool, and when we want to reuse a socket, we check first whether the
+     * connection ID of the current upstream connection is the same as that
+     * of the connection when the socket was opened.
+     */
+    apr_pool_t *p = r->connection->pool;
+    conn_rec *c = r->connection;
+    apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
+    proxy_ajp_conn_t *p_conn = apr_pcalloc(r->connection->pool,
+                                           sizeof(*p_conn));
+
+    
+    /* only use stored info for top-level pages. Sub requests don't share 
+     * in keepalives
+     */
+    if (!r->main) {
+        backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
+                                                      &proxy_ajp_module);
+    }
+    /* create space for state information */
+    if (!backend) {
+        backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
+        backend->connection = NULL;
+        backend->hostname = NULL;
+        backend->port = 0;
+        if (!r->main) {
+            ap_set_module_config(c->conn_config, &proxy_ajp_module, backend);
+        }
+    }
+
+    if (strncasecmp(url, "ajp:", 4) != 0) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: AJP: declining URL %s", url);
+        return DECLINED;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy: AJP: serving URL %s", url);
+    
+    
+    /* only use stored info for top-level pages. Sub requests don't share 
+     * in keepalives
+     */
+    if (!r->main) {
+        backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
+                                                      &proxy_ajp_module);
+    }
+    /* create space for state information */
+    if (!backend) {
+        backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
+        backend->connection = NULL;
+        backend->hostname = NULL;
+        backend->port = 0;
+        if (!r->main) {
+            ap_set_module_config(c->conn_config, &proxy_ajp_module, backend);
+        }
+    }
+
+    backend->is_ssl = is_ssl;
+
+    /* Step One: Determine Who To Connect To */
+    status = ap_proxy_http_determine_connection(p, r, p_conn, c, conf, uri,
+                                                &url, proxyname, proxyport,
+                                                server_portstr,
+                                                sizeof(server_portstr));
+    if ( status != OK ) {
+        return status;
+    }
+
+    /* Step Two: Make the Connection */
+    status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend,
+                                             conf, proxyname);
+    if ( status != OK ) {
+        return status;
+    }
+   
+    /* Step Three: Send the Request */
+    status = ap_proxy_ajp_request(p, r, p_conn, origin, conf, uri, url,
+                                   server_portstr);
+    if ( status != OK ) {
+        return status;
+    }
+
+    /* Step Four: Receive the Response */
+    status = ap_proxy_ajp_process_response(p, r, p_conn, origin, backend, conf,
+                                            server_portstr);
+    if ( status != OK ) {
+        /* clean up even if there is an error */
+        ap_proxy_http_cleanup(r, p_conn, backend);
+        return status;
+    }
+
+    /* Step Five: Clean Up */
+    status = ap_proxy_http_cleanup(r, p_conn, backend);
+    if ( status != OK ) {
+        return status;
+    }
+
+    return OK;
+}
+
+static void ap_proxy_http_register_hook(apr_pool_t *p)
+{
+    proxy_hook_scheme_handler(ap_proxy_ajp_handler, NULL, NULL, APR_HOOK_FIRST);
+    proxy_hook_canon_handler(ap_proxy_ajp_canon, NULL, NULL, APR_HOOK_FIRST);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_ajp_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 */
+};
+