You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ch...@apache.org on 2002/01/18 21:26:59 UTC

cvs commit: apache-1.3/src/modules/proxy mod_proxy.c mod_proxy.h proxy_cache.c proxy_connect.c proxy_ftp.c proxy_http.c proxy_util.c

chuck       02/01/18 12:26:59

  Modified:    src/modules/proxy mod_proxy.c mod_proxy.h proxy_cache.c
                        proxy_connect.c proxy_ftp.c proxy_http.c
                        proxy_util.c
  Log:
  The rest of the proxy http 1.1 switch
  
  Revision  Changes    Path
  1.76      +194 -193  apache-1.3/src/modules/proxy/mod_proxy.c
  
  Index: mod_proxy.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/modules/proxy/mod_proxy.c,v
  retrieving revision 1.75
  retrieving revision 1.76
  diff -u -d -b -u -r1.75 -r1.76
  --- mod_proxy.c	24 Sep 2001 20:14:27 -0000	1.75
  +++ mod_proxy.c	18 Jan 2002 20:26:58 -0000	1.76
  @@ -324,6 +324,7 @@
       if (p == NULL)
   	return HTTP_BAD_REQUEST;
   
  +    /* Try serve the request from the cache. If we suceed, we leave. */
       rc = ap_proxy_cache_check(r, url, &conf->cache, &cr);
       if (rc != DECLINED)
   	return rc;
  
  
  
  1.53      +112 -95   apache-1.3/src/modules/proxy/mod_proxy.h
  
  Index: mod_proxy.h
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/modules/proxy/mod_proxy.h,v
  retrieving revision 1.52
  retrieving revision 1.53
  diff -u -d -b -u -r1.52 -r1.53
  --- mod_proxy.h	5 Oct 2001 08:19:15 -0000	1.52
  +++ mod_proxy.h	18 Jan 2002 20:26:58 -0000	1.53
  @@ -70,36 +70,6 @@
      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 <be...@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 <ch...@topsail.org> 15 April 1997
  -
    */
   
   #define TESTING	0
  @@ -182,6 +152,13 @@
   #define DEFAULT_CACHE_COMPLETION (0.9)
   #define DEFAULT_CACHE_GCINTERVAL SEC_ONE_HR
   
  +#ifndef MAX
  +#define MAX(a,b)                ((a) > (b) ? (a) : (b))
  +#endif
  +#ifndef MIN
  +#define MIN(a,b)                ((a) < (b) ? (a) : (b))
  +#endif
  +
   /* static information about the local cache */
   struct cache_conf {
       const char *root;		/* the location of the cache directory */
  @@ -235,31 +212,46 @@
   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) */
  +    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 */
  +    char *protocol;             /* Protocol, and major/minor number,
  +                                   e.g. HTTP/1.1 */
       int status;			/* the status of the cached file */
       unsigned int written;	/* total *content* bytes written to cache */
       float cache_completion;	/* specific to this request */
  -    char *resp_line;		/* the whole status like (protocol, code + message) */
  -    table *hdrs;		/* the HTTP headers of the file */
  +    char *resp_line;            /* the whole status line
  +                                   (protocol, code + message) */
  +    table *req_hdrs;            /* the original request headers */
  +    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;
  @@ -303,9 +295,9 @@
   			 char **passwordp, char **hostp, int *port);
   const char *ap_proxy_date_canon(pool *p, const char *x);
   table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f);
  -long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c);
  -void ap_proxy_send_headers(request_rec *r, const char *respline, table *hdrs);
  -int ap_proxy_liststr(const char *list, const char *val);
  +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite);
  +void ap_proxy_write_headers(cache_req *c, const char *respline, table *t);
  +int ap_proxy_liststr(const char *list, const char *key, char **val);
   void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength);
   int ap_proxy_hex2sec(const char *x);
   void ap_proxy_sec2hex(int t, char *y);
  @@ -321,5 +313,30 @@
   /* 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*/
  
  
  
  1.76      +1085 -567 apache-1.3/src/modules/proxy/proxy_cache.c
  
  Index: proxy_cache.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/modules/proxy/proxy_cache.c,v
  retrieving revision 1.75
  retrieving revision 1.76
  diff -u -d -b -u -r1.75 -r1.76
  --- proxy_cache.c	5 Oct 2001 08:19:15 -0000	1.75
  +++ proxy_cache.c	18 Jan 2002 20:26:58 -0000	1.76
  @@ -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>
  @@ -384,7 +385,7 @@
       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);
  +        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);
   #else
  @@ -411,7 +412,7 @@
   static int sub_garbage_coll(request_rec *r, array_header *files,
   			  const char *cachebasedir, const char *cachesubdir)
   {
  -    char line[27];
  +    char line[17*(3)];
       char cachedir[HUGE_STRING_LEN];
       struct stat buf;
       int fd, i;
  @@ -427,7 +428,7 @@
   
       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,
  @@ -439,7 +440,7 @@
   	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);
  +        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) {
   /* then stat it to see how old it is; delete temporary files > 1 day old */
  @@ -450,7 +451,7 @@
   	    }
   	    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_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
  @@ -565,7 +566,7 @@
           }
   #endif
    
  -	i = read(fd, line, 26);
  +        i = read(fd, line, 17*(3)-1);
   	close(fd);
   	if (i == -1) {
   	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  @@ -573,8 +574,8 @@
   	    continue;
   	}
   	line[i] = '\0';
  -	garbage_expire = ap_proxy_hex2sec(line + 18);
  -	if (!ap_checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") ||
  +        garbage_expire = ap_proxy_hex2sec(line + 17*(2));
  +        if (!ap_checkmask(line, "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&") ||
   	    garbage_expire == BAD_DATE) {
   	    /* bad file */
   	    if (garbage_now != -1 && buf.st_atime > garbage_now + SEC_ONE_DAY &&
  @@ -612,21 +613,45 @@
   
   }
   
  +
   /*
  - * read a cache file;
  + * Read a cache file;
    * returns 1 on success,
    *         0 on failure (bad file or wrong URL)
    *        -1 on UNIX error
  + *
  + * We read the cache hex header, then the message response line and
  + * response headers, and finally we return with the filepointer
  + * pointing at the start of the message body itself, ready to be
  + * shipped to the client later on, if appropriate.
    */
   static int rdcache(request_rec *r, BUFF *cachefp, cache_req *c)
   {
  -    char urlbuff[1034], *strp;
  +    char urlbuff[HUGE_STRING_LEN], *strp;
       int len;
  -/* read the data from the cache file */
  -/* format
  - * date SP lastmod SP expire SP count SP content-length CRLF
  - * dates are stored as hex seconds since 1970
  +
  +    /* read the data from the cache file */
  +
  +    /* Format:
  +     *
  +     * The cache needs to keep track of the following information:
  +     * - Date, LastMod, Version, ReqTime, RespTime, ContentLength
  +     * - The original request headers (for Vary)
  +     * - The original response headers (for returning with a cached response)
  +     * - The body of the message
  +     *
  +     * date SP lastmod SP expire SP count SP request-time SP response-time SP content-lengthCRLF
  +     * (dates are stored as hex seconds since 1970)
  +     * Original URLCRLF
  +     * Original Request Headers
  +     * CRLF
  +     * Original Response Headers
  +     * CRLF
  +     * Body
  +     * 
    */
  +
  +    /* retrieve cachefile information values */
       len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
       if (len == -1)
   	return -1;
  @@ -635,16 +660,18 @@
       urlbuff[len - 1] = '\0';
   
       if (!ap_checkmask(urlbuff,
  -		   "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&"))
  +                   "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&"))
   	return 0;
   
  -    c->date = ap_proxy_hex2sec(urlbuff);
  -    c->lmod = ap_proxy_hex2sec(urlbuff + 9);
  -    c->expire = ap_proxy_hex2sec(urlbuff + 18);
  -    c->version = ap_proxy_hex2sec(urlbuff + 27);
  -    c->len = ap_proxy_hex2sec(urlbuff + 36);
  +    c->date = ap_proxy_hex2sec(urlbuff + 17*(0));
  +    c->lmod = ap_proxy_hex2sec(urlbuff + 17*(1));
  +    c->expire = ap_proxy_hex2sec(urlbuff + 17*(2));
  +    c->version = ap_proxy_hex2sec(urlbuff + 17*(3));
  +    c->req_time = ap_proxy_hex2sec(urlbuff + 17*(4));
  +    c->resp_time = ap_proxy_hex2sec(urlbuff + 17*(5));
  +    c->len = ap_proxy_hex2sec(urlbuff + 17*(6));
   
  -/* check that we have the same URL */
  +    /* check that we have the same URL */
       len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
       if (len == -1)
   	return -1;
  @@ -655,7 +682,12 @@
       if (strcmp(urlbuff + 7, c->url) != 0)
   	return 0;
   
  -/* What follows is the message */
  +    /* then the original request headers */
  +    c->req_hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
  +    if (c->req_hdrs == NULL)
  +        return -1;
  +
  +    /* then the original response headers */
       len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
       if (len == -1)
   	return -1;
  @@ -672,15 +704,201 @@
       c->hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
       if (c->hdrs == NULL)
   	return -1;
  -    if (c->len != -1) {		/* add a content-length header */
  +    if (c->len != -1)    /* add a content-length header */
   	if (ap_table_get(c->hdrs, "Content-Length") == NULL) {
   	    ap_table_set(c->hdrs, "Content-Length",
   			 ap_psprintf(r->pool, "%lu", (unsigned long)c->len));
   	}
  -    }
  +
  +    
       return 1;
   }
   
  +/*
  + * Call this to check the possible conditional status of
  + * the client request, and return the response from the cache
  + *
  + * Conditionals include If-Modified-Since, If-Match, If-Unmodified-Since
  + * and If-None-Match.
  + *
  + * We don't yet understand If-Range, but we will...
  + */
  +int ap_proxy_cache_conditional(request_rec *r, cache_req *c, BUFF *cachefp)
  +{
  +    const char *etag, *wetag = 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,71 +910,121 @@
    *      if cached file is not expired then
    *         if last modified after if-modified-since then send body
    *         else send 304 Not modified
  - *      else
  + *      else if cached file is expired then
    *         if last modified after if-modified-since then add
    *            last modified date to request
    */
   int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
   		      cache_req **cr)
   {
  -    char hashfile[66];
  -    const char *imstr, *pragma, *auth;
  +    const char *datestr, *pragma_req = NULL, *pragma_cresp = NULL, *cc_req = NULL, *cc_cresp = NULL, *vary = NULL;
       cache_req *c;
       time_t now;
       BUFF *cachefp;
  -    int cfd, i;
  -    const long int zero = 0L;
  +    int i;
       void *sconf = r->server->module_config;
       proxy_server_conf *pconf =
       (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
  +    const char *agestr = NULL;
  +    char *val;
  +    time_t age_c = 0;
  +    time_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale, minfresh;
   
       c = ap_pcalloc(r->pool, sizeof(cache_req));
       *cr = c;
       c->req = r;
       c->url = ap_pstrdup(r->pool, url);
  +    c->filename = NULL;
  +    c->tempfile = NULL;
  +    c->fp = NULL;
  +    c->origfp = NULL;
  +    c->version = 0;
  +    c->len = -1;
  +    c->req_hdrs = NULL;   
  +    c->hdrs = NULL;
  +    c->xcache = NULL;
   
  -/* get the If-Modified-Since date of the request */
  +    /* get the If-Modified-Since date of the request, if it exists */
       c->ims = BAD_DATE;
  -    imstr = ap_table_get(r->headers_in, "If-Modified-Since");
  -    if (imstr != NULL) {
  -/* this may modify the value in the original table */
  -	imstr = ap_proxy_date_canon(r->pool, imstr);
  -	c->ims = ap_parseHTTPdate(imstr);
  +    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 */
  +    if (conf->root != NULL) {
  +        char hashfile[66];
       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
  +    }
  +    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)
  @@ -770,71 +1038,237 @@
   	    ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
   	    cachefp = NULL;
   	}
  +        if (c->hdrs) {
  +            cc_cresp = ap_table_get(c->hdrs, "Cache-Control");
  +            pragma_cresp = ap_table_get(c->hdrs, "Pragma");
  +            vary = ap_table_get(c->hdrs, "Vary");
  +            if ((agestr = ap_table_get(c->hdrs, "Age"))) {
  +                age_c = atoi(agestr);
  +            }
  +        }
       }
  +
  +    /* if a cache file does not exist, create empty header array */
   /* fixed?  in this case, we want to get the headers from the remote server
      it will be handled later if we don't do this (I hope ;-)
  +
       if (cachefp == NULL)
   	c->hdrs = ap_make_table(r->pool, 20);
   */
       /* FIXME: Shouldn't we check the URL somewhere? */
  -    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
  +
  +    /* 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.
    */
  -		const char *q;
  +    if (c->hdrs && c->req_hdrs) {
  +        char *vary = ap_pstrdup(r->pool, ap_table_get(c->hdrs, "Vary"));
   
  -		if ((q = ap_table_get(c->hdrs, "Expires")) != NULL)
  -		    ap_table_set(r->headers_out, "Expires", q);
  +        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;
   	    }
  -	    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;
  +
  +            /* 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 {
   
  -/* 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);
  +                /* 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;
  +            }
   	}
  -	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 we already have data and a last-modified date, and it is not a head
  - * request, then add an If-Modified-Since
  +
  +    /* 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.
  +     * 
    */
   
  -    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
  +    /* 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);
  +    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);
  +
  +
  +    }
  +
  +    /* 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 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);
  +                    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;
   }
  @@ -859,75 +1293,119 @@
   #endif 
       request_rec *r = c->req;
       char *p;
  -    int i;
       const char *expire, *lmods, *dates, *clen;
       time_t expc, date, lmod, now;
  -    char buff[46];
  +    char buff[17*7+1];
       void *sconf = r->server->module_config;
       proxy_server_conf *conf =
       (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
  -    const long int zero = 0L;
  +    const char *cc_resp;
  +    table *req_hdrs;
  +
  +    cc_resp = ap_table_get(resp_hdrs, "Cache-Control");
   
       c->tempfile = NULL;
   
  -/* we've received the response */
  -/* read expiry date; if a bad date, then leave it so the client can
  - * read it
  - */
  +    /* we've received the response from the origin server */
  +    
  +    /* read expiry date; if a bad date, then leave it so the client can
  +     * read it */
       expire = ap_table_get(resp_hdrs, "Expires");
       if (expire != NULL)
   	expc = ap_parseHTTPdate(expire);
       else
   	expc = BAD_DATE;
   
  -/*
  - * read the last-modified date; if the date is bad, then delete it
  - */
  +    /* read the last-modified date; if the date is bad, then delete it */
       lmods = ap_table_get(resp_hdrs, "Last-Modified");
       if (lmods != NULL) {
   	lmod = ap_parseHTTPdate(lmods);
   	if (lmod == BAD_DATE) {
  -/* kill last modified date */
  +            /* kill last modified date */
   	    lmods = NULL;
   	}
       }
       else
   	lmod = BAD_DATE;
   
  -/*
  +
  +    /*
    * what responses should we not cache?
  - * Unknown status responses and those known to be uncacheable
  - * 304 HTTP_NOT_MODIFIED response when we have no valid cache file, or
  - * 200 HTTP_OK response from HTTP/1.0 and up without a Last-Modified header, or
  - * HEAD requests, or
  - * requests with an Authorization header, or
  - * protocol requests nocache (e.g. ftp with user/password)
  - */
  -/* @@@ XXX FIXME: is the test "r->status != HTTP_MOVED_PERMANENTLY" correct?
  - * or shouldn't it be "ap_is_HTTP_REDIRECT(r->status)" ? -MnKr */
  -    if ((r->status != HTTP_OK && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) ||
  +     *
  +     * At this point we decide based on the response headers whether it
  +     * is appropriate _NOT_ to cache the data from the server. There are
  +     * a whole lot of conditions that prevent us from caching this data.
  +     * They are tested here one by one to be clear and unambiguous. */
  +
  +    /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
  +     * We don't cache 206, because we don't (yet) cache partial responses.
  +     * We include 304 Not Modified here too as this is the origin server
  +     * telling us to serve the cached copy. */
  +    if ((r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE && r->status != HTTP_MULTIPLE_CHOICES && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) ||
  +
  +    /* if a broken Expires header is present, don't cache it */
   	(expire != NULL && expc == BAD_DATE) ||
  +
  +    /* if the server said 304 Not Modified but we have no cache file - pass
  +     * this untouched to the user agent, it's not for us. */
   	(r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) ||
  +
  +    /* 200 OK response from HTTP/1.0 and up without a Last-Modified header */
   	(r->status == HTTP_OK && lmods == NULL && is_HTTP1) ||
  +
  +    /* HEAD requests */
   	r->header_only ||
  -	ap_table_get(r->headers_in, "Authorization") != NULL ||
  +
  +    /* RFC2616 14.9.2 Cache-Control: no-store response indicating do not
  +     * cache, or stop now if you are trying to cache it */
  +        ap_proxy_liststr(cc_resp, "no-store", NULL) ||
  +
  +    /* RFC2616 14.9.1 Cache-Control: private
  +     * this object is marked for this user's eyes only. Behave as a tunnel. */
  +        ap_proxy_liststr(cc_resp, "private", NULL) ||
  +
  +    /* RFC2616 14.8 Authorisation:
  +     * if authorisation is included in the request, we don't cache, but we
  +     * can cache if the following exceptions are true:
  +     * 1) If Cache-Control: s-maxage is included
  +     * 2) If Cache-Control: must-revalidate is included
  +     * 3) If Cache-Control: public is included
  +     */
  +        (ap_table_get(r->headers_in, "Authorization") != NULL
  +
  +        && !(ap_proxy_liststr(cc_resp, "s-maxage", NULL) || ap_proxy_liststr(cc_resp, "must-revalidate", NULL) || ap_proxy_liststr(cc_resp, "public", NULL))
  +        ) ||
  +
  +    /* or we've been asked not to cache it above */
   	nocache) {
  -	ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Response is not cacheable, unlinking %s", c->filename);
  -/* close the file */
  +
  +        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 */
  +
  +        /* delete the previously cached file */
           if (c->filename)
               unlink(c->filename);
   	return DECLINED;	/* send data to client but not cache */
       }
   
  -/* otherwise, we are going to cache the response */
  -/*
  - * Read the date. Generate one if one is not supplied
  +
  +    /* It's safe to cache the response.
  +     *
  +     * We now want to update the cache file header information with
  +     * the new date, last modified, expire and content length and write
  +     * it away to our cache file. First, we determine these values from
  +     * the response, using heuristics if appropriate.
  +     *
  +     * In addition, we make HTTP/1.1 age calculations and write them away
  +     * too.
    */
  +
  +    /* Read the date. Generate one if one is not supplied */
       dates = ap_table_get(resp_hdrs, "Date");
       if (dates != NULL)
   	date = ap_parseHTTPdate(dates);
  @@ -943,21 +1421,24 @@
   	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");
  +        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");
  +        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");
  +        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. */
  @@ -973,7 +1454,7 @@
    *   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;
  @@ -984,7 +1465,7 @@
   	}
   	else
   	    expc = now + conf->cache.defaultexpire;
  -	ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Expiry date calculated %ld", (long)expc);
  +        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Expiry date calculated %ld", (long)expc);
       }
   
   /* get the content-length header */
  @@ -994,120 +1475,157 @@
       else
   	c->len = atoi(clen);
   
  -    ap_proxy_sec2hex(date, buff);
  -    buff[8] = ' ';
  -    ap_proxy_sec2hex(lmod, buff + 9);
  -    buff[17] = ' ';
  -    ap_proxy_sec2hex(expc, buff + 18);
  -    buff[26] = ' ';
  -    ap_proxy_sec2hex(c->version++, buff + 27);
  -    buff[35] = ' ';
  -    ap_proxy_sec2hex(c->len, buff + 36);
  -    buff[44] = '\n';
  -    buff[45] = '\0';
  +/* we have all the header information we need - write it to the cache file */
  +    c->version++;
  +    ap_proxy_sec2hex(date, buff + 17*(0));
  +    buff[17*(1)-1] = ' ';
  +    ap_proxy_sec2hex(lmod, buff + 17*(1));
  +    buff[17*(2)-1] = ' '; 
  +    ap_proxy_sec2hex(expc, buff + 17*(2));
  +    buff[17*(3)-1] = ' ';
  +    ap_proxy_sec2hex(c->version, buff + 17*(3));
  +    buff[17*(4)-1] = ' ';
  +    ap_proxy_sec2hex(c->req_time, buff + 17*(4));
  +    buff[17*(5)-1] = ' ';
  +    ap_proxy_sec2hex(c->resp_time, buff + 17*(5));
  +    buff[17*(6)-1] = ' '; 
  +    ap_proxy_sec2hex(c->len, buff + 17*(6));
  +    buff[17*(7)-1] = '\n';
  +    buff[17*(7)] = '\0';
   
  -/* if file not modified */
  +/* 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) {
  -	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);
  +/* 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);
   	    }
  -	    ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
  -	    return OK;
   	}
  +        else
  +          c->hdrs = resp_hdrs;
  +/* if we get here - the headers have changed. Go through the motions
  + * of creating a new temporary cache file below, we'll then serve
  + * the request like we would have in ap_proxy_cache_conditional()
  + * above, and at the same time we will also rewrite the contents
  + * to the new temporary file.
  + */
       }
  -/* new or modified file */
  +
  +/* 
  + * Ok - lets prepare and open the cached file
  + * 
  + * If a cached file (in c->fp) is already open, then we want to
  + * update that cached file. Copy the c->fp to c->origfp and open
  + * up a new one.
  + *  
  + * If the cached file (in c->fp) is NULL, we must open a new cached
  + * file from scratch.
  + *
  + * The new cache file will be moved to it's final location in the
  + * directory tree later, overwriting the old cache file should it exist.
  + */       
  +
  +/* if a cache file was already open */
       if (c->fp != NULL) {
  -	ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
  +      c->origfp = c->fp;
       }
  -    c->version = 0;
  -    ap_proxy_sec2hex(0, buff + 27);
  -    buff[35] = ' ';
   
  -/* open temporary file */
  -#if !defined(TPF) && !defined(NETWARE)
  +    while (1) {
  +/* create temporary filename */
  +#ifndef TPF
   #define TMPFILESTR	"/tmpXXXXXX"
  -    if (conf->cache.root == NULL)
  -	return DECLINED;
  +      if (conf->cache.root == NULL) {
  +          c = ap_proxy_cache_error(c);
  +          break;
  +      }
       c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) + sizeof(TMPFILESTR));
       strcpy(c->tempfile, conf->cache.root);
       strcat(c->tempfile, TMPFILESTR);
   #undef TMPFILESTR
       p = mktemp(c->tempfile);
   #else
  -    if (conf->cache.root == NULL)
  -    return DECLINED;
  +      if (conf->cache.root == NULL) {
  +          c = ap_proxy_cache_error(c);
  +          break;
  +      }
       c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) +1+ L_tmpnam);
       strcpy(c->tempfile, conf->cache.root);
       strcat(c->tempfile, "/");
       p = tmpnam(NULL);
       strcat(c->tempfile, p);
   #endif
  -    if (p == NULL)
  -	return DECLINED;
  +      if (p == NULL) {
  +          c = ap_proxy_cache_error(c);
  +          break;
  +      }
   
  -    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Create temporary file %s", c->tempfile);
  +      ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Create temporary file %s", c->tempfile);
   
  -    i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622);
  -    if (i == -1) {
  -	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  -		     "proxy: error creating cache file %s",
  -		     c->tempfile);
  -	return DECLINED;
  +/* create the new file */
  +      c->fp = ap_proxy_create_cachefile(r, c->tempfile);
  +      if (NULL == c->fp) {
  +          c = ap_proxy_cache_error(c);
  +          break;
       }
  -    ap_note_cleanups_for_fd(r->pool, i);
  -    c->fp = ap_bcreate(r->pool, B_WR);
  -    ap_bpushfd(c->fp, -1, i);
   
  +/* write away the cache header and the URL */
       if (ap_bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) {
   	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
   		     "proxy: error writing cache file(%s)", c->tempfile);
  -	ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
  -	unlink(c->tempfile);
  -	c->fp = NULL;
  +          c = ap_proxy_cache_error(c);
  +          break;
  +      }
  +
  +/* get original request headers */
  +      if (c->req_hdrs)
  +          req_hdrs = ap_copy_table(r->pool, c->req_hdrs);
  +      else
  +          req_hdrs = ap_copy_table(r->pool, r->headers_in);
  +
  +/* remove hop-by-hop headers */
  +      ap_proxy_clear_connection(r->pool, req_hdrs);
  +
  +/* save original request headers */
  +      if (c->req_hdrs)
  +            ap_table_do(ap_proxy_send_hdr_line, c, c->req_hdrs, NULL);
  +      else
  +            ap_table_do(ap_proxy_send_hdr_line, c, r->headers_in, NULL);
  +      if (ap_bputs(CRLF, c->fp) == -1) {
  +          ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  +                      "proxy: error writing request headers terminating CRLF to %s", c->tempfile);
  +          c = ap_proxy_cache_error(c);
  +          break;
  +      }
  +      break;
  +    }
  +
  +/* Was the server response a 304 Not Modified?
  + *
  + * If so, we have some work to do that we didn't do when we first
  + * checked above. We need to fulfil the request, and we need to
  + * copy the body from the old object to the new one.
  + */
  +
  +/* if response from server 304 not modified */
  +    if (r->status == HTTP_NOT_MODIFIED) {
  +
  +/* fulfil the request */
  +      c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL);
  +      return ap_proxy_cache_conditional(r, c, c->fp);
  +
       }
       return DECLINED;
   }
  @@ -1117,7 +1635,7 @@
       server_rec *s;
       long int bc;
   
  -    if (c == NULL || c->fp == NULL)
  +    if (!c || !c->fp)
   	return;
   
       s = c->req->server;
  @@ -1144,17 +1662,17 @@
   */
       else {
   /* update content-length of file */
  -	char buff[9];
  +        char buff[17];
   	off_t curpos;
   
   	c->len = bc;
   	ap_bflush(c->fp);
   	ap_proxy_sec2hex(c->len, buff);
  -	curpos = lseek(ap_bfileno(c->fp, B_WR), 36, SEEK_SET);
  +        curpos = lseek(ap_bfileno(c->fp, B_WR), 17*6, SEEK_SET);
   	if (curpos == -1)
   	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
   			 "proxy: error seeking on cache file %s", c->tempfile);
  -	else if (write(ap_bfileno(c->fp, B_WR), buff, 8) == -1)
  +        else if (write(ap_bfileno(c->fp, B_WR), buff, sizeof(buff) - 1) == -1)
   	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
   			 "proxy: error updating cache file %s", c->tempfile);
       }
  
  
  
  1.46      +113 -114  apache-1.3/src/modules/proxy/proxy_connect.c
  
  Index: proxy_connect.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/modules/proxy/proxy_connect.c,v
  retrieving revision 1.45
  retrieving revision 1.46
  diff -u -d -b -u -r1.45 -r1.46
  --- proxy_connect.c	5 Oct 2001 08:19:15 -0000	1.45
  +++ proxy_connect.c	18 Jan 2002 20:26:58 -0000	1.46
  @@ -126,7 +126,7 @@
       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;
  @@ -146,7 +146,8 @@
       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] == '*')
  +            || destaddr.s_addr == npent[i].addr.s_addr
  +            || npent[i].name[0] == '*')
   	    return ap_proxyerror(r, HTTP_FORBIDDEN,
   				 "Connect to remote machine blocked");
       }
  @@ -165,10 +166,12 @@
   	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));
  @@ -176,19 +179,17 @@
   
       if (err != NULL)
   	return ap_proxyerror(r,
  -			     proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR,
  -			     err);
  +           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");
  +        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,
  +        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 "
  @@ -210,8 +211,7 @@
       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));
  +            "Could not connect to remote machine:<br>", strerror(errno), NULL));
       }
   
       /* If we are connecting through a remote proxy, we need to pass
  @@ -222,25 +222,17 @@
   	 * 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
  +        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());
  -#ifdef CHARSET_EBCDIC
  -	ebcdic2ascii(buffer, buffer, strlen(buffer));
  -#endif
   	send(sock, buffer, strlen(buffer),0);
       }
       else {
  -	ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Returning 200 OK Status");
  +        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);
  @@ -251,34 +243,41 @@
   	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)");
  +        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,
  +            "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");
  +                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)
  +                    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);
  +                    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) {
  +                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);
  +                    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO,
  +                        r->server, "Wrote %d bytes to server", nbytes);
   		}
   		else
   		    break;
  
  
  
  1.89      +660 -669  apache-1.3/src/modules/proxy/proxy_ftp.c
  
  Index: proxy_ftp.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/modules/proxy/proxy_ftp.c,v
  retrieving revision 1.88
  retrieving revision 1.89
  diff -u -d -b -u -r1.88 -r1.89
  --- proxy_ftp.c	5 Oct 2001 08:19:15 -0000	1.88
  +++ proxy_ftp.c	18 Jan 2002 20:26:58 -0000	1.89
  @@ -459,9 +459,7 @@
       BUFF *data = NULL;
       pool *p = r->pool;
       int one = 1;
  -    const long int zero = 0L;
       NET_SIZE_T clen;
  -    struct tbl_do_args tdo;
   
       void *sconf = r->server->module_config;
       proxy_server_conf *conf =
  @@ -531,7 +529,7 @@
   	password = "apache_proxy@";
       }
   
  -/* check if ProxyBlock directive on this host */
  +    /* check if ProxyBlock directive on this host */
       destaddr.s_addr = ap_inet_addr(host);
       for (i = 0; i < conf->noproxies->nelts; i++) {
           if (destaddr.s_addr == npent[i].addr.s_addr ||
  @@ -610,6 +608,9 @@
   				strerror(errno), NULL));
       }
   
  +    /* record request_time for HTTP/1.1 age calculation */
  +    c->req_time = time(NULL);
  +
       f = ap_bcreate(p, B_RDWR | B_SOCKET);
       ap_bpushfd(f, sock, sock);
   /* shouldn't we implement telnet control options here? */
  @@ -618,7 +619,7 @@
       ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
   #endif /*CHARSET_EBCDIC*/
   
  -/* possible results: */
  +    /* possible results: */
       /* 120 Service ready in nnn minutes. */
       /* 220 Service ready for new user. */
       /* 421 Service not available, closing control connection. */
  @@ -658,8 +659,8 @@
       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. */
  @@ -691,7 +692,7 @@
   	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 */
  +    /* 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. */
  @@ -828,7 +829,7 @@
       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. */
  @@ -1211,32 +1212,22 @@
       }
   
       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);
  +    /* 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);
   
  -    if (!r->assbackwards)
  -	ap_rputs(CRLF, r);
  -    if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
  -	ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  -	    "proxy: error writing CRLF to %s", c->tempfile);
  -	c = ap_proxy_cache_error(c);
  -    }
  +    /* Setup the headers for our client from upstreams response-headers */
  +    ap_overlap_tables(r->headers_out, resp_hdrs, AP_OVERLAP_TABLES_SET);
  +    /* Add X-Cache header */
  +    ap_table_setn(r->headers_out, "X-Cache",
  +                  ap_pstrcat(r->pool, "MISS from ",
  +                             ap_get_server_name(r), NULL)); 
  +    /* The Content-Type of this response is the upstream one. */
  +    r->content_type = ap_table_get (r->headers_out, "Content-Type");
  +    /* 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
  @@ -1246,7 +1237,7 @@
   /* we need to set this for ap_proxy_send_fb()... */
   	    if (c != NULL)
   		c->cache_completion = 0;
  -	    ap_proxy_send_fb(data, r, c);
  +            ap_proxy_send_fb(data, r, c, -1, 0);
   	} else
   	    send_dir(data, r, c, cwd);
   
  
  
  
  1.80      +306 -286  apache-1.3/src/modules/proxy/proxy_http.c
  
  Index: proxy_http.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/modules/proxy/proxy_http.c,v
  retrieving revision 1.79
  retrieving revision 1.80
  diff -u -d -b -u -r1.79 -r1.80
  --- proxy_http.c	5 Oct 2001 17:46:39 -0000	1.79
  +++ proxy_http.c	18 Jan 2002 20:26:58 -0000	1.80
  @@ -76,7 +76,7 @@
       const char *err;
       int port;
   
  -/* do syntatic check.
  +    /* do syntatic check.
    * We break the URL into host, port, path, search
    */
       port = def_port;
  @@ -84,8 +84,8 @@
       if (err)
   	return HTTP_BAD_REQUEST;
   
  -/* now parse path/search args, according to rfc1738 */
  -/* N.B. if this isn't a true proxy request, then the URL _path_
  +    /* 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.
    */
  @@ -97,7 +97,7 @@
       else
   	search = r->args;
   
  -/* process path */
  +    /* process path */
       path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path,
   			     r->proxyreq);
       if (path == NULL)
  @@ -113,6 +113,7 @@
       return OK;
   }
    
  +/* handle the conversion of URLs in the ProxyPassReverse function */
   static const char *proxy_location_reverse_map(request_rec *r, const char *url)
   {
       void *sconf;
  @@ -135,29 +136,6 @@
       return url;
   }
   
  -/* Clear all connection-based headers from the incoming headers table */
  -static void clear_connection(pool *p, table *headers)
  -{
  -    const char *name;
  -    char *next = ap_pstrdup(p, ap_table_get(headers, "Connection"));
  -
  -    ap_table_unset(headers, "Proxy-Connection");
  -    if (!next)
  -	return;
  -
  -    while (*next) {
  -	name = next;
  -	while (*next && !ap_isspace(*next) && (*next != ','))
  -	    ++next;
  -	while (*next && (ap_isspace(*next) || (*next == ','))) {
  -	    *next = '\0';
  -	    ++next;
  -	}
  -	ap_table_unset(headers, name);
  -    }
  -    ap_table_unset(headers, "Connection");
  -}
  -
   /*
    * This handles http:// URLs, and other URLs using a remote proxy over http
    * If proxyhost is NULL, then contact the server directly, otherwise
  @@ -172,11 +150,11 @@
   {
       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 @@
       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,10 +174,12 @@
       struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
       int nocache = 0;
   
  +    if (conf->cache.root == NULL) nocache = 1;
  +
       memset(&server, '\0', sizeof(server));
       server.sin_family = AF_INET;
   
  -/* We break the URL into host, port, path-search */
  +    /* We break the URL into host, port, path-search */
   
       urlptr = strstr(url, "://");
       if (urlptr == NULL)
  @@ -230,7 +208,7 @@
   	}
       }
   
  -/* check if ProxyBlock directive on this host */
  +    /* check if ProxyBlock directive on this host */
       destaddr.s_addr = ap_inet_addr(desthost);
       for (i = 0; i < conf->noproxies->nelts; i++) {
           if (destaddr.s_addr == npent[i].addr.s_addr ||
  @@ -253,6 +231,10 @@
   	    return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
       }
   
  +
  +    /* we have worked out who exactly we are going to connect to, now
  +     * make that connection...
  +     */
       sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
       if (sock == -1) {
   	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  @@ -302,14 +284,27 @@
   				strerror(errno), NULL));
       }
   
  -    clear_connection(r->pool, r->headers_in);	/* Strip connection-based headers */
  +    /* record request_time for HTTP/1.1 age calculation */
  +    c->req_time = time(NULL);
  +
  +    /* build upstream-request headers by stripping r->headers_in from
  +     * connection specific headers.
  +     * We must not remove the Connection: header from r->headers_in,
  +     * we still have to react to Connection: close
  +     */
  +    req_hdrs = ap_copy_table(r->pool, r->headers_in);
  +    ap_proxy_clear_connection(r->pool, req_hdrs);
   
  +    /* At this point, we start sending the HTTP/1.1 request to the
  +     * remote server (proxy or otherwise).
  +     */
       f = ap_bcreate(p, B_RDWR | B_SOCKET);
       ap_bpushfd(f, sock, sock);
   
       ap_hard_timeout("proxy send", r);
       ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF,
   	   NULL);
  +    /* Send Host: now, adding it to req_hdrs wouldn't be much better */
       if (destportstr != NULL && destport != DEFAULT_HTTP_PORT)
   	ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL);
       else
  @@ -317,7 +312,7 @@
   
       if (conf->viaopt == via_block) {
   	/* Block all outgoing Via: headers */
  -	ap_table_unset(r->headers_in, "Via");
  +        ap_table_unset(req_hdrs, "Via");
       } else if (conf->viaopt != via_off) {
   	/* Create a "Via:" request header entry and merge it */
   	i = ap_get_server_port(r);
  @@ -327,7 +322,7 @@
   	    ap_snprintf(portstr, sizeof portstr, ":%d", i);
   	}
   	/* Generate outgoing Via: header with/without server comment: */
  -	ap_table_mergen(r->headers_in, "Via",
  +        ap_table_mergen(req_hdrs, "Via",
   		    (conf->viaopt == via_full)
   			? ap_psprintf(p, "%d.%d %s%s (%s)",
   				HTTP_VERSION_MAJOR(r->proto_num),
  @@ -341,24 +336,46 @@
   			);
       }
   
  -    reqhdrs_arr = ap_table_elts(r->headers_in);
  -    reqhdrs = (table_entry *) reqhdrs_arr->elts;
  +    /* Add X-Forwarded-For: so that the upstream has a chance to
  +       determine, where the original request came from. */
  +    ap_table_mergen(req_hdrs, "X-Forwarded-For", r->connection->remote_ip);
  +    
  +    /* we don't yet support keepalives - but we will soon, I promise! */
  +    ap_table_set(req_hdrs, "Connection", "close");
  +
  +    reqhdrs_arr = ap_table_elts(req_hdrs);
  +    reqhdrs_elts = (table_entry *) reqhdrs_arr->elts;
       for (i = 0; i < reqhdrs_arr->nelts; i++) {
  -	if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL
  -	/* Clear out headers not to send */
  -	    || !strcasecmp(reqhdrs[i].key, "Host")	/* Already sent */
  +        if (reqhdrs_elts[i].key == NULL || reqhdrs_elts[i].val == NULL
  +
  +        /* Clear out hop-by-hop request headers not to send:
  +         * RFC2616 13.5.1 says we should strip these headers:
  +         */
  +            || !strcasecmp(reqhdrs_elts[i].key, "Host") /* Already sent */
  +            || !strcasecmp(reqhdrs_elts[i].key, "Keep-Alive")
  +            || !strcasecmp(reqhdrs_elts[i].key, "TE")
  +            || !strcasecmp(reqhdrs_elts[i].key, "Trailer")
  +            || !strcasecmp(reqhdrs_elts[i].key, "Transfer-Encoding")
  +            || !strcasecmp(reqhdrs_elts[i].key, "Upgrade")
  +
   	    /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
   	     * suppressed if THIS server requested the authentication,
   	     * not when a frontend proxy requested it!
  +             *
  +             * The solution to this problem is probably to strip out
  +             * the Proxy-Authorisation header in the authorisation
  +             * code itself, not here. This saves us having to signal
  +             * somehow whether this request was authenticated or not.
   	     */
  -	    || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization"))
  +            || !strcasecmp(reqhdrs_elts[i].key, "Proxy-Authorization"))
   	    continue;
  -	ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL);
  +        ap_bvputs(f, reqhdrs_elts[i].key, ": ", reqhdrs_elts[i].val, CRLF, NULL);
       }
   
  +    /* the obligatory empty line to mark the end of the headers */
       ap_bputs(CRLF, f);
  -/* send the request data, if any. */
   
  +    /* send the request data, if any. */
       if (ap_should_client_block(r)) {
   	while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0)
   	    ap_bwrite(f, buffer, i);
  @@ -366,6 +383,9 @@
       ap_bflush(f);
       ap_kill_timeout(r);
   
  +
  +    /* Right - now it's time to listen for a response.
  +     */
       ap_hard_timeout("proxy receive", r);
   
       len = ap_bgets(buffer, sizeof buffer - 1, f);
  @@ -384,15 +404,19 @@
   			     "Document contains no data");
       }
   
  -/* Is it an HTTP/1 response?  This is buggy if we ever see an HTTP/1.10 */
  +    /* Is it an HTTP/1 response?
  +     * Do some sanity checks on the response.
  +     * (This is buggy if we ever see an HTTP/1.10)
  +     */
       if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
   	int major, minor;
   	if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
  +            /* if no response, default to HTTP/1.1 - is this correct? */
   	    major = 1;
  -	    minor = 0;
  +            minor = 1;
   	}
   
  -/* If not an HTTP/1 message or if the status line was > 8192 bytes */
  +        /* If not an HTTP/1 message or if the status line was > 8192 bytes */
   	if (buffer[5] != '1' || buffer[len - 1] != '\n') {
   	    ap_bclose(f);
   	    ap_kill_timeout(r);
  @@ -406,9 +430,9 @@
   	buffer[12] = ' ';
   	r->status_line = ap_pstrdup(p, &buffer[9]);
   
  -/* read the headers. */
  -/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
  -/* Also, take care with headers with multiple occurences. */
  +        /* read the response headers. */
  +        /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
  +        /* Also, take care with headers with multiple occurences. */
   
   	resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f);
   	if (resp_hdrs == NULL) {
  @@ -419,6 +443,7 @@
   	    nocache = 1;    /* do not cache this broken file */
   	}
   
  +        /* handle Via header in the response */
   	if (conf->viaopt != via_off && conf->viaopt != via_block) {
   	    /* Create a "Via:" response header entry and merge it */
   	    i = ap_get_server_port(r);
  @@ -439,26 +464,25 @@
   			    );
   	}
   
  -	clear_connection(p, resp_hdrs);	/* Strip Connection hdrs */
  +        /* 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 */
  +        /* an http/0.9 response */
   	backasswards = 1;
   	r->status = 200;
   	r->status_line = "200 OK";
   
  -/* no headers */
  +        /* no headers */
   	resp_hdrs = ap_make_table(p, 20);
       }
   
  -    c->hdrs = resp_hdrs;
  -
       ap_kill_timeout(r);
   
  -/*
  - * HTTP/1.0 requires us to accept 3 types of dates, but only generate
  +    /*
  +     * 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)
  @@ -468,20 +492,13 @@
       if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL)
   	ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr));
   
  -    if ((datestr = ap_table_get(resp_hdrs, "Location")) != NULL)
  -	ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, datestr));
  -    if ((datestr = ap_table_get(resp_hdrs, "URI")) != NULL)
  -	ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, datestr));
  -
  - /*
  -  * 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;
  -    }
  +    /* 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) {
  @@ -494,51 +511,47 @@
   	       break;
   	    }
   	}
  -    }
   
  +    /* update the cache file, possibly even fulfilling the request if
  +     * it turns out a conditional allowed us to serve the object from the
  +     * cache...
  +     */
       i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache);
       if (i != DECLINED) {
   	ap_bclose(f);
   	return i;
       }
   
  -    ap_hard_timeout("proxy receive", r);
  -
  -/* write status line */
  -    if (!r->assbackwards)
  -	ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL);
  -    if (c != NULL && c->fp != NULL &&
  -	ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) {
  -	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  -		"proxy: error writing status line to %s", c->tempfile);
  -	    c = ap_proxy_cache_error(c);
  +        /* write status line and headers to the cache file */
  +        ap_proxy_write_headers(c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), resp_hdrs);
       }
   
  -/* send headers */
  -    tdo.req = r;
  -    tdo.cache = c;
  -    ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL);
  -
  -    if (!r->assbackwards)
  -	ap_rputs(CRLF, r);
  -    if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
  -	ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  -	    "proxy: error writing CRLF to %s", c->tempfile);
  -	c = ap_proxy_cache_error(c);
  -    }
  +    /* 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);
   
  -    ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
  -    r->sent_bodyct = 1;
  -/* Is it an HTTP/0.9 respose? If so, send the extra data */
  +    /* Is it an HTTP/0.9 respose? If so, send the extra data we read
  +       from upstream as the start of the reponse to client */
       if (backasswards) {
  +        ap_hard_timeout("proxy send assbackward", r);
  +
   	ap_bwrite(r->connection->client, buffer, len);
   	if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) {
   	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
   		"proxy: error writing extra data to %s", c->tempfile);
   	    c = ap_proxy_cache_error(c);
   	}
  -    }
       ap_kill_timeout(r);
  +    }
  +
   
   #ifdef CHARSET_EBCDIC
       /* What we read/write after the header should not be modified
  @@ -551,10 +564,17 @@
   /* send body */
   /* if header only, then cache will be NULL */
   /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
  +/* XXX CHANGEME: We want to eventually support keepalives, which means
  + * we must read content-length bytes... */
       if (!r->header_only) {
   /* we need to set this for ap_proxy_send_fb()... */
   	c->cache_completion = conf->cache.cache_completion;
  -	ap_proxy_send_fb(f, r, c);
  +
  +/* XXX CHECKME: c->len should be the expected content length, or -1 if the
  + * content length is not known. We need to make 100% sure c->len is always
  + * set correctly before we get here to correctly do keepalive.
  + */
  +        ap_proxy_send_fb(f, r, c, c->len, 0);
       }
   
       ap_proxy_cache_tidy(c);
  
  
  
  1.101     +585 -437  apache-1.3/src/modules/proxy/proxy_util.c
  
  Index: proxy_util.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/modules/proxy/proxy_util.c,v
  retrieving revision 1.100
  retrieving revision 1.101
  diff -u -d -b -u -r1.100 -r1.101
  --- proxy_util.c	24 Sep 2001 20:14:27 -0000	1.100
  +++ proxy_util.c	18 Jan 2002 20:26:58 -0000	1.101
  @@ -290,6 +290,9 @@
   	return "Bad IP address in URL";
       }
   
  +/*    if (strchr(host,'.') == NULL && domain != NULL)
  +   host = pstrcat(p, host, domain, NULL);
  + */
       *urlp = url;
       *hostp = host;
   
  @@ -487,7 +490,12 @@
       return resp_hdrs;
   }
   
  -long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c)
  +/* read data from f, write it to:
  + * - c->fp, if it is open
  + * - r->connection->client, if nowrite == 0
  + */
  +
  +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite)
   {
       int  ok;
       char buf[IOBUFSIZE];
  @@ -543,7 +551,12 @@
               ap_hard_timeout("proxy recv body from upstream server", r);
   
   	/* 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);
  @@ -576,7 +589,7 @@
           }
   
   	/* Write the block to the client, detect aborted transfers */
  -        while (!con->aborted && n > 0) {
  +        while (!nowrite && !con->aborted && n > 0) {
               if (alternate_timeouts)
                   ap_soft_timeout("proxy send body", r);
   
  @@ -612,6 +625,11 @@
               n -= w;
               o += w;
           } /* while client alive and more data to send */
  +
  +        /* if we've received everything, leave now */
  +        if (total_bytes_rcvd == len)
  +            break;
  +
       } /* loop and ap_bread while "ok" */
   
       if (!con->aborted)
  @@ -622,28 +640,30 @@
   }
   
   /*
  - * Sends response line and headers.  Uses the client fd and the 
  - * headers_out array from the passed request_rec to talk to the client
  - * and to properly set the headers it sends for things such as logging.
  + * Writes response line and headers to the cache file.
    * 
  - * A timeout should be set before calling this routine.
  + * If respline is NULL, no response line will be written.
    */
  -void ap_proxy_send_headers(request_rec *r, const char *respline, table *t)
  +void ap_proxy_write_headers(cache_req *c, const char *respline, table *t)
   {
  -    int i;
  -    BUFF *fp = r->connection->client;
  -    table_entry *elts = (table_entry *) ap_table_elts(t)->elts;
  +    /* write status line */
  +    if (respline && c->fp != NULL &&
  +        ap_bvputs(c->fp, respline, CRLF, NULL) == -1) {
  +            ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  +                          "proxy: error writing status line to %s", c->tempfile);
  +            c = ap_proxy_cache_error(c);
  +        return;
  +        }
   
  -    ap_bvputs(fp, respline, CRLF, NULL);
  +    /* write response headers to the cache file */
  +    ap_table_do(ap_proxy_send_hdr_line, c, t, NULL);
   
  -    for (i = 0; i < ap_table_elts(t)->nelts; ++i) {
  -	if (elts[i].key != NULL) {
  -	    ap_bvputs(fp, elts[i].key, ": ", elts[i].val, CRLF, NULL);
  -	    ap_table_addn(r->headers_out, elts[i].key, elts[i].val);
  -	}
  +    /* write terminating CRLF */
  +    if (c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
  +        ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  +                      "proxy: error writing CRLF to %s", c->tempfile);
  +        c = ap_proxy_cache_error(c);
       }
  -
  -    ap_bputs(CRLF, fp);
   }
   
   
  @@ -653,12 +673,14 @@
    * The return returns 1 if the token val is found in the list, or 0
    * otherwise.
    */
  -int ap_proxy_liststr(const char *list, const char *val)
  +int ap_proxy_liststr(const char *list, const char *key, char **val)
   {
       int len, i;
       const char *p;
  +    char valbuf[HUGE_STRING_LEN];
  +    valbuf[sizeof(valbuf)-1] = 0; /* safety terminating zero */
   
  -    len = strlen(val);
  +    len = strlen(key);
   
       while (list != NULL) {
   	p = strchr(list, ',');
  @@ -673,8 +695,22 @@
   
   	while (i > 0 && ap_isspace(list[i - 1]))
   	    i--;
  -	if (i == len && strncasecmp(list, val, len) == 0)
  +        if (i == len && strncasecmp(list, key, len) == 0) {
  +            if (val) {
  +                p = strchr(list, ',');
  +                while (ap_isspace(*list)) {
  +                    list++;
  +                }
  +                if ('=' == list[0])
  +                    list++;
  +                while (ap_isspace(*list)) {
  +                    list++;
  +                }
  +                strncpy(valbuf, list, MIN(p-list, sizeof(valbuf)-1));
  +                *val = valbuf;
  +            }
   	    return 1;
  +        }
   	list = p;
       }
       return 0;
  @@ -784,14 +820,14 @@
   #endif /* CASE_BLIND_FILESYSTEM */
   
   /*
  - * Converts 8 hex digits to a time integer
  + * Converts 16 hex digits to a time integer
    */
   int ap_proxy_hex2sec(const char *x)
   {
       int i, ch;
       unsigned int j;
   
  -    for (i = 0, j = 0; i < 8; i++) {
  +    for (i = 0, j = 0; i < 16; i++) {
   	ch = x[i];
   	j <<= 4;
   	if (ap_isdigit(ch))
  @@ -801,21 +837,27 @@
   	else
   	    j |= ch - ('a' - 10);
       }
  -    if (j == 0xffffffff)
  -	return -1;		/* so that it works with 8-byte ints */
  -    else
  +/* no longer necessary, as the source hex is 8-byte int */
  +/*    if (j == 0xffffffff)*/
  +/*      return -1;*/            /* so that it works with 8-byte ints */
  +/*    else */
   	return j;
   }
   
   /*
  - * Converts a time integer to 8 hex digits
  + * Converts a time integer to 16 hex digits
    */
   void ap_proxy_sec2hex(int t, char *y)
   {
       int i, ch;
       unsigned int j = t;
   
  -    for (i = 7; i >= 0; i--) {
  +    if (-1 == t) {
  +        strcpy(y, "FFFFFFFFFFFFFFFF");
  +        return;
  +    }
  +
  +    for (i = 15; i >= 0; i--) {
   	ch = j & 0xF;
   	j >>= 4;
   	if (ch >= 10)
  @@ -823,7 +865,7 @@
   	else
   	    y[i] = ch + '0';
       }
  -    y[8] = '\0';
  +    y[16] = '\0';
   }
   
   
  @@ -834,7 +876,12 @@
   	    ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
   	    c->fp = NULL;
   	}
  -	if (c->tempfile) unlink(c->tempfile);
  +        if (c->origfp != NULL) {
  +            ap_pclosef(c->req->pool, ap_bfileno(c->origfp, B_WR));
  +            c->origfp = NULL;
  +        }
  +        if (c->tempfile)
  +            unlink(c->tempfile);
       }
       return NULL;
   }
  @@ -1259,25 +1306,22 @@
       return i;
   }
   
  -/* This function is called by ap_table_do() for all header lines */
  -/* (from proxy_http.c and proxy_ftp.c) */
  -/* It is passed a table_do_args struct pointer and a MIME field and value pair */
  +/* This function is called by ap_table_do() for all header lines
  + * (from proxy_http.c and proxy_ftp.c)
  + * It is passed a cache_req struct pointer and a MIME field and value pair
  + */
   int ap_proxy_send_hdr_line(void *p, const char *key, const char *value)
   {
  -    struct tbl_do_args *parm = (struct tbl_do_args *)p;
  +    cache_req *c = (cache_req *)p;
   
  -    if (key == NULL)
  +      if (key == NULL || value == NULL || value[0] == '\0')
   	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);
  +      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 */
   }
  @@ -1291,6 +1335,110 @@
       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;