You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modproxy-dev@apache.org by Christian von Roques <ro...@mti.ag> on 2001/02/22 18:11:25 UTC

Re: IE 5, mod_proxy, and ProxyPass (2/3)

+    
+    /* It's safe to cache the response.
+     *
+     * We now want to update the cache file header information with
+     * the new date, last modified, expire and content length and write
+     * it away to our cache file. First, we determine these values from
+     * the response, using heuristics if appropriate.
+     *
+     * In addition, we make HTTP/1.1 age calculations and write them away
+     * too.
+     */
+
 /*
  * Read the date. Generate one if one is not supplied
  */
@@ -950,6 +1432,9 @@
 	Explain0("Added date header");
     }
 
+    /* set response_time for HTTP/1.1 age calculations */
+    c->resp_time = now;
+    
 /* check last-modified date */
     if (lmod != BAD_DATE && lmod > date)
 /* if its in the future, then replace by date */
@@ -998,121 +1483,166 @@
     else
 	c->len = atoi(clen);
 
-    ap_proxy_sec2hex(date, buff);
-    buff[8] = ' ';
-    ap_proxy_sec2hex(lmod, buff + 9);
-    buff[17] = ' ';
-    ap_proxy_sec2hex(expc, buff + 18);
-    buff[26] = ' ';
-    ap_proxy_sec2hex(c->version++, buff + 27);
-    buff[35] = ' ';
-    ap_proxy_sec2hex(c->len, buff + 36);
-    buff[44] = '\n';
-    buff[45] = '\0';
+/* we have all the header information we need - write it to the cache file */
+    c->version++;
+    ap_proxy_sec2hex(date, buff + 17*(0));
+    buff[17*(1)-1] = ' ';
+    ap_proxy_sec2hex(lmod, buff + 17*(1));
+    buff[17*(2)-1] = ' ';
+    ap_proxy_sec2hex(expc, buff + 17*(2));
+    buff[17*(3)-1] = ' ';
+    ap_proxy_sec2hex(c->version, buff + 17*(3));
+    buff[17*(4)-1] = ' ';
+    ap_proxy_sec2hex(c->req_time, buff + 17*(4));
+    buff[17*(5)-1] = ' ';
+    ap_proxy_sec2hex(c->resp_time, buff + 17*(5));
+    buff[17*(6)-1] = ' ';
+    ap_proxy_sec2hex(c->len, buff + 17*(6));
+    buff[17*(7)-1] = '\n';
+    buff[17*(7)] = '\0';
+
+
+    /* Was the server response a 304 Not Modified?
+     *
+     * If it was, it means that we requested a revalidation, and that
+     * the result of that revalidation was that the object was fresh.
+     *
+     */
 
 /* if file not modified */
     if (r->status == HTTP_NOT_MODIFIED) {
-	if (c->ims != BAD_DATE && lmod != BAD_DATE && lmod <= c->ims) {
-/* set any changed headers somehow */
-/* update dates and version, but not content-length */
-	    if (lmod != c->lmod || expc != c->expire || date != c->date) {
-		off_t curpos = lseek(ap_bfileno(c->fp, B_WR), 0, SEEK_SET);
-		if (curpos == -1)
-		    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-				 "proxy: error seeking on cache file %s",
-				 c->filename);
-		else if (write(ap_bfileno(c->fp, B_WR), buff, 35) == -1)
-		    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-				 "proxy: error updating cache file %s",
-				 c->filename);
-	    }
-	    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? */
-	    return HTTP_NOT_MODIFIED;
-	}
-	else {
-/* return the whole document */
-	    Explain0("Remote document updated, sending");
-	    r->status_line = strchr(c->resp_line, ' ') + 1;
-	    r->status = c->status;
-	    if (!r->assbackwards) {
-		ap_soft_timeout("proxy send headers", r);
-		ap_proxy_send_headers(r, c->resp_line, c->hdrs);
-		ap_kill_timeout(r);
-	    }
-	    ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
-	    r->sent_bodyct = 1;
-	    if (!r->header_only)
-		ap_proxy_send_fb(c->fp, r, NULL);
-/* set any changed headers somehow */
-/* update dates and version, but not content-length */
-	    if (lmod != c->lmod || expc != c->expire || date != c->date) {
-		off_t curpos = lseek(ap_bfileno(c->fp, B_WR), 0, SEEK_SET);
 
-		if (curpos == -1)
-		    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-				 "proxy: error seeking on cache file %s",
-				 c->filename);
-		else if (write(ap_bfileno(c->fp, B_WR), buff, 35) == -1)
-		    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-				 "proxy: error updating cache file %s",
-				 c->filename);
+	/* Have the headers changed?
+	 *
+	 * if not - we fulfil the request and return now.
+	 */
+
+	if (c->hdrs) {
+	    if (!ap_replace_tables(c->hdrs, resp_hdrs)) {
+		c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL);
+		return ap_proxy_cache_conditional(r, c, c->fp);
 	    }
