httpd-bugs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bugzi...@apache.org
Subject DO NOT REPLY [Bug 37373] New: - Add persistent connection support to mod_proxy
Date Sun, 06 Nov 2005 13:57:11 GMT
DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG·
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://issues.apache.org/bugzilla/show_bug.cgi?id=37373>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND·
INSERTED IN THE BUG DATABASE.

http://issues.apache.org/bugzilla/show_bug.cgi?id=37373

           Summary: Add persistent connection support to mod_proxy
           Product: Apache httpd-1.3
           Version: 1.3.27
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: mod_proxy
        AssignedTo: bugs@httpd.apache.org
        ReportedBy: stoddard@apache.org


This patch is a backport of persistent connection support for mod_proxy from
v2.0.  The port was performed by Sean C Farley <sean-apache@farley.org> when he
was employed by IBM. The patch is against 1.3.27 (yes, this patch is old and dusty).

diff -ru apache_1.3.27/src/include/http_config.h mod/src/include/http_config.h
--- apache_1.3.27/src/include/http_config.h	Wed Mar 13 15:05:29 2002
+++ mod/src/include/http_config.h	Mon Mar  3 11:13:46 2003
@@ -354,6 +354,7 @@
 API_EXPORT(void) ap_init_modules(pool *p, server_rec *s);
 API_EXPORT(void) ap_child_init_modules(pool *p, server_rec *s);
 API_EXPORT(void) ap_child_exit_modules(pool *p, server_rec *s);
+CORE_EXPORT(void *) ap_create_conn_config(pool *p);
 API_EXPORT(void) ap_setup_prelinked_modules(void);
 API_EXPORT(void) ap_show_directives(void);
 API_EXPORT(void) ap_show_modules(void);
diff -ru apache_1.3.27/src/include/httpd.h mod/src/include/httpd.h
--- apache_1.3.27/src/include/httpd.h	Mon Sep 30 11:35:21 2002
+++ mod/src/include/httpd.h	Fri Apr  4 14:34:45 2003
@@ -698,7 +698,7 @@
 
     char *the_request;		/* First line of request, so we can log it */
     int assbackwards;		/* HTTP/0.9, "simple" request */
-    enum proxyreqtype proxyreq;/* A proxy request (calculated during
+    enum proxyreqtype proxyreq;	/* A proxy request (calculated during
 				 * post_read_request or translate_name) */
     int header_only;		/* HEAD request, as opposed to GET */
     char *protocol;		/* Protocol, as given to us, or HTTP/0.9 */
@@ -894,6 +894,12 @@
     char *local_host;		/* used for ap_get_server_name when
 				 * UseCanonicalName is set to DNS
 				 * (ignores setting of HostnameLookups) */
+
+    void *conn_config;		/* Notes on *this* connection */
+    /* The 'notes' table is for notes from one module to another, with no
+     * other set purpose in mind...
+     */
+    table *notes;
 };
 
 /* Per-vhost config... */
diff -ru apache_1.3.27/src/main/http_config.c mod/src/main/http_config.c
--- apache_1.3.27/src/main/http_config.c	Mon Sep 30 06:17:40 2002
+++ mod/src/main/http_config.c	Mon Mar  3 11:10:09 2003
@@ -216,6 +216,11 @@
     return create_empty_config(p);
 }
 
+CORE_EXPORT(void *) ap_create_conn_config(pool *p)
+{
+    return create_empty_config(p);
+}
+
 CORE_EXPORT(void *) ap_create_per_dir_config(pool *p)
 {
     return create_empty_config(p);
diff -ru apache_1.3.27/src/main/http_main.c mod/src/main/http_main.c
--- apache_1.3.27/src/main/http_main.c	Fri Sep 27 12:40:24 2002
+++ mod/src/main/http_main.c	Mon Mar  3 11:30:12 2003
@@ -3562,6 +3562,8 @@
     /* Got a connection structure, so initialize what fields we can
      * (the rest are zeroed out by pcalloc).
      */
+    conn->conn_config = ap_create_conn_config(p);
+    conn->notes = ap_make_table(p, 5);
 
     conn->child_num = child_num;
 
diff -ru apache_1.3.27/src/modules/proxy/mod_proxy.h
mod/src/modules/proxy/mod_proxy.h
--- apache_1.3.27/src/modules/proxy/mod_proxy.h	Sun Apr 21 06:35:07 2002
+++ mod/src/modules/proxy/mod_proxy.h	Fri Apr  4 17:16:35 2003
@@ -205,6 +205,19 @@
     char io_buffer_size_set;
 } proxy_server_conf;
 
