I have attached a version of the http/1.1 proxy patch that Chuck Murcko had worked on that will work with mod_ssl.  It turns out that when the configure script for mod_ssl runs, it adds EAPI support inside some of the files that the proxy patch touches and thus the patch blows up.  I have created a new patch that should be installed AFTER the configure script for mod_ssl runs.
 
This has been tested with:
    - apache 1.3.19
    - mod_ssl 2.8.2.1.3.19
    - proxy_http1.1.patch for apache 1.3.19
 
I hope this patch helps somebody out there.  Thanks to all who have worked on mod_proxy and the patches.  It is an awesome module!
 
 
-Luis
 
 
--- src/main/alloc.c    Wed May  9 10:00:03 2001
+++ src/main/alloc.c    Wed May  9 10:05:05 2001
@@ -1692,6 +1692,23 @@
     return res;
 }
 
+/* overlay one table on another - keys in base will be replaced by keys in 
overlay */
+API_EXPORT(int) ap_replace_tables(table *base, table *overlay)
+{
+    table_entry *elts = (table_entry *) overlay->a.elts;
+    int i, q = 0;
+    const char *val;
+
+    for (i = 0; i < overlay->a.nelts; ++i) {
+       val = ap_table_get(base, elts[i].key);
+       if (!val || strcmp(val, elts[i].val))
+           q = 1;
+       ap_table_set(base, elts[i].key, elts[i].val);
+    }
+
+    return q;
+}
+
 /* And now for something completely abstract ...
 
  * For each key value given as a vararg:
--- src/main/http_protocol.c    Wed May  9 10:00:03 2001
+++ src/main/http_protocol.c    Wed May  9 10:08:48 2001
@@ -1476,12 +1476,10 @@
     if (!r->status_line)
         r->status_line = status_lines[ap_index_of_response(r->status)];
 
-    /* mod_proxy is only HTTP/1.0, so avoid sending HTTP/1.1 error response;
-     * kluge around broken browsers when indicated by force-response-1.0
+       /* kluge around broken browsers when indicated by force-response-1.0
      */
-    if (r->proxyreq != NOT_PROXY
-        || (r->proto_num == HTTP_VERSION(1,0)
-            && ap_table_get(r->subprocess_env, "force-response-1.0"))) {
+    if (r->proto_num == HTTP_VERSION(1,0)
+       && ap_table_get(r->subprocess_env, "force-response-1.0")) {
 
         protocol = "HTTP/1.0";
         r->connection->keepalive = -1;
--- src/modules/proxy/mod_proxy.c       Wed May  9 10:00:04 2001
+++ src/modules/proxy/mod_proxy.c       Wed May  9 10:11:01 2001
@@ -370,6 +370,7 @@
     if (p == NULL)
        return HTTP_BAD_REQUEST;
 
+    /* Try serve the request from the cache. If we suceed, we leave. */
     rc = ap_proxy_cache_check(r, url, &conf->cache, &cr);
     if (rc != DECLINED)
        return rc;
--- src/modules/proxy/mod_proxy.h       Mon Jan 15 12:05:28 2001
+++ src/modules/proxy/mod_proxy.h       Thu Mar  8 01:48:17 2001
@@ -184,6 +184,13 @@
 #define DEFAULT_CACHE_COMPLETION (0.9)
 #define DEFAULT_CACHE_GCINTERVAL SEC_ONE_HR
 
+#ifndef MAX
+#define MAX(a,b)                ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b)                ((a) < (b) ? (a) : (b))
+#endif
+
 /* static information about the local cache */
 struct cache_conf {
     const char *root;          /* the location of the cache directory */
@@ -239,29 +246,33 @@
     char *url;                 /* the URL requested */
     char *filename;            /* name of the cache file, or NULL if no cache 
*/
     char *tempfile;            /* name of the temporary file, of NULL if not 
caching */
-    time_t ims;                        /* if-modified-since date of request; 
-1 if no header */
+    time_t ims;                        /* if-Modified-Since date of request; 
-1 if no header */
+    time_t ius;                        /* if-Unmodified-Since date of request; 
-1 if no header */
+    const char *im;            /* if-Match etag of request; NULL if no header 
*/
+    const char *inm;           /* if-None-Match etag of request; NULL if no 
header */
     BUFF *fp;                  /* the cache file descriptor if the file is 
cached
                                   and may be returned, or NULL if the file is
                                   not cached (or must be reloaded) */
+    BUFF *origfp;              /* the old cache file descriptor if the file has
+                                  been revalidated and is being rewritten to
+                                  disk */
     time_t expire;             /* calculated expire date of cached entity */
     time_t lmod;               /* last-modified date of cached entity */
     time_t date;               /* the date the cached file was last touched */
+    time_t req_time;           /* the time the request started */
+    time_t resp_time;          /* the time the response was received */
     int version;               /* update count of the file */
     off_t len;                 /* content length */
     char *protocol;            /* Protocol, and major/minor number, e.g. 
HTTP/1.1 */
     int status;                        /* the status of the cached file */
     unsigned int written;      /* total *content* bytes written to cache */
     float cache_completion;    /* specific to this request */
-    char *resp_line;           /* the whole status like (protocol, code + 
message) */
-    table *hdrs;               /* the HTTP headers of the file */
+    char *resp_line;           /* the whole status line (protocol, code + 
message) */
+    table *req_hdrs;           /* the original request headers when it was 
made */
+    table *hdrs;               /* the original HTTP response headers of the 
file */
+    char *xcache;              /* the X-Cache header value to be sent to 
client */
 } cache_req;
 
-/* Additional information passed to the function called by ap_table_do() */
-struct tbl_do_args {
-    request_rec *req;
-    cache_req *cache;
-};
-
 struct per_thread_data {
     struct hostent hpbuf;
     u_long ipaddr;
@@ -305,9 +316,9 @@
                         char **passwordp, char **hostp, int *port);
 const char *ap_proxy_date_canon(pool *p, const char *x);
 table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f);
-long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c);
-void ap_proxy_send_headers(request_rec *r, const char *respline, table *hdrs);
-int ap_proxy_liststr(const char *list, const char *val);
+long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, 
int nowrite);
+void ap_proxy_write_headers(cache_req *c, const char *respline, table *t);
+int ap_proxy_liststr(const char *list, const char *key, char **val);
 void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength);
 int ap_proxy_hex2sec(const char *x);
 void ap_proxy_sec2hex(int t, char *y);