-	    ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
-	    return OK;
 	}
+	else
+	    c->hdrs = resp_hdrs;
+
+
+	/* if we get here - the headers have changed. Go through the motions
+	 * of creating a new temporary cache file below, we'll then serve
+	 * the request like we would have in ap_proxy_cache_conditional()
+	 * above, and at the same time we will also rewrite the contents
+	 * to the new temporary file.
+	 */
     }
-/* new or modified file */
+
+
+    /*
+     * Ok - lets prepare and open the cached file
+     *
+     * If a cached file (in c->fp) is already open, then we want to
+     * update that cached file. Copy the c->fp to c->origfp and open
+     * up a new one.
+     *
+     * If the cached file (in c->fp) is NULL, we must open a new cached
+     * file from scratch.
+     *
+     * The new cache file will be moved to it's final location in the
+     * directory tree later, overwriting the old cache file should it exist.
+     */
+
+    /* if a cache file was already open */
     if (c->fp != NULL) {
-	ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
+	c->origfp = c->fp;
     }
-    c->version = 0;
-    ap_proxy_sec2hex(0, buff + 27);
-    buff[35] = ' ';
 
-/* open temporary file */
-#if !defined(TPF) && !defined(NETWARE)
+    while (1) {
+/* create temporary filename */
+#ifndef TPF
 #define TMPFILESTR	"/tmpXXXXXX"
-    if (conf->cache.root == NULL)
-	return DECLINED;
+	if (conf->cache.root == NULL) {
+	    c = ap_proxy_cache_error(c);
+	    break;
+	}
     c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) + sizeof(TMPFILESTR));
     strcpy(c->tempfile, conf->cache.root);
     strcat(c->tempfile, TMPFILESTR);
 #undef TMPFILESTR
     p = mktemp(c->tempfile);
 #else
-    if (conf->cache.root == NULL)
-    return DECLINED;
+	if (conf->cache.root == NULL) {
+	    c = ap_proxy_cache_error(c);
+	    break;
+	}
     c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) +1+ L_tmpnam);
     strcpy(c->tempfile, conf->cache.root);
     strcat(c->tempfile, "/");
     p = tmpnam(NULL);
     strcat(c->tempfile, p);
 #endif
-    if (p == NULL)
-	return DECLINED;
+	if (p == NULL) {
+	    c = ap_proxy_cache_error(c);
+	    break;
+	}
 
     Explain1("Create temporary file %s", c->tempfile);
 
-    i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622);
-    if (i == -1) {
-	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-		     "proxy: error creating cache file %s",
-		     c->tempfile);
-	return DECLINED;
+	/* create the new file */
+	c->fp = ap_proxy_create_cachefile(r, c->tempfile);
+	if (NULL == c->fp) {
+	    c = ap_proxy_cache_error(c);
+	    break;
     }
-    ap_note_cleanups_for_fd(r->pool, i);
-    c->fp = ap_bcreate(r->pool, B_WR);
-    ap_bpushfd(c->fp, -1, i);
 
