]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
The rest of the proxy http 1.1 switch
authorChuck Murcko <chuck@apache.org>
Fri, 18 Jan 2002 20:26:58 +0000 (20:26 +0000)
committerChuck Murcko <chuck@apache.org>
Fri, 18 Jan 2002 20:26:58 +0000 (20:26 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x@92911 13f79535-47bb-0310-9956-ffa450edef68

src/modules/proxy/mod_proxy.c
src/modules/proxy/mod_proxy.h
src/modules/proxy/proxy_cache.c
src/modules/proxy/proxy_connect.c
src/modules/proxy/proxy_ftp.c
src/modules/proxy/proxy_http.c
src/modules/proxy/proxy_util.c

index 5e44bcd4ffdadeeb8b2bfeb3d90325d51a3305e4..ae2497cd11954f10b952048e9f1b88fff038d76a 100644 (file)
@@ -76,7 +76,7 @@ static struct proxy_services defports[] =
     {"wais", DEFAULT_WAIS_PORT},
     {"snews", DEFAULT_SNEWS_PORT},
     {"prospero", DEFAULT_PROSPERO_PORT},
-    {NULL, -1}                 /* unknown port */
+    {NULL, -1}                  /* unknown port */
 };
 
 /*
@@ -98,29 +98,29 @@ static int alias_match(const char *uri, const char *alias_fakename)
     const char *aliasp = alias_fakename, *urip = uri;
 
     while (aliasp < end_fakename) {
-       if (*aliasp == '/') {
-           /* any number of '/' in the alias matches any number in
-            * the supplied URI, but there must be at least one...
-            */
-           if (*urip != '/')
-               return 0;
-
-           while (*aliasp == '/')
-               ++aliasp;
-           while (*urip == '/')
-               ++urip;
-       }
-       else {
-           /* Other characters are compared literally */
-           if (*urip++ != *aliasp++)
-               return 0;
-       }
+        if (*aliasp == '/') {
+            /* any number of '/' in the alias matches any number in
+             * the supplied URI, but there must be at least one...
+             */
+            if (*urip != '/')
+                return 0;
+
+            while (*aliasp == '/')
+                ++aliasp;
+            while (*urip == '/')
+                ++urip;
+        }
+        else {
+            /* Other characters are compared literally */
+            if (*urip++ != *aliasp++)
+                return 0;
+        }
     }
 
     /* Check last alias path component matched all the way */
 
     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
-       return 0;
+        return 0;
 
     /* Return number of characters from URI which matched (may be
      * greater than length of alias, since we may have matched
@@ -149,25 +149,25 @@ static int proxy_detect(request_rec *r)
     conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
 
     if (conf->req && r->parsed_uri.scheme) {
-       /* but it might be something vhosted */
+        /* but it might be something vhosted */
        if (!(r->parsed_uri.hostname
-           && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
-           && ap_matches_request_vhost(r, r->parsed_uri.hostname,
+            && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
+            && ap_matches_request_vhost(r, r->parsed_uri.hostname,
                r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
-           r->proxyreq = STD_PROXY;
-           r->uri = r->unparsed_uri;
-           r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
-           r->handler = "proxy-server";
+            r->proxyreq = STD_PROXY;
+            r->uri = r->unparsed_uri;
+            r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
+            r->handler = "proxy-server";
         }
     }
     /* We need special treatment for CONNECT proxying: it has no scheme part */
     else if (conf->req && r->method_number == M_CONNECT
-            && r->parsed_uri.hostname
-            && r->parsed_uri.port_str) {
-           r->proxyreq = STD_PROXY;
-           r->uri = r->unparsed_uri;
-           r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
-           r->handler = "proxy-server";
+             && r->parsed_uri.hostname
+             && r->parsed_uri.port_str) {
+            r->proxyreq = STD_PROXY;
+            r->uri = r->unparsed_uri;
+            r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
+            r->handler = "proxy-server";
     }
     return DECLINED;
 }
@@ -181,10 +181,10 @@ static int proxy_trans(request_rec *r)
     struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
 
     if (r->proxyreq != NOT_PROXY) {
-       /* someone has already set up the proxy, it was possibly ourselves
-        * in proxy_detect
-        */
-       return OK;
+        /* someone has already set up the proxy, it was possibly ourselves
+         * in proxy_detect
+         */
+        return OK;
     }
 
     /* XXX: since r->uri has been manipulated already we're not really
@@ -194,14 +194,14 @@ static int proxy_trans(request_rec *r)
 
     for (i = 0; i < conf->aliases->nelts; i++) {
         len = alias_match(r->uri, ent[i].fake);
-           
+            
        if (len > 0) {
            r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real,
                                  r->uri + len, NULL);
            r->handler = "proxy-server";
            r->proxyreq = PROXY_PASS;
            return OK;
-       }
+        }
     }
     return DECLINED;
 }
@@ -217,21 +217,21 @@ static int proxy_fixup(request_rec *r)
     char *url, *p;
 
     if (r->proxyreq == NOT_PROXY || strncmp(r->filename, "proxy:", 6) != 0)
-       return DECLINED;
+        return DECLINED;
 
     url = &r->filename[6];
 
 /* canonicalise each specific scheme */
     if (strncasecmp(url, "http:", 5) == 0)
-       return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT);
+        return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT);
     else if (strncasecmp(url, "ftp:", 4) == 0)
-       return ap_proxy_ftp_canon(r, url + 4);
+        return ap_proxy_ftp_canon(r, url + 4);
 
     p = strchr(url, ':');
     if (p == NULL || p == url)
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
 
-    return OK;         /* otherwise; we've done the best we can */
+    return OK;          /* otherwise; we've done the best we can */
 }
 
 static void proxy_init(server_rec *r, pool *p)
@@ -254,29 +254,29 @@ static int proxy_needsdomain(request_rec *r, const char *url, const char *domain
 
     /* We only want to worry about GETs */
     if (r->proxyreq == NOT_PROXY || r->method_number != M_GET || !r->parsed_uri.hostname)
-       return DECLINED;
+        return DECLINED;
 
     /* If host does contain a dot already, or it is "localhost", decline */
     if (strchr(r->parsed_uri.hostname, '.') != NULL
      || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
-       return DECLINED;        /* host name has a dot already */
+        return DECLINED;        /* host name has a dot already */
 
     ref = ap_table_get(r->headers_in, "Referer");
 
     /* Reassemble the request, but insert the domain after the host name */
     /* Note that the domain name always starts with a dot */
     r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname,
-                                    domain, NULL);
+                                     domain, NULL);
     nuri = ap_unparse_uri_components(r->pool,
-                                 &r->parsed_uri,
-                                 UNP_REVEALPASSWORD);
+                                  &r->parsed_uri,
+                                  UNP_REVEALPASSWORD);
 
     ap_table_set(r->headers_out, "Location", nuri);
     ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
-               "Domain missing: %s sent to %s%s%s", r->uri,
-               ap_unparse_uri_components(r->pool, &r->parsed_uri,
-                     UNP_OMITUSERINFO),
-               ref ? " from " : "", ref ? ref : "");
+                "Domain missing: %s sent to %s%s%s", r->uri,
+                ap_unparse_uri_components(r->pool, &r->parsed_uri,
+                      UNP_OMITUSERINFO),
+                ref ? " from " : "", ref ? ref : "");
 
     return HTTP_MOVED_PERMANENTLY;
 }
@@ -298,41 +298,42 @@ static int proxy_handler(request_rec *r)
     const char *maxfwd_str;
 
     if (r->proxyreq == NOT_PROXY || strncmp(r->filename, "proxy:", 6) != 0)
-       return DECLINED;
+        return DECLINED;
 
     if (r->method_number == M_TRACE &&
-       (maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) {
-       long maxfwd = strtol(maxfwd_str, NULL, 10);
-       if (maxfwd < 1) {
-           int access_status;
-           r->proxyreq = NOT_PROXY;
-           if ((access_status = ap_send_http_trace(r)))
-               ap_die(access_status, r);
-           else
-               ap_finalize_request_protocol(r);
-           return OK;
-       }
-       ap_table_setn(r->headers_in, "Max-Forwards", 
-                     ap_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd-1 : 0));
+        (maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) {
+        long maxfwd = strtol(maxfwd_str, NULL, 10);
+        if (maxfwd < 1) {
+            int access_status;
+            r->proxyreq = NOT_PROXY;
+            if ((access_status = ap_send_http_trace(r)))
+                ap_die(access_status, r);
+            else
+                ap_finalize_request_protocol(r);
+            return OK;
+        }
+        ap_table_setn(r->headers_in, "Max-Forwards", 
+                      ap_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd-1 : 0));
     }
 
     if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
-       return rc;
+        return rc;
 
     url = r->filename + 6;
     p = strchr(url, ':');
     if (p == NULL)
-       return HTTP_BAD_REQUEST;
+        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;
+        return rc;
 
     /* If the host doesn't have a domain name, add one and redirect. */
     if (conf->domain != NULL) {
-       rc = proxy_needsdomain(r, url, conf->domain);
-       if (ap_is_HTTP_REDIRECT(rc))
-           return HTTP_MOVED_PERMANENTLY;
+        rc = proxy_needsdomain(r, url, conf->domain);
+        if (ap_is_HTTP_REDIRECT(rc))
+            return HTTP_MOVED_PERMANENTLY;
     }
 
     *p = '\0';
@@ -344,47 +345,47 @@ static int proxy_handler(request_rec *r)
     /* we only know how to handle communication to a proxy via http */
     /*if (strcasecmp(scheme, "http") == 0) */
     {
-       int ii;
-       struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
+        int ii;
+        struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
 
-       for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
-           direct_connect = list[ii].matcher(&list[ii], r);
-       }
+        for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
+            direct_connect = list[ii].matcher(&list[ii], r);
+        }
 #if DEBUGGING
-       ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
-                    (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
-                    r->uri);
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
+                     (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
+                     r->uri);
 #endif
     }
 
 /* firstly, try a proxy, unless a NoProxy directive is active */
 
     if (!direct_connect)
-       for (i = 0; i < proxies->nelts; i++) {
-           p = strchr(ents[i].scheme, ':');    /* is it a partial URL? */
-           if (strcmp(ents[i].scheme, "*") == 0 ||
-               (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
-               (p != NULL &&
-              strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
-               /* CONNECT is a special method that bypasses the normal
-                * proxy code.
-                */
-               if (r->method_number == M_CONNECT)
-                   rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname,
-                                              ents[i].port);
+        for (i = 0; i < proxies->nelts; i++) {
+            p = strchr(ents[i].scheme, ':');    /* is it a partial URL? */
+            if (strcmp(ents[i].scheme, "*") == 0 ||
+                (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
+                (p != NULL &&
+               strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
+                /* CONNECT is a special method that bypasses the normal
+                 * proxy code.
+                 */
+                if (r->method_number == M_CONNECT)
+                    rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname,
+                                               ents[i].port);
 /* we only know how to handle communication to a proxy via http */
-               else if (strcasecmp(ents[i].protocol, "http") == 0)
-                   rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname,
-                                           ents[i].port);
-               else
-                   rc = DECLINED;
-
-               /* an error or success */
-               if (rc != DECLINED && rc != HTTP_BAD_GATEWAY)
-                   return rc;
-               /* we failed to talk to the upstream proxy */
-           }
-       }
+                else if (strcasecmp(ents[i].protocol, "http") == 0)
+                    rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname,
+                                            ents[i].port);
+                else
+                    rc = DECLINED;
+
+                /* an error or success */
+                if (rc != DECLINED && rc != HTTP_BAD_GATEWAY)
+                    return rc;
+                /* we failed to talk to the upstream proxy */
+            }
+        }
 
 /* otherwise, try it direct */
 /* N.B. what if we're behind a firewall, where we must use a proxy or
@@ -392,13 +393,13 @@ static int proxy_handler(request_rec *r)
  */
     /* handle the scheme */
     if (r->method_number == M_CONNECT)
-       return ap_proxy_connect_handler(r, cr, url, NULL, 0);
+        return ap_proxy_connect_handler(r, cr, url, NULL, 0);
     if (strcasecmp(scheme, "http") == 0)
-       return ap_proxy_http_handler(r, cr, url, NULL, 0);
+        return ap_proxy_http_handler(r, cr, url, NULL, 0);
     if (strcasecmp(scheme, "ftp") == 0)
-       return ap_proxy_ftp_handler(r, cr, url);
+        return ap_proxy_ftp_handler(r, cr, url);
     else
-       return HTTP_FORBIDDEN;
+        return HTTP_FORBIDDEN;
 }
 
 /* -------------------------------------------------------------- */
@@ -493,26 +494,26 @@ static const char *
 
     p = strchr(r, ':');
     if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0')
-       return "ProxyRemote: Bad syntax for a remote proxy server";
+        return "ProxyRemote: Bad syntax for a remote proxy server";
     q = strchr(p + 3, ':');
     if (q != NULL) {
-       if (sscanf(q + 1, "%u", &port) != 1 || port > 65535)
-           return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
-       *q = '\0';
+        if (sscanf(q + 1, "%u", &port) != 1 || port > 65535)
+            return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
+        *q = '\0';
     }
     else
-       port = -1;
+        port = -1;
     *p = '\0';
     if (strchr(f, ':') == NULL)
-       ap_str_tolower(f);              /* lowercase scheme */
-    ap_str_tolower(p + 3);             /* lowercase hostname */
+        ap_str_tolower(f);              /* lowercase scheme */
+    ap_str_tolower(p + 3);              /* lowercase hostname */
 
     if (port == -1) {
-       int i;
-       for (i = 0; defports[i].scheme != NULL; i++)
-           if (strcasecmp(defports[i].scheme, r) == 0)
-               break;
-       port = defports[i].port;
+        int i;
+        for (i = 0; defports[i].scheme != NULL; i++)
+            if (strcasecmp(defports[i].scheme, r) == 0)
+                break;
+        port = defports[i].port;
     }
 
     new = ap_push_array(conf->proxies);
@@ -566,19 +567,19 @@ static const char *
 
     /* Don't duplicate entries */
     for (i = 0; i < conf->noproxies->nelts; i++) {
-       if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
-           found = 1;
+        if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
+            found = 1;
     }
 
     if (!found) {
-       new = ap_push_array(conf->noproxies);
-       new->name = arg;
-       /* Don't do name lookups on things that aren't dotted */
-       if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
-           /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
-           memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
-       else
-           new->addr.s_addr = 0;
+        new = ap_push_array(conf->noproxies);
+        new->name = arg;
+        /* Don't do name lookups on things that aren't dotted */
+        if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
+            /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
+            memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
+        else
+            new->addr.s_addr = 0;
     }
     return NULL;
 }
@@ -595,7 +596,7 @@ static const char *
     int *New;
 
     if (!ap_isdigit(arg[0]))
-       return "AllowCONNECT: port number must be numeric";
+        return "AllowCONNECT: port number must be numeric";
 
     New = ap_push_array(conf->allowed_connect_ports);
     *New = atoi(arg);
@@ -618,39 +619,39 @@ static const char *
 
     /* Don't duplicate entries */
     for (i = 0; i < conf->dirconn->nelts; i++) {
-       if (strcasecmp(arg, list[i].name) == 0)
-           found = 1;
+        if (strcasecmp(arg, list[i].name) == 0)
+            found = 1;
     }
 
     if (!found) {
-       New = ap_push_array(conf->dirconn);
-       New->name = arg;
-       New->hostentry = NULL;
+        New = ap_push_array(conf->dirconn);
+        New->name = arg;
+        New->hostentry = NULL;
 
-       if (ap_proxy_is_ipaddr(New, parms->pool)) {
+        if (ap_proxy_is_ipaddr(New, parms->pool)) {
 #if DEBUGGING
-           fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr));
-           fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask));
+            fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr));
+            fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask));
 #endif
-       }
-       else if (ap_proxy_is_domainname(New, parms->pool)) {
-           ap_str_tolower(New->name);
+        }
+        else if (ap_proxy_is_domainname(New, parms->pool)) {
+            ap_str_tolower(New->name);
 #if DEBUGGING
-           fprintf(stderr, "Parsed domain %s\n", New->name);
+            fprintf(stderr, "Parsed domain %s\n", New->name);
 #endif
-       }
-       else if (ap_proxy_is_hostname(New, parms->pool)) {
-           ap_str_tolower(New->name);
+        }
+        else if (ap_proxy_is_hostname(New, parms->pool)) {
+            ap_str_tolower(New->name);
 #if DEBUGGING
-           fprintf(stderr, "Parsed host %s\n", New->name);
+            fprintf(stderr, "Parsed host %s\n", New->name);
 #endif
-       }
-       else {
-           ap_proxy_is_word(New, parms->pool);
+        }
+        else {
+            ap_proxy_is_word(New, parms->pool);
 #if DEBUGGING
-           fprintf(stderr, "Parsed word %s\n", New->name);
+            fprintf(stderr, "Parsed word %s\n", New->name);
 #endif
-       }
+        }
     }
     return NULL;
 }
@@ -662,7 +663,7 @@ static const char *
     ap_get_module_config(parms->server->module_config, &proxy_module);
 
     if (arg[0] != '.')
-       return "ProxyDomain: domain name must start with a dot.";
+        return "ProxyDomain: domain name must start with a dot.";
 
     psf->domain = arg;
     return NULL;
@@ -688,7 +689,7 @@ static const char *
     int val;
 
     if (sscanf(arg, "%d", &val) != 1)
-       return "CacheSize value must be an integer (kBytes)";
+        return "CacheSize value must be an integer (kBytes)";
     psf->cache.space = val;
     psf->cache.space_set = 1;
     return NULL;
@@ -713,7 +714,7 @@ static const char *
     double val;
 
     if (sscanf(arg, "%lg", &val) != 1)
-       return "CacheLastModifiedFactor value must be a float";
+        return "CacheLastModifiedFactor value must be a float";
     psf->cache.lmfactor = val;
     psf->cache.lmfactor_set = 1;
 
@@ -728,7 +729,7 @@ static const char *
     double val;
 
     if (sscanf(arg, "%lg", &val) != 1)
-       return "CacheMaxExpire value must be a float";
+        return "CacheMaxExpire value must be a float";
     psf->cache.maxexpire = (int) (val * (double) SEC_ONE_HR);
     psf->cache.maxexpire_set = 1;
     return NULL;
@@ -742,7 +743,7 @@ static const char *
     double val;
 
     if (sscanf(arg, "%lg", &val) != 1)
-       return "CacheDefaultExpire value must be a float";
+        return "CacheDefaultExpire value must be a float";
     psf->cache.defaultexpire = (int) (val * (double) SEC_ONE_HR);
     psf->cache.defaultexpire_set = 1;
     return NULL;
@@ -756,7 +757,7 @@ static const char *
     double val;
 
     if (sscanf(arg, "%lg", &val) != 1)
-       return "CacheGcInterval value must be a float";
+        return "CacheGcInterval value must be a float";
     psf->cache.gcinterval = (int) (val * (double) SEC_ONE_HR);
     psf->cache.gcinterval_set = 1;
     return NULL;
@@ -771,9 +772,9 @@ static const char *
 
     val = atoi(arg);
     if (val < 1)
-       return "CacheDirLevels value must be an integer greater than 0";
+        return "CacheDirLevels value must be an integer greater than 0";
     if (val * psf->cache.dirlength > CACHEFILE_LEN)
-       return "CacheDirLevels*CacheDirLength value must not be higher than 20";
+        return "CacheDirLevels*CacheDirLength value must not be higher than 20";
     psf->cache.dirlevels = val;
     psf->cache.dirlevels_set = 1;
     return NULL;
@@ -788,9 +789,9 @@ static const char *
 
     val = atoi(arg);
     if (val < 1)
-       return "CacheDirLength value must be an integer greater than 0";
+        return "CacheDirLength value must be an integer greater than 0";
     if (val * psf->cache.dirlevels > CACHEFILE_LEN)
-       return "CacheDirLevels*CacheDirLength value must not be higher than 20";
+        return "CacheDirLevels*CacheDirLength value must not be higher than 20";
     psf->cache.dirlength = val;
     psf->cache.dirlength_set = 1;
     return NULL;
@@ -810,19 +811,19 @@ static const char *
 
     /* Don't duplicate entries */
     for (i = 0; i < conf->nocaches->nelts; i++) {
-       if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
-           found = 1;
+        if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
+            found = 1;
     }
 
     if (!found) {
-       new = ap_push_array(conf->nocaches);
-       new->name = arg;
-       /* Don't do name lookups on things that aren't dotted */
-       if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
-           /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
-           memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
-       else
-           new->addr.s_addr = 0;
+        new = ap_push_array(conf->nocaches);
+        new->name = arg;
+        /* Don't do name lookups on things that aren't dotted */
+        if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
+            /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
+            memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
+        else
+            new->addr.s_addr = 0;
     }
     return NULL;
 }
@@ -834,7 +835,7 @@ static const char *
     ap_get_module_config(parms->server->module_config, &proxy_module);
     int s = atoi(arg);
     if (s < 512 && s != 0) {
-       return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
+        return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
     }
 
     psf->recv_buffer_size = s;
@@ -849,7 +850,7 @@ static const char*
     ap_get_module_config(parms->server->module_config, &proxy_module);
     int s = atoi(arg);
     if (s > 100 || s < 0) {
-       return "CacheForceCompletion must be <= 100 percent, "
+        return "CacheForceCompletion must be <= 100 percent, "
                "or 0 for system default.";
     }
 
@@ -875,7 +876,7 @@ static const char*
     else if (strcasecmp(arg, "Full") == 0)
         psf->viaopt = via_full;
     else {
-       return "ProxyVia must be one of: "
+        return "ProxyVia must be one of: "
                "off | on | full | block";
     }
 
@@ -937,24 +938,24 @@ static const command_rec proxy_cmds[] =
 module MODULE_VAR_EXPORT proxy_module =
 {
     STANDARD_MODULE_STUFF,
-    proxy_init,                        /* initializer */
-    NULL,                      /* create per-directory config structure */
-    NULL,                      /* merge per-directory config structures */
-    create_proxy_config,       /* create per-server config structure */
-    merge_proxy_config,                /* merge per-server config structures */
-    proxy_cmds,                        /* command table */
-    proxy_handlers,            /* handlers */
-    proxy_trans,               /* translate_handler */
-    NULL,                      /* check_user_id */
-    NULL,                      /* check auth */
-    NULL,                      /* check access */
-    NULL,                      /* type_checker */
-    proxy_fixup,               /* pre-run fixups */
-    NULL,                      /* logger */
-    NULL,                      /* header parser */
-    NULL,                      /* child_init */
-    NULL,                      /* child_exit */
-    proxy_detect               /* post read-request */
+    proxy_init,                 /* initializer */
+    NULL,                       /* create per-directory config structure */
+    NULL,                       /* merge per-directory config structures */
+    create_proxy_config,        /* create per-server config structure */
+    merge_proxy_config,         /* merge per-server config structures */
+    proxy_cmds,                 /* command table */
+    proxy_handlers,             /* handlers */
+    proxy_trans,                /* translate_handler */
+    NULL,                       /* check_user_id */
+    NULL,                       /* check auth */
+    NULL,                       /* check access */
+    NULL,                       /* type_checker */
+    proxy_fixup,                /* pre-run fixups */
+    NULL,                       /* logger */
+    NULL,                       /* header parser */
+    NULL,                       /* child_init */
+    NULL,                       /* child_exit */
+    proxy_detect                /* post read-request */
 };
 
 
