httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stodd...@apache.org
Subject cvs commit: httpd-2.0/modules/experimental cache_storage.c cache_util.c mod_mem_cache.c mod_cache.c mod_cache.h
Date Wed, 05 Jun 2002 03:45:13 GMT
stoddard    2002/06/04 20:45:13

  Modified:    modules/experimental cache_storage.c cache_util.c
                        mod_mem_cache.c mod_cache.c mod_cache.h
  Log:
  Add content negotiation and expiration policy to mod_cache and mod_mem_cache.
  mod_disk_cache still needs work.
  
  Revision  Changes    Path
  1.22      +55 -8     httpd-2.0/modules/experimental/cache_storage.c
  
  Index: cache_storage.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/cache_storage.c,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- cache_storage.c	28 May 2002 18:04:43 -0000	1.21
  +++ cache_storage.c	5 Jun 2002 03:45:13 -0000	1.22
  @@ -186,18 +186,65 @@
           type = ap_cache_tokstr(r->pool, next, &next);
           switch ((rv = cache_run_open_entity(cache->handle, r, type, key))) {
           case OK: {
  +            char *vary = NULL;
               info = &(cache->handle->cache_obj->info);
  -            /* XXX:
  -             * Handle being returned a collection of entities.
  +            if (cache_read_entity_headers(cache->handle, r) != APR_SUCCESS) {
  +                /* TODO: Handle this error */
  +                return DECLINED;
  +            }
  +
  +            /*
  +             * 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.
  +             * 
  +             * This code makes the assumption that the storage manager will
  +             * cache the info->req_hdrs if the response contains a Vary
  +             * header.
  +             * 
  +             * RFC2616 13.6 and 14.44 describe the Vary mechanism.
                */
  +            vary = ap_pstrdup(r->pool, ap_table_get(r->headers_out, "Vary"));
  +            while (vary && *vary) {
  +                char *name = vary;
  +                const char *h1, *h2;
   
  -            /* Has the cache entry expired? */
  -            if (r->request_time > info->expire)
  -                cache->fresh = 0;
  -            else
  -                cache->fresh = 1;
  +                /* isolate header name */
  +                while (*vary && !ap_isspace(*vary) && (*vary != ','))
  +                    ++vary;
  +                while (*vary && (ap_isspace(*vary) || (*vary == ','))) {
  +                    *vary = '\0';
  +                    ++vary;
  +                }
   
  -            /*** do content negotiation here */
  +                /*
  +                 * 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(info->req_hdrs, name);
  +                if (h1 == h2) {
  +                    /* both headers NULL, so a match - do nothing */
  +                }
  +                else if (h1 && h2 && !strcmp(h1, h2)) {
  +                    /* both headers exist and are equal - do nothing */
  +                }
  +                else {
  +                    /* headers do not match, so Vary failed */
  +                    ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, r->server,
  +                                 "cache_select_url(): Vary header mismatch - Cached document
cannot be used. \n");
  +                    apr_table_clear(r->headers_out);
  +                    r->status_line = NULL;
  +                    cache->handle = NULL;
  +                    return DECLINED;
  +                }
  +            }
               return OK;
           }
           case DECLINED: {
  
  
  
  1.16      +138 -1    httpd-2.0/modules/experimental/cache_util.c
  
  Index: cache_util.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/cache_util.c,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- cache_util.c	3 Apr 2002 17:34:01 -0000	1.15
  +++ cache_util.c	5 Jun 2002 03:45:13 -0000	1.16
  @@ -133,7 +133,144 @@
       return type;
   }
   
  -/*
  +
  +/* do a HTTP/1.1 age calculation */
  +CACHE_DECLARE(apr_time_t) ap_cache_current_age(cache_info *info, const apr_time_t age_value)
  +{
  +    apr_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, info->response_time - info->date);
  +    corrected_received_age = MAX(apparent_age, age_value);
  +    response_delay = info->response_time - info->request_time;
  +    corrected_initial_age = corrected_received_age + response_delay;
  +    resident_time = apr_time_now() - info->response_time;
  +    current_age = corrected_initial_age + resident_time;
  +
  +    return (current_age);
  +}
  +
  +CACHE_DECLARE(int) ap_cache_check_freshness(cache_request_rec *cache, 
  +                                            request_rec *r)
  +{
  +    apr_time_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale, minfresh;
  +    const char *cc_cresp, *cc_req, *pragma_cresp;
  +    const char *agestr = NULL;
  +    char *val;
  +    apr_time_t age_c = 0;
  +    cache_info *info = &(cache->handle->cache_obj->info);
  +
  +    /*
  +     * 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.
  +     * 
  +     */
  +    cc_cresp = ap_table_get(r->headers_out, "Cache-Control");
  +    cc_req = ap_table_get(r->headers_in, "Cache-Control");
  +    pragma_cresp = ap_table_get(r->headers_out, "Pragma");  /* TODO: pragma_cresp not
being used? */
  +    if ((agestr = ap_table_get(r->headers_out, "Age"))) {
  +        age_c = atoi(agestr);
  +    }
  +
  +    /* calculate age of object */
  +    age = ap_cache_current_age(info, age_c);
  +
  +    /* extract s-maxage */
  +    if (cc_cresp && ap_cache_liststr(cc_cresp, "s-maxage", &val))
  +        smaxage = atoi(val);
  +    else
  +        smaxage = -1;
  +
  +    /* extract max-age from request */
  +    if (cc_req && ap_cache_liststr(cc_req, "max-age", &val))
  +        maxage_req = atoi(val);
  +    else
  +        maxage_req = -1;
  +
  +    /* extract max-age from response */
  +    if (cc_cresp && ap_cache_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_cache_liststr(cc_req, "max-stale", &val))
  +        maxstale = atoi(val);
  +    else
  +        maxstale = 0;
  +
  +    /* extract min-fresh */
  +    if (cc_req && ap_cache_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_cache_liststr(cc_cresp, "must-revalidate", NULL))
  +                     || (cc_cresp && ap_cache_liststr(cc_cresp,
  +                                                      "proxy-revalidate", NULL))))
  +        maxstale = 0;
  +    /* handle expiration */
  +    if ((-1 < smaxage && age < (smaxage - minfresh)) ||
  +        (-1 < maxage && age < (maxage + maxstale - minfresh)) ||
  +        (info->expire != APR_DATE_BAD && age < (info->expire - info->date
+ maxstale - minfresh))) {
  +        /* it's fresh darlings... */
  +        /* set age header on response */
  +        ap_table_set(r->headers_out, "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) ||
  +              (info->expire != APR_DATE_BAD && (info->expire - info->date)
> age))) {
  +            /* make sure we don't stomp on a previous warning */
  +            ap_table_merge(r->headers_out, "Warning", "110 Response is stale");
  +        }
  +        return 1;    /* Cache object is fresh */
  +    }
  +    return 0;        /* Cache object is stale */
  +}
  +/* 
    * list is a comma-separated list of case-insensitive tokens, with
    * optional whitespace around the tokens.
    * The return returns 1 if the token val is found in the list, or 0
  
  
  
  1.64      +23 -5     httpd-2.0/modules/experimental/mod_mem_cache.c
  
  Index: mod_mem_cache.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/mod_mem_cache.c,v
  retrieving revision 1.63
  retrieving revision 1.64
  diff -u -r1.63 -r1.64
  --- mod_mem_cache.c	4 Jun 2002 20:08:47 -0000	1.63
  +++ mod_mem_cache.c	5 Jun 2002 03:45:13 -0000	1.64
  @@ -87,9 +87,11 @@
       apr_ssize_t num_header_out;
       apr_ssize_t num_subprocess_env;
       apr_ssize_t num_notes;
  +    apr_ssize_t num_req_hdrs;
       cache_header_tbl_t *header_out;
       cache_header_tbl_t *subprocess_env;
       cache_header_tbl_t *notes;
  +    cache_header_tbl_t *req_hdrs; /* for Vary negotiation */
       apr_size_t m_len;
       void *m;
       apr_os_file_t fd;
  @@ -363,7 +365,6 @@
       strncpy(obj->key, key, strlen(key) + 1);
       obj->info.len = len;
   
  -
       /* Allocate and init mem_cache_object_t */
       mobj = calloc(1, sizeof(*mobj));
       if (!mobj) {
  @@ -633,10 +634,15 @@
   {
       int rc;
       mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
  +    cache_info *info = &(h->cache_obj->info);
   
  +    info->req_hdrs = apr_table_make(r->pool, mobj->num_req_hdrs);
       r->headers_out = apr_table_make(r->pool,mobj->num_header_out);
       r->subprocess_env = apr_table_make(r->pool, mobj->num_subprocess_env);
       r->notes = apr_table_make(r->pool, mobj->num_notes);
  +    rc = unserialize_table(mobj->req_hdrs,
  +                           mobj->num_req_hdrs,
  +                           info->req_hdrs);
       rc = unserialize_table( mobj->header_out,
                               mobj->num_header_out, 
                               r->headers_out);
  @@ -684,7 +690,19 @@
       mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
       int rc;
   
  -    /* Precompute how much storage we need to hold the headers */
  +    /*
  +     * 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
  +     */
  +    rc = serialize_table(&mobj->req_hdrs,
  +                         &mobj->num_req_hdrs,
  +                         r->headers_in);
  +    if (rc != APR_SUCCESS) {
  +        return rc;
  +    }
       rc = serialize_table(&mobj->header_out, 
                            &mobj->num_header_out, 
                            r->headers_out);   
  @@ -710,14 +728,14 @@
       if (info->lastmod) {
           obj->info.lastmod = info->lastmod;
       }
  -    if (info->expire) {
  -        obj->info.expire = info->expire;
  -    }
       if (info->response_time) {
           obj->info.response_time = info->response_time;
       }
       if (info->request_time) {
           obj->info.request_time = info->request_time;
  +    }
  +    if (info->expire) {
  +        obj->info.expire = info->expire;
       }
       if (info->content_type) {
           obj->info.content_type = (char*) calloc(1, strlen(info->content_type) + 1);
  
  
  
  1.43      +5 -18     httpd-2.0/modules/experimental/mod_cache.c
  
  Index: mod_cache.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/mod_cache.c,v
  retrieving revision 1.42
  retrieving revision 1.43
  diff -u -r1.42 -r1.43
  --- mod_cache.c	17 May 2002 11:33:09 -0000	1.42
  +++ mod_cache.c	5 Jun 2002 03:45:13 -0000	1.43
  @@ -152,8 +152,6 @@
        * - RFC2616 14.9.2 Cache-Control: no-store
        * - Pragma: no-cache
        * - Any requests requiring authorization.
  -     * - Any URLs whose length exceeds MAX_URL_LENGTH
  -     * - TODO: Make MAX_URL_LENGTH a config directive?
        */
       if (conf->ignorecachecontrol_set == 1 && conf->ignorecachecontrol ==
1 && auth == NULL) {
           ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
  @@ -199,12 +197,13 @@
           return DECLINED;
       }
       else if (OK == rv) {
  -        /* cache file exists */
  +        /* RFC2616 13.2 - Check cache object expiration */
  +        cache->fresh = ap_cache_check_freshness(cache, r);
           if (cache->fresh) {
  +            /* fresh data available */
               apr_bucket_brigade *out;
               conn_rec *c = r->connection;
   
  -            /* fresh data available */
               if (lookup) {
                   return OK;
               }
  @@ -214,17 +213,8 @@
   
               /* We are in the quick handler hook, which means that no output
                * filters have been set. So lets run the insert_filter hook.
  -             * XXX - Should we be inserting filters in the output stream
  -             * for proxy requests? Certainly we need the core filters
  -             * (byterange, chunking, etc.).  I can also see the need to
  -             * conditionally insert tag processing filters (e.g. INCLUDES).
                */
               ap_run_insert_filter(r);
  -
  -            /* Now add the cache_out filter. cache_out is a FTYPE_CONTENT
  -             * which means it will be inserted first in the stream, which
  -             * is exactly what we need.
  -             */
               ap_add_output_filter("CACHE_OUT", NULL, r, r->connection);
   
               /* kick off the filter stack */
  @@ -252,7 +242,7 @@
                                r->server,
                                "cache: conditional - add cache_in filter and "
                                "DECLINE");
  -
  +                /* Why not add CACHE_CONDITIONAL? */
                   ap_add_output_filter("CACHE_IN", NULL, r, r->connection);
   
                   return DECLINED;
  @@ -339,10 +329,7 @@
       ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
               "cache: running CACHE_OUT filter");
   
  -    /* TODO: Handle getting errors on either of these calls 
  -     * ???: Should we return headers on a subrequest?
  -     */
  -    cache_read_entity_headers(cache->handle, r);    
  +    /* cache_read_entity_headers() was called in cache_select_url() */
       cache_read_entity_body(cache->handle, r->pool, bb);
   
       /* This filter is done once it has served up its content */
  
  
  
  1.27      +13 -1     httpd-2.0/modules/experimental/mod_cache.h
  
  Index: mod_cache.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/mod_cache.h,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -u -r1.26 -r1.27
  --- mod_cache.h	3 Apr 2002 17:34:01 -0000	1.26
  +++ mod_cache.h	5 Jun 2002 03:45:13 -0000	1.27
  @@ -198,6 +198,11 @@
       apr_time_t request_time;
       apr_time_t response_time;
       apr_size_t len;
  +    apr_time_t ims;    /*  If-Modified_Since header value    */
  +    apr_time_t ius;    /*  If-UnModified_Since header value    */
  +    const char *im;         /* If-Match header value */
  +    const char *inm;         /* If-None-Match header value */
  +    apr_table_t *req_hdrs;   /* These are the original request headers   */
   };
   
   /* cache handle information */
  @@ -238,9 +243,16 @@
   
   
   /* cache_util.c */
  +/* do a HTTP/1.1 age calculation */
  +CACHE_DECLARE(apr_time_t) ap_cache_current_age(cache_info *info, const apr_time_t age_value);
  +
   /**
  - *
  + * Check the freshness of the cache object per RFC2616 section 13.2 (Expiration Model)
  + * @param cache cache_request_rec
  + * @param r request_rec
  + * @return 0 ==> cache object is stale, 1 ==> cache object is fresh
    */
  +CACHE_DECLARE(int) ap_cache_check_freshness(cache_request_rec *cache, request_rec *r);
   CACHE_DECLARE(apr_time_t) ap_cache_hex2usec(const char *x);
   CACHE_DECLARE(void) ap_cache_usec2hex(apr_time_t j, char *y);
   CACHE_DECLARE(char *) generate_name(apr_pool_t *p, int dirlevels, 
  
  
  

Mime
View raw message