+	/* write away the cache header and the URL */
     if (ap_bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) {
 	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
 		     "proxy: error writing cache file(%s)", c->tempfile);
-	ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
-	unlink(c->tempfile);
-	c->fp = NULL;
+	    c = ap_proxy_cache_error(c);
+	    break;
+	}
+	
+	
+	/* get original request headers */
+	if (c->req_hdrs)
+	    req_hdrs = ap_copy_table(r->pool, c->req_hdrs);
+	else
+	    req_hdrs = ap_copy_table(r->pool, r->headers_in);
+	
+	/* remove hop-by-hop headers */
+	ap_proxy_clear_connection(r->pool, req_hdrs);
+	
+	/* save original request headers */
+	if (c->req_hdrs)
+	    ap_table_do(ap_proxy_send_hdr_line, c, c->req_hdrs, NULL);
+	else
+	    ap_table_do(ap_proxy_send_hdr_line, c, r->headers_in, NULL);
+	if (ap_bputs(CRLF, c->fp) == -1) {
+	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+			  "proxy: error writing request headers terminating CRLF to %s", c->tempfile);
+	    c = ap_proxy_cache_error(c);
+	    break;
+	}
+	
+	break;
+    }
+    
+
+    /* Was the server response a 304 Not Modified?
+     *
+     * If so, we have some work to do that we didn't do when we first
+     * checked above. We need to fulfil the request, and we need to
+     * copy the body from the old object to the new one.
+     */
+    
+    /* if response from server 304 not modified */
+    if (r->status == HTTP_NOT_MODIFIED) {
+	
+	/* fulfil the request */
+	c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL);
+	return ap_proxy_cache_conditional(r, c, c->fp);
+	
     }
+    
     return DECLINED;
 }
 
@@ -1148,17 +1678,17 @@
 */
     else {
 /* update content-length of file */
-	char buff[9];
+	char buff[17];
 	off_t curpos;
 
 	c->len = bc;
 	ap_bflush(c->fp);
 	ap_proxy_sec2hex(c->len, buff);
-	curpos = lseek(ap_bfileno(c->fp, B_WR), 36, SEEK_SET);
+	curpos = lseek(ap_bfileno(c->fp, B_WR), 17*6, SEEK_SET);
 	if (curpos == -1)
 	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
 			 "proxy: error seeking on cache file %s", c->tempfile);
-	else if (write(ap_bfileno(c->fp, B_WR), buff, 8) == -1)
+	else if (write(ap_bfileno(c->fp, B_WR), buff, sizeof(buff) - 1) == -1)
 	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
 			 "proxy: error updating cache file %s", c->tempfile);
     }
diff -X nodiff.pats -urd apache/src/modules/proxy/proxy_ftp.c apache+http1.1_cache/src/modules/proxy/proxy_ftp.c
--- apache/src/modules/proxy/proxy_ftp.c	Fri Feb  9 18:00:58 2001
+++ apache+http1.1_cache/src/modules/proxy/proxy_ftp.c	Mon Feb 12 17:20:50 2001
@@ -461,9 +461,7 @@
     BUFF *data = NULL;
     pool *p = r->pool;
     int one = 1;
-    const long int zero = 0L;
     NET_SIZE_T clen;
-    struct tbl_do_args tdo;
 
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
@@ -612,6 +610,9 @@
 				strerror(errno), NULL));
     }
 
+    /* record request_time for HTTP/1.1 age calculation */
+    c->req_time = time(NULL);
+
     f = ap_bcreate(p, B_RDWR | B_SOCKET);
     ap_bpushfd(f, sock, sock);
 /* shouldn't we implement telnet control options here? */
@@ -1205,40 +1206,30 @@
 	ap_bpushfd(data, dsock, dsock);
     }
 
-    ap_hard_timeout("proxy receive", r);
 /* send response */