index 3c45e0cecbbcd0a9618812485440951b6b2bc110..3874191eb6cdd01aaf7034ebf33e3fb6b6d27237 100644 (file)
    If TESTING is set, then garbage collection doesn't delete ... probably a good
    idea when hacking.
 
-   This code is still experimental!
-
-   Things to do:
-
-   1. Make it garbage collect in the background, not while someone is waiting for
-   a response!
-
-   2. Check the logic thoroughly.
-
-   3. Empty directories are only removed the next time round (but this does avoid
-   two passes). Consider doing them the first time round.
-
-   Ben Laurie <ben@algroup.co.uk> 30 Mar 96
-
-   More things to do:
-
-   0. Code cleanup (ongoing)
-
-   1. add 230 response output for ftp now that it works
-
-   2. Make the ftp proxy transparent, also same with (future) gopher & wais
-
-   3. Use protocol handler struct a la Apache module handlers (Dirk van Gulik)
-
-   4. Use a cache expiry database for more efficient GC (Jeremy Wohl)
-
-   5. Bulletproof GC against SIGALRM
-
-   Chuck Murcko <chuck@topsail.org> 15 April 1997
-
  */
 
-#define TESTING        0
+#define TESTING 0
 
 #include "httpd.h"
 #include "http_config.h"
@@ -118,26 +88,26 @@ enum enctype {
     enc_path, enc_search, enc_user, enc_fpath, enc_parm
 };
 
-#define HDR_APP (0)            /* append header, for proxy_add_header() */
-#define HDR_REP (1)            /* replace header, for proxy_add_header() */
+#define HDR_APP (0)             /* append header, for proxy_add_header() */
+#define HDR_REP (1)             /* replace header, for proxy_add_header() */
 
 /* number of characters in the hash */
 #define HASH_LEN (22*2)
 
 /* maximum  'CacheDirLevels*CacheDirLength' value */
-#define CACHEFILE_LEN 20       /* must be less than HASH_LEN/2 */
+#define CACHEFILE_LEN 20        /* must be less than HASH_LEN/2 */
 
-#define        SEC_ONE_DAY             86400   /* one day, in seconds */
-#define        SEC_ONE_HR              3600    /* one hour, in seconds */
+#define SEC_ONE_DAY             86400   /* one day, in seconds */
+#define SEC_ONE_HR              3600    /* one hour, in seconds */
 
-#define        DEFAULT_FTP_DATA_PORT   20
-#define        DEFAULT_FTP_PORT        21
-#define        DEFAULT_GOPHER_PORT     70
-#define        DEFAULT_NNTP_PORT       119
-#define        DEFAULT_WAIS_PORT       210
-#define        DEFAULT_HTTPS_PORT      443
-#define        DEFAULT_SNEWS_PORT      563
-#define        DEFAULT_PROSPERO_PORT   1525    /* WARNING: conflict w/Oracle */
+#define DEFAULT_FTP_DATA_PORT   20
+#define DEFAULT_FTP_PORT        21
+#define DEFAULT_GOPHER_PORT     70
+#define DEFAULT_NNTP_PORT       119
+#define DEFAULT_WAIS_PORT       210
+#define DEFAULT_HTTPS_PORT      443
+#define DEFAULT_SNEWS_PORT      563
+#define DEFAULT_PROSPERO_PORT   1525    /* WARNING: conflict w/Oracle */
 
 /* Some WWW schemes and their default ports; this is basically /etc/services */
 struct proxy_services {
@@ -147,10 +117,10 @@ struct proxy_services {
 
 /* static information about a remote proxy */
 struct proxy_remote {
-    const char *scheme;                /* the schemes handled by this proxy, or '*' */
-    const char *protocol;      /* the scheme used to talk to this proxy */
-    const char *hostname;      /* the hostname of this proxy */
-    int port;                  /* the port for this proxy */
+    const char *scheme;         /* the schemes handled by this proxy, or '*' */
+    const char *protocol;       /* the scheme used to talk to this proxy */
+    const char *hostname;       /* the hostname of this proxy */
+    int port;                   /* the port for this proxy */
 };
 
 struct proxy_alias {
@@ -182,29 +152,36 @@ struct nocache_entry {
 #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 */
-    off_t space;                       /* Maximum cache size (in 1024 bytes) */
+    const char *root;           /* the location of the cache directory */
+    off_t space;                /* Maximum cache size (in 1024 bytes) */
     char space_set;
-    time_t maxexpire;          /* Maximum time to keep cached files in secs */
+    time_t maxexpire;           /* Maximum time to keep cached files in secs */
     char maxexpire_set;
-    time_t defaultexpire;      /* default time to keep cached file in secs */
+    time_t defaultexpire;       /* default time to keep cached file in secs */
     char defaultexpire_set;
-    double lmfactor;           /* factor for estimating expires date */
+    double lmfactor;            /* factor for estimating expires date */
     char lmfactor_set;
-    time_t gcinterval;         /* garbage collection interval, in seconds */
+    time_t gcinterval;          /* garbage collection interval, in seconds */
     char gcinterval_set;
-    int dirlevels;             /* Number of levels of subdirectories */
+    int dirlevels;              /* Number of levels of subdirectories */
     char dirlevels_set;
-    int dirlength;             /* Length of subdirectory names */
+    int dirlength;              /* Length of subdirectory names */
     char dirlength_set;
-    float cache_completion;    /* Force cache completion after this point */
+    float cache_completion;     /* Force cache completion after this point */
     char cache_completion_set;
 };
 
 typedef struct {
-    struct cache_conf cache;   /* cache configuration */
+    struct cache_conf cache;    /* cache configuration */
     array_header *proxies;
     array_header *aliases;
     array_header *raliases;
@@ -212,8 +189,8 @@ typedef struct {
     array_header *dirconn;
     array_header *nocaches;
     array_header *allowed_connect_ports;
-    char *domain;              /* domain name to use in absence of a domain name in the request */
-    int req;                   /* true if proxy requests are enabled */
+    char *domain;               /* domain name to use in absence of a domain name in the request */
+    int req;                    /* true if proxy requests are enabled */
     char req_set;
     enum {
       via_off,
@@ -233,33 +210,48 @@ struct hdr_entry {
 
 /* caching information about a request */
 typedef struct {
-    request_rec *req;          /* the request */
-    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 */
-    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) */
-    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 */
-    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 */
+    request_rec *req;           /* the request */
+    char *url;                  /* the URL requested */
+    char *filename;             /* name of the cache file,
+                                   or NULL if no cache */
+    char *tempfile;             /* name of the temporary file,
+                                   or NULL if not caching */
+    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 line
+                                   (protocol, code + message) */
+    table *req_hdrs;            /* the original request headers */
+    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;
@@ -271,15 +263,15 @@ struct per_thread_data {
 
 void ap_proxy_cache_tidy(cache_req *c);
 int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
-                     cache_req **cr);
+                      cache_req **cr);
 int ap_proxy_cache_update(cache_req *c, table *resp_hdrs,
-                      const int is_HTTP1, int nocache);
+                       const int is_HTTP1, int nocache);
 void ap_proxy_garbage_coll(request_rec *r);
 
 /* proxy_connect.c */
 
 int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
-                         const char *proxyhost, int proxyport);
+                          const char *proxyhost, int proxyport);
 
 /* proxy_ftp.c */
 
@@ -289,23 +281,23 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url);
 /* proxy_http.c */
 
 int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme,
-                    int def_port);
+                     int def_port);
 int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
-                      const char *proxyhost, int proxyport);
+                       const char *proxyhost, int proxyport);
 
 /* proxy_util.c */
 
 int ap_proxy_hex2c(const char *x);
 void ap_proxy_c2hex(int ch, char *x);
 char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
-                       enum proxyreqtype isenc);
+                        enum proxyreqtype isenc);
 char *ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp,
-                        char **passwordp, char **hostp, int *port);
+                         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);
@@ -321,5 +313,30 @@ int ap_proxy_garbage_init(server_rec *, pool *);
 /* 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);
+int ap_proxy_table_replace(table *base, table *overlay);
+
+/* WARNING - PRIVATE DEFINITION BELOW */
+
+/* XXX: if you tweak this you should look at is_empty_table() and table_elts()
+ * in ap_alloc.h
+ *
+ * NOTE: this private definition is a duplicate of the one in alloc.c
+ * It's here for ap_proxy_table_replace() to avoid breaking binary compat
+ */
+struct table {
+    /* This has to be first to promote backwards compatibility with
+     * older modules which cast a table * to an array_header *...
+     * they should use the table_elts() function for most of the
+     * cases they do this for.
+     */
+    array_header a;
+#ifdef MAKE_TABLE_PROFILE
+    void *creator;
+#endif
+};
 
 #endif /*MOD_PROXY_H*/
index a70e5c8635c8699a8c402e521bc391cb92b6a343..10663038e478391b09b01573fc59bcc63dcd921d 100644 (file)
@@ -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>
@@ -86,7 +87,7 @@ struct gc_ent {
 
 /* Poor man's 61 bit arithmetic */
 typedef struct {
-    long lower;        /* lower 30 bits of result */
+    long lower; /* lower 30 bits of result */
     long upper; /* upper 31 bits of result */
 } long61_t;
 
@@ -106,7 +107,7 @@ typedef struct {
  */
 
 #define ROUNDUP2BLOCKS(_bytes) (((_bytes)+block_size-1) & ~(block_size-1))
-static long block_size = 512;  /* this must be a power of 2 */
+static long block_size = 512;   /* this must be a power of 2 */
 static long61_t curbytes, cachesize;
 static time_t garbage_now, garbage_expire;
 static mutex *garbage_mutex = NULL;
@@ -115,14 +116,14 @@ static mutex *garbage_mutex = NULL;
 int ap_proxy_garbage_init(server_rec *r, pool *p)
 {
     if (!garbage_mutex)
-       garbage_mutex = ap_create_mutex(NULL);
+        garbage_mutex = ap_create_mutex(NULL);
 
     return (0);
 }
 
 
 static int sub_garbage_coll(request_rec *r, array_header *files,
-                           const char *cachedir, const char *cachesubdir);
+                            const char *cachedir, const char *cachesubdir);
 static void help_proxy_garbage_coll(request_rec *r);
 static int should_proxy_garbage_coll(request_rec *r);
 #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF)
@@ -136,14 +137,14 @@ void ap_proxy_garbage_coll(request_rec *r)
 
     (void) ap_acquire_mutex(garbage_mutex);
     if (inside == 1) {
-       (void) ap_release_mutex(garbage_mutex);
-       return;
+        (void) ap_release_mutex(garbage_mutex);
+        return;
     }
     else
-       inside = 1;
+        inside = 1;
     (void) ap_release_mutex(garbage_mutex);
 
-    ap_block_alarms();         /* avoid SIGALRM on big cache cleanup */
+    ap_block_alarms();          /* avoid SIGALRM on big cache cleanup */
     if (should_proxy_garbage_coll(r))
 #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF)
         detached_proxy_garbage_coll(r);
@@ -188,7 +189,7 @@ static long
 cmp_long61 (long61_t *left, long61_t *right)
 {
     return (left->upper == right->upper) ? (left->lower - right->lower)
-                                        : (left->upper - right->upper);
+                                         : (left->upper - right->upper);
 }
 
 /* Compare two gc_ent's, sort them by expiration date */
@@ -198,11 +199,11 @@ static int gcdiff(const void *ap, const void *bp)
     const struct gc_ent *b = (const struct gc_ent *) bp;
 
     if (a->expire > b->expire)
-       return 1;
+        return 1;
     else if (a->expire < b->expire)
-       return -1;
+        return -1;
     else
-       return 0;
+        return 0;
 }
 
 #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF)
@@ -214,68 +215,68 @@ static void detached_proxy_garbage_coll(request_rec *r)
 
 #if 0
     ap_log_error(APLOG_MARK, APLOG_DEBUG, r->server,
-                        "proxy: Guess what; we fork() again...");
+                         "proxy: Guess what; we fork() again...");
 #endif
     switch (pid = fork()) {
-       case -1:
-           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                        "proxy: fork() for cache cleanup failed");
-           return;
+        case -1:
+            ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                         "proxy: fork() for cache cleanup failed");
+            return;
 
-       case 0: /* Child */
+        case 0: /* Child */
 
-           /* close all sorts of things, including the socket fd */
-           ap_cleanup_for_exec();
+            /* close all sorts of things, including the socket fd */
+            ap_cleanup_for_exec();
 
-           /* Fork twice to disassociate from the child */
-           switch (pid = fork()) {
-               case -1:
-                   ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                        "proxy: fork(2nd) for cache cleanup failed");
-                   exit(1);
+            /* Fork twice to disassociate from the child */
+            switch (pid = fork()) {
+                case -1:
+                    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                         "proxy: fork(2nd) for cache cleanup failed");
+                    exit(1);
 
-               case 0: /* Child */
-                   /* The setpgrp() stuff was snarfed from http_main.c */
+                case 0: /* Child */
+                    /* The setpgrp() stuff was snarfed from http_main.c */
 #ifndef NO_SETSID
-                   if ((pgrp = setsid()) == -1) {
-                       perror("setsid");
-                       fprintf(stderr, "%s: setsid failed\n",
-                               ap_server_argv0);
-                       exit(1);
-                   }
+                    if ((pgrp = setsid()) == -1) {
+                        perror("setsid");
+                        fprintf(stderr, "%s: setsid failed\n",
+                                ap_server_argv0);
+                        exit(1);
+                    }
 #elif defined(NEXT) || defined(NEWSOS)
-                   if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
-                       perror("setpgrp");
-                       fprintf(stderr, "%S: setpgrp or getpgrp failed\n",
-                               ap_server_argv0);
-                       exit(1);
-                   }
+                    if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
+                        perror("setpgrp");
+                        fprintf(stderr, "%S: setpgrp or getpgrp failed\n",
+                                ap_server_argv0);
+                        exit(1);
+                    }
 #else
-                   if ((pgrp = setpgrp(getpid(), 0)) == -1) {
-                       perror("setpgrp");
-                       fprintf(stderr, "%s: setpgrp failed\n",
-                               ap_server_argv0);
-                       exit(1);
-                   }
+                    if ((pgrp = setpgrp(getpid(), 0)) == -1) {
+                        perror("setpgrp");
+                        fprintf(stderr, "%s: setpgrp failed\n",
+                                ap_server_argv0);
+                        exit(1);
+                    }
 #endif
-                   help_proxy_garbage_coll(r);
-                   exit(0);
-
-               default:    /* Father */
-                   /* After grandson has been forked off, */
-                   /* there's nothing else to do. */
-                   exit(0);                
-           }
-       default:
-           /* Wait until grandson has been forked off */
-           /* (without wait we'd leave a zombie) */
-           waitpid(pid, &status, 0);
-           return;
+                    help_proxy_garbage_coll(r);
+                    exit(0);
+
+                default:    /* Father */
+                    /* After grandson has been forked off, */
+                    /* there's nothing else to do. */
+                    exit(0);
+            }
+        default:
+            /* Wait until grandson has been forked off */
+            /* (without wait we'd leave a zombie) */
+            waitpid(pid, &status, 0);
+            return;
     }
 }
 #endif /* ndef WIN32 */
 
-#define DOT_TIME "/.time"      /* marker */
+#define DOT_TIME "/.time"       /* marker */
 
 static int should_proxy_garbage_coll(request_rec *r)
 {
@@ -333,7 +334,7 @@ static int should_proxy_garbage_coll(request_rec *r)
         close(timefd);
     }
     else {
-       lastcheck = buf.st_mtime;       /* save the time */
+        lastcheck = buf.st_mtime;       /* save the time */
         if (garbage_now < lastcheck + every) {
             return 0;
         }
@@ -363,7 +364,7 @@ static void help_proxy_garbage_coll(request_rec *r)
     cachesize.lower = cachesize.upper = 0;
     add_long61(&cachesize, conf->space << 10);
 
-    ap_block_alarms();         /* avoid SIGALRM on big cache cleanup */
+    ap_block_alarms();          /* avoid SIGALRM on big cache cleanup */
 
     files = ap_make_array(r->pool, 100, sizeof(struct gc_ent));
     curbytes.upper = curbytes.lower = 0L;
@@ -371,47 +372,47 @@ static void help_proxy_garbage_coll(request_rec *r)
     sub_garbage_coll(r, files, cachedir, "/");
 
     if (cmp_long61(&curbytes, &cachesize) < 0L) {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
-                        "proxy GC: Cache is %ld%% full (nothing deleted)",
-                        (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space));
-       ap_unblock_alarms();
-       return;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+                         "proxy GC: Cache is %ld%% full (nothing deleted)",
+                         (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space));
+        ap_unblock_alarms();
+        return;
     }
 
     /* sort the files we found by expiration date */
     qsort(files->elts, files->nelts, sizeof(struct gc_ent), gcdiff);
 
     for (i = 0; i < files->nelts; i++) {
-       fent = &((struct gc_ent *) files->elts)[i];
-       sprintf(filename, "%s%s", cachedir, fent->file);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, (long)fent->expire, (long)garbage_now);
+        fent = &((struct gc_ent *) files->elts)[i];
+        sprintf(filename, "%s%s", cachedir, fent->file);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, (long)fent->expire, (long)garbage_now);
 #if TESTING
-       fprintf(stderr, "Would unlink %s\n", filename);
+        fprintf(stderr, "Would unlink %s\n", filename);
 #else
-       if (unlink(filename) == -1) {
-           if (errno != ENOENT)
-               ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                            "proxy gc: unlink(%s)", filename);
-       }
-       else
+        if (unlink(filename) == -1) {
+            if (errno != ENOENT)
+                ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                             "proxy gc: unlink(%s)", filename);
+        }
+        else
 #endif
-       {
-           sub_long61(&curbytes, ROUNDUP2BLOCKS(fent->len));
-           if (cmp_long61(&curbytes, &cachesize) < 0)
-               break;
-       }
+        {
+            sub_long61(&curbytes, ROUNDUP2BLOCKS(fent->len));
+            if (cmp_long61(&curbytes, &cachesize) < 0)
+                break;
+        }
     }
 
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
-                        "proxy GC: Cache is %ld%% full (%d deleted)",
-                        (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space), i);
+                         "proxy GC: Cache is %ld%% full (%d deleted)",
+                         (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space), i);
     ap_unblock_alarms();
 }
 
 static int sub_garbage_coll(request_rec *r, array_header *files,
-                         const char *cachebasedir, const char *cachesubdir)
+                          const char *cachebasedir, const char *cachesubdir)
 {
-    char line[27];
+    char line[17*(3)];
     char cachedir[HUGE_STRING_LEN];
     struct stat buf;
     int fd, i;
@@ -427,51 +428,51 @@ static int sub_garbage_coll(request_rec *r, array_header *files,
 
     ap_snprintf(cachedir, sizeof(cachedir), "%s%s", cachebasedir, cachesubdir);
     filename = ap_palloc(r->pool, strlen(cachedir) + HASH_LEN + 2);
-    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC Examining directory %s", cachedir);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC Examining directory %s", cachedir);
     dir = opendir(cachedir);
     if (dir == NULL) {
-       ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                    "proxy gc: opendir(%s)", cachedir);
-       return 0;
+        ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                     "proxy gc: opendir(%s)", cachedir);
+        return 0;
     }
 
     while ((ent = readdir(dir)) != NULL) {
-       if (ent->d_name[0] == '.')
-           continue;
-       sprintf(filename, "%s%s", cachedir, ent->d_name);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC Examining file %s", filename);
+        if (ent->d_name[0] == '.')
+            continue;
+        sprintf(filename, "%s%s", cachedir, ent->d_name);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC Examining file %s", filename);
 /* is it a temporary file? */
-       if (strncmp(ent->d_name, "tmp", 3) == 0) {
+        if (strncmp(ent->d_name, "tmp", 3) == 0) {
 /* then stat it to see how old it is; delete temporary files > 1 day old */
-           if (stat(filename, &buf) == -1) {
-               if (errno != ENOENT)
-                   ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                                "proxy gc: stat(%s)", filename);
-           }
-           else if (garbage_now != -1 && buf.st_atime < garbage_now - SEC_ONE_DAY &&
-                    buf.st_mtime < garbage_now - SEC_ONE_DAY) {
-               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC unlink %s", filename);
-               ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r->server,
-                            "proxy gc: deleting orphaned cache file %s", filename);
+            if (stat(filename, &buf) == -1) {
+                if (errno != ENOENT)
+                    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                                 "proxy gc: stat(%s)", filename);
+            }
+            else if (garbage_now != -1 && buf.st_atime < garbage_now - SEC_ONE_DAY &&
+                     buf.st_mtime < garbage_now - SEC_ONE_DAY) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC unlink %s", filename);
+                ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r->server,
+                             "proxy gc: deleting orphaned cache file %s", filename);
 #if TESTING
-               fprintf(stderr, "Would unlink %s\n", filename);
+                fprintf(stderr, "Would unlink %s\n", filename);
 #else
-               unlink(filename);
+                unlink(filename);
 #endif
-           }
-           continue;
-       }
-       ++nfiles;
+            }
+            continue;
+        }
+        ++nfiles;
 /* is it another file? */
-       /* FIXME: Shouldn't any unexpected files be deleted? */
-       /*      if (strlen(ent->d_name) != HASH_LEN) continue; */
+        /* FIXME: Shouldn't any unexpected files be deleted? */
+        /*      if (strlen(ent->d_name) != HASH_LEN) continue; */
 
 /* under OS/2 use dirent's d_attr to identify a diretory */
 /* under TPF use stat to identify a directory */
 #if defined(OS2) || defined(TPF)
 /* is it a directory? */
 #ifdef OS2
-       if (ent->d_attr & A_DIR) {
+        if (ent->d_attr & A_DIR) {
 #elif defined(TPF)
     if (stat(filename, &buf) == -1) {
         if (errno != ENOENT)
@@ -480,21 +481,21 @@ static int sub_garbage_coll(request_rec *r, array_header *files,
     }
     if (S_ISDIR(buf.st_mode)) {
 #endif
-           char newcachedir[HUGE_STRING_LEN];
-           ap_snprintf(newcachedir, sizeof(newcachedir),
-                       "%s%s/", cachesubdir, ent->d_name);
-           if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) {
-               ap_snprintf(newcachedir, sizeof(newcachedir),
-                           "%s%s", cachedir, ent->d_name);
+            char newcachedir[HUGE_STRING_LEN];
+            ap_snprintf(newcachedir, sizeof(newcachedir),
+                        "%s%s/", cachesubdir, ent->d_name);
+            if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) {
+                ap_snprintf(newcachedir, sizeof(newcachedir),
+                            "%s%s", cachedir, ent->d_name);
 #if TESTING
-               fprintf(stderr, "Would remove directory %s\n", newcachedir);
+                fprintf(stderr, "Would remove directory %s\n", newcachedir);
 #else
-               rmdir(newcachedir);
+                rmdir(newcachedir);
 #endif
-               --nfiles;
-           }
-           continue;
-       }
+                --nfiles;
+            }
+            continue;
+        }
 #endif
 
 /* read the file */