+typedef struct {
+    conn_rec *connection;
+    char *hostname;
+    unsigned short port;
+    BUFF *sock;
+} proxy_conn_rec;
+
+typedef struct {
+    const char     *name;
+    unsigned short  port;
+    int             close;              /* Close connection indicator */
+} proxy_http_conn_t;
+
 struct hdr_entry {
     const char *field;
     const char *value;
@@ -282,6 +295,13 @@
 
 /* proxy_http.c */
 
+int ap_proxy_http_create_connection(pool *p, request_rec *r,
+                                    proxy_http_conn_t *p_conn, conn_rec *conn,
+                                    proxy_conn_rec *backend,
+                                    proxy_server_conf *conf,
+                                    const char *proxyhost, BUFF **f,
+                                    struct hostent *server_hp,
+                                    struct sockaddr_in *server);
 int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme,
                      int def_port);
 int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
diff -ru apache_1.3.27/src/modules/proxy/proxy_http.c
mod/src/modules/proxy/proxy_http.c
--- apache_1.3.27/src/modules/proxy/proxy_http.c	Tue Sep  3 02:12:46 2002
+++ mod/src/modules/proxy/proxy_http.c	Thu Apr 10 16:58:12 2003
@@ -64,6 +64,8 @@
 #include "http_core.h"
 #include "util_date.h"
 