@@ -323,5 +334,9 @@
 /* This function is called by ap_table_do() for all header lines */
 int ap_proxy_send_hdr_line(void *p, const char *key, const char *value);
 unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache);
+time_t ap_proxy_current_age(cache_req *c, const time_t age_value);
+BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename);
+BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename);
+void ap_proxy_clear_connection(pool *p, table *headers);
 
 #endif /*MOD_PROXY_H*/
--- src/modules/proxy/proxy_cache.c     Mon Jan 15 12:05:29 2001
+++ src/modules/proxy/proxy_cache.c     Thu Mar  8 01:48:17 2001
@@ -62,6 +62,7 @@
 #include "http_conf_globals.h"
 #include "http_log.h"
 #include "http_main.h"
+#include "http_core.h"
 #include "util_date.h"
 #ifdef WIN32
 #include <sys/utime.h>
@@ -413,7 +414,7 @@
 static int sub_garbage_coll(request_rec *r, array_header *files,
                          const char *cachebasedir, const char *cachesubdir)
 {
-    char line[27];
+    char line[17*(3)];
     char cachedir[HUGE_STRING_LEN];
     struct stat buf;
     int fd, i;
@@ -567,7 +568,7 @@
         }
 #endif
  
-       i = read(fd, line, 26);
+       i = read(fd, line, 17*(3)-1);
        close(fd);
        if (i == -1) {
            ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
@@ -575,8 +576,8 @@
            continue;
        }
        line[i] = '\0';