@@ -505,51 +506,51 @@ static int sub_garbage_coll(request_rec *r, array_header *files,
          */
         if (stat(filename, &buf) == -1) {
             ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                        "proxy gc: stat(%s)", filename);
+                         "proxy gc: stat(%s)", filename);
             continue;
         }
         fd = -1;
 #else
-       fd = open(filename, O_RDONLY | O_BINARY);
-       if (fd == -1) {
-           if (errno != ENOENT)
-               ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                            "proxy gc: open(%s)", filename);
-           continue;
-       }
-       if (fstat(fd, &buf) == -1) {
-           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                        "proxy gc: fstat(%s)", filename);
-           close(fd);
-           continue;
-       }
+        fd = open(filename, O_RDONLY | O_BINARY);
+        if (fd == -1) {
+            if (errno != ENOENT)
+                ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                             "proxy gc: open(%s)", filename);
+            continue;
+        }
+        if (fstat(fd, &buf) == -1) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                         "proxy gc: fstat(%s)", filename);
+            close(fd);
+            continue;
+        }
 #endif
 
 /* In OS/2 and TPF this has already been done above */
 #if !defined(OS2) && !defined(TPF)
-       if (S_ISDIR(buf.st_mode)) {
-           char newcachedir[HUGE_STRING_LEN];
+        if (S_ISDIR(buf.st_mode)) {
+            char newcachedir[HUGE_STRING_LEN];
 #if !defined(WIN32)
             /* Win32 used stat, no file to close */
             close(fd);
 #endif
-           ap_snprintf(newcachedir, sizeof(newcachedir),
-                       "%s%s/", cachesubdir, ent->d_name);
-           if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) {
-               ap_snprintf(newcachedir, sizeof(newcachedir),
-                           "%s%s", cachedir, ent->d_name);
+            ap_snprintf(newcachedir, sizeof(newcachedir),
+                        "%s%s/", cachesubdir, ent->d_name);
+            if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) {
+                ap_snprintf(newcachedir, sizeof(newcachedir),
+                            "%s%s", cachedir, ent->d_name);
 #if TESTING
-               fprintf(stderr, "Would remove directory %s\n", newcachedir);
+                fprintf(stderr, "Would remove directory %s\n", newcachedir);
 #else
-               rmdir(newcachedir);
+                rmdir(newcachedir);
 #endif
-               --nfiles;
-           } else {
-               /* Directory is not empty. Account for its size: */
-               add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
-           }
-           continue;
-       }
+                --nfiles;
+            } else {
+                /* Directory is not empty. Account for its size: */
+                add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
+            }
+            continue;
+        }
 #endif
 
 #if defined(WIN32)
@@ -559,36 +560,36 @@ static int sub_garbage_coll(request_rec *r, array_header *files,
         fd = open(filename, O_RDONLY | O_BINARY);
         if (fd == -1) {
             if (errno != ENOENT)
-               ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                            "proxy gc: open(%s) = %d", filename, errno);
+                ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                             "proxy gc: open(%s) = %d", filename, errno);
             continue;
         }
 #endif
  
-       i = read(fd, line, 26);
-       close(fd);
-       if (i == -1) {
-           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
-                        "proxy gc: read(%s)", filename);
-           continue;
-       }
-       line[i] = '\0';
-       garbage_expire = ap_proxy_hex2sec(line + 18);
-       if (!ap_checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") ||
-           garbage_expire == BAD_DATE) {
-           /* bad file */
-           if (garbage_now != -1 && buf.st_atime > garbage_now + SEC_ONE_DAY &&
-               buf.st_mtime > garbage_now + SEC_ONE_DAY) {
-               ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
-                            "proxy: deleting bad cache file with future date: %s", filename);
+        i = read(fd, line, 17*(3)-1);
+        close(fd);
+        if (i == -1) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                         "proxy gc: read(%s)", filename);
+            continue;
+        }
+        line[i] = '\0';
+        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 &&
+                buf.st_mtime > garbage_now + SEC_ONE_DAY) {
+                ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
+                             "proxy: deleting bad cache file with future date: %s", filename);
 #if TESTING
-               fprintf(stderr, "Would unlink bad file %s\n", filename);
+                fprintf(stderr, "Would unlink bad file %s\n", filename);
 #else
-               unlink(filename);
+                unlink(filename);
 #endif
-           }
-           continue;
-       }
+            }
+            continue;
+        }
 
 /*
  * we need to calculate an 'old' factor, and remove the 'oldest' files
@@ -596,14 +597,14 @@ static int sub_garbage_coll(request_rec *r, array_header *files,
  * file.
  *
  */
-       fent = (struct gc_ent *) ap_push_array(files);
-       fent->len = buf.st_size;
-       fent->expire = garbage_expire;
-       strcpy(fent->file, cachesubdir);
-       strcat(fent->file, ent->d_name);
+        fent = (struct gc_ent *) ap_push_array(files);
+        fent->len = buf.st_size;
+        fent->expire = garbage_expire;
+        strcpy(fent->file, cachesubdir);
+        strcat(fent->file, ent->d_name);
 
 /* accumulate in blocks, to cope with directories > 4Gb */
-       add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
+        add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
     }
 
     closedir(dir);
@@ -612,75 +613,292 @@ static int sub_garbage_coll(request_rec *r, array_header *files,
 
 }
 
+
 /*
- * 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;
+        return -1;
     if (len == 0 || urlbuff[len - 1] != '\n')
-       return 0;
+        return 0;
     urlbuff[len - 1] = '\0';
 
     if (!ap_checkmask(urlbuff,
-                  "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&"))
-       return 0;
+                   "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&"))
+        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;
+        return -1;
     if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 ||
-       urlbuff[len - 1] != '\n')
-       return 0;
+        urlbuff[len - 1] != '\n')
+        return 0;
     urlbuff[len - 1] = '\0';
     if (strcmp(urlbuff + 7, c->url) != 0)
-       return 0;
+        return 0;
+
+    /* then the original request headers */
+    c->req_hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
+    if (c->req_hdrs == NULL)
+        return -1;
 
-/* What follows is the message */
+    /* then the original response headers */
     len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
     if (len == -1)
-       return -1;
+        return -1;
     if (len == 0 || urlbuff[len - 1] != '\n')
-       return 0;
+        return 0;
     urlbuff[--len] = '\0';
 
     c->resp_line = ap_pstrdup(r->pool, urlbuff);
     strp = strchr(urlbuff, ' ');
     if (strp == NULL)
-       return 0;
+        return 0;
 
     c->status = atoi(strp);
     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 (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;
+    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 = NULL;
+
+    /* 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))) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-Match specified, and it didn't - return 412");
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-Unmodified-Since specified, but it wasn't - return 412");
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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));
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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, "*")) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-None-Match: * specified, return 304");
+            }
+            else if (etag && ap_proxy_liststr(c->inm, etag, NULL)) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-None-Match: specified and we got a strong match - return 304");
+            }
+            else if (wetag && ap_proxy_liststr(c->inm, wetag, NULL)) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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));
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Use local copy, cached file hasn't changed");
+        return HTTP_NOT_MODIFIED;
+    }
+
+
+    /* No conditional - just send it cousin! */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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
@@ -692,149 +910,365 @@ static int rdcache(request_rec *r, BUFF *cachefp, cache_req *c)
  *      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)
+                      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;
+      ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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;
+      ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "no-store forbids caching. Declining.");
+      return DECLINED;
+    }
+
+/* if the cache file exists, open it */
     cachefp = NULL;
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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");
-    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "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) {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "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);
-       else
-           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "File %s not found", c->filename);
+        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)
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "proxy: error reading cache file %s", 
-                        c->filename);
-       else if (i == 0)
-           ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
-                        "proxy: bad (short?) cache file: %s", c->filename);
-       if (i != 1) {
-           ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
-           cachefp = NULL;
-       }
+        i = rdcache(r, cachefp, c);
+        if (i == -1)
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: error reading cache file %s", 
+                         c->filename);
+        else if (i == 0)
+            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
+                         "proxy: bad (short?) cache file: %s", c->filename);
+        if (i != 1) {
+            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);
+        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;
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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) {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "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));
-           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Use local copy, cached file hasn't changed");
-           return HTTP_NOT_MODIFIED;
-       }
-
-/* Ok, has been modified */
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "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;
+    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... */
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Unexpired data available");
+
+        /* 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");
+        }
+
+        /* 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;
 
-    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Local copy not present or expired. Declining.");
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Local copy not present or expired. Declining.");
 
     return DECLINED;
 }
@@ -852,119 +1286,166 @@ int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
  *  otherwise, delete the old cached file and open a new temporary file
  */
 int ap_proxy_cache_update(cache_req *c, table *resp_hdrs,
-                      const int is_HTTP1, int nocache)
+                       const int is_HTTP1, int nocache)
 {
 #if defined(ULTRIX_BRAIN_DEATH) || defined(SINIX_D_RESOLVER_BUG)
   extern char *mktemp(char *template);
 #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);
+        expc = ap_parseHTTPdate(expire);
     else
-       expc = BAD_DATE;
+        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 */
-           lmods = NULL;
-       }
+        lmod = ap_parseHTTPdate(lmods);
+        if (lmod == BAD_DATE) {
+            /* kill last modified date */
+            lmods = NULL;
+        }
     }
     else
-       lmod = BAD_DATE;
+        lmod = BAD_DATE;
+
+
+    /*
+     * 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 ||
+
+    /* 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
 
-/*
- * 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) ||
-       (expire != NULL && expc == BAD_DATE) ||
-       (r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) ||
-       (r->status == HTTP_OK && lmods == NULL && is_HTTP1) ||
-       r->header_only ||
-       ap_table_get(r->headers_in, "Authorization") != NULL ||
-       nocache) {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Response is not cacheable, unlinking %s", c->filename);
-/* 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 */
+        && !(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) {
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Response is not cacheable, unlinking %s", c->filename);
+
+        /* 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 */
         if (c->filename)
             unlink(c->filename);
-       return DECLINED;        /* send data to client but not cache */
+        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);
+        date = ap_parseHTTPdate(dates);
     else
-       date = BAD_DATE;
+        date = BAD_DATE;
 
     now = time(NULL);
 
-    if (date == BAD_DATE) {    /* No, or bad date */
+    if (date == BAD_DATE) {     /* No, or bad date */
 /* no date header! */
 /* add one; N.B. use the time _now_ rather than when we were checking the cache
  */
-       date = now;
-       dates = ap_gm_timestr_822(r->pool, now);
-       ap_table_set(resp_hdrs, "Date", dates);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Added date header");
+        date = now;
+        dates = ap_gm_timestr_822(r->pool, now);
+        ap_table_set(resp_hdrs, "Date", dates);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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 */
     {
-       lmod = date;
-       lmods = dates;
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Last modified is in the future, replacing with now");
+        lmod = date;
+        lmods = dates;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Last modified is in the future, replacing with now");
     }
 /* if the response did not contain the header, then use the cached version */
     if (lmod == BAD_DATE && c->fp != NULL) {
-       lmod = c->lmod;
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Reusing cached last modified");
+        lmod = c->lmod;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Reusing cached last modified");
     }
 
 /* we now need to calculate the expire data for the object. */
-    if (expire == NULL && c->fp != NULL) {     /* no expiry data sent in response */
-       expire = ap_table_get(c->hdrs, "Expires");
-       if (expire != NULL)
-           expc = ap_parseHTTPdate(expire);
+    if (expire == NULL && c->fp != NULL) {     /* no expiry data sent in response */
+        expire = ap_table_get(c->hdrs, "Expires");
+        if (expire != NULL)
+            expc = ap_parseHTTPdate(expire);
     }
 /* so we now have the expiry date */
 /* if no expiry date then
@@ -973,141 +1454,178 @@ int ap_proxy_cache_update(cache_req *c, table *resp_hdrs,
  *   else
  *      expire date = now + defaultexpire
  */
-    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Expiry date is %ld", (long)expc);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Expiry date is %ld", (long)expc);
     if (expc == BAD_DATE) {
-       if (lmod != BAD_DATE) {
-           double x = (double) (date - lmod) * conf->cache.lmfactor;
-           double maxex = conf->cache.maxexpire;
-           if (x > maxex)
-               x = maxex;
-           expc = now + (int) x;
-       }
-       else
-           expc = now + conf->cache.defaultexpire;
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Expiry date calculated %ld", (long)expc);
+        if (lmod != BAD_DATE) {
+            double x = (double) (date - lmod) * conf->cache.lmfactor;
+            double maxex = conf->cache.maxexpire;
+            if (x > maxex)
+                x = maxex;
+            expc = now + (int) x;
+        }
+        else
+            expc = now + conf->cache.defaultexpire;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Expiry date calculated %ld", (long)expc);
     }
 
 /* get the content-length header */
     clen = ap_table_get(resp_hdrs, "Content-Length");
     if (clen == NULL)
-       c->len = -1;
+        c->len = -1;
     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';
-
-/* 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));
-           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "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 */
-           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "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);
-           }
-           ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
-           return OK;
-       }
-    }
-/* new or modified file */
+        c->len = atoi(clen);
+
+/* 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 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_proxy_table_replace(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);
+            }
+        }
+        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;
+      }
+
+      ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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;
+    }
 
-    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "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;
 }
@@ -1117,8 +1635,8 @@ void ap_proxy_cache_tidy(cache_req *c)
     server_rec *s;
     long int bc;
 
-    if (c == NULL || c->fp == NULL)
-       return;
+    if (!c || !c->fp)
+        return;
 
     s = c->req->server;
 
@@ -1129,98 +1647,98 @@ void ap_proxy_cache_tidy(cache_req *c)
 
     if (c->len != -1) {
 /* file lengths don't match; don't cache it */
-       if (bc != c->len) {
-           ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));  /* no need to flush */
-           unlink(c->tempfile);
-           return;
-       }
+        if (bc != c->len) {
+            ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));  /* no need to flush */
+            unlink(c->tempfile);
+            return;
+        }
     }
 /* don't care if aborted, cache it if fully retrieved from host!
     else if (c->req->connection->aborted) {
-       ap_pclosef(c->req->pool, c->fp->fd);    / no need to flush /
-       unlink(c->tempfile);
-       return;
+        ap_pclosef(c->req->pool, c->fp->fd);    / no need to flush /
+        unlink(c->tempfile);
+        return;
     }
 */
     else {
 /* update content-length of file */
-       char buff[9];
-       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);
-       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)
-           ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                        "proxy: error updating cache file %s", c->tempfile);
+        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), 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, sizeof(buff) - 1) == -1)
+            ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                         "proxy: error updating cache file %s", c->tempfile);
     }
 
     if (ap_bflush(c->fp) == -1) {
-       ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                    "proxy: error writing to cache file %s",
-                    c->tempfile);
-       ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
-       unlink(c->tempfile);
-       return;
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "proxy: error writing to cache file %s",
+                     c->tempfile);
+        ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
+        unlink(c->tempfile);
+        return;
     }
 
     if (ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)) == -1) {
-       ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                    "proxy: error closing cache file %s", c->tempfile);
-       unlink(c->tempfile);
-       return;
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "proxy: error closing cache file %s", c->tempfile);
+        unlink(c->tempfile);
+        return;
     }
 
     if (unlink(c->filename) == -1 && errno != ENOENT) {
-       ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                    "proxy: error deleting old cache file %s",
-                    c->tempfile);
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "proxy: error deleting old cache file %s",
+                     c->tempfile);
     }
     else {
-       char *p;
-       proxy_server_conf *conf =
-       (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
-
-       for (p = c->filename + strlen(conf->cache.root) + 1;;) {
-           p = strchr(p, '/');
-           if (!p)
-               break;
-           *p = '\0';
+        char *p;
+        proxy_server_conf *conf =
+        (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
+
+        for (p = c->filename + strlen(conf->cache.root) + 1;;) {
+            p = strchr(p, '/');
+            if (!p)
+                break;
+            *p = '\0';
 #if defined(WIN32) || defined(NETWARE)
-           if (mkdir(c->filename) < 0 && errno != EEXIST)
+            if (mkdir(c->filename) < 0 && errno != EEXIST)
 #elif defined(__TANDEM)
-           if (mkdir(c->filename, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
+            if (mkdir(c->filename, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
 #else
-           if (mkdir(c->filename, S_IREAD | S_IWRITE | S_IEXEC) < 0 && errno != EEXIST)
+            if (mkdir(c->filename, S_IREAD | S_IWRITE | S_IEXEC) < 0 && errno != EEXIST)
 #endif /* WIN32 */
-               ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                            "proxy: error creating cache directory %s",
-                            c->filename);
-           *p = '/';
-           ++p;
-       }
+                ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                             "proxy: error creating cache directory %s",
+                             c->filename);
+            *p = '/';
+            ++p;
+        }
 #if defined(OS2) || defined(WIN32) || defined(NETWARE) || defined(MPE)
-       /* Under OS/2 use rename. */
-       if (rename(c->tempfile, c->filename) == -1)
-           ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                        "proxy: error renaming cache file %s to %s",
-                        c->tempfile, c->filename);
+        /* Under OS/2 use rename. */
+        if (rename(c->tempfile, c->filename) == -1)
+            ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                         "proxy: error renaming cache file %s to %s",
+                         c->tempfile, c->filename);
     }
 #else
 
-       if (link(c->tempfile, c->filename) == -1)
-           ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                        "proxy: error linking cache file %s to %s",
-                        c->tempfile, c->filename);
+        if (link(c->tempfile, c->filename) == -1)
+            ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                         "proxy: error linking cache file %s to %s",
+                         c->tempfile, c->filename);
     }
 
     if (unlink(c->tempfile) == -1)
-       ap_log_error(APLOG_MARK, APLOG_ERR, s,
-                    "proxy: error deleting temp file %s", c->tempfile);
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "proxy: error deleting temp file %s", c->tempfile);
 #endif
 
 }
index b40db4259642ce2f53b7250ee3b696853a2629d1..d10b84300dd6b99322d99383013b1305646c3ce5 100644 (file)
@@ -63,7 +63,7 @@
 #include "http_main.h"
 
 #ifdef HAVE_BSTRING_H
-#include <bstring.h>           /* for IRIX, FD_SET calls bzero() */
+#include <bstring.h>            /* for IRIX, FD_SET calls bzero() */
 #endif
 
 /*  
@@ -103,15 +103,15 @@ allowed_port(proxy_server_conf *conf, int port)
     int *list = (int *) conf->allowed_connect_ports->elts;
 
     for(i = 0; i < conf->allowed_connect_ports->nelts; i++) {
-       if(port == list[i])
-           return 1;
+        if(port == list[i])
+            return 1;
     }
     return 0;
 }
 
 
 int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
-                         const char *proxyhost, int proxyport)
+                          const char *proxyhost, int proxyport)
 {
     struct sockaddr_in server;
     struct in_addr destaddr;
@@ -126,7 +126,7 @@ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
-    struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
+    struct noproxy_entry *npent = (struct noproxy_entry *)conf->noproxies->elts;
 
     memset(&server, '\0', sizeof(server));
     server.sin_family = AF_INET;
@@ -136,158 +136,157 @@ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
     host = url;
     p = strchr(url, ':');
     if (p == NULL)
-       port = DEFAULT_HTTPS_PORT;
+        port = DEFAULT_HTTPS_PORT;
     else {
-       port = atoi(p + 1);
-       *p = '\0';
+        port = atoi(p + 1);
+        *p = '\0';
     }
 
 /* check if ProxyBlock directive on this host */
     destaddr.s_addr = ap_inet_addr(host);
     for (i = 0; i < conf->noproxies->nelts; i++) {
-       if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
-           || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
-           return ap_proxyerror(r, HTTP_FORBIDDEN,
-                                "Connect to remote machine blocked");
+        if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
+            || destaddr.s_addr == npent[i].addr.s_addr
+            || npent[i].name[0] == '*')
+            return ap_proxyerror(r, HTTP_FORBIDDEN,
+                                 "Connect to remote machine blocked");
     }
 
     /* Check if it is an allowed port */
     if (conf->allowed_connect_ports->nelts == 0) {
-       /* Default setting if not overridden by AllowCONNECT */
-       switch (port) {
-           case DEFAULT_HTTPS_PORT:
-           case DEFAULT_SNEWS_PORT:
-               break;
-           default:
-               return HTTP_FORBIDDEN;
-       }
+        /* Default setting if not overridden by AllowCONNECT */
+        switch (port) {
+            case DEFAULT_HTTPS_PORT:
+            case DEFAULT_SNEWS_PORT:
+                break;
+            default:
+                return HTTP_FORBIDDEN;
+        }
     } else if(!allowed_port(conf, port))
-       return HTTP_FORBIDDEN;
+        return HTTP_FORBIDDEN;
 
     if (proxyhost) {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "CONNECT to remote proxy %s on port %d", proxyhost, proxyport);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+            "CONNECT to remote proxy %s on port %d", proxyhost, proxyport);
     }
     else {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "CONNECT to %s on port %d", host, port);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+            "CONNECT to %s on port %d", host, port);
     }
 
     server.sin_port = (proxyport ? htons(proxyport) : htons(port));
     err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp);
 
     if (err != NULL)