+module MODULE_VAR_EXPORT proxy_http_module;
+
 /*
  * Canonicalise http-like URLs.
  *  scheme is the scheme for the URL
@@ -114,6 +116,353 @@
     return OK;
 }
 
+int ap_proxy_http_create_connection(pool *p,
+                                    request_rec *r,
+                                    proxy_http_conn_t *p_conn,
+                                    conn_rec *conn,
+                                    proxy_conn_rec *backend,
+                                    proxy_server_conf *conf,
+                                    const char *proxyhost,
+                                    BUFF **f,
+                                    struct hostent *server_hp,
+                                    struct sockaddr_in *server)
+{
+    int i, j, sock;
+
+    /* if a keepalive socket is already open, check whether it must stay
+     * open, or whether it should be closed and a new socket created.
+     */
+    if (backend->connection)
+    {
+        *f = backend->sock;
+        /*
+         * Only GET/HEAD requests are re-used.  The reasoning behind this is
+         * that the proxy does not save the data sent from the client within a
+         * POST.  If an existing connection is closed by the backend while
+         * POST'd data is being sent to the backend, the data is lost and canoot
+         * be resent.  This would result in a 502 being sent back to the client.
+         *
+         * It is safer to just close the connection and create a new one to
+         * avoid a timeout by the server.  This will prevent 502's being issued
+         * because of connection timeouts.
+         */
+        if (r->method_number != M_GET)
+        {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO,
+                         r->server,
+                         "proxy: non-GET/HEAD request.  Closing connection");
+            ap_bclose(*f);
+            backend->connection = NULL;
+        }
+        else if ((backend->connection->child_num == conn->child_num) &&
+                 (backend->port == p_conn->port) &&
+                 (backend->hostname) &&
+                 (!strcasecmp(backend->hostname, p_conn->name)))
+        {
+            fd_set fds;
+            struct timeval tv;
+
+            ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server,
+                         "proxy: keepalive address match "
+                         "(keep original socket)");
+            /*
+             * Verify connection to backend is still alive.
+             *
+             * If the connection is readable, there is either leftover data on
+             * the connection (should not be) or an EOF from the server.
+             */
+            do
+            {
+                FD_ZERO(&fds);
+                FD_SET((*f)->fd_in, &fds);
+                tv.tv_sec = 0;
+                tv.tv_usec = 0;
+                i = ap_select((*f)->fd_in + 1, &fds, NULL, NULL, &tv);
+            } while (i < 0 && errno == EINTR && !((*f)->flags &
B_EOUT));
+            if (FD_ISSET((*f)->fd_in, &fds))
+            {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO,
+                             r->server,
+                             "proxy: previous connection is closed");
+                ap_bclose(*f);
+                backend->connection = NULL;
+            }
+        }
+        else
+        {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 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);
+            ap_bclose(*f);
+            backend->connection = NULL;
+        }
+    }
+
+    /* Make connection to backend. */
+    if (!backend->connection)
+    {
+        /*
+         * we have worked out who exactly we are going to connect to, now make
+         * that connection...
+         */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server,
+                     "proxy: creating connection to backend");
+        sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
+        if (sock == -1)
+        {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                          "proxy: error creating socket");
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+#if !defined(TPF) && !defined(BEOS)
+        if (conf->recv_buffer_size)
+        {
+            if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+                           (const char *)&conf->recv_buffer_size, sizeof(int))
+                == -1)
+            {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                              "setsockopt(SO_RCVBUF): Failed to set
ProxyReceiveBufferSize, using default");
+            }
+        }
+#endif
+
+        /* Initialize.  Start out as an error. */
+        i = -1;
+
+#ifdef SINIX_D_RESOLVER_BUG
+        {
+            struct in_addr *ip_addr = (struct in_addr *)*server_hp->h_addr_list;
+
+            for (; ip_addr->s_addr != 0; ++ip_addr)
+            {
+                memcpy(&server->sin_addr, ip_addr, sizeof(struct in_addr));
+                i = ap_proxy_doconnect(sock, server, r);
+                if (i == 0)
+                    break;
+            }
+        }
+#else
+        j = 0;
+        while (server_hp->h_addr_list[j] != NULL)
+        {
+            memcpy(&server->sin_addr, server_hp->h_addr_list[j],
+                   sizeof(struct in_addr));
+            i = ap_proxy_doconnect(sock, server, r);
+            if (i == 0)
+                break;
+            j++;
+        }
+#endif
+        if (i == -1)
+        {
+            if (proxyhost != NULL)
+                return DECLINED;    /* try again another way */
+            else
+                return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
+                                                                     "Could not
connect to remote machine: ",
+                                                                    
strerror(errno), NULL));
+        }
+
+        /*
+         * At this point, we start sending the HTTP/1.1 request to the remote
+         * server (proxy or otherwise).
+         */
+        *f = ap_bcreate(p, B_RDWR | B_SOCKET);
+        ap_bpushfd(*f, sock, sock);
+
+        backend->connection = conn;
+        backend->hostname = ap_pstrdup(p, p_conn->name);
+        backend->port = p_conn->port;
+        backend->sock = *f;
+    }
+
+    return OK;
+}
+
+void ap_proxy_http_build_request(pool *p,
+                                 request_rec *r,
+                                 proxy_http_conn_t *p_conn,
+                                 const char *url,
+                                 const char *desthost,
+                                 const char *destportstr,
+                                 proxy_server_conf *conf,
+                                 table **req_hdrs,
+                                 char **requestLine)
+{
+    int i;
+    char portstr[32];
+
+    /*
+     * build upstream-request headers by stripping r->headers_in from
+     * connection specific headers. We must not remove the Connection: header
+     * from r->headers_in, we still have to react to Connection: close
+     */
+    *req_hdrs = ap_copy_table(r->pool, r->headers_in);
+    ap_proxy_clear_connection(r->pool, *req_hdrs);
+
+    if (conf->viaopt == via_block) {
+        /* Block all outgoing Via: headers */
+        ap_table_unset(*req_hdrs, "Via");
+    }
+    else if (conf->viaopt != via_off) {
+        /* Create a "Via:" request header entry and merge it */
+        i = ap_get_server_port(r);
+        if (ap_is_default_port(i, r)) {
+            strcpy(portstr, "");
+        }
+        else {
+            ap_snprintf(portstr, sizeof portstr, ":%d", i);
+        }
+        /* Generate outgoing Via: header with/without server comment: */
+        ap_table_mergen(*req_hdrs, "Via",
+                        (conf->viaopt == via_full)
+                        ? ap_psprintf(p, "%d.%d %s%s (%s)",
+                                      HTTP_VERSION_MAJOR(r->proto_num),
+                                      HTTP_VERSION_MINOR(r->proto_num),
+                                      ap_get_server_name(r), portstr,
+                                      SERVER_BASEVERSION)
+                        : ap_psprintf(p, "%d.%d %s%s",
+                                      HTTP_VERSION_MAJOR(r->proto_num),
+                                      HTTP_VERSION_MINOR(r->proto_num),
+                                      ap_get_server_name(r), portstr)
+            );
+    }
+
+    /* the X-* headers are only added if we are a reverse
+     * proxy, otherwise we would be giving away private information.
+     */
+    if (r->proxyreq == PROXY_PASS) {
+        const char *buf;
+
+        /*
+         * Add X-Forwarded-For: so that the upstream has a chance to determine,
+         * where the original request came from.
+         */
+        ap_table_mergen(*req_hdrs, "X-Forwarded-For", r->connection->remote_ip);
+
+        /* Add X-Forwarded-Host: so that upstream knows what the
+         * original request hostname was.
+         */
+        if ((buf = ap_table_get(r->headers_in, "Host"))) {
+            ap_table_mergen(*req_hdrs, "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?
+         */
+        ap_table_mergen(*req_hdrs, "X-Forwarded-Server",
r->server->server_hostname);
+    } 
+
+    /* sub-requests never use keepalives */
+    if ((ap_proxy_liststr(ap_table_get(r->headers_in, "Connection"),
+                          "close", NULL)) ||
+        (r->main))
+    {
+        p_conn->close = 1;
+        ap_table_set(*req_hdrs, "Connection", "close");
+    }
+
+    if (destportstr)
+    {
+        *requestLine = ap_pstrcat(p, r->method, " ", url, " HTTP/1.1", CRLF,
+                                  "Host: ", desthost, ":", destportstr, CRLF,
+                                  NULL);
+    }
+    else
+    {
+        *requestLine = ap_pstrcat(p, r->method, " ", url, " HTTP/1.1", CRLF,
+                                  "Host: ", desthost, CRLF, NULL);
+    }
+
+    return;
+}
+
+void ap_proxy_http_send_request(BUFF *f,
+                                request_rec *r,
+                                table *req_hdrs,
+                                const char *requestLine)
+{
+    int i;
+    array_header *reqhdrs_arr = ap_table_elts(req_hdrs);
+    table_entry *reqhdrs_elts = (table_entry *)reqhdrs_arr->elts;
+
+    ap_hard_timeout("proxy send", r);
+    ap_bvputs(f, requestLine, NULL);
+    for (i = 0; i < reqhdrs_arr->nelts; i++) {
+        if (reqhdrs_elts[i].key == NULL || reqhdrs_elts[i].val == NULL
+
+        /*
+         * Clear out hop-by-hop request headers not to send: RFC2616 13.5.1
+         * says we should strip these headers:
+         */
+            || !strcasecmp(reqhdrs_elts[i].key, "Host") /* Already sent */
+            || !strcasecmp(reqhdrs_elts[i].key, "Keep-Alive")
+            || !strcasecmp(reqhdrs_elts[i].key, "TE")
+            || !strcasecmp(reqhdrs_elts[i].key, "Trailer")
+            || !strcasecmp(reqhdrs_elts[i].key, "Transfer-Encoding")
+            || !strcasecmp(reqhdrs_elts[i].key, "Upgrade")
+        /*
+         * XXX: @@@ FIXME: "Proxy-Authorization" should *only* be suppressed
+         * if THIS server requested the authentication, not when a frontend
+         * proxy requested it!
+         * 
+         * The solution to this problem is probably to strip out the
+         * Proxy-Authorisation header in the authorisation code itself, not
+         * here. This saves us having to signal somehow whether this request
+         * was authenticated or not.
+         */
+            || !strcasecmp(reqhdrs_elts[i].key, "Proxy-Authorization"))
+            continue;
+
+        /* for sub-requests, ignore freshness/expiry headers */
+        if (r->main) {
+            if (reqhdrs_elts[i].key == NULL || reqhdrs_elts[i].val == NULL
+                || !strcasecmp(reqhdrs_elts[i].key, "If-Match")
+                || !strcasecmp(reqhdrs_elts[i].key, "If-Modified-Since")
+                || !strcasecmp(reqhdrs_elts[i].key, "If-Range")
+                || !strcasecmp(reqhdrs_elts[i].key, "If-Unmodified-Since")
+                || !strcasecmp(reqhdrs_elts[i].key, "If-None-Match")) {
+                continue;
+            }
+
+            /* If you POST to a page that gets server-side parsed
+             * by mod_include, and the parsing results in a reverse
+             * proxy call, the proxied request will be a GET, but
+             * its request_rec will have inherited the Content-Length
+             * of the original request (the POST for the enclosing
+             * page).  We can't send the original POST's request body
+             * as part of the proxied subrequest, so we need to avoid
+             * sending the corresponding content length.  Otherwise,
+             * the server to which we're proxying will sit there
+             * forever, waiting for a request body that will never
+             * arrive.
+             */
+            if ((r->method_number == M_GET) && reqhdrs_elts[i].key &&
+                !strcasecmp(reqhdrs_elts[i].key, "Content-Length")) {
+                continue;
+            }
+        }
+
+        ap_bvputs(f, reqhdrs_elts[i].key, ": ", reqhdrs_elts[i].val, CRLF, NULL);
+    }
+
+    /* the obligatory empty line to mark the end of the headers */
+    ap_bputs(CRLF, f);
+
+    /* and flush the above away */
+    ap_bflush(f);
+
+    /* and kill the send timeout */
+    ap_kill_timeout(r);
+
+    return;
+}
+
 /* handle the conversion of URLs in the ProxyPassReverse function */
 static const char *proxy_location_reverse_map(request_rec *r, const char *url)
 {
@@ -152,24 +501,28 @@
     const char *strp;
     char *strp2;
     const char *err, *desthost;
-    int i, j, sock,/* len,*/ backasswards;
+    int i, /* len,*/ backasswards;
     table *req_hdrs, *resp_hdrs;
-    array_header *reqhdrs_arr;
-    table_entry *reqhdrs_elts;
     struct sockaddr_in server;
     struct in_addr destaddr;
     struct hostent server_hp;
-    BUFF *f;
+    BUFF *f = NULL;
     char buffer[HUGE_STRING_LEN];
     char portstr[32];
-    pool *p = r->pool;
+    conn_rec *conn = r->connection;
+    pool *p = conn->pool;
+    /* Previous connection information to downstream. */
+    proxy_conn_rec *backend = NULL;
     int destport = 0;
     int chunked = 0;
     char *destportstr = NULL;
     const char *urlptr = NULL;
+    char *requestLine;
     const char *datestr, *urlstr;
-    int result, major, minor;
+    int result = 0;
+    int major, minor;
     const char *content_length;
+    int connectAttempts;
 
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
@@ -178,6 +531,29 @@
     struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
     int nocache = 0;
 
+    /* Current connection information to downstream. */
+    proxy_http_conn_t *p_conn = ap_pcalloc(p, 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(conn->conn_config,
+                                                          &proxy_http_module);
+    }
+
+    /* create space for state information */
+    if (!backend) {
+        backend = ap_pcalloc(p, sizeof(*backend));
+        backend->connection = NULL;
+        backend->hostname = NULL;
+        backend->port = 0;
+        if (!r->main) {
+            ap_set_module_config(conn->conn_config, &proxy_http_module,
+                                 backend);
+        }
+    }
+
     if (conf->cache.root == NULL)
         nocache = 1;
 
@@ -224,195 +600,95 @@
     }
 
     if (proxyhost != NULL) {
+        p_conn->port = proxyport;
+        p_conn->name = proxyhost;
         server.sin_port = htons((unsigned short)proxyport);
         err = ap_proxy_host2addr(proxyhost, &server_hp);
         if (err != NULL)
             return DECLINED;    /* try another */
     }
     else {
+        p_conn->port = destport;
+        p_conn->name = desthost;
         server.sin_port = htons((unsigned short)destport);
         err = ap_proxy_host2addr(desthost, &server_hp);
         if (err != NULL)
             return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
     }
 
-
-    /*
-     * we have worked out who exactly we are going to connect to, now make
-     * that connection...
-     */
-    sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (sock == -1) {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                      "proxy: error creating socket");
-        return HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-#if !defined(TPF) && !defined(BEOS)
-    if (conf->recv_buffer_size) {
-        if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
-                       (const char *)&conf->recv_buffer_size, sizeof(int))
-            == -1) {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                          "setsockopt(SO_RCVBUF): Failed to set
ProxyReceiveBufferSize, using default");
-        }
-    }
-#endif
-
-#ifdef SINIX_D_RESOLVER_BUG
-    {
-        struct in_addr *ip_addr = (struct in_addr *)*server_hp.h_addr_list;
-
-        for (; ip_addr->s_addr != 0; ++ip_addr) {
-            memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
-            i = ap_proxy_doconnect(sock, &server, r);
-            if (i == 0)
-                break;
-        }
-    }
-#else
-    j = 0;
-    while (server_hp.h_addr_list[j] != NULL) {
-        memcpy(&server.sin_addr, server_hp.h_addr_list[j],
-               sizeof(struct in_addr));
-        i = ap_proxy_doconnect(sock, &server, r);
-        if (i == 0)
-            break;
-        j++;
-    }
-#endif
-    if (i == -1) {
-        if (proxyhost != NULL)
-            return DECLINED;    /* try again another way */
-        else
-            return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
-                                    "Could not connect to remote machine: ",
-                                                    strerror(errno), NULL));
-    }
+    /* Build request. */
+    ap_proxy_http_build_request(p, r, p_conn, proxyhost ? url : urlptr,
+                                desthost, (destport != DEFAULT_HTTP_PORT ?
+                                           destportstr : NULL), conf, &req_hdrs,
+                                &requestLine);
 
     /* record request_time for HTTP/1.1 age calculation */
     c->req_time = time(NULL);
 
     /*
-     * build upstream-request headers by stripping r->headers_in from
-     * connection specific headers. We must not remove the Connection: header
-     * from r->headers_in, we still have to react to Connection: close
-     */
-    req_hdrs = ap_copy_table(r->pool, r->headers_in);
-    ap_proxy_clear_connection(r->pool, req_hdrs);
-
-    /*
-     * At this point, we start sending the HTTP/1.1 request to the remote
-     * server (proxy or otherwise).
+     * Make connection (hopefully verified) to backend.  Only GET's are actually
+     * verified.
      */