-       garbage_expire = ap_proxy_hex2sec(line + 18);
-       if (!ap_checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") ||
+       garbage_expire = ap_proxy_hex2sec(line + 17*(2));
+       if (!ap_checkmask(line, "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& 
&&&&&&&&&&&&&&&&") ||
            garbage_expire == BAD_DATE) {
            /* bad file */
            if (garbage_now != -1 && buf.st_atime > garbage_now + SEC_ONE_DAY &&
@@ -614,21 +615,45 @@
 
 }
 
+
 /*
- * read a cache file;
+ * Read a cache file;
  * returns 1 on success,
  *         0 on failure (bad file or wrong URL)
  *        -1 on UNIX error
+ *
+ * We read the cache hex header, then the message response line and
+ * response headers, and finally we return with the filepointer
+ * pointing at the start of the message body itself, ready to be
+ * shipped to the client later on, if appropriate.
  */
 static int rdcache(request_rec *r, BUFF *cachefp, cache_req *c)
 {
-    char urlbuff[1034], *strp;
+    char urlbuff[HUGE_STRING_LEN], *strp;
     int len;
-/* read the data from the cache file */
-/* format
- * date SP lastmod SP expire SP count SP content-length CRLF
- * dates are stored as hex seconds since 1970
- */
+
+    /* read the data from the cache file */
+
+    /* Format:
+     *
+     * The cache needs to keep track of the following information:
+     * - Date, LastMod, Version, ReqTime, RespTime, ContentLength
+     * - The original request headers (for Vary)
+     * - The original response headers (for returning with a cached response)
+     * - The body of the message
+     *
+     * date SP lastmod SP expire SP count SP request-time SP response-time SP 
content-lengthCRLF
+     * (dates are stored as hex seconds since 1970)
+     * Original URLCRLF
+     * Original Request Headers
+     * CRLF
+     * Original Response Headers
+     * CRLF
+     * Body
+     * 
+     */
+
+    /* retrieve cachefile information values */
     len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
     if (len == -1)
        return -1;
@@ -637,16 +662,18 @@
     urlbuff[len - 1] = '\0';
 
     if (!ap_checkmask(urlbuff,
-                  "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&"))
+                  "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& 
&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&"))
        return 0;
 
-    c->date = ap_proxy_hex2sec(urlbuff);
-    c->lmod = ap_proxy_hex2sec(urlbuff + 9);
-    c->expire = ap_proxy_hex2sec(urlbuff + 18);
-    c->version = ap_proxy_hex2sec(urlbuff + 27);
-    c->len = ap_proxy_hex2sec(urlbuff + 36);
+    c->date = ap_proxy_hex2sec(urlbuff + 17*(0));
+    c->lmod = ap_proxy_hex2sec(urlbuff + 17*(1));
+    c->expire = ap_proxy_hex2sec(urlbuff + 17*(2));
+    c->version = ap_proxy_hex2sec(urlbuff + 17*(3));
+    c->req_time = ap_proxy_hex2sec(urlbuff + 17*(4));
+    c->resp_time = ap_proxy_hex2sec(urlbuff + 17*(5));
+    c->len = ap_proxy_hex2sec(urlbuff + 17*(6));
 
-/* check that we have the same URL */
+    /* check that we have the same URL */
     len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
     if (len == -1)
        return -1;
@@ -657,7 +684,12 @@
     if (strcmp(urlbuff + 7, c->url) != 0)
        return 0;
 
-/* What follows is the message */
+    /* then the original request headers */
+    c->req_hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
+    if (c->req_hdrs == NULL)
+       return -1;
+
+    /* then the original response headers */
     len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
     if (len == -1)
        return -1;
@@ -674,15 +706,201 @@
     c->hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
     if (c->hdrs == NULL)
        return -1;
-    if (c->len != -1) {                /* add a content-length header */
+    if (c->len != -1)    /* add a content-length header */
        if (ap_table_get(c->hdrs, "Content-Length") == NULL) {
            ap_table_set(c->hdrs, "Content-Length",
                         ap_psprintf(r->pool, "%lu", (unsigned long)c->len));
        }
-    }
+
+    
     return 1;
 }
 
+/*
+ * Call this to check the possible conditional status of
+ * the client request, and return the response from the cache
+ *
+ * Conditionals include If-Modified-Since, If-Match, If-Unmodified-Since
+ * and If-None-Match.
+ *
+ * We don't yet understand If-Range, but we will...
+ */
+int ap_proxy_cache_conditional(request_rec *r, cache_req *c, BUFF *cachefp)
+{
+    const char *etag, *wetag;
+
+    /* get etag */
+    if ((etag = ap_table_get(c->hdrs, "Etag"))) {
+       wetag = ap_pstrcat(r->pool, "W/", etag, NULL);
+    }
+
+    /* check for If-Match, If-Unmodified-Since */
+    while (1) {
+
+       /* check If-Match and If-Unmodified-Since exist
+        *
+        * If neither of these exist, the request is not conditional, and
+        * we serve it normally
+        */
+       if (!c->im && BAD_DATE == c->ius) {
+           break;
+       }
+
+       /* check If-Match
+        *
+        * we check if the Etag on the cached file is in the list of Etags
+        * in the If-Match field. The comparison must be a strong comparison,
+        * so the Etag cannot be marked as weak. If the comparision fails
+        * we return 412 Precondition Failed.
+        *
+        * if If-Match is specified AND
+        * If-Match is not a "*" AND
+        * Etag is missing or weak or not in the list THEN
+        * return 412 Precondition Failed
+        */
+
+       if (c->im) {
+           if (strcmp(c->im, "*") &&
+           (!etag || (strlen(etag) > 1 && 'W' == etag[0] && '/' == etag[1]) || 
!ap_proxy_liststr(c->im, etag, NULL))) {
+               Explain0("If-Match specified, and it didn't - return 412");
+           }
+           else {
+               Explain0("If-Match specified, and it matched");
+               break;
+           }
+       }
+
+       /* check If-Unmodified-Since
+        *
+        * if If-Unmodified-Since is specified AND
+        * Last-Modified is specified somewhere AND
+        * If-Unmodified-Since is in the past compared to Last-Modified THEN
+        * return 412 Precondition Failed
+        */
+       if (BAD_DATE != c->ius && BAD_DATE != c->lmod) {
+           if (c->ius < c->lmod) {
+               Explain0("If-Unmodified-Since specified, but it wasn't - return 
412");
+           }
+           else {
+               Explain0("If-Unmodified-Since specified, and it was 
unmodified");
+               break;
+           }
+       }
+
+       /* if cache file is being updated */
+       if (c->origfp) {
+            ap_proxy_write_headers(c, c->resp_line, c->hdrs);
+           ap_proxy_send_fb(c->origfp, r, c, c->len, 1);
+           ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR));
+           ap_proxy_cache_tidy(c);
+       }
+       else
+           ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
+
+       Explain0("Use your cached copy, conditional precondition failed.");
+       return HTTP_PRECONDITION_FAILED;
+    }
+
+
+    /* check for If-None-Match, If-Modified-Since */
+    while (1) {
+
+       /* check for existance of If-None-Match and If-Modified-Since
+        *
+        * if neither of these headers have been set, then the request
+        * is not conditional, and we just send the cached response and
+        * be done with it.
+        */
+       if (!c->inm && BAD_DATE == c->ims) {
+           break;
+       }
+
+       /* check If-None-Match
+        *
+        * we check if the Etag on the cached file is in the list of Etags
+        * in the If-None-Match field. The comparison must be a strong 
comparison,
+        * so the Etag cannot be marked as weak. If the comparision fails
+        * we return 412 Precondition Failed.
+        *
+        * if If-None-Match is specified:
+        * if If-None-Match is a "*" THEN 304
+        * else if Etag is specified AND we get a match THEN 304
+        * else if Weak Etag is specified AND we get a match THEN 304
+        * else sent the original object
+        */
+       if (c->inm) {
+           if (!strcmp(c->inm, "*")) {
+               Explain0("If-None-Match: * specified, return 304");
+           }
+           else if (etag && ap_proxy_liststr(c->inm, etag, NULL)) {
+               Explain0("If-None-Match: specified and we got a strong match - 
return 304");
+           }
+           else if (wetag && ap_proxy_liststr(c->inm, wetag, NULL)) {
+               Explain0("If-None-Match specified, and we got a weak match - 
return 304");
+           }
+           else
+               break;
+       }
+
+       /* check If-Modified-Since
+        *
+        * if If-Modified-Since is specified AND
+        * Last-Modified is specified somewhere:
+        * if last modification date is earlier than If-Modified-Since THEN 304
+        * else send the original object
+        */
+       if (BAD_DATE != c->ims && BAD_DATE != c->lmod) {
+           if (c->ims >= c->lmod) {
+               Explain0("If-Modified-Since specified and not modified, try 
return 304");
+           }
+           else
+               break;
+       }
+
+
+       /* are we updating the cache file? */
+       if (c->origfp) {
+            ap_proxy_write_headers(c, c->resp_line, c->hdrs);
+           ap_proxy_send_fb(c->origfp, r, c, c->len, 1);
+           ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR));
+           ap_proxy_cache_tidy(c);
+       }
+       else
+           ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
+
+       Explain0("Use local copy, cached file hasn't changed");
+       return HTTP_NOT_MODIFIED;
+    }
+
+
+    /* No conditional - just send it cousin! */
+    Explain0("Local copy modified, send it");
+    r->status_line = strchr(c->resp_line, ' ') + 1;
+    r->status = c->status;
+
+    /* Prepare and send headers to client */
+    ap_overlap_tables(r->headers_out, c->hdrs, AP_OVERLAP_TABLES_SET);
+    ap_table_setn(r->headers_out, "X-Cache", c->xcache);
+    r->content_type = ap_table_get(r->headers_out, "Content-Type");
+    ap_send_http_header(r);
+
+    /* are we rewriting the cache file? */
+    if (c->origfp) {
+        ap_proxy_write_headers(c, c->resp_line, c->hdrs);
+       ap_proxy_send_fb(c->origfp, r, c, c->len, r->header_only);
+       ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR));
+       ap_proxy_cache_tidy(c);
+       return OK;
+    }
+
+    /* no, we not */
+    if (!r->header_only)
+       ap_proxy_send_fb(cachefp, r, NULL, c->len, 0);
+
+    ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
+    return OK;
+}
+
 
 /*
  * Call this to test for a resource in the cache
@@ -694,73 +912,121 @@
  *      if cached file is not expired then
  *         if last modified after if-modified-since then send body
  *         else send 304 Not modified
- *      else
+ *      else if cached file is expired then
  *         if last modified after if-modified-since then add
  *            last modified date to request
  */
 int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
                      cache_req **cr)
 {
-    char hashfile[66];
-    const char *imstr, *pragma, *auth;
+    const char *datestr, *pragma_req = NULL, *pragma_cresp = NULL, *cc_req = 
NULL, *cc_cresp = NULL, *vary = NULL;
     cache_req *c;
     time_t now;
     BUFF *cachefp;
-    int cfd, i;
-    const long int zero = 0L;
+    int i;
     void *sconf = r->server->module_config;
     proxy_server_conf *pconf =
     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+    const char *agestr = NULL;
+    char *val;
+    time_t age_c = 0;
+    time_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale, minfresh;
 
     c = ap_pcalloc(r->pool, sizeof(cache_req));
     *cr = c;
     c->req = r;
     c->url = ap_pstrdup(r->pool, url);
+    c->filename = NULL;
+    c->tempfile = NULL;
+    c->fp = NULL;
+    c->origfp = NULL;
+    c->version = 0;
+    c->len = -1;
+    c->req_hdrs = NULL;   
+    c->hdrs = NULL;
+    c->xcache = NULL;
 
-/* get the If-Modified-Since date of the request */
+    /* get the If-Modified-Since date of the request, if it exists */
     c->ims = BAD_DATE;
-    imstr = ap_table_get(r->headers_in, "If-Modified-Since");
-    if (imstr != NULL) {
-/* this may modify the value in the original table */
-       imstr = ap_proxy_date_canon(r->pool, imstr);
-       c->ims = ap_parseHTTPdate(imstr);
-       if (c->ims == BAD_DATE) /* bad or out of range date; remove it */
-           ap_table_unset(r->headers_in, "If-Modified-Since");
-    }
+    datestr = ap_table_get(r->headers_in, "If-Modified-Since");
+    if (datestr != NULL) {
+        /* this may modify the value in the original table */
+      datestr = ap_proxy_date_canon(r->pool, datestr);
+      c->ims = ap_parseHTTPdate(datestr);
+      if (c->ims == BAD_DATE)  /* bad or out of range date; remove it */
+         ap_table_unset(r->headers_in, "If-Modified-Since");
+    }
+
+/* get the If-Unmodified-Since date of the request, if it exists */
+    c->ius = BAD_DATE;
+    datestr = ap_table_get(r->headers_in, "If-Unmodified-Since");
+    if (datestr != NULL) {
+        /* this may modify the value in the original table */
+      datestr = ap_proxy_date_canon(r->pool, datestr); 
+      c->ius = ap_parseHTTPdate(datestr);
+      if (c->ius == BAD_DATE) /* bad or out of range date; remove it */
+          ap_table_unset(r->headers_in, "If-Unmodified-Since");
+    }
+     
+/* get the If-Match of the request, if it exists */
+    c->im = ap_table_get(r->headers_in, "If-Match");
+     
+/* get the If-None-Match of the request, if it exists */
+    c->inm = ap_table_get(r->headers_in, "If-None-Match");
 
 /* find the filename for this cache entry */
-    ap_proxy_hash(url, hashfile, pconf->cache.dirlevels, 
pconf->cache.dirlength);
-    if (conf->root != NULL)
-       c->filename = ap_pstrcat(r->pool, conf->root, "/", hashfile, NULL);
-    else
-       c->filename = NULL;
+    if (conf->root != NULL) {
+       char hashfile[66];
+       ap_proxy_hash(url, hashfile, pconf->cache.dirlevels, 
pconf->cache.dirlength);
+      c->filename = ap_pstrcat(r->pool, conf->root, "/", hashfile, NULL);
+    }
+    else {
+      c->filename = NULL;
+      c->fp = NULL;
+      Explain0("No CacheRoot, so no caching. Declining.");
+      return DECLINED;
+    }
+
+/* find certain cache controlling headers */
+    pragma_req = ap_table_get(r->headers_in, "Pragma");
+    cc_req = ap_table_get(r->headers_in, "Cache-Control");
+
+/* first things first - does the request allow us to return
+ * cached information at all? If not, just decline the request.
+ *
+ * Note that there is a big difference between not being allowed
+ * to cache a request (no-store) and not being allowed to return
+ * a cached request without revalidation (max-age=0).
+ *
+ * Caching is forbidden under the following circumstances:
+ *
+ * - RFC2616 14.9.2 Cache-Control: no-store
+ * we are not supposed to store this request at all. Behave as a tunnel.
+ *
+ */
+    if (ap_proxy_liststr(cc_req, "no-store", NULL)) {
+
+/* delete the previously cached file */
+      if (c->filename)
+          unlink(c->filename);
+      c->fp = NULL;
+      c->filename = NULL;
+      Explain0("no-store forbids caching. Declining.");
+      return DECLINED;
+    }
 
+/* if the cache file exists, open it */
     cachefp = NULL;
+    Explain3("Request for %s, pragma_req=%s, ims=%ld", url,
+            pragma_req, c->ims);
 /* find out about whether the request can access the cache */
-    pragma = ap_table_get(r->headers_in, "Pragma");
-    auth = ap_table_get(r->headers_in, "Authorization");
-    Explain5("Request for %s, pragma=%s, auth=%s, ims=%ld, imstr=%s", url,
-            pragma, auth, (long)c->ims, imstr);
     if (c->filename != NULL && r->method_number == M_GET &&
-       strlen(url) < 1024 && !ap_proxy_liststr(pragma, "no-cache") &&
-       auth == NULL) {
-       Explain1("Check file %s", c->filename);
-       cfd = open(c->filename, O_RDWR | O_BINARY);
-       if (cfd != -1) {
-           ap_note_cleanups_for_fd(r->pool, cfd);
-           cachefp = ap_bcreate(r->pool, B_RD | B_WR);
-           ap_bpushfd(cachefp, cfd, cfd);
-       }
-       else if (errno != ENOENT)
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "proxy: error opening cache file %s",
-                        c->filename);
-#ifdef EXPLAIN
-       else
-           Explain1("File %s not found", c->filename);
-#endif
+        strlen(url) < 1024 ) {
+      cachefp = ap_proxy_open_cachefile(r, c->filename); 
     }
 
+
+    /* if a cache file exists, try reading body and headers from cache file */
     if (cachefp != NULL) {
        i = rdcache(r, cachefp, c);
        if (i == -1)
@@ -774,68 +1040,234 @@
            ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
            cachefp = NULL;
        }
+       if (c->hdrs) {
+           cc_cresp = ap_table_get(c->hdrs, "Cache-Control");
+           pragma_cresp = ap_table_get(c->hdrs, "Pragma");
+           vary = ap_table_get(c->hdrs, "Vary");
+           if ((agestr = ap_table_get(c->hdrs, "Age"))) {
+               age_c = atoi(agestr);
+           }
+       }
     }