-       return ap_proxyerror(r,
-                            proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR,
-                            err);
+        return ap_proxyerror(r,
+           proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR, err);
 
     sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (sock == -1) {
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                   "proxy: error creating socket");
-       return HTTP_INTERNAL_SERVER_ERROR;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "proxy: error creating socket");
+        return HTTP_INTERNAL_SERVER_ERROR;
     }
 
 #ifdef CHECK_FD_SETSIZE
     if (sock >= FD_SETSIZE) {
-       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r->server,
-           "proxy_connect_handler: filedescriptor (%u) "
-           "larger than FD_SETSIZE (%u) "
-           "found, you probably need to rebuild Apache with a "
-           "larger FD_SETSIZE", sock, FD_SETSIZE);
-       ap_pclosesocket(r->pool, sock);
-       return HTTP_INTERNAL_SERVER_ERROR;
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+            "proxy_connect_handler: filedescriptor (%u) "
+            "larger than FD_SETSIZE (%u) "
+            "found, you probably need to rebuild Apache with a "
+            "larger FD_SETSIZE", sock, FD_SETSIZE);
+        ap_pclosesocket(r->pool, sock);
+        return HTTP_INTERNAL_SERVER_ERROR;
     }
 #endif
 
     j = 0;
     while (server_hp.h_addr_list[j] != NULL) {
-       memcpy(&server.sin_addr, server_hp.h_addr_list[j],
-              sizeof(struct in_addr));
-       i = ap_proxy_doconnect(sock, &server, r);
-       if (i == 0)
-           break;
-       j++;
+        memcpy(&server.sin_addr, server_hp.h_addr_list[j],
+               sizeof(struct in_addr));
+        i = ap_proxy_doconnect(sock, &server, r);
+        if (i == 0)
+            break;
+        j++;
     }
     if (i == -1) {
-       ap_pclosesocket(r->pool, sock);
-       return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool,
-                                       "Could not connect to remote machine:<br>",
-                                       strerror(errno), NULL));
+        ap_pclosesocket(r->pool, sock);
+        return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool,
+            "Could not connect to remote machine:<br>", strerror(errno), NULL));
     }
 
     /* If we are connecting through a remote proxy, we need to pass
      * the CONNECT request on to it.
      */
     if (proxyport) {
-       /* FIXME: We should not be calling write() directly, but we currently
-        * have no alternative.  Error checking ignored.  Also, we force
-        * a HTTP/1.0 request to keep things simple.
-        */
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Sending the CONNECT request to the remote proxy");
-       ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF,
-                   r->uri);
-#ifdef CHARSET_EBCDIC
-       /* We are writing to the pure socket,
-        * so we must convert our string to ASCII first
-        */
-       ebcdic2ascii(buffer, buffer, strlen(buffer));
-#endif
-       send(sock, buffer, strlen(buffer),0);
-       ap_snprintf(buffer, sizeof(buffer),
-                   "Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
-#ifdef CHARSET_EBCDIC
-       ebcdic2ascii(buffer, buffer, strlen(buffer));
-#endif
-       send(sock, buffer, strlen(buffer),0);
+        /* FIXME: We should not be calling write() directly, but we currently
+         * have no alternative.  Error checking ignored.  Also, we force
+         * a HTTP/1.0 request to keep things simple.
+         */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+            "Sending the CONNECT request to the remote proxy");
+        ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF, r->uri);
+        send(sock, buffer, strlen(buffer),0);
+        ap_snprintf(buffer, sizeof(buffer),
+            "Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
+        send(sock, buffer, strlen(buffer),0);
     }
     else {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Returning 200 OK Status");
-       ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL);
-       ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL);
-       ap_bflush(r->connection->client);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+            "Returning 200 OK Status");
+        ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL);
+        ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL);
+        ap_bflush(r->connection->client);
     }
 
-    while (1) {                        /* Infinite loop until error (one side closes the connection) */
-       FD_ZERO(&fds);
-       FD_SET(sock, &fds);
-       FD_SET(ap_bfileno(r->connection->client, B_WR), &fds);
+    while (1) { /* Infinite loop until error (one side closes the connection) */
+        FD_ZERO(&fds);
+        FD_SET(sock, &fds);
+        FD_SET(ap_bfileno(r->connection->client, B_WR), &fds);
 
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Going to sleep (select)");
-       i = ap_select((ap_bfileno(r->connection->client, B_WR) > sock ?
-                      ap_bfileno(r->connection->client, B_WR) + 1 :
-                      sock + 1), &fds, NULL, NULL, NULL);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Woke from select(), i=%d", i);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+            "Going to sleep (select)");
+        i = ap_select((ap_bfileno(r->connection->client, B_WR) > sock ?
+            ap_bfileno(r->connection->client, B_WR) + 1 :
+            sock + 1), &fds, NULL, NULL, NULL);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+            "Woke from select(), i=%d", i);
 
-       if (i) {
-           if (FD_ISSET(sock, &fds)) {
-               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "sock was set");
-               if ((nbytes = recv(sock, buffer, HUGE_STRING_LEN,0)) != 0) {
-                   if (nbytes == -1)
-                       break;
-                   if (send(ap_bfileno(r->connection->client, B_WR), buffer, nbytes,0) == EOF)
-                       break;
-                   ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Wrote %d bytes to client", nbytes);
-               }
-               else
-                   break;
-           }
-           else if (FD_ISSET(ap_bfileno(r->connection->client, B_WR), &fds)) {
-               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "client->fd was set");
-               if ((nbytes = recv(ap_bfileno(r->connection->client, B_WR), buffer,
-                                  HUGE_STRING_LEN, 0)) != 0) {
-                   if (nbytes == -1)
-                       break;
-                   if (send(sock, buffer, nbytes, 0) == EOF)
-                       break;
-                   ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Wrote %d bytes to server", nbytes);
-               }
-               else
-                   break;
-           }
-           else
-               break;          /* Must be done waiting */
-       }
-       else
-           break;
+        if (i) {
+            if (FD_ISSET(sock, &fds)) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+                    "sock was set");
+                if ((nbytes = recv(sock, buffer, HUGE_STRING_LEN,0)) != 0) {
+                    if (nbytes == -1)
+                        break;
+                    if (send(ap_bfileno(r->connection->client, B_WR), buffer,
+                        nbytes,0) == EOF)
+                        break;
+                    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO,
+                        r->server, "Wrote %d bytes to client", nbytes);
+                }
+                else
+                    break;
+            }
+            else if (FD_ISSET(ap_bfileno(r->connection->client, B_WR), &fds)) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
+                    "client->fd was set");
+                if ((nbytes = recv(ap_bfileno(r->connection->client, B_WR),
+                    buffer, HUGE_STRING_LEN, 0)) != 0) {
+                    if (nbytes == -1)
+                        break;
+                    if (send(sock, buffer, nbytes, 0) == EOF)
+                        break;
+                    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO,
+                        r->server, "Wrote %d bytes to server", nbytes);
+                }
+                else
+                    break;
+            }
+            else
+                break;          /* Must be done waiting */
+        }
+        else
+            break;
     }
 
     ap_pclosesocket(r->pool, sock);
index 3c674a174b8cb013e1fb6f691a2bc8470e83931e..036ebc31bb28d05ff425f0bbf4faf77a63936844 100644 (file)
@@ -73,15 +73,15 @@ static int decodeenc(char *x)
     int i, j, ch;
 
     if (x[0] == '\0')
-       return 0;               /* special case for no characters */
+        return 0;                /* special case for no characters */
     for (i = 0, j = 0; x[i] != '\0'; i++, j++) {
 /* decode it if not already done */
-       ch = x[i];
-       if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
-           ch = ap_proxy_hex2c(&x[i + 1]);
-           i += 2;
-       }
-       x[j] = ch;
+        ch = x[i];
+        if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
+            ch = ap_proxy_hex2c(&x[i + 1]);
+            i += 2;
+        }
+        x[j] = ch;
     }
     x[j] = '\0';
     return j;
@@ -96,13 +96,13 @@ static int ftp_check_string(const char *x)
     int i, ch;
 
     for (i = 0; x[i] != '\0'; i++) {
-       ch = x[i];
-       if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
-           ch = ap_proxy_hex2c(&x[i + 1]);
-           i += 2;
-       }
-       if (ch == CR || ch == LF || (OS_ASC(ch) & 0x80))
-           return 0;
+        ch = x[i];
+        if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
+            ch = ap_proxy_hex2c(&x[i + 1]);
+            i += 2;
+        }
+        if (ch == CR || ch == LF || (OS_ASC(ch) & 0x80))
+            return 0;
     }
     return 1;
 }
@@ -120,11 +120,11 @@ int ap_proxy_ftp_canon(request_rec *r, char *url)
     port = DEFAULT_FTP_PORT;
     err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port);
     if (err)
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
     if (user != NULL && !ftp_check_string(user))
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
     if (password != NULL && !ftp_check_string(password))
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
 
 /* now parse path/parameters args, according to rfc1738 */
 /* N.B. if this isn't a true proxy request, then the URL path
@@ -134,49 +134,49 @@ int ap_proxy_ftp_canon(request_rec *r, char *url)
  */
     strp = strchr(url, ';');
     if (strp != NULL) {
-       *(strp++) = '\0';
-       parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm,
-                                 r->proxyreq);
-       if (parms == NULL)
-           return HTTP_BAD_REQUEST;
+        *(strp++) = '\0';
+        parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm,
+                                  r->proxyreq);
+        if (parms == NULL)
+            return HTTP_BAD_REQUEST;
     }
     else
-       parms = "";
+        parms = "";
 
     path = ap_proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq);
     if (path == NULL)
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
     if (!ftp_check_string(path))
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
 
     if (r->proxyreq == NOT_PROXY && r->args != NULL) {
-       if (strp != NULL) {
-           strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, STD_PROXY);
-           if (strp == NULL)
-               return HTTP_BAD_REQUEST;
-           parms = ap_pstrcat(p, parms, "?", strp, NULL);
-       }
-       else {
-           strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, STD_PROXY);
-           if (strp == NULL)
-               return HTTP_BAD_REQUEST;
-           path = ap_pstrcat(p, path, "?", strp, NULL);
-       }
-       r->args = NULL;
+        if (strp != NULL) {
+            strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, STD_PROXY);
+            if (strp == NULL)
+                return HTTP_BAD_REQUEST;
+            parms = ap_pstrcat(p, parms, "?", strp, NULL);
+        }
+        else {
+            strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, STD_PROXY);
+            if (strp == NULL)
+                return HTTP_BAD_REQUEST;
+            path = ap_pstrcat(p, path, "?", strp, NULL);
+        }
+        r->args = NULL;
     }
 
 /* now, rebuild URL */
 
     if (port != DEFAULT_FTP_PORT)
-       ap_snprintf(sport, sizeof(sport), ":%d", port);
+        ap_snprintf(sport, sizeof(sport), ":%d", port);
     else
-       sport[0] = '\0';
+        sport[0] = '\0';
 
     r->filename = ap_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "",
-                              (password != NULL) ? ":" : "",
-                              (password != NULL) ? password : "",
-                         (user != NULL) ? "@" : "", host, sport, "/", path,
-                              (parms[0] != '\0') ? ";" : "", parms, NULL);
+                               (password != NULL) ? ":" : "",
+                               (password != NULL) ? password : "",
+                          (user != NULL) ? "@" : "", host, sport, "/", path,
+                               (parms[0] != '\0') ? ";" : "", parms, NULL);
 
     return OK;
 }
@@ -192,30 +192,30 @@ static int ftp_getrc(BUFF *f)
 
     len = ap_bgets(linebuff, sizeof linebuff, f);
     if (len == -1)
-       return -1;
+        return -1;
 /* check format */
     if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
-       !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
-       status = 0;
+        !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
+        status = 0;
     else
-       status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
+        status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
 
     if (linebuff[len - 1] != '\n') {
-       (void)ap_bskiplf(f);
+        (void)ap_bskiplf(f);
     }
 
 /* skip continuation lines */
     if (linebuff[3] == '-') {
-       memcpy(buff, linebuff, 3);
-       buff[3] = ' ';
-       do {
-           len = ap_bgets(linebuff, sizeof linebuff, f);
-           if (len == -1)
-               return -1;
-           if (linebuff[len - 1] != '\n') {
-               (void)ap_bskiplf(f);
-           }
-       } while (memcmp(linebuff, buff, 4) != 0);
+        memcpy(buff, linebuff, 3);
+        buff[3] = ' ';
+        do {
+            len = ap_bgets(linebuff, sizeof linebuff, f);
+            if (len == -1)
+                return -1;
+            if (linebuff[len - 1] != '\n') {
+                (void)ap_bskiplf(f);
+            }
+        } while (memcmp(linebuff, buff, 4) != 0);
     }
 
     return status;
@@ -230,34 +230,34 @@ static int ftp_getrc_msg(BUFF *f, char *msgbuf, int msglen)
     int len, status;
     char linebuff[100], buff[5];
     char *mb = msgbuf,
-        *me = &msgbuf[msglen];
+         *me = &msgbuf[msglen];
 
     len = ap_bgets(linebuff, sizeof linebuff, f);
     if (len == -1)
-       return -1;
+        return -1;
     if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
-       !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
-       status = 0;
+        !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
+        status = 0;
     else
-       status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
+        status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
 
     mb = ap_cpystrn(mb, linebuff+4, me - mb);
 
     if (linebuff[len - 1] != '\n')
-       (void)ap_bskiplf(f);
+        (void)ap_bskiplf(f);
 
     if (linebuff[3] == '-') {
-       memcpy(buff, linebuff, 3);
-       buff[3] = ' ';
-       do {
-           len = ap_bgets(linebuff, sizeof linebuff, f);
-           if (len == -1)
-               return -1;
-           if (linebuff[len - 1] != '\n') {
-               (void)ap_bskiplf(f);
-           }
-           mb = ap_cpystrn(mb, linebuff+4, me - mb);
-       } while (memcmp(linebuff, buff, 4) != 0);
+        memcpy(buff, linebuff, 3);
+        buff[3] = ' ';
+        do {
+            len = ap_bgets(linebuff, sizeof linebuff, f);
+            if (len == -1)
+                return -1;
+            if (linebuff[len - 1] != '\n') {
+                (void)ap_bskiplf(f);
+            }
+            mb = ap_cpystrn(mb, linebuff+4, me - mb);
+        } while (memcmp(linebuff, buff, 4) != 0);
     }
     return status;
 }
@@ -284,118 +284,118 @@ static long int send_dir(BUFF *f, request_rec *r, cache_req *c, char *cwd)
     /* Copy path, strip (all except the last) trailing slashes */
     path = dir = ap_pstrcat(r->pool, path, "/", NULL);
     while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/')
-       path[n-1] = '\0';
+        path[n-1] = '\0';
 
     /* print "ftp://host/" */
     n = ap_snprintf(buf, sizeof(buf), DOCTYPE_HTML_3_2
-               "<HTML><HEAD><TITLE>%s%s</TITLE>\n"
-               "<BASE HREF=\"%s%s\"></HEAD>\n"
-               "<BODY><H2>Directory of "
-               "<A HREF=\"/\">%s</A>/",
-               site, path, site, path, site);
+                "<HTML><HEAD><TITLE>%s%s</TITLE>\n"
+                "<BASE HREF=\"%s%s\"></HEAD>\n"
+                "<BODY><H2>Directory of "
+                "<A HREF=\"/\">%s</A>/",
+                site, path, site, path, site);
     total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
 
     while ((dir = strchr(dir+1, '/')) != NULL)
     {
-       *dir = '\0';
-       if ((reldir = strrchr(path+1, '/'))==NULL)
-           reldir = path+1;
-       else
-           ++reldir;
-       /* print "path/" component */
-       ap_snprintf(buf, sizeof(buf), "<A HREF=\"/%s/\">%s</A>/", path+1, reldir);
-       total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
-       *dir = '/';
+        *dir = '\0';
+        if ((reldir = strrchr(path+1, '/'))==NULL)
+            reldir = path+1;
+        else
+            ++reldir;
+        /* print "path/" component */
+        ap_snprintf(buf, sizeof(buf), "<A HREF=\"/%s/\">%s</A>/", path+1, reldir);
+        total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
+        *dir = '/';
     }
     /* If the caller has determined the current directory, and it differs */
     /* from what the client requested, then show the real name */
     if (cwd == NULL || strncmp (cwd, path, strlen(cwd)) == 0) {
-       ap_snprintf(buf, sizeof(buf), "</H2>\n<HR><PRE>");
+        ap_snprintf(buf, sizeof(buf), "</H2>\n<HR><PRE>");
     } else {
-       ap_snprintf(buf, sizeof(buf), "</H2>\n(%s)\n<HR><PRE>", cwd);
+        ap_snprintf(buf, sizeof(buf), "</H2>\n(%s)\n<HR><PRE>", cwd);
     }
     total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
 
     while (!con->aborted) {
-       n = ap_bgets(buf, sizeof buf, f);
-       if (n == -1) {          /* input error */
-           if (c != NULL) {
-               ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
-                   "proxy: error reading from %s", c->url);
-               c = ap_proxy_cache_error(c);
-           }
-           break;
-       }
-       if (n == 0)
-           break;              /* EOF */
-       if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
-           char *link_ptr = filename;
-
-           do {
-               filename--;
-           } while (filename[0] != ' ');
-           *(filename++) = '\0';
-           *(link_ptr++) = '\0';
-           if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
-             link_ptr[n - 1] = '\0';
-           ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s %s</A>\n", buf, filename, filename, link_ptr);
-           ap_cpystrn(buf, buf2, sizeof(buf));
-           n = strlen(buf);
-       }
-       else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
-           if (ap_isdigit(buf[0])) {   /* handle DOS dir */
-               searchptr = strchr(buf, '<');
-               if (searchptr != NULL)
-                   *searchptr = '[';
-               searchptr = strchr(buf, '>');
-               if (searchptr != NULL)
-                   *searchptr = ']';
-           }
-
-           filename = strrchr(buf, ' ');
-           *(filename++) = 0;
-           filename[strlen(filename) - 1] = 0;
-
-           /* handle filenames with spaces in 'em */
-           if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
-               firstfile = 0;
-               searchidx = filename - buf;
-           }
-           else if (searchidx != 0 && buf[searchidx] != 0) {
-               *(--filename) = ' ';
-               buf[searchidx - 1] = 0;
-               filename = &buf[searchidx];
-           }
-
-           /* Special handling for '.' and '..' */
-           if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
-               ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s/\">%s</A>\n",
-                   buf, filename, filename);
-           }
-           else {
-               ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\n", buf, filename, filename);
-           }
-           ap_cpystrn(buf, buf2, sizeof(buf));
-           n = strlen(buf);
-       }
-
-       o = 0;
-       total_bytes_sent += n;
-
-       if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
-               "proxy: error writing to %s", c->tempfile);
-           c = ap_proxy_cache_error(c);
-       }
-
-       while (n && !r->connection->aborted) {
-           w = ap_bwrite(con->client, &buf[o], n);
-           if (w <= 0)
-               break;
-           ap_reset_timeout(r);        /* reset timeout after successfule write */
-           n -= w;
-           o += w;
-       }
+        n = ap_bgets(buf, sizeof buf, f);
+        if (n == -1) {          /* input error */
+            if (c != NULL) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+                    "proxy: error reading from %s", c->url);
+                c = ap_proxy_cache_error(c);
+            }
+            break;
+        }
+        if (n == 0)
+            break;                /* EOF */
+        if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
+            char *link_ptr = filename;
+
+            do {
+                filename--;
+            } while (filename[0] != ' ');
+            *(filename++) = '\0';
+            *(link_ptr++) = '\0';
+            if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
+              link_ptr[n - 1] = '\0';
+            ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s %s</A>\n", buf, filename, filename, link_ptr);
+            ap_cpystrn(buf, buf2, sizeof(buf));
+            n = strlen(buf);
+        }
+        else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
+            if (ap_isdigit(buf[0])) {   /* handle DOS dir */
+                searchptr = strchr(buf, '<');
+                if (searchptr != NULL)
+                    *searchptr = '[';
+                searchptr = strchr(buf, '>');
+                if (searchptr != NULL)
+                    *searchptr = ']';
+            }
+
+            filename = strrchr(buf, ' ');
+            *(filename++) = 0;
+            filename[strlen(filename) - 1] = 0;
+
+            /* handle filenames with spaces in 'em */
+            if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
+                firstfile = 0;
+                searchidx = filename - buf;
+            }
+            else if (searchidx != 0 && buf[searchidx] != 0) {
+                *(--filename) = ' ';
+                buf[searchidx - 1] = 0;
+                filename = &buf[searchidx];
+            }
+
+            /* Special handling for '.' and '..' */
+            if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
+                ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s/\">%s</A>\n",
+                    buf, filename, filename);
+            }
+            else {
+                ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\n", buf, filename, filename);
+            }
+            ap_cpystrn(buf, buf2, sizeof(buf));
+            n = strlen(buf);
+        }
+
+        o = 0;
+        total_bytes_sent += n;
+
+        if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+                "proxy: error writing to %s", c->tempfile);
+            c = ap_proxy_cache_error(c);
+        }
+
+        while (n && !r->connection->aborted) {
+            w = ap_bwrite(con->client, &buf[o], n);
+            if (w <= 0)
+                break;
+            ap_reset_timeout(r); /* reset timeout after successfule write */
+            n -= w;
+            o += w;
+        }
     }
 
     total_bytes_sent += ap_proxy_bputs2("</PRE><HR>\n", con->client, c);
@@ -421,16 +421,16 @@ static int ftp_unauthorized (request_rec *r, int log_it)
      * (log username/password guessing attempts)
      */
     if (log_it)
-       ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
-                     "proxy: missing or failed auth to %s",
-                     ap_unparse_uri_components(r->pool,
-                     &r->parsed_uri, UNP_OMITPATHINFO));
+        ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
+                      "proxy: missing or failed auth to %s",
+                      ap_unparse_uri_components(r->pool,
+                      &r->parsed_uri, UNP_OMITPATHINFO));
 
     ap_table_setn(r->err_headers_out, "WWW-Authenticate",
                   ap_pstrcat(r->pool, "Basic realm=\"",
-                 ap_unparse_uri_components(r->pool, &r->parsed_uri,
-                                           UNP_OMITPASSWORD|UNP_OMITPATHINFO),
-                 "\"", NULL));
+                  ap_unparse_uri_components(r->pool, &r->parsed_uri,
+                                            UNP_OMITPASSWORD|UNP_OMITPATHINFO),
+                  "\"", NULL));
 
     return HTTP_UNAUTHORIZED;
 }
@@ -459,9 +459,7 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     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 =
@@ -485,14 +483,14 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
 /* we only support GET and HEAD */
 
     if (r->method_number != M_GET)
-       return HTTP_NOT_IMPLEMENTED;
+        return HTTP_NOT_IMPLEMENTED;
 
 /* We break the URL into host, port, path-search */
 
     host = r->parsed_uri.hostname;
     port = (r->parsed_uri.port != 0)
-           ? r->parsed_uri.port
-           : ap_default_port_for_request(r);
+            ? r->parsed_uri.port
+            : ap_default_port_for_request(r);
     path = ap_pstrdup(p, r->parsed_uri.path);
     path = (path != NULL && path[0] != '\0') ? &path[1] : "";
 