-/* write status line */
-    if (!r->assbackwards)
-	ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL);
-    if (c != NULL && c->fp != NULL
-	&& ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) {
-	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
-		"proxy: error writing CRLF to %s", c->tempfile);
-	    c = ap_proxy_cache_error(c);
-    }
+    /* write status line and headers to the cache file */
+    ap_proxy_write_headers(c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), resp_hdrs);
 
-/* send headers */
-    tdo.req = r;
-    tdo.cache = c;
-    ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL);
+    /* Setup the headers for our client from upstreams response-headers */
+    ap_overlap_tables(r->headers_out, resp_hdrs, AP_OVERLAP_TABLES_SET);
+    /* Add X-Cache header */
+    ap_table_setn(r->headers_out, "X-Cache",
+                  ap_pstrcat(r->pool, "MISS from ",
+                             ap_get_server_name(r), NULL));
+    /* The Content-Type of this response is the upstream one. */
+    r->content_type = ap_table_get (r->headers_out, "Content-Type");
+    /* finally output the headers to the client */
+    ap_send_http_header(r);
 
-    if (!r->assbackwards)
-	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);
-	c = ap_proxy_cache_error(c);
-    }
+    ap_hard_timeout("proxy receive", r);
 
-    ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
-    r->sent_bodyct = 1;
 /* send body */
     if (!r->header_only) {
 	if (parms[0] != 'd') {
 /* we need to set this for ap_proxy_send_fb()... */
 	    if (c != NULL)
 		c->cache_completion = 0;
-	    ap_proxy_send_fb(data, r, c);
+            ap_proxy_send_fb(data, r, c, -1, 0);
 	} else
 	    send_dir(data, r, c, cwd);
 
diff -X nodiff.pats -urd apache/src/modules/proxy/proxy_http.c apache+http1.1_cache/src/modules/proxy/proxy_http.c
--- apache/src/modules/proxy/proxy_http.c	Fri Feb  9 18:00:58 2001
+++ apache+http1.1_cache/src/modules/proxy/proxy_http.c	Thu Feb 22 12:38:43 2001
@@ -113,6 +113,7 @@
     return OK;
 }
  
+/* handle the conversion of URLs in the ProxyPassReverse function */
 static const char *proxy_location_reverse_map(request_rec *r, const char *url)
 {
     void *sconf;
@@ -135,29 +136,6 @@
     return url;
 }
 
-/* Clear all connection-based headers from the incoming headers table */
-static void clear_connection(pool *p, table *headers)
-{
-    const char *name;
-    char *next = ap_pstrdup(p, ap_table_get(headers, "Connection"));
-
-    ap_table_unset(headers, "Proxy-Connection");
-    if (!next)
-	return;
-
-    while (*next) {
-	name = next;
-	while (*next && !ap_isspace(*next) && (*next != ','))
-	    ++next;
-	while (*next && (ap_isspace(*next) || (*next == ','))) {
-	    *next = '\0';
-	    ++next;
-	}
-	ap_table_unset(headers, name);
-    }
-    ap_table_unset(headers, "Connection");
-}
-
 /*
  * This handles http:// URLs, and other URLs using a remote proxy over http
  * If proxyhost is NULL, then contact the server directly, otherwise
@@ -174,9 +152,9 @@
     char *strp2;
     const char *err, *desthost;
     int i, j, sock, len, backasswards;
+    table *req_hdrs, *resp_hdrs;
     array_header *reqhdrs_arr;
-    table *resp_hdrs;
-    table_entry *reqhdrs;
+    table_entry *reqhdrs_elts;
     struct sockaddr_in server;
     struct in_addr destaddr;
     struct hostent server_hp;
@@ -184,12 +162,10 @@
     char buffer[HUGE_STRING_LEN];
     char portstr[32];
     pool *p = r->pool;
-    const long int zero = 0L;
     int destport = 0;
     char *destportstr = NULL;
     const char *urlptr = NULL;
-    const char *datestr;
-    struct tbl_do_args tdo;
+    const char *datestr, *urlstr;
 
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
@@ -198,6 +174,8 @@
     struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
     int nocache = 0;
 
+    if (conf->cache.root == NULL) nocache = 1;
+
     memset(&server, '\0', sizeof(server));
     server.sin_family = AF_INET;
 
@@ -253,6 +231,10 @@
 	    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,
@@ -302,14 +284,27 @@
 				strerror(errno), NULL));
     }
 
-    clear_connection(r->pool, r->headers_in);	/* Strip connection-based headers */
+    /* 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).
+     */
     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.0" CRLF,