-    f = ap_bcreate(p, B_RDWR | B_SOCKET);
-    ap_bpushfd(f, sock, sock);
-
-    ap_hard_timeout("proxy send", r);
-    ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.1" CRLF,
-              NULL);
-    /* Send Host: now, adding it to req_hdrs wouldn't be much better */
-    if (destportstr != NULL && destport != DEFAULT_HTTP_PORT)
-        ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL);
-    else
-        ap_bvputs(f, "Host: ", desthost, CRLF, NULL);
-
-    if (conf->viaopt == via_block) {
-        /* Block all outgoing Via: headers */
-        ap_table_unset(req_hdrs, "Via");
+    if (r->method_number == M_GET)
+    {
+        connectAttempts = 2;
     }
-    else if (conf->viaopt != via_off) {
-        /* Create a "Via:" request header entry and merge it */
-        i = ap_get_server_port(r);
-        if (ap_is_default_port(i, r)) {
-            strcpy(portstr, "");
-        }
-        else {
-            ap_snprintf(portstr, sizeof portstr, ":%d", i);
-        }
-        /* Generate outgoing Via: header with/without server comment: */
-        ap_table_mergen(req_hdrs, "Via",
-                        (conf->viaopt == via_full)
-                        ? ap_psprintf(p, "%d.%d %s%s (%s)",
-                                      HTTP_VERSION_MAJOR(r->proto_num),
-                                      HTTP_VERSION_MINOR(r->proto_num),
-                                      ap_get_server_name(r), portstr,
-                                      SERVER_BASEVERSION)
-                        : ap_psprintf(p, "%d.%d %s%s",
-                                      HTTP_VERSION_MAJOR(r->proto_num),
-                                      HTTP_VERSION_MINOR(r->proto_num),
-                                      ap_get_server_name(r), portstr)
-            );
+    else
+    {
+        connectAttempts = 0;
     }
+    do
+    {
+        /* Perform this test only one time. */
+        connectAttempts--;
 
-    /* the X-* headers are only added if we are a reverse
-     * proxy, otherwise we would be giving away private information.
-     */
-    if (r->proxyreq == PROXY_PASS) {
-        const char *buf;
-
-        /*
-         * Add X-Forwarded-For: so that the upstream has a chance to determine,
-         * where the original request came from.
-         */
-        ap_table_mergen(req_hdrs, "X-Forwarded-For", r->connection->remote_ip);
-
-        /* Add X-Forwarded-Host: so that upstream knows what the
-         * original request hostname was.
-         */
-        if ((buf = ap_table_get(r->headers_in, "Host"))) {
-            ap_table_mergen(req_hdrs, "X-Forwarded-Host", buf);
+        /* Make connection to backend. */
+        result = ap_proxy_http_create_connection(p, r, p_conn, conn, backend,
+                                                 conf, proxyhost, &f,
+                                                 &server_hp, &server);
+        if (result != OK)
+        {
+            return result;
         }
 
-        /* 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?
-         */
-        ap_table_mergen(req_hdrs, "X-Forwarded-Server",
r->server->server_hostname);
-    } 
-
-    /* we don't yet support keepalives - but we will soon, I promise! */
-    ap_table_set(req_hdrs, "Connection", "close");
-
-    reqhdrs_arr = ap_table_elts(req_hdrs);
-    reqhdrs_elts = (table_entry *)reqhdrs_arr->elts;
-    for (i = 0; i < reqhdrs_arr->nelts; i++) {
-        if (reqhdrs_elts[i].key == NULL || reqhdrs_elts[i].val == NULL
+        /* Send request. */
+        ap_proxy_http_send_request(f, r, req_hdrs, requestLine);
 
         /*
-         * Clear out hop-by-hop request headers not to send: RFC2616 13.5.1
-         * says we should strip these headers:
+         * Verify that server has not timed-out on us.  If it has, then try once
+         * more with a new connection.  This only works on GET requests.
          */
-            || !strcasecmp(reqhdrs_elts[i].key, "Host") /* Already sent */
-            || !strcasecmp(reqhdrs_elts[i].key, "Keep-Alive")
-            || !strcasecmp(reqhdrs_elts[i].key, "TE")
-            || !strcasecmp(reqhdrs_elts[i].key, "Trailer")
-            || !strcasecmp(reqhdrs_elts[i].key, "Transfer-Encoding")
-            || !strcasecmp(reqhdrs_elts[i].key, "Upgrade")
-        /*
-         * XXX: @@@ FIXME: "Proxy-Authorization" should *only* be suppressed
-         * if THIS server requested the authentication, not when a frontend
-         * proxy requested it!
-         * 
-         * The solution to this problem is probably to strip out the
-         * Proxy-Authorisation header in the authorisation code itself, not
-         * here. This saves us having to signal somehow whether this request
-         * was authenticated or not.
-         */
-            || !strcasecmp(reqhdrs_elts[i].key, "Proxy-Authorization"))
-            continue;
-        ap_bvputs(f, reqhdrs_elts[i].key, ": ", reqhdrs_elts[i].val, CRLF, NULL);
-    }
-
-    /* the obligatory empty line to mark the end of the headers */
-    ap_bputs(CRLF, f);
+        if (r->method_number == M_GET)
+        {
+            /*
+             * Grab the response line.  It doubles as a test of connectivity to
+             * the server.
+             */
+            ap_hard_timeout("proxy receive response status line", r);
+            result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer),
+                                                 &backasswards, &major, &minor);
+            ap_kill_timeout(r);
 
-    /* and flush the above away */
-    ap_bflush(f);
-
-    /* and kill the send timeout */
-    ap_kill_timeout(r);
+            if (result != OK)
+            {
+                if (connectAttempts > 0)
+                {
+                    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO,
+                                 r->server,
+                                 "proxy: previous connection is closed");
+                    ap_bclose(f);
+                    backend->connection = NULL;
+                }
+                else
+                {
+                    ap_bclose(f);
+                    backend->connection = NULL;
 
+                    return (result);
+                }
+            }
+        }
+    } while (result != OK);
 
     /* read the request data, and pass it to the backend.
      * we might encounter a stray 100-continue reponse from a PUT or POST,
@@ -420,46 +696,52 @@
      * response again.
      */
     {
-        /* send the request data, if any. */
-        ap_hard_timeout("proxy receive request data", r);
-        if (ap_should_client_block(r)) {
-            while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) {
-                ap_reset_timeout(r);
-                ap_bwrite(f, buffer, i);
+        if (r->method_number != M_GET)
+        {
+            /* send the request data, if any. */
+            ap_hard_timeout("proxy receive request data", r);
+            if (ap_should_client_block(r)) {
+                while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0)
+                {
+                    ap_reset_timeout(r);
+                    ap_bwrite(f, buffer, i);
+                }
             }
-        }
-        ap_bflush(f);
-        ap_kill_timeout(r);
-
+            ap_bflush(f);
+            ap_kill_timeout(r);
 
-        /* then, read a response line */
-        ap_hard_timeout("proxy receive response status line", r);
-        result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer)-1,
&backasswards, &major, &minor);
-        ap_kill_timeout(r);
+            /* then, read a response line */
+            ap_hard_timeout("proxy receive response status line", r);
+            result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer),
+                                                 &backasswards, &major, &minor);
+            ap_kill_timeout(r);
 