@@ -505,33 +503,33 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
      * But chances are still smaller that the URL is logged regularly.
      */
     if ((password = ap_table_get(r->headers_in, "Authorization")) != NULL
-       && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0
-       && (password = ap_pbase64decode(r->pool, password))[0] != ':') {
-       /* Note that this allocation has to be made from r->connection->pool
-        * because it has the lifetime of the connection.  The other allocations
-        * are temporary and can be tossed away any time.
-        */
-       user = ap_getword_nulls (r->connection->pool, &password, ':');
-       r->connection->ap_auth_type = "Basic";
-       r->connection->user = r->parsed_uri.user = user;
-       nocache = 1;    /* This resource only accessible with username/password */
+        && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0
+        && (password = ap_pbase64decode(r->pool, password))[0] != ':') {
+        /* Note that this allocation has to be made from r->connection->pool
+         * because it has the lifetime of the connection.  The other allocations
+         * are temporary and can be tossed away any time.
+         */
+        user = ap_getword_nulls (r->connection->pool, &password, ':');
+        r->connection->ap_auth_type = "Basic";
+        r->connection->user = r->parsed_uri.user = user;
+        nocache = 1;    /* This resource only accessible with username/password */
     }
     else if ((user = r->parsed_uri.user) != NULL) {
-       user = ap_pstrdup(p, user);
-       decodeenc(user);
-       if ((password = r->parsed_uri.password) != NULL) {
-           char *tmp = ap_pstrdup(p, password);
-           decodeenc(tmp);
-           password = tmp;
-       }
-       nocache = 1;    /* This resource only accessible with username/password */
+        user = ap_pstrdup(p, user);
+        decodeenc(user);
+        if ((password = r->parsed_uri.password) != NULL) {
+            char *tmp = ap_pstrdup(p, password);
+            decodeenc(tmp);
+            password = tmp;
+        }
+        nocache = 1;    /* This resource only accessible with username/password */
     }
     else {
-       user = "anonymous";
-       password = "apache_proxy@";
+        user = "anonymous";
+        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 ||
@@ -545,71 +543,74 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
 
     parms = strchr(path, ';');
     if (parms != NULL)
-       *(parms++) = '\0';
+        *(parms++) = '\0';
 
     memset(&server, 0, sizeof(struct sockaddr_in));
     server.sin_family = AF_INET;
     server.sin_port = htons(port);
     err = ap_proxy_host2addr(host, &server_hp);
     if (err != NULL)
-       return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
+        return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
 
     sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (sock == -1) {
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                    "proxy: error creating socket");
-       return HTTP_INTERNAL_SERVER_ERROR;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                     "proxy: error creating socket");
+        return HTTP_INTERNAL_SERVER_ERROR;
     }
 
 #if !defined(TPF) && !defined(BEOS)
     if (conf->recv_buffer_size > 0
-       && setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
-                      (const char *) &conf->recv_buffer_size, sizeof(int))
-           == -1) {
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
+        && setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+                       (const char *) &conf->recv_buffer_size, sizeof(int))
+            == -1) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
     }
 #endif
 
     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
-                  sizeof(one)) == -1) {
+                   sizeof(one)) == -1) {
 #ifndef _OSD_POSIX /* BS2000 has this option "always on" */
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                    "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)");
-       ap_pclosesocket(p, sock);
-       return HTTP_INTERNAL_SERVER_ERROR;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                     "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)");
+        ap_pclosesocket(p, sock);
+        return HTTP_INTERNAL_SERVER_ERROR;
 #endif /*_OSD_POSIX*/
     }
 
 #ifdef SINIX_D_RESOLVER_BUG
     {
-       struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
-
-       for (; ip_addr->s_addr != 0; ++ip_addr) {
-           memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
-           i = ap_proxy_doconnect(sock, &server, r);
-           if (i == 0)
-               break;
-       }
+        struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
+
+        for (; ip_addr->s_addr != 0; ++ip_addr) {
+            memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
+            i = ap_proxy_doconnect(sock, &server, r);
+            if (i == 0)
+                break;
+        }
     }
 #else
     j = 0;
     while (server_hp.h_addr_list[j] != NULL) {
-       memcpy(&server.sin_addr, server_hp.h_addr_list[j],
-              sizeof(struct in_addr));
-       i = ap_proxy_doconnect(sock, &server, r);
-       if (i == 0)
-           break;
-       j++;
+        memcpy(&server.sin_addr, server_hp.h_addr_list[j],
+               sizeof(struct in_addr));
+        i = ap_proxy_doconnect(sock, &server, r);
+        if (i == 0)
+            break;
+        j++;
     }
 #endif
     if (i == -1) {
-       ap_pclosesocket(p, sock);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
-                               "Could not connect to remote machine: ",
-                               strerror(errno), NULL));
+        ap_pclosesocket(p, sock);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
+                                "Could not connect to remote machine: ",
+                                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? */
@@ -618,7 +619,7 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     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. */
@@ -626,40 +627,40 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     i = ftp_getrc_msg(f, resp, sizeof resp);
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
     if (i == -1) {
-       ap_kill_timeout(r);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                            "Error reading from remote server");
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
     }
 #if 0
     if (i == 120) {
-       ap_kill_timeout(r);
-       /* RFC2068 states:
-        * 14.38 Retry-After
-        * 
-        *  The Retry-After response-header field can be used with a 503 (Service
-        *  Unavailable) response to indicate how long the service is expected to
-        *  be unavailable to the requesting client. The value of this field can
-        *  be either an HTTP-date or an integer number of seconds (in decimal)
-        *  after the time of the response.
-        *     Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )
-        */
-       ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins);
-       return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp);
+        ap_kill_timeout(r);
+        /* RFC2068 states:
+         * 14.38 Retry-After
+         
+         *  The Retry-After response-header field can be used with a 503 (Service
+         *  Unavailable) response to indicate how long the service is expected to
+         *  be unavailable to the requesting client. The value of this field can
+         *  be either an HTTP-date or an integer number of seconds (in decimal)
+         *  after the time of the response.
+         *     Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )
+         */
+        ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins);
+        return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp);
     }
 #endif
     if (i != 220) {
-       ap_kill_timeout(r);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY, resp);
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY, resp);
     }
 
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: connected.");
 
     ap_bvputs(f, "USER ", user, CRLF, NULL);
-    ap_bflush(f);                      /* capture any errors */
+    ap_bflush(f);                        /* capture any errors */
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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. */
@@ -671,27 +672,27 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     i = ftp_getrc(f);
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
     if (i == -1) {
-       ap_kill_timeout(r);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                            "Error reading from remote server");
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
     }
     if (i == 530) {
-       ap_kill_timeout(r);
-       return ftp_unauthorized (r, 1); /* log it: user name guessing attempt? */
+        ap_kill_timeout(r);
+        return ftp_unauthorized (r, 1);        /* log it: user name guessing attempt? */
     }
     if (i != 230 && i != 331) {
-       ap_kill_timeout(r);
-       return HTTP_BAD_GATEWAY;
+        ap_kill_timeout(r);
+        return HTTP_BAD_GATEWAY;
     }
 
-    if (i == 331) {            /* send password */
-       if (password == NULL) {
-           return ftp_unauthorized (r, 0);
-       }
-       ap_bvputs(f, "PASS ", password, CRLF, NULL);
-       ap_bflush(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASS %s", password);
-/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
+    if (i == 331) {             /* send password */
+        if (password == NULL) {
+            return ftp_unauthorized (r, 0);
+        }
+        ap_bvputs(f, "PASS ", password, CRLF, NULL);
+        ap_bflush(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASS %s", password);
+    /* 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. */
@@ -699,27 +700,27 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     /* 501 Syntax error in parameters or arguments. */
     /* 503 Bad sequence of commands. */
     /* 530 Not logged in. */
-       i = ftp_getrc(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
-       if (i == -1) {
-           ap_kill_timeout(r);
-           return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                "Error reading from remote server");
-       }
-       if (i == 332) {
-           ap_kill_timeout(r);
-           return ap_proxyerror(r, HTTP_UNAUTHORIZED,
-                                "Need account for login");
-       }
-       /* @@@ questionable -- we might as well return a 403 Forbidden here */
-       if (i == 530) {
-           ap_kill_timeout(r);
-           return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */
-       }
-       if (i != 230 && i != 202) {
-           ap_kill_timeout(r);
-           return HTTP_BAD_GATEWAY;
-       }
+        i = ftp_getrc(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
+        if (i == -1) {
+            ap_kill_timeout(r);
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                 "Error reading from remote server");
+        }
+        if (i == 332) {
+            ap_kill_timeout(r);
+            return ap_proxyerror(r, HTTP_UNAUTHORIZED,
+                                 "Need account for login");
+        }
+        /* @@@ questionable -- we might as well return a 403 Forbidden here */
+        if (i == 530) {
+            ap_kill_timeout(r);
+            return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */
+        }
+        if (i != 230 && i != 202) {
+            ap_kill_timeout(r);
+            return HTTP_BAD_GATEWAY;
+        }
     }
 
 /* set the directory (walk directory component by component):
@@ -727,16 +728,16 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
  * machine
  */
     for (;;) {
-       strp = strchr(path, '/');
-       if (strp == NULL)
-           break;
-       *strp = '\0';
-
-       len = decodeenc(path);
-       ap_bvputs(f, "CWD ", path, CRLF, NULL);
-       ap_bflush(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
-       *strp = '/';
+        strp = strchr(path, '/');
+        if (strp == NULL)
+            break;
+        *strp = '\0';
+
+        len = decodeenc(path);
+        ap_bvputs(f, "CWD ", path, CRLF, NULL);
+        ap_bflush(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
+        *strp = '/';
 /* responses: 250, 421, 500, 501, 502, 530, 550 */
     /* 250 Requested file action okay, completed. */
     /* 421 Service not available, closing control connection. */
@@ -745,43 +746,43 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     /* 502 Command not implemented. */
     /* 530 Not logged in. */
     /* 550 Requested action not taken. */
-       i = ftp_getrc(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
-       if (i == -1) {
-           ap_kill_timeout(r);
-           return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                "Error reading from remote server");
-       }
-       if (i == 550) {
-           ap_kill_timeout(r);
-           return HTTP_NOT_FOUND;
-       }
-       if (i != 250) {
-           ap_kill_timeout(r);
-           return HTTP_BAD_GATEWAY;
-       }
-
-       path = strp + 1;
+        i = ftp_getrc(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
+        if (i == -1) {
+            ap_kill_timeout(r);
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                 "Error reading from remote server");
+        }
+        if (i == 550) {
+            ap_kill_timeout(r);
+            return HTTP_NOT_FOUND;
+        }
+        if (i != 250) {
+            ap_kill_timeout(r);
+            return HTTP_BAD_GATEWAY;
+        }
+
+        path = strp + 1;
     }
 
     if (parms != NULL && strncmp(parms, "type=", 5) == 0) {
-       parms += 5;
-       if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') ||
-           parms[1] != '\0')
-           parms = "";
+        parms += 5;
+        if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') ||
+            parms[1] != '\0')
+            parms = "";
     }
     else
-       parms = "";
+        parms = "";
 
     /* changed to make binary transfers the default */
 
     if (parms[0] != 'a') {
-       /* set type to image */
-       /* TM - Added CRLF to the end of TYPE I, otherwise it hangs the
-          connection */
-       ap_bputs("TYPE I" CRLF, f);
-       ap_bflush(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: TYPE I");
+        /* set type to image */
+        /* TM - Added CRLF to the end of TYPE I, otherwise it hangs the
+           connection */
+        ap_bputs("TYPE I" CRLF, f);
+        ap_bflush(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: TYPE I");
 /* responses: 200, 421, 500, 501, 504, 530 */
     /* 200 Command okay. */
     /* 421 Service not available, closing control connection. */
@@ -789,46 +790,46 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     /* 501 Syntax error in parameters or arguments. */
     /* 504 Command not implemented for that parameter. */
     /* 530 Not logged in. */
-       i = ftp_getrc(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
-       if (i == -1) {
-           ap_kill_timeout(r);
-           return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                "Error reading from remote server");
-       }
-       if (i != 200 && i != 504) {
-           ap_kill_timeout(r);
-           return HTTP_BAD_GATEWAY;
-       }
+        i = ftp_getrc(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
+        if (i == -1) {
+            ap_kill_timeout(r);
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                 "Error reading from remote server");
+        }
+        if (i != 200 && i != 504) {
+            ap_kill_timeout(r);
+            return HTTP_BAD_GATEWAY;
+        }
 /* Allow not implemented */
-       if (i == 504)
-           parms[0] = '\0';
+        if (i == 504)
+            parms[0] = '\0';
     }
 
 /* try to set up PASV data connection first */
     dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (dsock == -1) {
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                    "proxy: error creating PASV socket");
-       ap_bclose(f);
-       ap_kill_timeout(r);
-       return HTTP_INTERNAL_SERVER_ERROR;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                     "proxy: error creating PASV socket");
+        ap_bclose(f);
+        ap_kill_timeout(r);
+        return HTTP_INTERNAL_SERVER_ERROR;
     }
 
 #if !defined (TPF) && !defined(BEOS)
     if (conf->recv_buffer_size) {
-       if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF,
-              (const char *) &conf->recv_buffer_size, sizeof(int)) == -1) {
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
-       }
+        if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF,
+               (const char *) &conf->recv_buffer_size, sizeof(int)) == -1) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
+        }
     }
 #endif
 
     ap_bputs("PASV" CRLF, f);
     ap_bflush(f);
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASV command issued");
-    /* possible results: 227, 421, 500, 501, 502, 530 */
+/* possible results: 227, 421, 500, 501, 502, 530 */
     /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
     /* 421 Service not available, closing control connection. */
     /* 500 Syntax error, command unrecognized. */
@@ -837,102 +838,102 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     /* 530 Not logged in. */
     i = ap_bgets(pasv, sizeof(pasv), f);
     if (i == -1) {
-       ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
-                    "PASV: control connection is toast");
-       ap_pclosesocket(p, dsock);
-       ap_bclose(f);
-       ap_kill_timeout(r);
-       return HTTP_INTERNAL_SERVER_ERROR;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
+                     "PASV: control connection is toast");
+        ap_pclosesocket(p, dsock);
+        ap_bclose(f);
+        ap_kill_timeout(r);
+        return HTTP_INTERNAL_SERVER_ERROR;
     }
     else {
-       pasv[i - 1] = '\0';
-       pstr = strtok(pasv, " ");       /* separate result code */
-       if (pstr != NULL) {
-           presult = atoi(pstr);
-           if (*(pstr + strlen(pstr) + 1) == '=')
-               pstr += strlen(pstr) + 2;
-           else
-           {
-               pstr = strtok(NULL, "(");  /* separate address & port params */
-               if (pstr != NULL)
-                   pstr = strtok(NULL, ")");
-           }
-       }
-       else
-           presult = atoi(pasv);
-
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", presult);
-
-       if (presult == 227 && pstr != NULL && (sscanf(pstr,
-                "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) {
-           /* pardon the parens, but it makes gcc happy */
-           paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0;
-           pport = (p1 << 8) + p0;
-           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: contacting host %d.%d.%d.%d:%d",
-                    h3, h2, h1, h0, pport);
-           data_addr.sin_family = AF_INET;
-           data_addr.sin_addr.s_addr = htonl(paddr);
-           data_addr.sin_port = htons(pport);
-           i = ap_proxy_doconnect(dsock, &data_addr, r);
-
-           if (i == -1) {
-               ap_kill_timeout(r);
-               return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                    ap_pstrcat(r->pool,
-                                               "Could not connect to remote machine: ",
-                                               strerror(errno), NULL));
-           }
-           else {
-               pasvmode = 1;
-           }
-       }
-       else
-           ap_pclosesocket(p, dsock);  /* and try the regular way */
+        pasv[i - 1] = '\0';
+        pstr = strtok(pasv, " ");        /* separate result code */
+        if (pstr != NULL) {
+            presult = atoi(pstr);
+            if (*(pstr + strlen(pstr) + 1) == '=')
+                pstr += strlen(pstr) + 2;
+            else
+            {
+                pstr = strtok(NULL, "(");  /* separate address & port params */
+                if (pstr != NULL)
+                    pstr = strtok(NULL, ")");
+            }
+        }
+        else
+            presult = atoi(pasv);
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", presult);
+
+        if (presult == 227 && pstr != NULL && (sscanf(pstr,
+                 "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) {
+            /* pardon the parens, but it makes gcc happy */
+            paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0;
+            pport = (p1 << 8) + p0;
+            ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: contacting host %d.%d.%d.%d:%d",
+                     h3, h2, h1, h0, pport);
+            data_addr.sin_family = AF_INET;
+            data_addr.sin_addr.s_addr = htonl(paddr);
+            data_addr.sin_port = htons(pport);
+            i = ap_proxy_doconnect(dsock, &data_addr, r);
+
+            if (i == -1) {
+                ap_kill_timeout(r);
+                return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                     ap_pstrcat(r->pool,
+                                                "Could not connect to remote machine: ",
+                                                strerror(errno), NULL));
+            }
+            else {
+                pasvmode = 1;
+            }
+        }
+        else
+            ap_pclosesocket(p, dsock);  /* and try the regular way */
     }
 
-    if (!pasvmode) {           /* set up data connection */
-       clen = sizeof(struct sockaddr_in);
-       if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) {
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "proxy: error getting socket address");
-           ap_bclose(f);
-           ap_kill_timeout(r);
-           return HTTP_INTERNAL_SERVER_ERROR;
-       }
-
-       dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
-       if (dsock == -1) {
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "proxy: error creating socket");
-           ap_bclose(f);
-           ap_kill_timeout(r);
-           return HTTP_INTERNAL_SERVER_ERROR;
-       }
-
-       if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
-                      sizeof(one)) == -1) {
+    if (!pasvmode) {            /* set up data connection */
+        clen = sizeof(struct sockaddr_in);
+        if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: error getting socket address");
+            ap_bclose(f);
+            ap_kill_timeout(r);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
+        if (dsock == -1) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: error creating socket");
+            ap_bclose(f);
+            ap_kill_timeout(r);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
+                       sizeof(one)) == -1) {
 #ifndef _OSD_POSIX /* BS2000 has this option "always on" */
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "proxy: error setting reuseaddr option");
-           ap_pclosesocket(p, dsock);
-           ap_bclose(f);
-           ap_kill_timeout(r);
-           return HTTP_INTERNAL_SERVER_ERROR;
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: error setting reuseaddr option");
+            ap_pclosesocket(p, dsock);
+            ap_bclose(f);
+            ap_kill_timeout(r);
+            return HTTP_INTERNAL_SERVER_ERROR;
 #endif /*_OSD_POSIX*/
-       }
-
-       if (bind(dsock, (struct sockaddr *) &server,
-                sizeof(struct sockaddr_in)) == -1) {
-           char buff[22];
-
-           ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "proxy: error binding to ftp data socket %s", buff);
-           ap_bclose(f);
-           ap_pclosesocket(p, dsock);
-           return HTTP_INTERNAL_SERVER_ERROR;
-       }
-       listen(dsock, 2);       /* only need a short queue */
+        }
+
+        if (bind(dsock, (struct sockaddr *) &server,
+                 sizeof(struct sockaddr_in)) == -1) {
+            char buff[22];
+
+            ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: error binding to ftp data socket %s", buff);
+            ap_bclose(f);
+            ap_pclosesocket(p, dsock);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        listen(dsock, 2);        /* only need a short queue */
     }
 
 /* set request; "path" holds last path component */
@@ -941,55 +942,55 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */
 
     if (len == 0) {
-       parms = "d";
+        parms = "d";
     }
     else {
-       ap_bvputs(f, "SIZE ", path, CRLF, NULL);
-       ap_bflush(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE %s", path);
-       i = ftp_getrc_msg(f, resp, sizeof resp);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d with response %s", i, resp);
-       if (i != 500) {         /* Size command not recognized */
-           if (i == 550) {     /* Not a regular file */
-               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE shows this is a directory");
-               parms = "d";
-               ap_bvputs(f, "CWD ", path, CRLF, NULL);
-               ap_bflush(f);
-               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
-               i = ftp_getrc(f);
-               /* possible results: 250, 421, 500, 501, 502, 530, 550 */
-               /* 250 Requested file action okay, completed. */
-               /* 421 Service not available, closing control connection. */
-               /* 500 Syntax error, command unrecognized. */
-               /* 501 Syntax error in parameters or arguments. */
-               /* 502 Command not implemented. */
-               /* 530 Not logged in. */
-               /* 550 Requested action not taken. */
-               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
-               if (i == -1) {
-                   ap_kill_timeout(r);
-                   return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                        "Error reading from remote server");
-               }
-               if (i == 550) {
-                   ap_kill_timeout(r);
-                   return HTTP_NOT_FOUND;
-               }
-               if (i != 250) {
-                   ap_kill_timeout(r);
-                   return HTTP_BAD_GATEWAY;
-               }
-               path = "";
-               len = 0;
-           }
-           else if (i == 213) { /* Size command ok */
-               for (j = 0; j < sizeof resp && ap_isdigit(resp[j]); j++)
-                       ;
-               resp[j] = '\0';
-               if (resp[0] != '\0')
-                   size = ap_pstrdup(p, resp);
-           }
-       }
+        ap_bvputs(f, "SIZE ", path, CRLF, NULL);
+        ap_bflush(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE %s", path);
+        i = ftp_getrc_msg(f, resp, sizeof resp);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d with response %s", i, resp);
+        if (i != 500) {         /* Size command not recognized */
+            if (i == 550) {     /* Not a regular file */
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE shows this is a directory");
+                parms = "d";
+                ap_bvputs(f, "CWD ", path, CRLF, NULL);
+                ap_bflush(f);
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
+                i = ftp_getrc(f);
+                /* possible results: 250, 421, 500, 501, 502, 530, 550 */
+                /* 250 Requested file action okay, completed. */
+                /* 421 Service not available, closing control connection. */
+                /* 500 Syntax error, command unrecognized. */
+                /* 501 Syntax error in parameters or arguments. */
+                /* 502 Command not implemented. */
+                /* 530 Not logged in. */
+                /* 550 Requested action not taken. */
+                ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
+                if (i == -1) {
+                    ap_kill_timeout(r);
+                    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                         "Error reading from remote server");
+                }
+                if (i == 550) {
+                    ap_kill_timeout(r);
+                    return HTTP_NOT_FOUND;
+                }
+                if (i != 250) {
+                    ap_kill_timeout(r);
+                    return HTTP_BAD_GATEWAY;
+                }
+                path = "";
+                len = 0;
+            }
+            else if (i == 213) { /* Size command ok */
+                for (j = 0; j < sizeof resp && ap_isdigit(resp[j]); j++)
+                        ;
+                resp[j] = '\0';
+                if (resp[0] != '\0')
+                    size = ap_pstrdup(p, resp);
+            }
+        }
     }
 
 #ifdef AUTODETECT_PWD