+    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
@@ -317,7 +312,7 @@
 
     if (conf->viaopt == via_block) {
 	/* Block all outgoing Via: headers */
-	ap_table_unset(r->headers_in, "Via");
+        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);
@@ -327,7 +322,7 @@
 	    ap_snprintf(portstr, sizeof portstr, ":%d", i);
 	}
 	/* Generate outgoing Via: header with/without server comment: */
-	ap_table_mergen(r->headers_in, "Via",
+	ap_table_mergen(req_hdrs, "Via",
 		    (conf->viaopt == via_full)
 			? ap_psprintf(p, "%d.%d %s%s (%s)",
 				HTTP_VERSION_MAJOR(r->proto_num),
@@ -341,24 +336,46 @@
 			);
     }
 
-    reqhdrs_arr = ap_table_elts(r->headers_in);
-    reqhdrs = (table_entry *) reqhdrs_arr->elts;
+    /* 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);
+    
+    /* 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[i].key == NULL || reqhdrs[i].val == NULL
-	/* Clear out headers not to send */
-	    || !strcasecmp(reqhdrs[i].key, "Host")	/* Already sent */
+        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[i].key, "Proxy-Authorization"))
+	    || !strcasecmp(reqhdrs_elts[i].key, "Proxy-Authorization"))
 	    continue;
-	ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL);
+	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);
-/* send the request data, if any. */
 
+/* send the request data, if any. */
     if (ap_should_client_block(r)) {
 	while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0)
 	    ap_bwrite(f, buffer, i);
@@ -366,6 +383,9 @@
     ap_bflush(f);
     ap_kill_timeout(r);
 
+
+    /* Right - now it's time to listen for a response.
+     */
     ap_hard_timeout("proxy receive", r);
 
     len = ap_bgets(buffer, sizeof buffer - 1, f);
@@ -384,10 +404,14 @@
 			     "Document contains no data");
     }
 
-/* Is it an HTTP/1 response?  This is buggy if we ever see an HTTP/1.10 */
+    /* Is it an HTTP/1 response?
+     * Do some sanity checks on the response.
+     * (This is buggy if we ever see an HTTP/1.10)
+     */
     if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
 	int major, minor;
 	if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
+            /* if no response, default to HTTP/1.0 - is this correct? */
 	    major = 1;
 	    minor = 0;
 	}
@@ -406,7 +430,7 @@
 	buffer[12] = ' ';
 	r->status_line = ap_pstrdup(p, &buffer[9]);
 
-/* read the headers. */
+        /* read the response headers. */
 /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
 /* Also, take care with headers with multiple occurences. */
 
@@ -419,6 +443,7 @@
 	    nocache = 1;    /* do not cache this broken file */
 	}
 
+        /* handle Via header in the response */
 	if (conf->viaopt != via_off && conf->viaopt != via_block) {
 	    /* Create a "Via:" response header entry and merge it */
 	    i = ap_get_server_port(r);
@@ -439,7 +464,8 @@
 			    );
 	}
 
-	clear_connection(p, resp_hdrs);	/* Strip Connection hdrs */
+        /* strip hop-by-hop headers defined by Connection */
+        ap_proxy_clear_connection(p, resp_hdrs);
     }
     else {
 /* an http/0.9 response */
@@ -451,12 +477,10 @@
 	resp_hdrs = ap_make_table(p, 20);
     }
 
-    c->hdrs = resp_hdrs;
-
     ap_kill_timeout(r);
 
 /*
- * HTTP/1.0 requires us to accept 3 types of dates, but only generate
+ * HTTP/1.1 requires us to accept 3 types of dates, but only generate
  * one type
  */
     if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL)