-        /* trap any errors */
-        if (result != OK) {
-            ap_bclose(f);
-            return result;
+            /* trap any errors */
+            if (result != OK) {
+                ap_bclose(f);
+                backend->connection = NULL;
+                return result;
+            }
         }
 
         /* if this response was 100-continue, a stray response has been caught.
          * read the line again for the real response
          */
-        if (r->status == 100) {
+        if (r->status == HTTP_CONTINUE) {
             ap_hard_timeout("proxy receive response status line", r);
-            result = ap_proxy_read_response_line(f, r, buffer,
sizeof(buffer)-1, &backasswards, &major, &minor);
+            result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer),
+                                                 &backasswards, &major, &minor);
             ap_kill_timeout(r);
 
             /* trap any errors */
             if (result != OK) {
                 ap_bclose(f);
+                backend->connection = NULL;
                 return result;
             }
         }
     }
 
-
     /*
      * We have our response status line from the convoluted code above,
      * now we read the headers to continue.
@@ -512,6 +794,16 @@
                                      ap_table_get(resp_hdrs, "Transfer-Encoding"),
                                      "chunked");
 
+        /* Will the backend close the connection on us? */
+        if (ap_proxy_liststr(ap_table_get(resp_hdrs, "Connection"),
+                             "close", NULL) ||
+            ((major == 1) && (minor == 0) &&
+             !ap_proxy_liststr(ap_table_get(resp_hdrs, "Connection"),
+                               "keep-alive", NULL)))
+        {
+            p_conn->close = 1;
+        }
+
         /* strip hop-by-hop headers defined by Connection and RFC2616 */
         ap_proxy_clear_connection(p, resp_hdrs);
 