+
+    /* if a cache file does not exist, create empty header array */
 /* fixed?  in this case, we want to get the headers from the remote server
    it will be handled later if we don't do this (I hope ;-)
+
     if (cachefp == NULL)
        c->hdrs = ap_make_table(r->pool, 20);
 */
     /* FIXME: Shouldn't we check the URL somewhere? */
+
+    /* Check Content-Negotiation - Vary
+     *
+     * At this point we need to make sure that the object we found in the cache
+     * is the same object that would be delivered to the client, when the
+     * effects of content negotiation are taken into effect.
+     *
+     * In plain english, we want to make sure that a language-negotiated
+     * document in one language is not given to a client asking for a
+     * language negotiated document in a different language by mistake.
+     *
+     * RFC2616 13.6 and 14.44 describe the Vary mechanism.
+     */
+    if (c->hdrs && c->req_hdrs) {
+       char *vary = ap_pstrdup(r->pool, ap_table_get(c->hdrs, "Vary"));
+
+       while (vary && *vary) {
+           char *name = vary;
+           const char *h1, *h2;
+
+           /* isolate header name */
+           while (*vary && !ap_isspace(*vary) && (*vary != ','))
+               ++vary;
+           while (*vary && (ap_isspace(*vary) || (*vary == ','))) {
+               *vary = '\0';
+               ++vary;
+           }
+
+           /* is this header in the request and the header in the cached
+            * request identical? If not, we give up and do a straight get */
+           h1 = ap_table_get(r->headers_in, name);
+           h2 = ap_table_get(c->req_hdrs, name);
+           if (h1 == h2) {
+               /* both headers NULL, so a match - do nothing */
+           }
+           else if (h1 && h2 && !strcmp(h1, h2)) {
+               /* both headers exist and are equal - do nothing */
+           }
+           else {
+
+               /* headers do not match, so Vary failed */
+               c->fp = cachefp;
+               Explain0("Vary header mismatch - object must be fetched from 
scratch. Declining.");
+               return DECLINED;
+           }
+       }
+    }
+
+
+    /* We now want to check if our cached data is still fresh. This depends
+     * on a few things, in this order:
+     *
+     * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache
+     * no-cache in either the request or the cached response means that
+     * we must revalidate the request unconditionally, overriding any
+     * expiration mechanism. It's equivalent to max-age=0,must-revalidate.
+     *
+     * - RFC2616 14.32 Pragma: no-cache
+     * This is treated the same as Cache-Control: no-cache.
+     *
+     * - RFC2616 14.9.3 Cache-Control: max-stale, must-revalidate, 
proxy-revalidate
+     * if the max-stale request header exists, modify the stale calculations
+     * below so that an object can be at most <max-stale> seconds stale before
+     * we request a revalidation, _UNLESS_ a must-revalidate or
+     * proxy-revalidate cached response header exists to stop us doing this.
+     *
+     * - RFC2616 14.9.3 Cache-Control: s-maxage
+     * the origin server specifies the maximum age an object can be before
+     * it is considered stale. This directive has the effect of proxy|must
+     * revalidate, which in turn means simple ignore any max-stale setting.
+     *
+     * - RFC2616 14.9.4 Cache-Control: max-age
+     * this header can appear in both requests and responses. If both are
+     * specified, the smaller of the two takes priority.
+     *
+     * - RFC2616 14.21 Expires:
+     * if this request header exists in the cached entity, and it's value is
+     * in the past, it has expired.
+     * 
+     */
+
+    /* calculate age of object */
+    age = ap_proxy_current_age(c, age_c);
+
+    /* extract s-maxage */
+    if (cc_cresp && ap_proxy_liststr(cc_cresp, "s-maxage", &val))
+       smaxage = atoi(val);
+    else
+       smaxage = -1;
+
+    /* extract max-age from request */
+    if (cc_cresp && ap_proxy_liststr(cc_req, "max-age", &val))
+       maxage_req =  atoi(val);
+    else
+       maxage_req = -1;
+
+    /* extract max-age from response */
+    if (cc_cresp && ap_proxy_liststr(cc_cresp, "max-age", &val))
+       maxage_cresp =  atoi(val);
+    else
+       maxage_cresp = -1;
+
+    /* if both maxage request and response, the smaller one takes priority */
+    if (-1 == maxage_req)
+       maxage = maxage_cresp;
+    else if (-1 == maxage_cresp)
+       maxage = maxage_req;
+    else
+       maxage = MIN(maxage_req, maxage_cresp);
+
+    /* extract max-stale */
+    if (cc_req && ap_proxy_liststr(cc_req, "max-stale", &val))
+       maxstale =  atoi(val);
+    else
+       maxstale = 0;
+
+    /* extract min-fresh */
+    if (cc_req && ap_proxy_liststr(cc_req, "min-fresh", &val))
+       minfresh =  atoi(val);
+    else
+       minfresh = 0;
+
+    /* override maxstale if must-revalidate or proxy-revalidate */
+    if (maxstale && ( (cc_cresp && ap_proxy_liststr(cc_cresp, 
"must-revalidate", NULL)) || (cc_cresp && ap_proxy_liststr(cc_cresp, 
"proxy-revalidate", NULL)) ))
+       maxstale = 0;
+
     now = time(NULL);
-/* Ok, have we got some un-expired data? */
-    if (cachefp != NULL && c->expire != BAD_DATE && now < c->expire) {
+    if (cachefp != NULL &&
+
+       /* handle no-cache */
+       !( (cc_req && ap_proxy_liststr(cc_req, "no-cache", NULL)) ||
+         (pragma_req && ap_proxy_liststr(pragma_req, "no-cache", NULL)) ||
+         (cc_cresp && ap_proxy_liststr(cc_cresp, "no-cache", NULL)) ||
+         (pragma_cresp && ap_proxy_liststr(pragma_cresp, "no-cache", NULL)) ) 
&&
+
+       /* handle expiration */
+       ( (-1 < smaxage && age < (smaxage - minfresh)) ||
+         (-1 < maxage && age < (maxage + maxstale - minfresh)) ||
+         (c->expire != BAD_DATE && age < (c->expire - c->date + maxstale - 
minfresh)) )
+
+       ) {
+
+       /* it's fresh darlings... */
+
        Explain0("Unexpired data available");
-/* check IMS */
-       if (c->lmod != BAD_DATE && c->ims != BAD_DATE && c->ims >= c->lmod) {
-/* has the cached file changed since this request? */
-           if (c->date == BAD_DATE || c->date > c->ims) {
-/* No, but these header values may have changed, so we send them with the
- * 304 HTTP_NOT_MODIFIED response
- */
-               const char *q;
 
-               if ((q = ap_table_get(c->hdrs, "Expires")) != NULL)
-                   ap_table_set(r->headers_out, "Expires", q);
-           }
-           ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
-           Explain0("Use local copy, cached file hasn't changed");
-           return HTTP_NOT_MODIFIED;
+       /* set age header on response */
+       ap_table_set(c->hdrs, "Age",
+                       ap_psprintf(r->pool, "%lu", (unsigned long)age));
+
+       /* add warning if maxstale overrode freshness calculation */
+       if (!( (-1 < smaxage && age < smaxage) ||
+            (-1 < maxage && age < maxage) ||
+            (c->expire != BAD_DATE && (c->expire - c->date) > age) )) {
+           ap_table_set(c->hdrs, "Warning", "110 Response is stale");
        }
 
-/* Ok, has been modified */
-       Explain0("Local copy modified, send it");
-       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(cachefp, r, NULL);
-       ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
-       return OK;
+       /* check conditionals (If-Modified-Since, etc) */
+       c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), 
NULL);
+       return ap_proxy_cache_conditional(r, c, cachefp);
+
+
     }
 
-/* if we already have data and a last-modified date, and it is not a head
- * request, then add an If-Modified-Since
- */
+    /* at this point we have determined our cached data needs revalidation
+     * but first - we check 1 thing:
+     *
+     * RFC2616 14.9.4 - if "only-if-cached" specified, send a
+     * 504 Gateway Timeout - we're not allowed to revalidate the object
+     */
+    if (ap_proxy_liststr(cc_req, "only-if-cached", NULL)) {
+       if (cachefp)
+           ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
+       return HTTP_GATEWAY_TIME_OUT;
+    }
 
-    if (cachefp != NULL && c->lmod != BAD_DATE && !r->header_only) {
-/*
- * use the later of the one from the request and the last-modified date
- * from the cache
- */
-       if (c->ims == BAD_DATE || c->ims < c->lmod) {
-           const char *q;
 
-           if ((q = ap_table_get(c->hdrs, "Last-Modified")) != NULL)
-               ap_table_set(r->headers_in, "If-Modified-Since",
-                         (char *) q);
+    /* If we already have cached data and a last-modified date, and it is
+     * not a head request, then add an If-Modified-Since.
+     *
+     * If we also have an Etag, then the object must have come from
+     * an HTTP/1.1 server. Add an If-None-Match as well.
+     *
+     * See RFC2616 13.3.4
+     */
+
+    if (cachefp != NULL && !r->header_only) {
+
+       const char *etag = ap_table_get(c->hdrs, "Etag");
+
+       /* If-Modified-Since */
+       if (c->lmod != BAD_DATE) {
+           /* use the later of the one from the request and the last-modified 
date
+            * from the cache */
+           if (c->ims == BAD_DATE || c->ims < c->lmod) {
+               const char *q;
+
+               if ((q = ap_table_get(c->hdrs, "Last-Modified")) != NULL)
+                   ap_table_set(r->headers_in, "If-Modified-Since", (char *) 
q);
+           }
        }
+
+       /* If-None-Match */
+       if (etag) {
+           ap_table_set(r->headers_in, "If-None-Match", etag);
+       }
+
     }
+
+
     c->fp = cachefp;
 
     Explain0("Local copy not present or expired. Declining.");
@@ -863,75 +1295,119 @@
 #endif 
     request_rec *r = c->req;
     char *p;
-    int i;
     const char *expire, *lmods, *dates, *clen;
     time_t expc, date, lmod, now;
-    char buff[46];
+    char buff[17*7+1];
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
-    const long int zero = 0L;
+    const char *cc_resp;
+    table *req_hdrs;
+
+    cc_resp = ap_table_get(resp_hdrs, "Cache-Control");
 
     c->tempfile = NULL;
 
-/* we've received the response */
-/* read expiry date; if a bad date, then leave it so the client can
- * read it
- */
+    /* we've received the response from the origin server */
+    
+    /* read expiry date; if a bad date, then leave it so the client can
+     * read it */
     expire = ap_table_get(resp_hdrs, "Expires");
     if (expire != NULL)
        expc = ap_parseHTTPdate(expire);
     else
        expc = BAD_DATE;
 
-/*
- * read the last-modified date; if the date is bad, then delete it
- */
+    /* read the last-modified date; if the date is bad, then delete it */
     lmods = ap_table_get(resp_hdrs, "Last-Modified");
     if (lmods != NULL) {
        lmod = ap_parseHTTPdate(lmods);
        if (lmod == BAD_DATE) {
-/* kill last modified date */
+           /* kill last modified date */
            lmods = NULL;
        }
     }
     else
        lmod = BAD_DATE;
 
-/*
- * what responses should we not cache?
- * Unknown status responses and those known to be uncacheable
- * 304 HTTP_NOT_MODIFIED response when we have no valid cache file, or
- * 200 HTTP_OK response from HTTP/1.0 and up without a Last-Modified header, or
- * HEAD requests, or
- * requests with an Authorization header, or
- * protocol requests nocache (e.g. ftp with user/password)
- */
-/* @@@ XXX FIXME: is the test "r->status != HTTP_MOVED_PERMANENTLY" correct?
- * or shouldn't it be "ap_is_HTTP_REDIRECT(r->status)" ? -MnKr */
-    if ((r->status != HTTP_OK && r->status != HTTP_MOVED_PERMANENTLY && 
r->status != HTTP_NOT_MODIFIED) ||
+
+    /*
+     * what responses should we not cache?
+     *
+     * At this point we decide based on the response headers whether it
+     * is appropriate _NOT_ to cache the data from the server. There are
+     * a whole lot of conditions that prevent us from caching this data.
+     * They are tested here one by one to be clear and unambiguous. */
+
+    /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
+     * We don't cache 206, because we don't (yet) cache partial responses.
+     * We include 304 Not Modified here too as this is the origin server
+     * telling us to serve the cached copy. */
+    if ((r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE && 
r->status != HTTP_MULTIPLE_CHOICES && r->status != HTTP_MOVED_PERMANENTLY && 
r->status != HTTP_NOT_MODIFIED) ||
+
+    /* if a broken Expires header is present, don't cache it */
        (expire != NULL && expc == BAD_DATE) ||
+
+    /* if the server said 304 Not Modified but we have no cache file - pass
+     * this untouched to the user agent, it's not for us. */
        (r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) ||
+
+    /* 200 OK response from HTTP/1.0 and up without a Last-Modified header */
        (r->status == HTTP_OK && lmods == NULL && is_HTTP1) ||
+
+    /* HEAD requests */
        r->header_only ||
-       ap_table_get(r->headers_in, "Authorization") != NULL ||
+
+    /* RFC2616 14.9.2 Cache-Control: no-store response indicating do not
+     * cache, or stop now if you are trying to cache it */
+        ap_proxy_liststr(cc_resp, "no-store", NULL) ||
+
+    /* RFC2616 14.9.1 Cache-Control: private
+     * this object is marked for this user's eyes only. Behave as a tunnel. */
+        ap_proxy_liststr(cc_resp, "private", NULL) ||
+
+    /* RFC2616 14.8 Authorisation:
+     * if authorisation is included in the request, we don't cache, but we
+     * can cache if the following exceptions are true:
+     * 1) If Cache-Control: s-maxage is included
+     * 2) If Cache-Control: must-revalidate is included
+     * 3) If Cache-Control: public is included
+     */
+       (ap_table_get(r->headers_in, "Authorization") != NULL
+
+       && !(ap_proxy_liststr(cc_resp, "s-maxage", NULL) || 
ap_proxy_liststr(cc_resp, "must-revalidate", NULL) || ap_proxy_liststr(cc_resp, 
"public", NULL))
+       ) ||
+
+    /* or we've been asked not to cache it above */
        nocache) {
+
        Explain1("Response is not cacheable, unlinking %s", c->filename);
-/* close the file */
+
+       /* close the file */
        if (c->fp != NULL) {
            ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
            c->fp = NULL;
        }
-/* delete the previously cached file */
+
+       /* delete the previously cached file */
         if (c->filename)
             unlink(c->filename);
        return DECLINED;        /* send data to client but not cache */
     }
 
-/* otherwise, we are going to cache the response */
-/*
- * Read the date. Generate one if one is not supplied
- */
+
+    /* 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 */
     dates = ap_table_get(resp_hdrs, "Date");
     if (dates != NULL)
        date = ap_parseHTTPdate(dates);
@@ -950,6 +1426,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,120 +1477,157 @@
     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';
 
-/* 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);
+/* 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 response from server 304 not modified */
+      if (r->status == HTTP_NOT_MODIFIED) {
+
+/* 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;
-       }
-    }
-/* new or modified file */
+        }
+        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.
+ */
+      }
+
+/* 
+ * 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)
-#define TMPFILESTR     "/tmpXXXXXX"
-    if (conf->cache.root == NULL)
-       return DECLINED;
-    c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) + 
sizeof(TMPFILESTR));
-    strcpy(c->tempfile, conf->cache.root);
-    strcat(c->tempfile, TMPFILESTR);
+    while (1) {
+/* create temporary filename */
+#ifndef TPF
+#define TMPFILESTR    "/tmpXXXXXX"
+      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);
+      p = mktemp(c->tempfile);
 #else
-    if (conf->cache.root == NULL)
-    return DECLINED;
-    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);
+      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);
+
+/* create the new file */
+      c->fp = ap_proxy_create_cachefile(r, c->tempfile);
+      if (NULL == c->fp) {
+          c = ap_proxy_cache_error(c);
+          break;
+      }
+
+/* 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);
+          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;
+    }
 
-    Explain1("Create temporary file %s", c->tempfile);
+/* 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);
 
-    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;
-    }
-    ap_note_cleanups_for_fd(r->pool, i);
-    c->fp = ap_bcreate(r->pool, B_WR);
-    ap_bpushfd(c->fp, -1, i);
-
-    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;
     }
     return DECLINED;
 }
@@ -1121,7 +1637,7 @@
     server_rec *s;
     long int bc;
 
-    if (c == NULL || c->fp == NULL)
+    if (!c || !c->fp)
        return;
 
     s = c->req->server;
@@ -1148,17 +1664,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);
     }
--- src/modules/proxy/proxy_ftp.c       Fri Feb  9 07:40:27 2001
+++ src/modules/proxy/proxy_ftp.c       Thu Mar  8 01:48:17 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 =
@@ -533,7 +531,7 @@
        password = "apache_proxy@";
     }
 
-/* check if ProxyBlock directive on this host */
+    /* check if ProxyBlock directive on this host */
     destaddr.s_addr = ap_inet_addr(host);
     for (i = 0; i < conf->noproxies->nelts; i++) {
         if (destaddr.s_addr == npent[i].addr.s_addr ||
@@ -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? */
@@ -620,7 +621,7 @@
     ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
 #endif /*CHARSET_EBCDIC*/
 
-/* possible results: */
+    /* possible results: */
     /* 120 Service ready in nnn minutes. */
     /* 220 Service ready for new user. */
     /* 421 Service not available, closing control connection. */
@@ -660,8 +661,8 @@
     ap_bflush(f);                      /* capture any errors */
     Explain1("FTP: USER %s", user);
 
-/* possible results; 230, 331, 332, 421, 500, 501, 530 */
-/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
+    /* possible results; 230, 331, 332, 421, 500, 501, 530 */
+    /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
     /* 230 User logged in, proceed. */
     /* 331 User name okay, need password. */
     /* 332 Need account for login. */
@@ -693,7 +694,7 @@
        ap_bvputs(f, "PASS ", password, CRLF, NULL);
        ap_bflush(f);
        Explain1("FTP: PASS %s", password);
-/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
+    /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
     /* 230 User logged in, proceed. */
     /* 332 Need account for login. */
     /* 421 Service not available, closing control connection. */
@@ -1205,40 +1206,30 @@
        ap_bpushfd(data, dsock, dsock);
     }
 
+    /* send response */
+    /* 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);
+
+    /* 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);
+
     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);
-    }
-
-/* 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);
-    }
 
-    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);
 
--- src/modules/proxy/proxy_http.c      Wed May  9 10:00:04 2001
+++ src/modules/proxy/proxy_http.c      Wed May  9 10:42:45 2001
@@ -76,19 +76,19 @@
     const char *err;
     int port;
 
-/* do syntatic check.
- * We break the URL into host, port, path, search
- */
+       /* do syntatic check.
+        * We break the URL into host, port, path, search
+        */
     port = def_port;
     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
     if (err)
        return HTTP_BAD_REQUEST;
 
-/* now parse path/search args, according to rfc1738 */
-/* N.B. if this isn't a true proxy request, then the URL _path_
- * has already been decoded.  True proxy requests have r->uri
- * == r->unparsed_uri, and no others have that property.
- */
+       /* now parse path/search args, according to rfc1738 */
+       /* N.B. if this isn't a true proxy request, then the URL _path_
+        * has already been decoded.  True proxy requests have r->uri
+        * == r->unparsed_uri, and no others have that property.
+        */
     if (r->uri == r->unparsed_uri) {
        search = strchr(url, '?');
        if (search != NULL)
@@ -97,7 +97,7 @@
     else
        search = r->args;
 
-/* process path */
+       /* process path */
     path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path,
                             r->proxyreq);
     if (path == NULL)
@@ -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;
 #ifdef EAPI
     char *peer;
 #endif
@@ -201,10 +177,12 @@
     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;
 
-/* We break the URL into host, port, path-search */
+       /* We break the URL into host, port, path-search */
 
     urlptr = strstr(url, "://");
     if (urlptr == NULL)
@@ -239,7 +217,7 @@
        }
     }
 