@@ -466,10 +490,13 @@
     if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL)
 	ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr));
 
-    if ((datestr = ap_table_get(resp_hdrs, "Location")) != NULL)
-	ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, datestr));
-    if ((datestr = ap_table_get(resp_hdrs, "URI")) != NULL)
-	ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, datestr));
+    /* handle the ProxyPassReverse mappings */
+    if ((urlstr = ap_table_get(resp_hdrs, "Location")) != NULL)
+        ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, urlstr));
+    if ((urlstr = ap_table_get(resp_hdrs, "URI")) != NULL)
+        ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, urlstr));
+    if ((urlstr = ap_table_get(resp_hdrs, "Content-Location")) != NULL)
+        ap_table_set(resp_hdrs, "Content-Location", proxy_location_reverse_map(r, urlstr));
 
 /* check if NoCache directive on this host */
     if (nocache == 0) {
@@ -482,51 +509,47 @@
 	       break;
 	    }
 	}
-    }
 
-    i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache);
-    if (i != DECLINED) {
-	ap_bclose(f);
-	return i;
-    }
-
-    ap_hard_timeout("proxy receive", r);
-
-/* write status line */
-    if (!r->assbackwards)
-	ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL);
-    if (c != NULL && c->fp != NULL &&
-	ap_bvputs(c->fp, "HTTP/1.0 ", 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);
+	/* update the cache file, possibly even fulfilling the request if
+	 * it turns out a conditional allowed us to serve the object from the
+	 * cache...
+	 */
+	i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache);
+	if (i != DECLINED) {
+	  ap_bclose(f);
+	  return i;
+	}
+	
+	/* write status line and headers to the cache file */
+	ap_proxy_write_headers(c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), resp_hdrs);
     }
 
-/* send headers */
-    tdo.req = r;
-    tdo.cache = c;
-    ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL);
-
-    if (!r->assbackwards)
-	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);
-	c = ap_proxy_cache_error(c);
-    }
+    /* Setup the headers for our client from upstreams response-headers */
+    ap_overlap_tables(r->headers_out, resp_hdrs, AP_OVERLAP_TABLES_SET);
+    /* Add X-Cache header */
+    ap_table_setn(r->headers_out, "X-Cache",
+                  ap_pstrcat(r->pool, "MISS from ",
+                             ap_get_server_name(r), NULL));
+    /* The Content-Type of this response is the upstream one. */
+    r->content_type = ap_table_get (r->headers_out, "Content-Type");
+    Explain1("Content-Type: %s", r->content_type);
+    /* finally output the headers to the client */
+    ap_send_http_header(r);
 
-    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 */
+    /* Is it an HTTP/0.9 respose? If so, send the extra data we read
+       from upstream as the start of the reponse to client */
     if (backasswards) {
+        ap_hard_timeout("proxy send assbackward", r);
+
 	ap_bwrite(r->connection->client, buffer, len);
 	if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) {
 	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
 		"proxy: error writing extra data to %s", c->tempfile);
 	    c = ap_proxy_cache_error(c);
 	}
+        ap_kill_timeout(r);
     }
-    ap_kill_timeout(r);
+
 
 #ifdef CHARSET_EBCDIC
     /* What we read/write after the header should not be modified
@@ -539,10 +562,17 @@
 /* send body */
 /* if header only, then cache will be NULL */
 /* 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) {
 /* we need to set this for ap_proxy_send_fb()... */
 	c->cache_completion = conf->cache.cache_completion;
-	ap_proxy_send_fb(f, r, c);
+	
+	/* XXX CHECKME: c->len should be the expected content length, or -1 if the
+	 * content length is not known. We need to make 100% sure c->len is always
+	 * set correctly before we get here to correctly do keepalive.
+	 */
+        ap_proxy_send_fb(f, r, c, c->len, 0);
     }
 
     ap_proxy_cache_tidy(c);