@@ -526,6 +818,9 @@
 
         /* no headers */
         resp_hdrs = ap_make_table(p, 20);
+
+        /* Connection will close. */
+        p_conn->close = 1;
     }
 
     ap_kill_timeout(r);
@@ -573,6 +868,7 @@
         i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache);
         if (i != DECLINED) {
             ap_bclose(f);
+            backend->connection = NULL;
             return i;
         }
 
@@ -630,7 +926,12 @@
 /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
 /* XXX CHANGEME: We want to eventually support keepalives, which means
  * we must read content-length bytes... */
-    if (!r->header_only) {
+    if ((!r->header_only) &&
+        (r->status >= HTTP_OK) &&              /* not any 1xx response */
+        (r->status != HTTP_NO_CONTENT) &&      /* not 204 */
+        (r->status != HTTP_RESET_CONTENT) &&   /* not 205 */
+        (r->status != HTTP_NOT_MODIFIED)) {    /* not 304 */
+
 /* we need to set this for ap_proxy_send_fb()... */
         c->cache_completion = conf->cache.cache_completion;
 
@@ -641,10 +942,43 @@
         ap_proxy_send_fb(f, r, c, c->len, 0, chunked, conf->io_buffer_size);
     }
 
-    /* ap_proxy_send_fb() closes the socket f for us */
+    /* Update the backend if the connection is to be closed. */
+    if (p_conn->close)
+    {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server,
+                     "proxy: closing connection to backend");
+        ap_bclose(f);
+        backend->connection = NULL;
+    }
 
     ap_proxy_cache_tidy(c);
 
     ap_proxy_garbage_coll(r);
     return OK;
 }
