Here's a patch to mod_proxy that enables keepalives on the browser connection. It's not well tested, so please report any problems you find. The patch enables a new directive, ProxyPostMax <maxfilesize>[KM][B] Setting the ProxyPostMax directive makes mod_proxy store the request's body to a tempfile on the proxy BEFORE opening the server conection. This should lighten the load on a 'heavy' mod_perl backend server, if you're processing a lot of POST data with it. (In theory, anyway.) If this 'store-and-forward' feature isn't something that interests you, don't bother testing this patch. I understand Apache 1.3.13 will include a full HTTP/1.1 proxy. Chuck Murcko <[EMAIL PROTECTED]> writes: > Hi Joe. There's actually already a complete HTTP/1.1 patch for 1.3.13, > but we'll only be making this available for user integration, as no > further development is happening for 1.3.x Apache other than bug fixes. > If you want to contribute this patch, I can put it into the contrib area > at www.apache.org for the web server project. > > [EMAIL PROTECTED] wrote: > > > > Chuck, > > > > Are you still working on mod_proxy for > > Apache 1.3.x? > > > > I've been putting together a patch to mod_proxy > > that enables HTTP/1.1 responses (in particular > > keep-alive connections) on the browser-side. > > It also adds a new configuration directive > > "ProxyPostMax". > > > > Setting this directive, e.g. > > > > ProxyPostMax 8MB , > > > > would make mod_proxy behave as a > > store-and-forward proxy on the upload side. > > Basically, this means the origin server's connection isn't > > opened until the request body is uploaded to a tempfile > > on the proxy. Lots of us mod_perl folks have been > > looking for such a feature. > > > > This is just a hack- it's not an attempt to implement a > > fully compliant HTTP/1.1 proxy. If you're interested, > > I'll send you the patch to 1.3.12 once I've tested got it > > working with the cache enabled. > > > > -- > > Joe Schaefer > > [EMAIL PROTECTED] > > > > SunStar Systems, Inc. > > -- > Chuck > Chuck Murcko > Topsail Group > [EMAIL PROTECTED] > Here's the patch. You should cd to apache's src/modules subdir before applying it. diff -ur proxy/mod_proxy.c /usr/src/apache_1.3.12/src/modules/proxy/mod_proxy.c --- proxy/mod_proxy.c Wed Mar 15 00:25:29 2000 +++ /usr/src/apache_1.3.12/src/modules/proxy/mod_proxy.c Fri Sep 1 23:20:07 +2000 @@ -361,7 +361,7 @@ ap_psprintf(r->pool, "%d", (maxfwd > 0) ? maxfwd-1 : 0)); } - if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) + if (rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) return rc; url = r->filename + 6; @@ -487,7 +487,7 @@ ps->req_set = 0; ps->recv_buffer_size = 0; /* this default was left unset for some reason */ ps->recv_buffer_size_set = 0; - + ps->postmax = DEFAULT_POST_MAX; ps->cache.root = NULL; ps->cache.space = DEFAULT_CACHE_SPACE; ps->cache.space_set = 0; @@ -530,6 +530,7 @@ ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt; ps->req = (overrides->req_set == 0) ? base->req : overrides->req; ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size; + ps->postmax = (overrides->postmax <= 0) ? base->postmax : overrides->postmax; ps->cache.root = (overrides->cache.root == NULL) ? base->cache.root : overrides->cache.root; ps->cache.space = (overrides->cache.space_set == 0) ? base->cache.space : overrides->cache.space; @@ -743,6 +744,16 @@ return NULL; } +static const char * + set_proxy_postmax(cmd_parms *parms, void *dummy, long int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->postmax = flag; + return NULL; +} + static const char * set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg) @@ -947,6 +958,33 @@ return NULL; } +static const char* + set_post_max(cmd_parms *parms, void *dummy, char *arg) +{ + long int size; + int scan; + char a; + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + scan = sscanf(arg, "%ld %[MmKk]", &size, &a); + if (scan <= 0) + return "ProxyPostMax not a number in B, KB, or MB"; + if (scan == 2) { + switch (a) { + case 'M': + case 'm': + size *= 1024; + /* fall through */ + case 'K': + case 'k': + size *= 1024; + } + } + psf->postmax = size; + + return NULL; +} + static const handler_rec proxy_handlers[] = { {"proxy-server", proxy_handler}, @@ -995,6 +1033,8 @@ "Force a http cache completion after this percentage is loaded"}, {"ProxyVia", set_via_opt, NULL, RSRC_CONF, TAKE1, "Configure Via: proxy header header to one of: on | off | block | full"}, + {"ProxyPostMax", set_post_max, NULL, RSRC_CONF, TAKE1, + "Maximum size of request body"}, {NULL} }; diff -ur proxy/mod_proxy.h /usr/src/apache_1.3.12/src/modules/proxy/mod_proxy.h --- proxy/mod_proxy.h Tue Jan 11 09:13:44 2000 +++ /usr/src/apache_1.3.12/src/modules/proxy/mod_proxy.h Sat Sep 2 02:26:35 +2000 @@ -139,7 +139,7 @@ #define DEFAULT_HTTPS_PORT 443 #define DEFAULT_SNEWS_PORT 563 #define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */ - +#define DEFAULT_POST_MAX (-1) /* Some WWW schemes and their default ports; this is basically /etc/services */ struct proxy_services { const char *scheme; @@ -222,6 +222,7 @@ via_full } viaopt; /* how to deal with proxy Via: headers */ char viaopt_set; + long int postmax; size_t recv_buffer_size; char recv_buffer_size_set; } proxy_server_conf; diff -ur proxy/proxy_cache.c /usr/src/apache_1.3.12/src/modules/proxy/proxy_cache.c --- proxy/proxy_cache.c Wed Dec 8 18:02:43 1999 +++ /usr/src/apache_1.3.12/src/modules/proxy/proxy_cache.c Sat Sep 2 01:34:56 +2000 @@ -769,6 +769,11 @@ } ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); Explain0("Use local copy, cached file hasn't changed"); +/*JS*/ + r->status = 304; + ap_proxy_send_headers(r, c->resp_line, c->hdrs); + r->sent_bodyct = 1; +/*end JS*/ return HTTP_NOT_MODIFIED; } @@ -999,6 +1004,11 @@ ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR)); Explain0("Remote document not modified, use local copy"); /* CHECKME: Is this right? Shouldn't we check IMS again here? */ +/*JS*/ + r->status = 304; + ap_proxy_send_headers(r, c->resp_line, c->hdrs); + r->sent_bodyct = 1; +/*end JS*/ return HTTP_NOT_MODIFIED; } else { diff -ur proxy/proxy_http.c /usr/src/apache_1.3.12/src/modules/proxy/proxy_http.c --- proxy/proxy_http.c Wed Mar 15 00:25:29 2000 +++ /usr/src/apache_1.3.12/src/modules/proxy/proxy_http.c Fri Sep 1 23:01:29 +2000 @@ -63,6 +63,13 @@ #include "http_core.h" #include "util_date.h" +long int delta (long int new, long int *old) +{ + long int result; + result = new - *old; + *old = new; + return result; +} /* * Canonicalise http-like URLs. * scheme is the scheme for the URL @@ -172,7 +179,7 @@ const char *strp; char *strp2; const char *err, *desthost; - int i, j, sock, len, backasswards; + int i, j, sock, len, backasswards, rc; array_header *reqhdrs_arr; table *resp_hdrs; table_entry *reqhdrs; @@ -180,6 +187,7 @@ struct in_addr destaddr; struct hostent server_hp; BUFF *f; + FILE *infile = NULL; char buffer[HUGE_STRING_LEN]; char portstr[32]; pool *p = r->pool; @@ -266,6 +274,60 @@ #endif } + /* JS: get client data before connecting to host! */ + /* content-length limitations ? (add new directive)*/ + /* setup_client_block is caller's responsibility */ + + if (conf->postmax >= 0 && ap_should_client_block(r)) { + long int len, s, t, dt, da, a = 0, b = 0, eta, rate; + int upload = 0; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "GOTCHA: %d", conf->postmax); + len = r->remaining; + s = time(&t); /* start time */ + + if (len > conf->postmax) + return ap_proxyerror(r, HTTP_REQUEST_ENTITY_TOO_LARGE, ap_pstrcat(r->pool, + "[proxy] request body (%d bytes )too large: %d bytes MAX", + r->remaining, conf->postmax, NULL)); + + if (! (infile = tmpfile()) ) + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, + "couldn't make temp file", NULL)); + ap_note_cleanups_for_file(p, infile); + +/* prep fifo, use ticket hash for filename + if (len > conf->postmin) { + + upload = 1; + } +*/ + ap_hard_timeout("[proxy] reading data from client", r); + + while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) { + fwrite(buffer, 1, i, infile); + ap_reset_timeout(r); + a += i; + /* test for upload fifo */ + if (upload && time(NULL) - t > 1) { + da = delta(a, &b); + dt = delta(time(NULL), &t); + eta = r->remaining * ( t - s ) / a; + rate = da / dt; + + } + } + /*JS:necessary? fflush(infile); */ + + ap_kill_timeout(r); + fseek(infile, 0, 0); + if (i<0) + return ap_proxyerror(r, HTTP_PARTIAL_CONTENT, + "error reading dataset from client"); + + } + + sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, @@ -315,7 +377,8 @@ strerror(errno), NULL)); } - clear_connection(r->pool, r->headers_in); /* Strip connection-based headers */ + /* JS: this affects keep-alives !! + clear_connection(r->pool, r->headers_in);*/ /* Strip connection-based headers +*/ f = ap_bcreate(p, B_RDWR | B_SOCKET); ap_bpushfd(f, sock, sock); @@ -335,6 +398,7 @@ ap_hard_timeout("proxy send", r); ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF, NULL); + ap_bvputs(f, "Connection: close", CRLF, NULL); #ifdef EAPI { int rc = DECLINED; @@ -392,22 +456,41 @@ * suppressed if THIS server requested the authentication, * not when a frontend proxy requested it! */ + || !strcasecmp(reqhdrs[i].key, "Connection") + || !strcasecmp(reqhdrs[i].key, "Keep-Alive") || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization")) continue; ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL); } ap_bputs(CRLF, f); + ap_kill_timeout(r); + /* send the request data, if any. */ + ap_hard_timeout("[proxy] transferring data from client to server", r); + + if (conf->postmax < 0 && ap_should_client_block(r)) { + + while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) { + ap_bwrite(f,buffer,i); + ap_reset_timeout(r); + } + if (i<0) + return ap_proxyerror(r, HTTP_PARTIAL_CONTENT, + "error reading dataset from client"); - if (ap_should_client_block(r)) { - while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) + } else if (infile) { + while (( i = fread(buffer, 1, sizeof buffer, infile)) > 0 ) { ap_bwrite(f, buffer, i); + ap_reset_timeout(r); + } + fclose(infile); } + ap_bflush(f); ap_kill_timeout(r); - ap_hard_timeout("proxy receive", r); + ap_hard_timeout("[proxy] parsing http headers from server", r); len = ap_bgets(buffer, sizeof buffer - 1, f); if (len == -1) { @@ -479,7 +562,7 @@ ap_get_server_name(r), portstr) ); } - + /* JS: This one makes sense! */ clear_connection(p, resp_hdrs); /* Strip Connection hdrs */ } else { @@ -493,8 +576,7 @@ } c->hdrs = resp_hdrs; - - ap_kill_timeout(r); + ap_kill_timeout(r); /*JS parsing http headers from server */ /* * HTTP/1.0 requires us to accept 3 types of dates, but only generate @@ -525,13 +607,14 @@ return i; } - ap_hard_timeout("proxy receive", r); + ap_hard_timeout("proxy receive content", r); /* write status line */ if (!r->assbackwards) - ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); + ap_proxy_send_headers(r, "IGNORED", resp_hdrs); + if (c != NULL && c->fp != NULL && - ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) { + ap_bvputs(c->fp, "HTTP/1.1 ", r->status_line, CRLF, NULL) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, "proxy: error writing status line to %s", c->tempfile); c = ap_proxy_cache_error(c); @@ -543,7 +626,7 @@ ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL); if (!r->assbackwards) - ap_rputs(CRLF, r); +/* ap_rputs(CRLF, r); */ if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, "proxy: error writing CRLF to %s", c->tempfile); @@ -552,6 +635,7 @@ ap_bsetopt(r->connection->client, BO_BYTECT, &zero); r->sent_bodyct = 1; + /* Is it an HTTP/0.9 respose? If so, send the extra data */ if (backasswards) { ap_bwrite(r->connection->client, buffer, len); diff -ur proxy/proxy_util.c /usr/src/apache_1.3.12/src/modules/proxy/proxy_util.c --- proxy/proxy_util.c Mon Feb 7 19:34:40 2000 +++ /usr/src/apache_1.3.12/src/modules/proxy/proxy_util.c Mon Aug 28 02:48:14 +2000 @@ -499,8 +499,9 @@ int alternate_timeouts = 1; /* 1 if we alternate between soft & hard timeouts */ total_bytes_rcvd = 0; - if (c != NULL) - c->written = 0; + if (c != NULL) { + c->written = 0; + } #ifdef CHARSET_EBCDIC /* The cache copy is ASCII, not EBCDIC, even for text/html) */ @@ -542,7 +543,7 @@ */ for (ok = 1; ok; ) { if (alternate_timeouts) - ap_hard_timeout("proxy recv body from upstream server", r); + ap_hard_timeout("[proxy] recv body from server", r); /* Read block from server */ n = ap_bread(f, buf, IOBUFSIZE); @@ -555,40 +556,42 @@ if (n == -1) { /* input error */ if (c != NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error reading from %s", c->url); + "[proxy] error reading from %s", c->url); c = ap_proxy_cache_error(c); } break; } if (n == 0) break; /* EOF */ - o = 0; + o=0; total_bytes_rcvd += n; /* Write to cache first. */ /*@@@ XXX FIXME: Assuming that writing the cache file won't time out?!!? */ - if (c != NULL && c->fp != NULL) { - if (ap_bwrite(c->fp, &buf[0], n) != n) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error writing to %s", c->tempfile); + + if (c != NULL && c->fp != NULL) { + if (ap_bwrite(c->fp, buf, n) != n) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "[proxy] error writing to %s", c->tempfile); c = ap_proxy_cache_error(c); - } else { - c->written += n; - } - } + } else { + c->written += n; + } - /* Write the block to the client, detect aborted transfers */ - while (!con->aborted && n > 0) { - if (alternate_timeouts) - ap_soft_timeout("proxy send body", r); - - w = ap_bwrite(con->client, &buf[o], n); - - if (alternate_timeouts) - ap_kill_timeout(r); - else - ap_reset_timeout(r); + } + while(!con->aborted && n > 0) { + + if (alternate_timeouts) + ap_soft_timeout("proxy send body", r); + + w = ap_bwrite(con->client, buf+o, n); + + if (alternate_timeouts) + ap_kill_timeout(r); + else + ap_reset_timeout(r); + if (w <= 0) { if (c != NULL && c->fp != NULL) { /* when a send failure occurs, we need to decide @@ -603,14 +606,18 @@ ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); c->fp = NULL; unlink(c->tempfile); - c = NULL; + c = NULL; } } con->aborted = 1; break; } + +/* if (buf[n] == EOF) + break; */ + n -= w; - o += w; + o += w; } /* while client alive and more data to send */ } /* loop and ap_bread while "ok" */ @@ -621,6 +628,7 @@ return total_bytes_rcvd; } + /* * Sends response line and headers. Uses the client fd and the * headers_out array from the passed request_rec to talk to the client @@ -634,16 +642,19 @@ BUFF *fp = r->connection->client; table_entry *elts = (table_entry *) ap_table_elts(t)->elts; - ap_bvputs(fp, respline, CRLF, NULL); +/*JS ap_bvputs(fp, respline, CRLF, NULL); */ for (i = 0; i < ap_table_elts(t)->nelts; ++i) { if (elts[i].key != NULL) { - ap_bvputs(fp, elts[i].key, ": ", elts[i].val, CRLF, NULL); +/*JS ap_bvputs(fp, elts[i].key, ": ", elts[i].val, CRLF, NULL); */ ap_table_addn(r->headers_out, elts[i].key, elts[i].val); } } - - ap_bputs(CRLF, fp); + r->proxyreq = NOT_PROXY; + r->content_type = ap_table_get(t, "Content-Type"); + ap_send_http_header(r); + r->proxyreq = 1; +/*JS ap_bputs(CRLF, fp); */ } @@ -1270,8 +1281,8 @@ if (key == NULL || value == NULL || value[0] == '\0') return 1; - if (!parm->req->assbackwards) - ap_rvputs(parm->req, key, ": ", value, CRLF, NULL); +/*JS if (!parm->req->assbackwards) + ap_rvputs(parm->req, key, ": ", value, CRLF, NULL); */ if (parm->cache != NULL && parm->cache->fp != NULL && ap_bvputs(parm->cache->fp, key, ": ", value, CRLF, NULL) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, parm->cache->req, Joe Schaefer [EMAIL PROTECTED] SunStar Systems, Inc.