diff -X nodiff.pats -urd apache/src/modules/proxy/proxy_util.c apache+http1.1_cache/src/modules/proxy/proxy_util.c
--- apache/src/modules/proxy/proxy_util.c	Wed Feb  7 17:05:14 2001
+++ apache+http1.1_cache/src/modules/proxy/proxy_util.c	Sun Feb 18 13:30:37 2001
@@ -490,7 +490,12 @@
     return resp_hdrs;
 }
 
-long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c)
+/* read data from f, write it to:
+ * - c->fp, if it is open
+ * - r->connection->client, if nowrite == 0
+ */
+
+long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite)
 {
     int  ok;
     char buf[IOBUFSIZE];
@@ -546,7 +551,12 @@
             ap_hard_timeout("proxy recv body from upstream server", r);
 
 	/* Read block from server */
-	n = ap_bread(f, buf, IOBUFSIZE);
+        if (-1 == len) {
+	    n = ap_bread(f, buf, IOBUFSIZE);
+        }
+        else {
+            n = ap_bread(f, buf, MIN(IOBUFSIZE, len - total_bytes_rcvd));
+        }
 
         if (alternate_timeouts)
             ap_kill_timeout(r);
@@ -579,7 +589,7 @@
         }
 
 	/* Write the block to the client, detect aborted transfers */
-        while (!con->aborted && n > 0) {
+        while (!nowrite && !con->aborted && n > 0) {
             if (alternate_timeouts)
                 ap_soft_timeout("proxy send body", r);
 
@@ -613,6 +623,11 @@
             n -= w;
             o += w;
         } /* while client alive and more data to send */
+
+        /* if we've received everything, leave now */
+        if (total_bytes_rcvd == len)
+            break;
+
     } /* loop and ap_bread while "ok" */
 
     if (!con->aborted)
@@ -623,28 +638,30 @@
 }
 
 /*
- * Sends response line and headers.  Uses the client fd and the 
- * headers_out array from the passed request_rec to talk to the client
- * and to properly set the headers it sends for things such as logging.
+ * Writes response line and headers to the cache file.
  * 
- * A timeout should be set before calling this routine.
+ * If respline is NULL, no response line will be written.
  */
-void ap_proxy_send_headers(request_rec *r, const char *respline, table *t)
+void ap_proxy_write_headers(cache_req *c, const char *respline, table *t)
 {
-    int i;
-    BUFF *fp = r->connection->client;
-    table_entry *elts = (table_entry *) ap_table_elts(t)->elts;
+    /* write status line */
+    if (respline && c->fp != NULL &&
+        ap_bvputs(c->fp, respline, 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);
+        return;
+    }
 
-    ap_bvputs(fp, respline, CRLF, NULL);
+    /* write response headers to the cache file */
+    ap_table_do(ap_proxy_send_hdr_line, c, t, 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);
-	    ap_table_addn(r->headers_out, elts[i].key, elts[i].val);
-	}
+    /* write terminating CRLF */
+    if (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);
+        c = ap_proxy_cache_error(c);
     }
-
-    ap_bputs(CRLF, fp);
 }
 
 
@@ -654,12 +671,14 @@
  * The return returns 1 if the token val is found in the list, or 0
  * otherwise.
  */