@@ -1006,30 +1007,30 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     i = ftp_getrc_msg(f, resp, sizeof resp);
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i);
     if (i == -1 || i == 421) {
-       ap_kill_timeout(r);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                            "Error reading from remote server");
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
     }
     if (i == 550) {
-       ap_kill_timeout(r);
-       return HTTP_NOT_FOUND;
+        ap_kill_timeout(r);
+        return HTTP_NOT_FOUND;
     }
     if (i == 257) {
-       const char *dirp = resp;
-       cwd = ap_getword_conf(r->pool, &dirp);
+        const char *dirp = resp;
+        cwd = ap_getword_conf(r->pool, &dirp);
     }
 #endif /*AUTODETECT_PWD*/
 
     if (parms[0] == 'd') {
-       if (len != 0)
-           ap_bvputs(f, "LIST ", path, CRLF, NULL);
-       else
-           ap_bputs("LIST -lag" CRLF, f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST %s", (len == 0 ? "" : path));
+        if (len != 0)
+            ap_bvputs(f, "LIST ", path, CRLF, NULL);
+        else
+            ap_bputs("LIST -lag" CRLF, f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST %s", (len == 0 ? "" : path));
     }
     else {
-       ap_bvputs(f, "RETR ", path, CRLF, NULL);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR %s", path);
+        ap_bvputs(f, "RETR ", path, CRLF, NULL);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR %s", path);
     }
     ap_bflush(f);
 /* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550
@@ -1051,80 +1052,80 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     rc = ftp_getrc(f);
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
     if (rc == -1) {
-       ap_kill_timeout(r);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                            "Error reading from remote server");
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
     }
     if (rc == 550) {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR failed, trying LIST instead");
-       parms = "d";
-       ap_bvputs(f, "CWD ", path, CRLF, NULL);
-       ap_bflush(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
-       /* possible results: 250, 421, 500, 501, 502, 530, 550 */
-       /* 250 Requested file action okay, completed. */
-       /* 421 Service not available, closing control connection. */
-       /* 500 Syntax error, command unrecognized. */
-       /* 501 Syntax error in parameters or arguments. */
-       /* 502 Command not implemented. */
-       /* 530 Not logged in. */
-       /* 550 Requested action not taken. */
-       rc = ftp_getrc(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
-       if (rc == -1) {
-           ap_kill_timeout(r);
-           return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                "Error reading from remote server");
-       }
-       if (rc == 550) {
-           ap_kill_timeout(r);
-           return HTTP_NOT_FOUND;
-       }
-       if (rc != 250) {
-           ap_kill_timeout(r);
-           return HTTP_BAD_GATEWAY;
-       }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR failed, trying LIST instead");
+        parms = "d";
+        ap_bvputs(f, "CWD ", path, CRLF, NULL);
+        ap_bflush(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
+        /* possible results: 250, 421, 500, 501, 502, 530, 550 */
+        /* 250 Requested file action okay, completed. */
+        /* 421 Service not available, closing control connection. */
+        /* 500 Syntax error, command unrecognized. */
+        /* 501 Syntax error in parameters or arguments. */
+        /* 502 Command not implemented. */
+        /* 530 Not logged in. */
+        /* 550 Requested action not taken. */
+        rc = ftp_getrc(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
+        if (rc == -1) {
+            ap_kill_timeout(r);
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                 "Error reading from remote server");
+        }
+        if (rc == 550) {
+            ap_kill_timeout(r);
+            return HTTP_NOT_FOUND;
+        }
+        if (rc != 250) {
+            ap_kill_timeout(r);
+            return HTTP_BAD_GATEWAY;
+        }
 
 #ifdef AUTODETECT_PWD
-       ap_bvputs(f, "PWD", CRLF, NULL);
-       ap_bflush(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD");
+        ap_bvputs(f, "PWD", CRLF, NULL);
+        ap_bflush(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD");
 /* responses: 257, 500, 501, 502, 421, 550 */
-       /* 257 "<directory-name>" <commentary> */
-       /* 421 Service not available, closing control connection. */
-       /* 500 Syntax error, command unrecognized. */
-       /* 501 Syntax error in parameters or arguments. */
-       /* 502 Command not implemented. */
-       /* 550 Requested action not taken. */
-       i = ftp_getrc_msg(f, resp, sizeof resp);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i);
-       if (i == -1 || i == 421) {
-           ap_kill_timeout(r);
-           return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                "Error reading from remote server");
-       }
-       if (i == 550) {
-           ap_kill_timeout(r);
-           return HTTP_NOT_FOUND;
-       }
-       if (i == 257) {
-           const char *dirp = resp;
-           cwd = ap_getword_conf(r->pool, &dirp);
-       }
+        /* 257 "<directory-name>" <commentary> */
+        /* 421 Service not available, closing control connection. */
+        /* 500 Syntax error, command unrecognized. */
+        /* 501 Syntax error in parameters or arguments. */
+        /* 502 Command not implemented. */
+        /* 550 Requested action not taken. */
+        i = ftp_getrc_msg(f, resp, sizeof resp);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i);
+        if (i == -1 || i == 421) {
+            ap_kill_timeout(r);
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                 "Error reading from remote server");
+        }
+        if (i == 550) {
+            ap_kill_timeout(r);
+            return HTTP_NOT_FOUND;
+        }
+        if (i == 257) {
+            const char *dirp = resp;
+            cwd = ap_getword_conf(r->pool, &dirp);
+        }
 #endif /*AUTODETECT_PWD*/
 
-       ap_bputs("LIST -lag" CRLF, f);
-       ap_bflush(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST -lag");
-       rc = ftp_getrc(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
-       if (rc == -1)
-           return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                                "Error reading from remote server");
+        ap_bputs("LIST -lag" CRLF, f);
+        ap_bflush(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST -lag");
+        rc = ftp_getrc(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
+        if (rc == -1)
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                 "Error reading from remote server");
     }
     ap_kill_timeout(r);
     if (rc != 125 && rc != 150 && rc != 226 && rc != 250)
-       return HTTP_BAD_GATEWAY;
+        return HTTP_BAD_GATEWAY;
 
     r->status = HTTP_OK;
     r->status_line = "200 OK";
@@ -1136,135 +1137,125 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     ap_table_setn(resp_hdrs, "Server", ap_get_server_version());
 
     if (parms[0] == 'd') {
-       ap_table_setn(resp_hdrs, "Content-Type", "text/html");
+        ap_table_setn(resp_hdrs, "Content-Type", "text/html");
 #ifdef CHARSET_EBCDIC
-       r->ebcdic.conv_out = 1; /* server-generated */
+        r->ebcdic.conv_out = 1; /* server-generated */
 #endif
     }
     else {
 #ifdef CHARSET_EBCDIC
-       r->ebcdic.conv_out = 0; /* do not convert what we read from the ftp server */
+        r->ebcdic.conv_out = 0; /* do not convert what we read from the ftp server */
 #endif
-       if (r->content_type != NULL) {
-           ap_table_setn(resp_hdrs, "Content-Type", r->content_type);
-           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Type set to %s", r->content_type);
-       }
-       else {
-           ap_table_setn(resp_hdrs, "Content-Type", ap_default_type(r));
-       }
-       if (parms[0] != 'a' && size != NULL) {
-           /* We "trust" the ftp server to really serve (size) bytes... */
-           ap_table_set(resp_hdrs, "Content-Length", size);
-           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Length set to %s", size);
-       }
+        if (r->content_type != NULL) {
+            ap_table_setn(resp_hdrs, "Content-Type", r->content_type);
+            ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Type set to %s", r->content_type);
+        }
+        else {
+            ap_table_setn(resp_hdrs, "Content-Type", ap_default_type(r));
+        }
+        if (parms[0] != 'a' && size != NULL) {
+            /* We "trust" the ftp server to really serve (size) bytes... */
+            ap_table_set(resp_hdrs, "Content-Length", size);
+            ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Length set to %s", size);
+        }
     }
     if (r->content_encoding != NULL && r->content_encoding[0] != '\0') {
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Encoding set to %s", r->content_encoding);
-       ap_table_setn(resp_hdrs, "Content-Encoding", r->content_encoding);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Encoding set to %s", r->content_encoding);
+        ap_table_setn(resp_hdrs, "Content-Encoding", r->content_encoding);
     }
 
 /* check if NoCache directive on this host */
     if (nocache == 0) {
-       for (i = 0; i < conf->nocaches->nelts; i++) {
-           if (destaddr.s_addr == ncent[i].addr.s_addr ||
-               (ncent[i].name != NULL &&
-                 (ncent[i].name[0] == '*' ||
-                  strstr(host, ncent[i].name) != NULL))) {
-              nocache = 1;
-              break;
-           }
-       }
+        for (i = 0; i < conf->nocaches->nelts; i++) {
+            if (destaddr.s_addr == ncent[i].addr.s_addr ||
+                (ncent[i].name != NULL &&
+                  (ncent[i].name[0] == '*' ||
+                   strstr(host, ncent[i].name) != NULL))) {
+               nocache = 1;
+               break;
+            }
+        }
     }
 
     i = ap_proxy_cache_update(c, resp_hdrs, 0, nocache);
 
     if (i != DECLINED) {
-       ap_pclosesocket(p, dsock);
-       ap_bclose(f);
-       return i;
+        ap_pclosesocket(p, dsock);
+        ap_bclose(f);
+        return i;
     }
 
-    if (!pasvmode) {           /* wait for connection */
-       ap_hard_timeout("proxy ftp data connect", r);
-       clen = sizeof(struct sockaddr_in);
-       do
-           csd = accept(dsock, (struct sockaddr *) &server, &clen);
-       while (csd == -1 && errno == EINTR);
-       if (csd == -1) {
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "proxy: failed to accept data connection");
-           ap_pclosesocket(p, dsock);
-           ap_bclose(f);
-           ap_kill_timeout(r);
-           if (c != NULL)
-               c = ap_proxy_cache_error(c);
-           return HTTP_BAD_GATEWAY;
-       }
-       ap_note_cleanups_for_socket(p, csd);
-       data = ap_bcreate(p, B_RDWR | B_SOCKET);
-       ap_bpushfd(data, csd, -1);
-       ap_kill_timeout(r);
+    if (!pasvmode) {            /* wait for connection */
+        ap_hard_timeout("proxy ftp data connect", r);
+        clen = sizeof(struct sockaddr_in);
+        do
+            csd = accept(dsock, (struct sockaddr *) &server, &clen);
+        while (csd == -1 && errno == EINTR);
+        if (csd == -1) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "proxy: failed to accept data connection");
+            ap_pclosesocket(p, dsock);
+            ap_bclose(f);
+            ap_kill_timeout(r);
+            if (c != NULL)
+                c = ap_proxy_cache_error(c);
+            return HTTP_BAD_GATEWAY;
+        }
+        ap_note_cleanups_for_socket(p, csd);
+        data = ap_bcreate(p, B_RDWR | B_SOCKET);
+        ap_bpushfd(data, csd, -1);
+        ap_kill_timeout(r);
     }
     else {
-       data = ap_bcreate(p, B_RDWR | B_SOCKET);
-       ap_bpushfd(data, dsock, dsock);
+        data = ap_bcreate(p, B_RDWR | B_SOCKET);
+        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);
-    }
 
-/* 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);
-    }
+    /* 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_bsetopt(r->connection->client, BO_BYTECT, &zero);
-    r->sent_bodyct = 1;
 #ifdef CHARSET_EBCDIC
     ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out);
 #endif
 /* send body */
     if (!r->header_only) {
-       if (parms[0] != 'd') {
+        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);
-       } else
-           send_dir(data, r, c, cwd);
+            if (c != NULL)
+                c->cache_completion = 0;
+            ap_proxy_send_fb(data, r, c, -1, 0);
+        } else
+            send_dir(data, r, c, cwd);
 
-       if (rc == 125 || rc == 150)
-           rc = ftp_getrc(f);
+        if (rc == 125 || rc == 150)
+            rc = ftp_getrc(f);
 
-       /* XXX: we checked for 125||150||226||250 above. This is redundant. */
-       if (rc != 226 && rc != 250)
+        /* XXX: we checked for 125||150||226||250 above. This is redundant. */
+        if (rc != 226 && rc != 250)
             /* XXX: we no longer log an "error writing to c->tempfile" - should we? */
-           c = ap_proxy_cache_error(c);
+            c = ap_proxy_cache_error(c);
     }
     else {
 /* abort the transfer */
-       ap_bputs("ABOR" CRLF, f);
-       ap_bflush(f);
-       if (!pasvmode)
-           ap_bclose(data);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: ABOR");
+        ap_bputs("ABOR" CRLF, f);
+        ap_bflush(f);
+        if (!pasvmode)
+            ap_bclose(data);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: ABOR");
 /* responses: 225, 226, 421, 500, 501, 502 */
     /* 225 Data connection open; no transfer in progress. */
     /* 226 Closing data connection. */
@@ -1272,8 +1263,8 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     /* 500 Syntax error, command unrecognized. */
     /* 501 Syntax error in parameters or arguments. */
     /* 502 Command not implemented. */
-       i = ftp_getrc(f);
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
+        i = ftp_getrc(f);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
     }
 
     ap_kill_timeout(r);
@@ -1290,10 +1281,10 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: QUIT: status %d", i);
 
     if (pasvmode)
-       ap_bclose(data);
+        ap_bclose(data);
     ap_bclose(f);
 
-    ap_rflush(r);      /* flush before garbage collection */
+    ap_rflush(r);        /* flush before garbage collection */
 
     ap_proxy_garbage_coll(r);
 
index 8136d640a67ed75e791f2da882d9544a10a59483..2d339595f3dce2384fd55c11943de025c1cee8ee 100644 (file)
@@ -76,43 +76,44 @@ int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_p
     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;
+        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)
-           *(search++) = '\0';
+        search = strchr(url, '?');
+        if (search != NULL)
+            *(search++) = '\0';
     }
     else
-       search = r->args;
+        search = r->args;
 
-/* process path */
+    /* process path */
     path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path,
-                            r->proxyreq);
+                             r->proxyreq);
     if (path == NULL)
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
 
     if (port != def_port)
-       ap_snprintf(sport, sizeof(sport), ":%d", port);
+        ap_snprintf(sport, sizeof(sport), ":%d", port);
     else
-       sport[0] = '\0';
+        sport[0] = '\0';
 
     r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
-                  path, (search) ? "?" : "", (search) ? search : "", NULL);
+                   path, (search) ? "?" : "", (search) ? search : "", NULL);
     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 @@ static const char *proxy_location_reverse_map(request_rec *r, const char *url)
     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
@@ -168,15 +146,15 @@ static void clear_connection(pool *p, table *headers)
  * route.)
  */
 int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
-                      const char *proxyhost, int proxyport)
+                       const char *proxyhost, int proxyport)
 {
     const char *strp;
     char *strp2;
-    const char *err, *desthost, *pragma;
+    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 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
     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,39 +174,41 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
     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)
-       return HTTP_BAD_REQUEST;
+        return HTTP_BAD_REQUEST;
     urlptr += 3;
     destport = DEFAULT_HTTP_PORT;
     strp = strchr(urlptr, '/');
     if (strp == NULL) {
-       desthost = ap_pstrdup(p, urlptr);
-       urlptr = "/";
+        desthost = ap_pstrdup(p, urlptr);
+        urlptr = "/";
     }
     else {
-       char *q = ap_palloc(p, strp - urlptr + 1);
-       memcpy(q, urlptr, strp - urlptr);
-       q[strp - urlptr] = '\0';
-       urlptr = strp;
-       desthost = q;
+        char *q = ap_palloc(p, strp - urlptr + 1);
+        memcpy(q, urlptr, strp - urlptr);
+        q[strp - urlptr] = '\0';
+        urlptr = strp;
+        desthost = q;
     }
 
     strp2 = strchr(desthost, ':');
     if (strp2 != NULL) {
-       *(strp2++) = '\0';
-       if (ap_isdigit(*strp2)) {
-           destport = atoi(strp2);
-           destportstr = strp2;
-       }
+        *(strp2++) = '\0';
+        if (ap_isdigit(*strp2)) {
+            destport = atoi(strp2);
+            destportstr = strp2;
+        }
     }
 
-/* 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 ||
@@ -241,304 +219,339 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
     }
 
     if (proxyhost != NULL) {
-       server.sin_port = htons(proxyport);
-       err = ap_proxy_host2addr(proxyhost, &server_hp);
-       if (err != NULL)
-           return DECLINED;    /* try another */
+        server.sin_port = htons(proxyport);
+        err = ap_proxy_host2addr(proxyhost, &server_hp);
+        if (err != NULL)
+            return DECLINED;    /* try another */
     }
     else {
-       server.sin_port = htons(destport);
-       err = ap_proxy_host2addr(desthost, &server_hp);
-       if (err != NULL)
-           return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
+        server.sin_port = htons(destport);
+        err = ap_proxy_host2addr(desthost, &server_hp);
+        if (err != NULL)
+            return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
     }
 
+
+    /* we have worked out who exactly we are going to connect to, now
+     * make that connection...
+     */
     sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (sock == -1) {
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                   "proxy: error creating socket");
-       return HTTP_INTERNAL_SERVER_ERROR;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                    "proxy: error creating socket");
+        return HTTP_INTERNAL_SERVER_ERROR;
     }
 
 #if !defined(TPF) && !defined(BEOS)
     if (conf->recv_buffer_size) {
-       if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
-                      (const char *) &conf->recv_buffer_size, sizeof(int))
-           == -1) {
-           ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                        "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
-       }
+        if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+                       (const char *) &conf->recv_buffer_size, sizeof(int))
+            == -1) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                         "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
+        }
     }
 #endif
 
 #ifdef SINIX_D_RESOLVER_BUG
     {
-       struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
-
-       for (; ip_addr->s_addr != 0; ++ip_addr) {
-           memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
-           i = ap_proxy_doconnect(sock, &server, r);
-           if (i == 0)
-               break;
-       }
+        struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
+
+        for (; ip_addr->s_addr != 0; ++ip_addr) {
+            memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
+            i = ap_proxy_doconnect(sock, &server, r);
+            if (i == 0)
+                break;
+        }
     }
 #else
     j = 0;
     while (server_hp.h_addr_list[j] != NULL) {
-       memcpy(&server.sin_addr, server_hp.h_addr_list[j],
-              sizeof(struct in_addr));
-       i = ap_proxy_doconnect(sock, &server, r);
-       if (i == 0)
-           break;
-       j++;
+        memcpy(&server.sin_addr, server_hp.h_addr_list[j],
+               sizeof(struct in_addr));
+        i = ap_proxy_doconnect(sock, &server, r);
+        if (i == 0)
+            break;
+        j++;
     }
 #endif
     if (i == -1) {
-       if (proxyhost != NULL)
-           return DECLINED;    /* try again another way */
-       else
-           return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
-                               "Could not connect to remote machine: ",
-                               strerror(errno), NULL));
+        if (proxyhost != NULL)
+            return DECLINED;    /* try again another way */
+        else
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
+                                "Could not connect to remote machine: ",
+                                strerror(errno), NULL));
     }
 
-    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,
-          NULL);
+           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);
+        ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL);
     else
-       ap_bvputs(f, "Host: ", desthost, CRLF, NULL);
+        ap_bvputs(f, "Host: ", desthost, CRLF, NULL);
 
     if (conf->viaopt == via_block) {
-       /* Block all outgoing Via: headers */
-       ap_table_unset(r->headers_in, "Via");
+        /* Block all outgoing Via: headers */
+        ap_table_unset(req_hdrs, "Via");
     } else if (conf->viaopt != via_off) {
-       /* Create a "Via:" request header entry and merge it */
-       i = ap_get_server_port(r);
-       if (ap_is_default_port(i,r)) {
-           strcpy(portstr,"");
-       } else {
-           ap_snprintf(portstr, sizeof portstr, ":%d", i);
-       }
-       /* Generate outgoing Via: header with/without server comment: */
-       ap_table_mergen(r->headers_in, "Via",
-                   (conf->viaopt == via_full)
-                       ? ap_psprintf(p, "%d.%d %s%s (%s)",
-                               HTTP_VERSION_MAJOR(r->proto_num),
-                               HTTP_VERSION_MINOR(r->proto_num),
-                               ap_get_server_name(r), portstr,
-                               SERVER_BASEVERSION)
-                       : ap_psprintf(p, "%d.%d %s%s",
-                               HTTP_VERSION_MAJOR(r->proto_num),
-                               HTTP_VERSION_MINOR(r->proto_num),
-                               ap_get_server_name(r), portstr)
-                       );
+        /* Create a "Via:" request header entry and merge it */
+        i = ap_get_server_port(r);
+        if (ap_is_default_port(i,r)) {
+            strcpy(portstr,"");
+        } else {
+            ap_snprintf(portstr, sizeof portstr, ":%d", i);
+        }
+        /* Generate outgoing Via: header with/without server comment: */
+        ap_table_mergen(req_hdrs, "Via",
+                    (conf->viaopt == via_full)
+                        ? ap_psprintf(p, "%d.%d %s%s (%s)",
+                                HTTP_VERSION_MAJOR(r->proto_num),
+                                HTTP_VERSION_MINOR(r->proto_num),
+                                ap_get_server_name(r), portstr,
+                                SERVER_BASEVERSION)
+                        : ap_psprintf(p, "%d.%d %s%s",
+                                HTTP_VERSION_MAJOR(r->proto_num),
+                                HTTP_VERSION_MINOR(r->proto_num),
+                                ap_get_server_name(r), portstr)
+                        );
     }
 
-    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 */
-           /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
-            * suppressed if THIS server requested the authentication,
-            * not when a frontend proxy requested it!
-            */
-           || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization"))
-           continue;
-       ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL);
+        if (reqhdrs_elts[i].key == NULL || reqhdrs_elts[i].val == NULL
+
+        /* Clear out hop-by-hop request headers not to send:
+         * RFC2616 13.5.1 says we should strip these headers:
+         */
+            || !strcasecmp(reqhdrs_elts[i].key, "Host") /* Already sent */
+            || !strcasecmp(reqhdrs_elts[i].key, "Keep-Alive")
+            || !strcasecmp(reqhdrs_elts[i].key, "TE")
+            || !strcasecmp(reqhdrs_elts[i].key, "Trailer")
+            || !strcasecmp(reqhdrs_elts[i].key, "Transfer-Encoding")
+            || !strcasecmp(reqhdrs_elts[i].key, "Upgrade")
+
+            /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
+             * suppressed if THIS server requested the authentication,
+             * not when a frontend proxy requested it!
+             *
+             * The solution to this problem is probably to strip out
+             * the Proxy-Authorisation header in the authorisation
+             * code itself, not here. This saves us having to signal
+             * somehow whether this request was authenticated or not.
+             */
+            || !strcasecmp(reqhdrs_elts[i].key, "Proxy-Authorization"))
+            continue;
+        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);
+        while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0)
+            ap_bwrite(f, buffer, i);
     }
     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);
     if (len == -1) {
-       ap_bclose(f);
-       ap_kill_timeout(r);
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                    "ap_bgets() - proxy receive - Error reading from remote server %s (length %d)",
-                    proxyhost ? proxyhost : desthost, len);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                            "Error reading from remote server");
+        ap_bclose(f);
+        ap_kill_timeout(r);
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                     "ap_bgets() - proxy receive - Error reading from remote server %s (length %d)",
+                     proxyhost ? proxyhost : desthost, len);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
     } else if (len == 0) {
-       ap_bclose(f);
-       ap_kill_timeout(r);
-       return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                            "Document contains no data");
+        ap_bclose(f);
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "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)) {
-           major = 1;
-           minor = 0;
-       }
-
-/* 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);
-           return HTTP_BAD_GATEWAY;
-       }
-       backasswards = 0;
-       buffer[--len] = '\0';
-
-       buffer[12] = '\0';
-       r->status = atoi(&buffer[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. */
-
-       resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f);
-       if (resp_hdrs == NULL) {
-           ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
-                "proxy: Bad HTTP/%d.%d header returned by %s (%s)",
-                major, minor, r->uri, r->method);
-           resp_hdrs = ap_make_table(p, 20);
-           nocache = 1;    /* do not cache this broken file */
-       }
-
-       if (conf->viaopt != via_off && conf->viaopt != via_block) {
-           /* Create a "Via:" response header entry and merge it */
-           i = ap_get_server_port(r);
-           if (ap_is_default_port(i,r)) {
-               strcpy(portstr,"");
-           } else {
-               ap_snprintf(portstr, sizeof portstr, ":%d", i);
-           }
-           ap_table_mergen((table *)resp_hdrs, "Via",
-                           (conf->viaopt == via_full)
-                           ? ap_psprintf(p, "%d.%d %s%s (%s)",
-                               major, minor,
-                               ap_get_server_name(r), portstr,
-                               SERVER_BASEVERSION)
-                           : ap_psprintf(p, "%d.%d %s%s",
-                               major, minor,
-                               ap_get_server_name(r), portstr)
-                           );
-       }
-
-       clear_connection(p, resp_hdrs); /* Strip Connection hdrs */
+        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 = 1;
+        }
+
+        /* 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);
+            return HTTP_BAD_GATEWAY;
+        }
+        backasswards = 0;
+        buffer[--len] = '\0';
+
+        buffer[12] = '\0';
+        r->status = atoi(&buffer[9]);
+        buffer[12] = ' ';
+        r->status_line = ap_pstrdup(p, &buffer[9]);
+
+        /* 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) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
+                 "proxy: Bad HTTP/%d.%d header returned by %s (%s)",
+                 major, minor, r->uri, r->method);
+            resp_hdrs = ap_make_table(p, 20);
+            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);
+            if (ap_is_default_port(i,r)) {
+                strcpy(portstr,"");
+            } else {
+                ap_snprintf(portstr, sizeof portstr, ":%d", i);
+            }
+            ap_table_mergen((table *)resp_hdrs, "Via",
+                            (conf->viaopt == via_full)
+                            ? ap_psprintf(p, "%d.%d %s%s (%s)",
+                                major, minor,
+                                ap_get_server_name(r), portstr,
+                                SERVER_BASEVERSION)
+                            : ap_psprintf(p, "%d.%d %s%s",
+                                major, minor,
+                                ap_get_server_name(r), portstr)
+                            );
+        }
+
+        /* strip hop-by-hop headers defined by Connection */
+        ap_proxy_clear_connection(p, resp_hdrs);
         /* Now add out bound headers set by other modules */
         resp_hdrs = ap_overlay_tables(r->pool, r->err_headers_out, resp_hdrs);