+
+/*
+ * Used to hold back-end connection information.
+ */
+module MODULE_VAR_EXPORT proxy_http_module =
+{
+    STANDARD_MODULE_STUFF,
+    NULL,                       /* initializer */
+    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 table */
+    NULL,                       /* handlers */
+    NULL,                       /* translate_handler */
+    NULL,                       /* check_user_id */
+    NULL,                       /* check auth */
+    NULL,                       /* check access */
+    NULL,                       /* type_checker */
+    NULL,                       /* pre-run fixups */
+    NULL,                       /* logger */
+    NULL,                       /* header parser */
+    NULL,                       /* child_init */
+    NULL,                       /* child_exit */
+    NULL                        /* post read-request */
+};
diff -ru apache_1.3.27/src/modules/proxy/proxy_util.c
mod/src/modules/proxy/proxy_util.c
--- apache_1.3.27/src/modules/proxy/proxy_util.c	Mon Jul 22 11:26:03 2002
+++ mod/src/modules/proxy/proxy_util.c	Thu Mar 27 10:08:06 2003
@@ -616,17 +616,6 @@
         total_bytes_rcvd += n;
 
         /* if we've received everything... */
-        /*
-         * in the case of slow frontends and expensive backends, we want to
-         * avoid leaving a backend connection hanging while the frontend
-         * takes it's time to absorb the bytes. so: if we just read the last
-         * block, we close the backend connection now instead of later - it's
-         * no longer needed.
-         */
-        if (total_bytes_rcvd == len) {
-            ap_bclose(f);
-            f = NULL;
-        }
 
         /* Write to cache first. */
         /*
@@ -689,11 +678,6 @@
 
     }                           /* loop and ap_bread while "ok" */
 