-/* check if ProxyBlock directive on this host */
+       /* check if ProxyBlock directive on this host */
     destaddr.s_addr = ap_inet_addr(desthost);
     for (i = 0; i < conf->noproxies->nelts; i++) {
         if (destaddr.s_addr == npent[i].addr.s_addr ||
@@ -268,6 +246,9 @@
 #endif
     }
 
+    /* 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,
@@ -317,8 +298,20 @@
                                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);
 
@@ -352,6 +345,7 @@
         }
     }
 #else /* EAPI */
+    /* 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
@@ -360,7 +354,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);
@@ -370,7 +364,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),
@@ -384,24 +378,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);
@@ -409,6 +425,8 @@
     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);
@@ -427,15 +445,19 @@
                             "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.1 - is this correct? */
            major = 1;
-           minor = 0;
+           minor = 1;
        }
 
-/* If not an HTTP/1 message or if the status line was > 8192 bytes */
+               /* If not an HTTP/1 message or if the status line was > 8192 
bytes */
        if (buffer[5] != '1' || buffer[len - 1] != '\n') {
            ap_bclose(f);
            ap_kill_timeout(r);
@@ -449,9 +471,9 @@
        buffer[12] = ' ';
        r->status_line = ap_pstrdup(p, &buffer[9]);
 
-/* read the headers. */
-/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
-/* Also, take care with headers with multiple occurences. */
+       /* 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. */
 
        resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f);
        if (resp_hdrs == NULL) {
@@ -462,6 +484,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);
@@ -482,26 +505,25 @@
                            );
        }
 