-int ap_proxy_liststr(const char *list, const char *val)
+int ap_proxy_liststr(const char *list, const char *key, char **val)
 {
     int len, i;
     const char *p;
+    char valbuf[HUGE_STRING_LEN];
+    valbuf[sizeof(valbuf)-1] = 0; /* safety terminating zero */
 
-    len = strlen(val);
+    len = strlen(key);
 
     while (list != NULL) {
 	p = strchr(list, ',');
@@ -674,8 +693,22 @@
 
 	while (i > 0 && ap_isspace(list[i - 1]))
 	    i--;
-	if (i == len && strncasecmp(list, val, len) == 0)
+        if (i == len && strncasecmp(list, key, len) == 0) {
+            if (val) {
+                p = strchr(list, ',');
+                while (ap_isspace(*list)) {
+                    list++;
+                }
+                if ('=' == list[0])
+                    list++;
+                while (ap_isspace(*list)) {
+                    list++;
+                }
+                strncpy(valbuf, list, MIN(p-list, sizeof(valbuf)-1));
+                *val = valbuf;
+            }
 	    return 1;
+        }
 	list = p;
     }
     return 0;
@@ -785,14 +818,14 @@
 #endif /* CASE_BLIND_FILESYSTEM */
 
 /*
- * Converts 8 hex digits to a time integer
+ * Converts 16 hex digits to a time integer
  */
 int ap_proxy_hex2sec(const char *x)
 {
     int i, ch;
     unsigned int j;
 
-    for (i = 0, j = 0; i < 8; i++) {
+    for (i = 0, j = 0; i < 16; i++) {
 	ch = x[i];
 	j <<= 4;
 	if (ap_isdigit(ch))
@@ -802,21 +835,27 @@
 	else
 	    j |= ch - ('a' - 10);
     }
-    if (j == 0xffffffff)
-	return -1;		/* so that it works with 8-byte ints */
-    else
+/* no longer necessary, as the source hex is 8-byte int */
+/*    if (j == 0xffffffff)*/
+/*      return -1;*/            /* so that it works with 8-byte ints */
+/*    else */
 	return j;
 }
 
 /*
- * Converts a time integer to 8 hex digits
+ * Converts a time integer to 16 hex digits
  */
 void ap_proxy_sec2hex(int t, char *y)
 {
     int i, ch;
     unsigned int j = t;
 
-    for (i = 7; i >= 0; i--) {
+    if (-1 == t) {
+        strcpy(y, "FFFFFFFFFFFFFFFF");
+        return;
+    }
+
+    for (i = 15; i >= 0; i--) {
 	ch = j & 0xF;
 	j >>= 4;
 	if (ch >= 10)
@@ -824,7 +863,7 @@
 	else
 	    y[i] = ch + '0';
     }
-    y[8] = '\0';
+    y[16] = '\0';
 }
 
 
@@ -835,7 +874,12 @@
 	    ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
 	    c->fp = NULL;
 	}
-	if (c->tempfile) unlink(c->tempfile);
+        if (c->origfp != NULL) {
+            ap_pclosef(c->req->pool, ap_bfileno(c->origfp, B_WR));
+            c->origfp = NULL;
+        }
+        if (c->tempfile)
+            unlink(c->tempfile);
     }
     return NULL;
 }
@@ -1260,22 +1304,22 @@
     return i;
 }
 
-/* This function is called by ap_table_do() for all header lines */
-/* (from proxy_http.c and proxy_ftp.c) */
-/* It is passed a table_do_args struct pointer and a MIME field and value pair */
+/* This function is called by ap_table_do() for all header lines
+ * (from proxy_http.c and proxy_ftp.c)
+ * It is passed a cache_req struct pointer and a MIME field and value pair
+ */
 int ap_proxy_send_hdr_line(void *p, const char *key, const char *value)
 {
-    struct tbl_do_args *parm = (struct tbl_do_args *)p;
+    cache_req *c = (cache_req *)p;
 
     if (key == NULL || value == NULL || value[0] == '\0')
 	return 1;
-    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,
-		    "proxy: error writing header to %s", parm->cache->tempfile);
-	    parm->cache = ap_proxy_cache_error(parm->cache);
+    if (c->fp != NULL &&
+        ap_bvputs(c->fp, key, ": ", value, CRLF, NULL) == -1) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+                    "proxy: error writing header to %s", c->tempfile);
+            c = ap_proxy_cache_error(c);
+            return 0; /* no need to continue, it failed already */
     }
     return 1; /* tell ap_table_do() to continue calling us for more headers */
 }
@@ -1287,6 +1331,93 @@
     if (cache != NULL && cache->fp != NULL)
 	ap_bputs(data, cache->fp);