-    /* if the backend connection is still open, close it */
-    if (f) {
-        ap_bclose(f);
-    }
-
     if (!con->aborted) {
         ap_bflush(con->client);
     }
@@ -1608,11 +1592,11 @@
         *backasswards = 0;
 
         /* there need not be a reason phrase in the response,
-	 * and ap_getline() already deleted trailing whitespace.
-	 * But RFC2616 requires a SP after the Status-Code. Add one:
-	 */
-	if (strlen(buffer) < sizeof("HTTP/1.x 200 ")-1)
-	  buffer = ap_pstrcat(r->pool, buffer, " ", NULL);
+         * and ap_getline() already deleted trailing whitespace.
+         * But RFC2616 requires a SP after the Status-Code. Add one:
+         */
+        if (strlen(buffer) < sizeof("HTTP/1.x 200 ")-1)
+            buffer = ap_pstrcat(r->pool, buffer, " ", NULL);
         buffer[12] = '\0';
         r->status = atoi(&buffer[9]);
         buffer[12] = ' ';
@@ -1636,7 +1620,6 @@
     }
 
     return OK;
-
 }
 
 
diff -ru apache_1.3.27/src/support/httpd.exp mod/src/support/httpd.exp
--- apache_1.3.27/src/support/httpd.exp	Mon Jun 17 21:30:00 2002
+++ mod/src/support/httpd.exp	Mon Mar  3 13:07:21 2003
@@ -85,6 +85,7 @@
 ap_coredump_dir
 ap_count_dirs
 ap_cpystrn
+ap_create_conn_config
 ap_create_environment
 ap_create_per_dir_config
 ap_create_request_config

-- 
Configure bugmail: http://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.

---------------------------------------------------------------------
To unsubscribe, e-mail: bugs-unsubscribe@httpd.apache.org
For additional commands, e-mail: bugs-help@httpd.apache.org


Mime
View raw message