-       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 */
+               /* an http/0.9 response */
        backasswards = 1;
        r->status = 200;
        r->status_line = "200 OK";
 
-/* no headers */
+               /* no headers */
        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
- * one type
- */
+       /*
+        * 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)
        ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr));
     if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL)
@@ -509,10 +531,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) {
@@ -524,52 +549,48 @@
               nocache = 1;
               break;
            }
-       }
     }
 
+    /* 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;
     }
 
-    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);
+       /* 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);
-    }
-
-    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 */
+    /* 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);
+
+    /* 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
@@ -582,10 +603,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);
--- src/modules/proxy/proxy_util.c      Wed Feb  7 08:09:59 2001
+++ src/modules/proxy/proxy_util.c      Thu Mar  8 01:48:17 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.
- * 
- * A timeout should be set before calling this routine.
+ * Writes response line and headers to the cache file.
+ *
+ * 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);
     return len;
+}
+
+/* do a HTTP/1.1 age calculation */
+time_t ap_proxy_current_age(cache_req *c, const time_t age_value)
+{
+    time_t apparent_age, corrected_received_age, response_delay, 
corrected_initial_age, resident_time, current_age;
+
+    /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */
+
+    apparent_age = MAX(0, c->resp_time - c->date);
+    corrected_received_age = MAX(apparent_age, age_value);
+    response_delay = c->resp_time - c->req_time;
+    corrected_initial_age = corrected_received_age + response_delay;
+    resident_time = time(NULL) - c->resp_time;
+    current_age = corrected_initial_age + resident_time;
+
+    return (current_age);
+}
+
+/* open a cache file and return a pointer to a BUFF */
+BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename)
+{
+    BUFF *cachefp = NULL;
+    int cfd;
+
+    if (filename != NULL) {
+       cfd = open(filename, O_RDWR | O_BINARY);
+       if (cfd != -1) {
+           ap_note_cleanups_for_fd(r->pool, cfd);
+           cachefp = ap_bcreate(r->pool, B_RD | B_WR);
+           ap_bpushfd(cachefp, cfd, cfd);
+       }
+       else if (errno != ENOENT)
+           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: error opening cache file %s",
+                         filename);
+#ifdef EXPLAIN
+       else
+           Explain1("File %s not found", filename);
+#endif
+
+    }
+    return cachefp;
+}
+
+/* create a cache file and return a pointer to a BUFF */
+BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename)
+{
+    BUFF *cachefp = NULL;
+    int cfd;
+
+    if (filename != NULL) {
+       cfd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622);
+       if (cfd != -1) {
+           ap_note_cleanups_for_fd(r->pool, cfd);
+           cachefp = ap_bcreate(r->pool, B_WR);
+           ap_bpushfd(cachefp, -1, cfd);
+       }
+       else if (errno != ENOENT)
+           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: error creating cache file %s",
+                         filename);
+    }
+    return cachefp;
+}
+
+/* Clear all connection-based headers from headers table */
+void ap_proxy_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");
 }
 
 #if defined WIN32

Reply via email to