Return-Path: Delivered-To: apmail-httpd-bugs-archive@www.apache.org Received: (qmail 53383 invoked from network); 6 Nov 2005 13:57:38 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 6 Nov 2005 13:57:38 -0000 Received: (qmail 3923 invoked by uid 500); 6 Nov 2005 13:57:36 -0000 Delivered-To: apmail-httpd-bugs-archive@httpd.apache.org Received: (qmail 3867 invoked by uid 500); 6 Nov 2005 13:57:35 -0000 Mailing-List: contact bugs-help@httpd.apache.org; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: Reply-To: "Apache HTTPD Bugs Notification List" List-Id: Delivered-To: mailing list bugs@httpd.apache.org Received: (qmail 3847 invoked by uid 99); 6 Nov 2005 13:57:35 -0000 X-ASF-Spam-Status: No, hits=0.6 required=10.0 tests=NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [192.87.106.226] (HELO ajax.apache.org) (192.87.106.226) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 06 Nov 2005 05:57:34 -0800 Received: by ajax.apache.org (Postfix, from userid 99) id 58F1F5C8; Sun, 6 Nov 2005 14:57:11 +0100 (CET) From: bugzilla@apache.org To: bugs@httpd.apache.org Subject: DO NOT REPLY [Bug 37373] New: - Add persistent connection support to mod_proxy X-Bugzilla-Reason: AssignedTo Message-Id: <20051106135711.58F1F5C8@ajax.apache.org> Date: Sun, 6 Nov 2005 14:57:11 +0100 (CET) X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG� RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT . 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 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