-    }
+}
     else {
-/* an http/0.9 response */
-       backasswards = 1;
-       r->status = 200;
-       r->status_line = "200 OK";
+        /* an http/0.9 response */
+        backasswards = 1;
+        r->status = 200;
+        r->status_line = "200 OK";
 
-/* no headers */
-       resp_hdrs = ap_make_table(p, 20);
+        /* 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));
+        ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr));
     if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL)
-       ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr));
+        ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr));
     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));
-
- /*
-  * If "Pragma: no-cache" set nocache and make reply un-buffered to
-  * ensure timely delivery
-  */
-    if (((pragma = ap_table_get(resp_hdrs, "Pragma")) != NULL &&
-        ap_proxy_liststr(pragma, "no-cache"))) {
-        nocache = 1;
-        r->connection->client->flags &= ~B_WR;
-    }
+        ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, 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) {
-       for (i = 0; i < conf->nocaches->nelts; i++) {
-           if (destaddr.s_addr == ncent[i].addr.s_addr ||
-               (ncent[i].name != NULL &&
-                 (ncent[i].name[0] == '*' ||
-                  strstr(desthost, ncent[i].name) != NULL))) {
-              nocache = 1;
-              break;
-           }
-       }
-    }
+        for (i = 0; i < conf->nocaches->nelts; i++) {
+            if (destaddr.s_addr == ncent[i].addr.s_addr ||
+                (ncent[i].name != NULL &&
+                  (ncent[i].name[0] == '*' ||
+                   strstr(desthost, ncent[i].name) != NULL))) {
+               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);
+        ap_bclose(f);
+        return i;
     }
 
-/* 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);
+        /* 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);
     }
 
-    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");
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "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_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_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
@@ -551,10 +564,17 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
 /* 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);
+        c->cache_completion = conf->cache.cache_completion;
+
+/* 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);
index bbadcd6260013207f0e231ad0a7f45e0ae1d6793..5fc9ac44505a2b5f79b75a1f68dc13b37b6725ab 100644 (file)
@@ -63,7 +63,7 @@
 #include "multithread.h"
 #include "http_log.h"
 #include "util_uri.h"
-#include "util_date.h" /* get ap_checkmask() decl. */
+#include "util_date.h"  /* get ap_checkmask() decl. */
 
 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
@@ -78,20 +78,20 @@ int ap_proxy_hex2c(const char *x)
 #ifndef CHARSET_EBCDIC
     ch = x[0];
     if (ap_isdigit(ch))
-       i = ch - '0';
+        i = ch - '0';
     else if (ap_isupper(ch))
-       i = ch - ('A' - 10);
+        i = ch - ('A' - 10);
     else
-       i = ch - ('a' - 10);
+        i = ch - ('a' - 10);
     i <<= 4;
 
     ch = x[1];
     if (ap_isdigit(ch))
-       i += ch - '0';
+        i += ch - '0';
     else if (ap_isupper(ch))
-       i += ch - ('A' - 10);
+        i += ch - ('A' - 10);
     else
-       i += ch - ('a' - 10);
+        i += ch - ('a' - 10);
     return i;
 #else /*CHARSET_EBCDIC*/
     return (1 == sscanf(x, "%2x", &i)) ? os_toebcdic[i&0xFF] : 0;
@@ -106,15 +106,15 @@ void ap_proxy_c2hex(int ch, char *x)
     x[0] = '%';
     i = (ch & 0xF0) >> 4;
     if (i >= 10)
-       x[1] = ('A' - 10) + i;
+        x[1] = ('A' - 10) + i;
     else
-       x[1] = '0' + i;
+        x[1] = '0' + i;
 
     i = ch & 0x0F;
     if (i >= 10)
-       x[2] = ('A' - 10) + i;
+        x[2] = ('A' - 10) + i;
     else
-       x[2] = '0' + i;
+        x[2] = '0' + i;
 #else /*CHARSET_EBCDIC*/
     static const char ntoa[] = { "0123456789ABCDEF" };
     ch = os_toascii[ch & 0xFF];
@@ -136,12 +136,12 @@ void ap_proxy_c2hex(int ch, char *x)
  * those which must not be touched.
  */
 char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
-                       enum proxyreqtype isenc)
+                        enum proxyreqtype isenc)
 {
     int i, j, ch;
     char *y;
-    const char *allowed;       /* characters which should not be encoded */
-    const char *reserved;      /* characters which much not be en/de-coded */
+    const char *allowed;        /* characters which should not be encoded */
+    const char *reserved;       /* characters which much not be en/de-coded */
 
 /* N.B. in addition to :@&=, this allows ';' in an http path
  * and '?' in an ftp path -- this may be revised
@@ -151,51 +151,51 @@ char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
  * it only permits ; / ? : @ = & as reserved chars.)
  */
     if (t == enc_path)
-       allowed = "$-_.+!*'(),;:@&=";
+        allowed = "$-_.+!*'(),;:@&=";
     else if (t == enc_search)
-       allowed = "$-_.!*'(),;:@&=";
+        allowed = "$-_.!*'(),;:@&=";
     else if (t == enc_user)
-       allowed = "$-_.+!*'(),;@&=";
+        allowed = "$-_.+!*'(),;@&=";
     else if (t == enc_fpath)
-       allowed = "$-_.+!*'(),?:@&=";
-    else                       /* if (t == enc_parm) */
-       allowed = "$-_.+!*'(),?/:@&=";
+        allowed = "$-_.+!*'(),?:@&=";
+    else                        /* if (t == enc_parm) */
+        allowed = "$-_.+!*'(),?/:@&=";
 
     if (t == enc_path)
-       reserved = "/";
+        reserved = "/";
     else if (t == enc_search)
-       reserved = "+";
+        reserved = "+";
     else
-       reserved = "";
+        reserved = "";
 
     y = ap_palloc(p, 3 * len + 1);
 
     for (i = 0, j = 0; i < len; i++, j++) {
 /* always handle '/' first */
-       ch = x[i];
-       if (strchr(reserved, ch)) {
-           y[j] = ch;
-           continue;
-       }
+        ch = x[i];
+        if (strchr(reserved, ch)) {
+            y[j] = ch;
+            continue;
+        }
 /* decode it if not already done */
-       if (isenc != NOT_PROXY && ch == '%') {
-           if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2]))
-               return NULL;
-           ch = ap_proxy_hex2c(&x[i + 1]);
-           i += 2;
-           if (ch != 0 && strchr(reserved, ch)) {      /* keep it encoded */
-               ap_proxy_c2hex(ch, &y[j]);
-               j += 2;
-               continue;
-           }
-       }
+        if (isenc != NOT_PROXY && ch == '%') {
+            if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2]))
+                return NULL;
+            ch = ap_proxy_hex2c(&x[i + 1]);
+            i += 2;
+            if (ch != 0 && strchr(reserved, ch)) {      /* keep it encoded */
+                ap_proxy_c2hex(ch, &y[j]);
+                j += 2;
+                continue;
+            }
+        }
 /* recode it, if necessary */
-       if (!ap_isalnum(ch) && !strchr(allowed, ch)) {
-           ap_proxy_c2hex(ch, &y[j]);
-           j += 2;
-       }
-       else
-           y[j] = ch;
+        if (!ap_isalnum(ch) && !strchr(allowed, ch)) {
+            ap_proxy_c2hex(ch, &y[j]);
+            j += 2;
+        }
+        else
+            y[j] = ch;
     }
     y[j] = '\0';
     return y;
@@ -213,73 +213,73 @@ char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
  */
 char *
      ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp,
-                       char **passwordp, char **hostp, int *port)
+                        char **passwordp, char **hostp, int *port)
 {
     int i;
     char *strp, *host, *url = *urlp;
     char *user = NULL, *password = NULL;
 
     if (url[0] != '/' || url[1] != '/')
-       return "Malformed URL";
+        return "Malformed URL";
     host = url + 2;
     url = strchr(host, '/');
     if (url == NULL)
-       url = "";
+        url = "";
     else
-       *(url++) = '\0';        /* skip seperating '/' */
+        *(url++) = '\0';        /* skip seperating '/' */
 
     /* find _last_ '@' since it might occur in user/password part */
     strp = strrchr(host, '@');
 
     if (strp != NULL) {
-       *strp = '\0';
-       user = host;
-       host = strp + 1;
+        *strp = '\0';
+        user = host;
+        host = strp + 1;
 
 /* find password */
-       strp = strchr(user, ':');
-       if (strp != NULL) {
-           *strp = '\0';
-           password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, STD_PROXY);
-           if (password == NULL)
-               return "Bad %-escape in URL (password)";
-       }
-
-       user = ap_proxy_canonenc(p, user, strlen(user), enc_user, STD_PROXY);
-       if (user == NULL)
-           return "Bad %-escape in URL (username)";
+        strp = strchr(user, ':');
+        if (strp != NULL) {
+            *strp = '\0';
+            password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, STD_PROXY);
+            if (password == NULL)
+                return "Bad %-escape in URL (password)";
+        }
+
+        user = ap_proxy_canonenc(p, user, strlen(user), enc_user, STD_PROXY);
+        if (user == NULL)
+            return "Bad %-escape in URL (username)";
     }
     if (userp != NULL) {
-       *userp = user;
+        *userp = user;
     }
     if (passwordp != NULL) {
-       *passwordp = password;
+        *passwordp = password;
     }
 
     strp = strrchr(host, ':');
     if (strp != NULL) {
-       *(strp++) = '\0';
-
-       for (i = 0; strp[i] != '\0'; i++)
-           if (!ap_isdigit(strp[i]))
-               break;
-
-       /* if (i == 0) the no port was given; keep default */
-       if (strp[i] != '\0') {
-           return "Bad port number in URL";
-       } else if (i > 0) {
-           *port = atoi(strp);
-           if (*port > 65535)
-               return "Port number in URL > 65535";
-       }
+        *(strp++) = '\0';
+
+        for (i = 0; strp[i] != '\0'; i++)
+            if (!ap_isdigit(strp[i]))
+                break;
+
+        /* if (i == 0) the no port was given; keep default */
+        if (strp[i] != '\0') {
+            return "Bad port number in URL";
+        } else if (i > 0) {
+            *port = atoi(strp);
+            if (*port > 65535)
+                return "Port number in URL > 65535";
+        }
     }
-    ap_str_tolower(host);              /* DNS names are case-insensitive */
+    ap_str_tolower(host);                /* DNS names are case-insensitive */
     if (*host == '\0')
-       return "Missing host in URL";
+        return "Missing host in URL";
 /* check hostname syntax */
     for (i = 0; host[i] != '\0'; i++)
-       if (!ap_isdigit(host[i]) && host[i] != '.')
-           break;
+        if (!ap_isdigit(host[i]) && host[i] != '.')
+            break;
     /* must be an IP address */
 #if defined(WIN32) || defined(NETWARE) || defined(TPF) || defined(BEOS)
     if (host[i] == '\0' && (inet_addr(host) == -1))
@@ -287,9 +287,12 @@ char *
     if (host[i] == '\0' && (ap_inet_addr(host) == -1 || inet_network(host) == -1))
 #endif
     {
-       return "Bad IP address in URL";
+        return "Bad IP address in URL";
     }
 
+/*    if (strchr(host,'.') == NULL && domain != NULL)
+   host = pstrcat(p, host, domain, NULL);
+ */
     *urlp = url;
     *hostp = host;
 
@@ -315,49 +318,49 @@ const char *
     q = strchr(x, ',');
     /* check for RFC 850 date */
     if (q != NULL && q - x > 3 && q[1] == ' ') {
-       *q = '\0';
-       for (wk = 0; wk < 7; wk++)
-           if (strcmp(x, lwday[wk]) == 0)
-               break;
-       *q = ',';
-       if (wk == 7)
-           return x;           /* not a valid date */
-       if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
-           q[17] != ':' || strcmp(&q[20], " GMT") != 0)
-           return x;
-       if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
-                  &hour, &min, &sec, zone) != 7)
-           return x;
-       if (year < 70)
-           year += 2000;
-       else
-           year += 1900;
+        *q = '\0';
+        for (wk = 0; wk < 7; wk++)
+            if (strcmp(x, lwday[wk]) == 0)
+                break;
+        *q = ',';
+        if (wk == 7)
+            return x;           /* not a valid date */
+        if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
+            q[17] != ':' || strcmp(&q[20], " GMT") != 0)
+            return x;
+        if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
+                   &hour, &min, &sec, zone) != 7)
+            return x;
+        if (year < 70)
+            year += 2000;
+        else
+            year += 1900;
     }
     else {
 /* check for acstime() date */
-       if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
-           x[16] != ':' || x[19] != ' ' || x[24] != '\0')
-           return x;
-       if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
-                  &min, &sec, &year) != 7)
-           return x;
-       for (wk = 0; wk < 7; wk++)
-           if (strcmp(week, ap_day_snames[wk]) == 0)
-               break;
-       if (wk == 7)
-           return x;
+        if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
+            x[16] != ':' || x[19] != ' ' || x[24] != '\0')
+            return x;
+        if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
+                   &min, &sec, &year) != 7)
+            return x;
+        for (wk = 0; wk < 7; wk++)
+            if (strcmp(week, ap_day_snames[wk]) == 0)
+                break;
+        if (wk == 7)
+            return x;
     }
 
 /* check date */
     for (mon = 0; mon < 12; mon++)
-       if (strcmp(month, ap_month_snames[mon]) == 0)
-           break;
+        if (strcmp(month, ap_month_snames[mon]) == 0)
+            break;
     if (mon == 12)
-       return x;
+        return x;
 
     q = ap_palloc(p, 30);
     ap_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[wk], mday,
-               ap_month_snames[mon], year, hour, min, sec);
+                ap_month_snames[mon], year, hour, min, sec);
     return q;
 }
 
@@ -440,61 +443,66 @@ table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f)
      * the connection closes (EOF), or we timeout.
      */
     while ((len = proxy_getline(buffer, size, f, 1)) > 0) {
-       
-       if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
-
-           /* Buggy MS IIS servers sometimes return invalid headers
-            * (an extra "HTTP/1.0 200, OK" line sprinkled in between
-            * the usual MIME headers). Try to deal with it in a sensible
-            * way, but log the fact.
-            * XXX: The mask check is buggy if we ever see an HTTP/1.10 */
-
-           if (!ap_checkmask(buffer, "HTTP/#.# ###*")) {
-               /* Nope, it wasn't even an extra HTTP header. Give up. */
-               return NULL;
-           }
-
-           ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
-                        "proxy: Ignoring duplicate HTTP header "
-                        "returned by %s (%s)", r->uri, r->method);
-           continue;
-       }
+        
+        if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
+
+            /* Buggy MS IIS servers sometimes return invalid headers
+             * (an extra "HTTP/1.0 200, OK" line sprinkled in between
+             * the usual MIME headers). Try to deal with it in a sensible
+             * way, but log the fact.
+             * XXX: The mask check is buggy if we ever see an HTTP/1.10 */
+
+            if (!ap_checkmask(buffer, "HTTP/#.# ###*")) {
+                /* Nope, it wasn't even an extra HTTP header. Give up. */
+                return NULL;
+            }
+
+            ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
+                         "proxy: Ignoring duplicate HTTP header "
+                         "returned by %s (%s)", r->uri, r->method);
+            continue;
+        }
 
         *value = '\0';
         ++value;
-       /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
-        * wrong... and so are many others probably.
-        */
+        /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
+         * wrong... and so are many others probably.
+         */
         while (ap_isspace(*value))
             ++value;            /* Skip to start of value   */
 
-       /* should strip trailing whitespace as well */
-       for (end = &value[strlen(value)-1]; end > value && ap_isspace(*end); --end)
-           *end = '\0';
+        /* should strip trailing whitespace as well */
+        for (end = &value[strlen(value)-1]; end > value && ap_isspace(*end); --end)
+            *end = '\0';
 
         ap_table_add(resp_hdrs, buffer, value);
 
-       /* the header was too long; at the least we should skip extra data */
-       if (len >= size - 1) { 
-           while ((len = proxy_getline(field, MAX_STRING_LEN, f, 1))
-                   >= MAX_STRING_LEN - 1) {
-               /* soak up the extra data */
-           }
-           if (len == 0) /* time to exit the larger loop as well */
-               break;
-       }
+        /* the header was too long; at the least we should skip extra data */
+        if (len >= size - 1) { 
+            while ((len = proxy_getline(field, MAX_STRING_LEN, f, 1))
+                    >= MAX_STRING_LEN - 1) {
+                /* soak up the extra data */
+            }
+            if (len == 0) /* time to exit the larger loop as well */
+                break;
+        }
     }
     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];
     long total_bytes_rcvd;
     register int n, o, w;
     conn_rec *con = r->connection;
-    int alternate_timeouts = 1;        /* 1 if we alternate between soft & hard timeouts */
+    int alternate_timeouts = 1; /* 1 if we alternate between soft & hard timeouts */
 
     total_bytes_rcvd = 0;
     if (c != NULL)
@@ -504,7 +512,7 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c)
     /* The cache copy is ASCII, not EBCDIC, even for text/html) */
     ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
     if (c != NULL && c->fp != NULL)
-       ap_bsetflag(c->fp, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
+        ap_bsetflag(c->fp, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
     ap_bsetflag(con->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
 #endif
 
@@ -542,41 +550,46 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c)
         if (alternate_timeouts)
             ap_hard_timeout("proxy recv body from upstream server", r);
 
-       /* Read block from server */
-       n = ap_bread(f, buf, IOBUFSIZE);
+        /* Read block from server */
+        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);
         else
             ap_reset_timeout(r);
 
-       if (n == -1) {          /* input error */
-           if (c != NULL) {
-               ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
-                   "proxy: error reading from %s", c->url);
-               c = ap_proxy_cache_error(c);
-           }
-           break;
-       }
-       if (n == 0)
-           break;              /* EOF */
-       o = 0;
-       total_bytes_rcvd += n;
-
-       /* Write to cache first. */
-       /*@@@ XXX FIXME: Assuming that writing the cache file won't time out?!!? */
+        if (n == -1) {          /* input error */
+            if (c != NULL) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+                    "proxy: error reading from %s", c->url);
+                c = ap_proxy_cache_error(c);
+            }
+            break;
+        }
+        if (n == 0)
+            break;                /* EOF */
+        o = 0;
+        total_bytes_rcvd += n;
+
+        /* Write to cache first. */
+        /*@@@ XXX FIXME: Assuming that writing the cache file won't time out?!!? */
         if (c != NULL && c->fp != NULL) {
             if (ap_bwrite(c->fp, &buf[0], n) != n) {
                 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
-                   "proxy: error writing to %s", c->tempfile);
-               c = ap_proxy_cache_error(c);
+                    "proxy: error writing to %s", c->tempfile);
+                c = ap_proxy_cache_error(c);
             } else {
                 c->written += n;
             }
         }
 
-       /* Write the block to the client, detect aborted transfers */
-        while (!con->aborted && n > 0) {
+        /* Write the block to the client, detect aborted transfers */
+        while (!nowrite && !con->aborted && n > 0) {
             if (alternate_timeouts)
                 ap_soft_timeout("proxy send body", r);
 
@@ -603,7 +616,7 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c)
                         c->fp = NULL;
                         }
                         unlink(c->tempfile);
-                       c = NULL;
+                        c = NULL;
                     }
                 }
                 con->aborted = 1;
@@ -612,38 +625,45 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c)
             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)
-       ap_bflush(con->client);
+        ap_bflush(con->client);
 
     ap_kill_timeout(r);
     return total_bytes_rcvd;
 }
 
 /*
- * Sends response line and headers.  Uses the client fd and the 
- * headers_out array from the passed request_rec to talk to the client
- * 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);
 }
 
 
@@ -653,29 +673,45 @@ void ap_proxy_send_headers(request_rec *r, const char *respline, table *t)
  * 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, ',');
-       if (p != NULL) {
-           i = p - list;
-           do
-               p++;
-           while (ap_isspace(*p));
-       }
-       else
-           i = strlen(list);
-
-       while (i > 0 && ap_isspace(list[i - 1]))
-           i--;
-       if (i == len && strncasecmp(list, val, len) == 0)
-           return 1;
-       list = p;
+        p = strchr(list, ',');
+        if (p != NULL) {
+            i = p - list;
+            do
+                p++;
+            while (ap_isspace(*p));
+        }
+        else
+            i = strlen(list);
+
+        while (i > 0 && ap_isspace(list[i - 1]))
+            i--;
+        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;
 }
@@ -704,28 +740,28 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength)
  * i.e. 128 bits is 3 x 5 bytes + 1 byte -> 3 * 8 characters + 2 characters
  */
     for (i = 0, k = 0; i < 15; i += 5) {
-       x = (digest[i] << 24) | (digest[i + 1] << 16) | (digest[i + 2] << 8) | digest[i + 3];
-       tmp[k++] = enc_table[x >> 27];
-       tmp[k++] = enc_table[(x >> 22) & 0x1f];
-       tmp[k++] = enc_table[(x >> 17) & 0x1f];
-       tmp[k++] = enc_table[(x >> 12) & 0x1f];
-       tmp[k++] = enc_table[(x >> 7) & 0x1f];
-       tmp[k++] = enc_table[(x >> 2) & 0x1f];
-       x = ((x & 0x3) << 8) | digest[i + 4];
-       tmp[k++] = enc_table[x >> 5];
-       tmp[k++] = enc_table[x & 0x1f];
+        x = (digest[i] << 24) | (digest[i + 1] << 16) | (digest[i + 2] << 8) | digest[i + 3];
+        tmp[k++] = enc_table[x >> 27];
+        tmp[k++] = enc_table[(x >> 22) & 0x1f];
+        tmp[k++] = enc_table[(x >> 17) & 0x1f];
+        tmp[k++] = enc_table[(x >> 12) & 0x1f];
+        tmp[k++] = enc_table[(x >> 7) & 0x1f];
+        tmp[k++] = enc_table[(x >> 2) & 0x1f];
+        x = ((x & 0x3) << 8) | digest[i + 4];
+        tmp[k++] = enc_table[x >> 5];
+        tmp[k++] = enc_table[x & 0x1f];
     }
 /* one byte left */
     x = digest[15];
-    tmp[k++] = enc_table[x >> 3];      /* use up 5 bits */
+    tmp[k++] = enc_table[x >> 3];       /* use up 5 bits */
     tmp[k++] = enc_table[x & 0x7];
     /* now split into directory levels */
 
     for (i = k = d = 0; d < ndepth; ++d) {
-       memcpy(&val[i], &tmp[k], nlength);
-       k += nlength;
-       val[i + nlength] = '/';
-       i += nlength + 1;
+        memcpy(&val[i], &tmp[k], nlength);
+        k += nlength;
+        val[i + nlength] = '/';
+        i += nlength + 1;
     }
     memcpy(&val[i], &tmp[k], 26 - k);
     val[i + 26 - k] = '\0';
@@ -744,10 +780,10 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength)
     /* Believe it or not, AIX 1.x does not allow you to name a file '@',
      * so hack around it in the encoding. */
     static const char enc_table[64] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_%";
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_%";
 #else
     static const char enc_table[64] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
 #endif
 
     ap_MD5Init(&context);
@@ -759,23 +795,23 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength)
  * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
  */
     for (i = 0, k = 0; i < 15; i += 3) {
-       x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
-       tmp[k++] = enc_table[x >> 18];
-       tmp[k++] = enc_table[(x >> 12) & 0x3f];
-       tmp[k++] = enc_table[(x >> 6) & 0x3f];
-       tmp[k++] = enc_table[x & 0x3f];
+        x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
+        tmp[k++] = enc_table[x >> 18];
+        tmp[k++] = enc_table[(x >> 12) & 0x3f];
+        tmp[k++] = enc_table[(x >> 6) & 0x3f];
+        tmp[k++] = enc_table[x & 0x3f];
     }
 /* one byte left */
     x = digest[15];
-    tmp[k++] = enc_table[x >> 2];      /* use up 6 bits */
+    tmp[k++] = enc_table[x >> 2];        /* use up 6 bits */
     tmp[k++] = enc_table[(x << 4) & 0x3f];
     /* now split into directory levels */
 
     for (i = k = d = 0; d < ndepth; ++d) {
-       memcpy(&val[i], &tmp[k], nlength);
-       k += nlength;
-       val[i + nlength] = '/';
-       i += nlength + 1;
+        memcpy(&val[i], &tmp[k], nlength);
+        k += nlength;
+        val[i + nlength] = '/';
+        i += nlength + 1;
     }
     memcpy(&val[i], &tmp[k], 22 - k);
     val[i + 22 - k] = '\0';
@@ -784,57 +820,68 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength)
 #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++) {
-       ch = x[i];
-       j <<= 4;
-       if (ap_isdigit(ch))
-           j |= ch - '0';
-       else if (ap_isupper(ch))
-           j |= ch - ('A' - 10);
-       else
-           j |= ch - ('a' - 10);
+    for (i = 0, j = 0; i < 16; i++) {
+        ch = x[i];
+        j <<= 4;
+        if (ap_isdigit(ch))
+            j |= ch - '0';
+        else if (ap_isupper(ch))
+            j |= ch - ('A' - 10);
+        else
+            j |= ch - ('a' - 10);
     }
-    if (j == 0xffffffff)
-       return -1;              /* so that it works with 8-byte ints */
-    else
-       return j;
+/* 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--) {
-       ch = j & 0xF;
-       j >>= 4;
-       if (ch >= 10)
-           y[i] = ch + ('A' - 10);
-       else
-           y[i] = ch + '0';
+    if (-1 == t) {
+        strcpy(y, "FFFFFFFFFFFFFFFF");
+        return;
     }
-    y[8] = '\0';
+
+    for (i = 15; i >= 0; i--) {
+        ch = j & 0xF;
+        j >>= 4;
+        if (ch >= 10)
+            y[i] = ch + ('A' - 10);
+        else
+            y[i] = ch + '0';
+    }
+    y[16] = '\0';
 }
 
 
 cache_req *ap_proxy_cache_error(cache_req *c)
 {
     if (c != NULL) {
-       if (c->fp != NULL) {
-           ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
-           c->fp = NULL;
-       }
-       if (c->tempfile) unlink(c->tempfile);
+        if (c->fp != NULL) {
+            ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
+            c->fp = NULL;
+        }
+        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;
 }
@@ -842,15 +889,15 @@ cache_req *ap_proxy_cache_error(cache_req *c)
 int ap_proxyerror(request_rec *r, int statuscode, const char *message)
 {
     ap_table_setn(r->notes, "error-notes",
-                 ap_pstrcat(r->pool, 
-                            "The proxy server could not handle the request "
-                            "<EM><A HREF=\"", ap_escape_uri(r->pool, r->uri),
-                            "\">", ap_escape_html(r->pool, r->method),
-                            "&nbsp;", 
-                            ap_escape_html(r->pool, r->uri), "</A></EM>.<P>\n"
-                            "Reason: <STRONG>",
-                            ap_escape_html(r->pool, message), 
-                            "</STRONG>", NULL));
+                  ap_pstrcat(r->pool, 
+                             "The proxy server could not handle the request "
+                             "<EM><A HREF=\"", ap_escape_uri(r->pool, r->uri),
+                             "\">", ap_escape_html(r->pool, r->method),
+                             "&nbsp;", 
+                             ap_escape_html(r->pool, r->uri), "</A></EM>.<P>\n"
+                             "Reason: <STRONG>",
+                             ap_escape_html(r->pool, message), 
+                             "</STRONG>", NULL));
 
     /* Allow "error-notes" string to be printed by ap_send_error_response() */
     ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*"));
@@ -870,27 +917,27 @@ const char *
     struct per_thread_data *ptd = get_per_thread_data();
 
     for (i = 0; host[i] != '\0'; i++)
-       if (!ap_isdigit(host[i]) && host[i] != '.')
-           break;
+        if (!ap_isdigit(host[i]) && host[i] != '.')
+            break;
 
     if (host[i] != '\0') {
-       hp = gethostbyname(host);
-       if (hp == NULL)
-           return "Host not found";
+        hp = gethostbyname(host);
+        if (hp == NULL)
+            return "Host not found";
     }
     else {
-       ptd->ipaddr = ap_inet_addr(host);
-       hp = gethostbyaddr((char *) &ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET);
-       if (hp == NULL) {
-           memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf));
-           ptd->hpbuf.h_name = 0;
-           ptd->hpbuf.h_addrtype = AF_INET;
-           ptd->hpbuf.h_length = sizeof(ptd->ipaddr);
-           ptd->hpbuf.h_addr_list = ptd->charpbuf;
-           ptd->hpbuf.h_addr_list[0] = (char *) &ptd->ipaddr;
-           ptd->hpbuf.h_addr_list[1] = 0;
-           hp = &ptd->hpbuf;
-       }
+        ptd->ipaddr = ap_inet_addr(host);
+        hp = gethostbyaddr((char *) &ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET);
+        if (hp == NULL) {
+            memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf));
+            ptd->hpbuf.h_name = 0;
+            ptd->hpbuf.h_addrtype = AF_INET;
+            ptd->hpbuf.h_length = sizeof(ptd->ipaddr);
+            ptd->hpbuf.h_addr_list = ptd->charpbuf;
+            ptd->hpbuf.h_addr_list[0] = (char *) &ptd->ipaddr;
+            ptd->hpbuf.h_addr_list[1] = 0;
+            hp = &ptd->hpbuf;
+        }
     }
     *reqhp = *hp;
     return NULL;
@@ -903,24 +950,24 @@ static const char *
     int port = -1;
 
     if (r->hostname != NULL)
-       return r->hostname;
+        return r->hostname;
 
     /* Set url to the first char after "scheme://" */
     if ((url = strchr(r->uri, ':')) == NULL
-       || url[1] != '/' || url[2] != '/')
-       return NULL;
+        || url[1] != '/' || url[2] != '/')
+        return NULL;
 
-    url = ap_pstrdup(r->pool, &url[1]);        /* make it point to "//", which is what proxy_canon_netloc expects */
+    url = ap_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */
 
     err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
 
     if (err != NULL)
-       ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
-                    "%s", err);
+        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
+                     "%s", err);
 
     r->hostname = host;
 
-    return host;               /* ought to return the port, too */
+    return host;                /* ought to return the port, too */
 }
 
 /* Return TRUE if addr represents an IP address (or an IP network address) */
@@ -945,84 +992,84 @@ int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p)
 
     /* Iterate over up to 4 (dotted) quads. */
     for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
-       char *tmp;
+        char *tmp;
 
-       if (*addr == '/' && quads > 0)  /* netmask starts here. */
-           break;
+        if (*addr == '/' && quads > 0)  /* netmask starts here. */
+            break;
 
-       if (!ap_isdigit(*addr))
-           return 0;           /* no digit at start of quad */
+        if (!ap_isdigit(*addr))
+            return 0;           /* no digit at start of quad */
 
-       ip_addr[quads] = strtol(addr, &tmp, 0);
+        ip_addr[quads] = strtol(addr, &tmp, 0);
 
-       if (tmp == addr)        /* expected a digit, found something else */
-           return 0;
+        if (tmp == addr)        /* expected a digit, found something else */
+            return 0;
 
-       if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
-           /* invalid octet */
-           return 0;
-       }
+        if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
+            /* invalid octet */
+            return 0;
+        }
 
-       addr = tmp;
+        addr = tmp;
 
-       if (*addr == '.' && quads != 3)
-           ++addr;             /* after the 4th quad, a dot would be illegal */
+        if (*addr == '.' && quads != 3)
+            ++addr;             /* after the 4th quad, a dot would be illegal */
     }
 
     for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
-       This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
+        This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
 
-    if (addr[0] == '/' && ap_isdigit(addr[1])) {       /* net mask follows: */
-       char *tmp;
+    if (addr[0] == '/' && ap_isdigit(addr[1])) {        /* net mask follows: */
+        char *tmp;
 
-       ++addr;
+        ++addr;
 
-       bits = strtol(addr, &tmp, 0);
+        bits = strtol(addr, &tmp, 0);
 
-       if (tmp == addr)        /* expected a digit, found something else */
-           return 0;
+        if (tmp == addr)        /* expected a digit, found something else */
+            return 0;
 
-       addr = tmp;
+        addr = tmp;
 
-       if (bits < 0 || bits > 32)      /* netmask must be between 0 and 32 */
-           return 0;
+        if (bits < 0 || bits > 32)      /* netmask must be between 0 and 32 */
+            return 0;
 
     }
     else {
-       /* Determine (i.e., "guess") netmask by counting the */
-       /* number of trailing .0's; reduce #quads appropriately */
-       /* (so that 192.168.0.0 is equivalent to 192.168.)        */
-       while (quads > 0 && ip_addr[quads - 1] == 0)
-           --quads;
-
-       /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
-       if (quads < 1)
-           return 0;
-
-       /* every zero-byte counts as 8 zero-bits */
-       bits = 8 * quads;
-
-       if (bits != 32)         /* no warning for fully qualified IP address */
-           fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
-                   inet_ntoa(This->addr), bits);
+        /* Determine (i.e., "guess") netmask by counting the */
+        /* number of trailing .0's; reduce #quads appropriately */
+        /* (so that 192.168.0.0 is equivalent to 192.168.)        */
+        while (quads > 0 && ip_addr[quads - 1] == 0)
+            --quads;
+
+        /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
+        if (quads < 1)
+            return 0;
+
+        /* every zero-byte counts as 8 zero-bits */
+        bits = 8 * quads;
+
+        if (bits != 32)         /* no warning for fully qualified IP address */
+            fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
+                    inet_ntoa(This->addr), bits);
     }
 
     This->mask.s_addr = htonl(INADDR_NONE << (32 - bits));
 
     if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
-       fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n",
-               inet_ntoa(This->addr), bits);
-       This->addr.s_addr &= This->mask.s_addr;
-       fprintf(stderr, "         Set to %s/%ld\n",
-               inet_ntoa(This->addr), bits);
+        fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n",
+                inet_ntoa(This->addr), bits);
+        This->addr.s_addr &= This->mask.s_addr;
+        fprintf(stderr, "         Set to %s/%ld\n",
+                inet_ntoa(This->addr), bits);
     }
 
     if (*addr == '\0') {
-       This->matcher = proxy_match_ipaddr;
-       return 1;
+        This->matcher = proxy_match_ipaddr;
+        return 1;
     }
     else
-       return (*addr == '\0'); /* okay iff we've parsed the whole string */
+        return (*addr == '\0'); /* okay iff we've parsed the whole string */
 }
 
 /* Return TRUE if addr represents an IP address (or an IP network address) */
@@ -1043,62 +1090,62 @@ static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
     memset(ip_addr, '\0', sizeof ip_addr);
 
     if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
-       for (addr.s_addr = 0, i = 0; i < 4; ++i)
-           addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
+        for (addr.s_addr = 0, i = 0; i < 4; ++i)
+            addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
 
-       if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
+        if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
 #if DEBUGGING
-           fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
-           fprintf(stderr, "%s/", inet_ntoa(This->addr));
-           fprintf(stderr, "%s\n", inet_ntoa(This->mask));
+            fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
+            fprintf(stderr, "%s/", inet_ntoa(This->addr));
+            fprintf(stderr, "%s\n", inet_ntoa(This->mask));
 #endif
-           return 1;
-       }
+            return 1;
+        }
 #if DEBUGGING
-       else {
-           fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
-           fprintf(stderr, "%s/", inet_ntoa(This->addr));
-           fprintf(stderr, "%s\n", inet_ntoa(This->mask));
-       }
+        else {
+            fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
+            fprintf(stderr, "%s/", inet_ntoa(This->addr));
+            fprintf(stderr, "%s\n", inet_ntoa(This->mask));
+        }
 #endif
     }
     else {
-       struct hostent the_host;
+        struct hostent the_host;
 
-       memset(&the_host, '\0', sizeof the_host);
-       found = ap_proxy_host2addr(host, &the_host);
+        memset(&the_host, '\0', sizeof the_host);
+        found = ap_proxy_host2addr(host, &the_host);
 
-       if (found != NULL) {
+        if (found != NULL) {
 #if DEBUGGING
-           fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found);
+            fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found);
 #endif
-           return 0;
-       }
-
-       if (the_host.h_name != NULL)
-           found = the_host.h_name;
-       else
-           found = host;
-
-       /* Try to deal with multiple IP addr's for a host */
-       for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) {
-           ip_list = (struct in_addr *) *ip_listptr;
-           if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) {
+            return 0;
+        }
+
+        if (the_host.h_name != NULL)
+            found = the_host.h_name;
+        else
+            found = host;
+
+        /* Try to deal with multiple IP addr's for a host */
+        for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) {
+            ip_list = (struct in_addr *) *ip_listptr;
+            if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) {
 #if DEBUGGING
-               fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list));
-               fprintf(stderr, "%s/", inet_ntoa(This->addr));
-               fprintf(stderr, "%s\n", inet_ntoa(This->mask));
+                fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list));
+                fprintf(stderr, "%s/", inet_ntoa(This->addr));
+                fprintf(stderr, "%s\n", inet_ntoa(This->mask));
 #endif
-               return 1;
-           }
+                return 1;
+            }
 #if DEBUGGING
-           else {
-               fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list));
-               fprintf(stderr, "%s/", inet_ntoa(This->addr));
-               fprintf(stderr, "%s\n", inet_ntoa(This->mask));
-           }
+            else {
+                fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list));
+                fprintf(stderr, "%s/", inet_ntoa(This->addr));
+                fprintf(stderr, "%s\n", inet_ntoa(This->mask));
+            }
 #endif
-       }
+        }
     }
 
     return 0;
@@ -1112,25 +1159,25 @@ int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p)
 
     /* Domain name must start with a '.' */
     if (addr[0] != '.')
-       return 0;
+        return 0;
 
     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
     for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
-       continue;
+        continue;
 
 #if 0
     if (addr[i] == ':') {
-       fprintf(stderr, "@@@@ handle optional port in proxy_is_domainname()\n");
-       /* @@@@ handle optional port */
+        fprintf(stderr, "@@@@ handle optional port in proxy_is_domainname()\n");
+        /* @@@@ handle optional port */
     }
 #endif
 
     if (addr[i] != '\0')
-       return 0;
+        return 0;
 
     /* Strip trailing dots */
     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
-       addr[i] = '\0';
+        addr[i] = '\0';
 
     This->matcher = proxy_match_domainname;
     return 1;
@@ -1142,19 +1189,19 @@ static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
     const char *host = proxy_get_host_of_request(r);
     int d_len = strlen(This->name), h_len;
 
-    if (host == NULL)          /* some error was logged already */
-       return 0;
+    if (host == NULL)           /* some error was logged already */
+        return 0;
 
     h_len = strlen(host);
 
     /* @@@ do this within the setup? */
     /* Ignore trailing dots in domain comparison: */
     while (d_len > 0 && This->name[d_len - 1] == '.')
-       --d_len;
+        --d_len;
     while (h_len > 0 && host[h_len - 1] == '.')
-       --h_len;
+        --h_len;
     return h_len > d_len
-       && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
+        && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
 }
 
 /* Return TRUE if addr represents a host name */
@@ -1166,26 +1213,26 @@ int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p)
 
     /* Host names must not start with a '.' */
     if (addr[0] == '.')
-       return 0;
+        return 0;
 
     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
     for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i);
 
 #if 0
     if (addr[i] == ':') {
-       fprintf(stderr, "@@@@ handle optional port in proxy_is_hostname()\n");
-       /* @@@@ handle optional port */
+        fprintf(stderr, "@@@@ handle optional port in proxy_is_hostname()\n");
+        /* @@@@ handle optional port */
     }
 #endif
 
     if (addr[i] != '\0' || ap_proxy_host2addr(addr, &host) != NULL)
-       return 0;
+        return 0;
 
     This->hostentry = ap_pduphostent (p, &host);
 
     /* Strip trailing dots */
     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
-       addr[i] = '\0';
+        addr[i] = '\0';
 
     This->matcher = proxy_match_hostname;
     return 1;
@@ -1210,17 +1257,17 @@ static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
 
     /* Try to deal with multiple IP addr's for a host */
     for (ip_list = *This->hostentry->h_addr_list; *ip_list != 0UL; ++ip_list)
-       if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?)
-           return 1;
+        if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?)
+            return 1;
 #endif
 
     /* Ignore trailing dots in host2 comparison: */
     while (h2_len > 0 && host2[h2_len - 1] == '.')
-       --h2_len;
+        --h2_len;
     while (h1_len > 0 && host[h1_len - 1] == '.')
-       --h1_len;
+        --h1_len;
     return h1_len == h2_len
-       && strncasecmp(host, host2, h1_len) == 0;
+        && strncasecmp(host, host2, h1_len) == 0;
 }
 
 /* Return TRUE if addr is to be matched as a word */
@@ -1243,41 +1290,38 @@ int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r)
 
     ap_hard_timeout("proxy connect", r);
     do {
-       i = connect(sock, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
+        i = connect(sock, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
 #if defined(WIN32) || defined(NETWARE)
-       if (i == SOCKET_ERROR)
-           errno = WSAGetLastError();
+        if (i == SOCKET_ERROR)
+            errno = WSAGetLastError();
 #endif /* WIN32 */
     } while (i == -1 && errno == EINTR);
     if (i == -1) {
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                    "proxy connect to %s port %d failed",
-                    inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                     "proxy connect to %s port %d failed",
+                     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
     }
     ap_kill_timeout(r);
 
     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;
-
-    if (key == NULL)
-       return 1;
-    if (value == NULL)
-       value = "";
-    if (!parm->req->assbackwards)
-       ap_rvputs(parm->req, key, ": ", value, CRLF, NULL);
-    ap_table_add(parm->req->headers_out, key, value);
-    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);
+    cache_req *c = (cache_req *)p;
+
+      if (key == NULL || value == NULL || value[0] == '\0')
+        return 1;
+      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,10 +1331,114 @@ unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache)
 {
     unsigned len = ap_bputs(data, client);
     if (cache != NULL && cache->fp != NULL)
-       ap_bputs(data, cache->fp);
+        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);
+        else
+            ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "File %s not found", filename);
+
+    }
+    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");
+}
+
+/* overlay one table on another
+ * keys in base will be replaced by keys in overlay
+ */
+int ap_proxy_table_replace(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;
+}
+
 #if defined WIN32
 
 static DWORD tls_index;
@@ -1301,17 +1449,17 @@ BOOL WINAPI DllMain (HINSTANCE dllhandle, DWORD reason, LPVOID reserved)
 
     switch (reason) {
     case DLL_PROCESS_ATTACH:
-       tls_index = TlsAlloc();
+        tls_index = TlsAlloc();
     case DLL_THREAD_ATTACH: /* intentional no break */
-       TlsSetValue (tls_index, malloc (sizeof (struct per_thread_data)));
-       break;
+        TlsSetValue (tls_index, malloc (sizeof (struct per_thread_data)));
+        break;
     case DLL_THREAD_DETACH:
-       memptr = TlsGetValue (tls_index);
-       if (memptr) {
-           free (memptr);
-           TlsSetValue (tls_index, 0);
-       }
-       break;
+        memptr = TlsGetValue (tls_index);
+        if (memptr) {
+            free (memptr);
+            TlsSetValue (tls_index, 0);
+        }
+        break;
     }
 
     return TRUE;