coar        97/09/12 11:56:13

  Modified:    src/main http_core.c http_protocol.c http_protocol.h
                        util_script.c
               src/modules/standard mod_include.c
  Log:
        Separate last-modified and etag processing into separate routines,
        and insert calls in appropriate places.  (Lotta sins covered by
        that sentence..)
  
  Reviewed by:  Roy Fielding
  
  Revision  Changes    Path
  1.121     +7 -3      apachen/src/main/http_core.c
  
  Index: http_core.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/http_core.c,v
  retrieving revision 1.120
  retrieving revision 1.121
  diff -u -r1.120 -r1.121
  --- http_core.c       1997/09/11 18:46:44     1.120
  +++ http_core.c       1997/09/12 18:56:01     1.121
  @@ -1658,9 +1658,13 @@
           return FORBIDDEN;
       }
        
  -    if ((errstatus = set_last_modified (r, r->finfo.st_mtime))
  -     || (errstatus = set_content_length (r, r->finfo.st_size)))
  -        return errstatus;
  +    update_mtime (r, r->finfo.st_mtime);
  +    set_last_modified(r);
  +    set_etag(r);
  +    if (((errstatus = meets_conditions(r)) != OK)
  +     || (errstatus = set_content_length (r, r->finfo.st_size))) {
  +         return errstatus;
  +    }
   
   #ifdef USE_MMAP_FILES
       block_alarms();
  
  
  
  1.161     +126 -62   apachen/src/main/http_protocol.c
  
  Index: http_protocol.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/http_protocol.c,v
  retrieving revision 1.160
  retrieving revision 1.161
  diff -u -r1.160 -r1.161
  --- http_protocol.c   1997/09/06 01:03:33     1.160
  +++ http_protocol.c   1997/09/12 18:56:02     1.161
  @@ -348,10 +348,15 @@
       return 0;
   }
   
  -API_EXPORT(int) set_last_modified(request_rec *r, time_t mtime)
  +/*
  + * Return the latest rational time from a request/mtime (modification time)
  + * pair.  We return the mtime unless it's in the future, in which case we
  + * return the current time.  We use the request time as a reference in order
  + * to limit the number of calls to time().  We don't check for futurosity
  + * unless the mtime is at least as new as the reference.
  + */
  +API_EXPORT(time_t) rationalize_mtime(request_rec *r, time_t mtime)
   {
  -    char *etag, weak_etag[MAX_STRING_LEN];
  -    char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
       time_t now;
   
       /* For all static responses, it's almost certain that the file was
  @@ -359,90 +364,97 @@
        * no reason to call time(NULL) again.  But if the response has been
        * created on demand, then it might be newer than the time the request
        * started.  In this event we really have to call time(NULL) again
  -     * so that we can give the clients the most accurate Last-Modified.
  -     */
  -    now = (mtime <= r->request_time) ? r->request_time : time(NULL);
  -
  -    table_set(r->headers_out, "Last-Modified",
  -              gm_timestr_822(r->pool, (mtime > now) ? now : mtime));
  -
  -    /* Make an ETag header out of various pieces of information. We use
  -     * the last-modified date and, if we have a real file, the
  -     * length and inode number - note that this doesn't have to match
  -     * the content-length (i.e. includes), it just has to be unique
  -     * for the file.
  -     *
  -     * If the request was made within a second of the last-modified date,
  -     * we send a weak tag instead of a strong one, since it could
  -     * be modified again later in the second, and the validation
  -     * would be incorrect.
  -     */
  +     * so that we can give the clients the most accurate Last-Modified.  If 
we
  +     * were given a time in the future, we return the current time - the
  +     * Last-Modified can't be in the future.
  +     */
  +    now = (mtime < r->request_time) ? r->request_time : time(NULL);
  +    return (mtime > now) ? now : mtime;
  +}
   
  -    if (r->finfo.st_mode != 0)
  -        ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx-%lx-%lx\"", 
  -             (unsigned long)r->finfo.st_ino,
  -             (unsigned long)r->finfo.st_size, (unsigned long)mtime);
  -    else
  -        ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx\"",
  -             (unsigned long)mtime);
  -
  -    etag = weak_etag + ((r->request_time - mtime > 1) ? 2 : 0);
  -    table_set(r->headers_out, "ETag", etag);
  +API_EXPORT(int) meets_conditions(request_rec *r)
  +{
  +    char *etag = table_get(r->headers_out, "ETag");
  +    char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
  +    time_t mtime;
   
  -    /* Check for conditional requests --- note that we only want to do
  +    /*
  +     * Check for conditional requests --- note that we only want to do
        * this if we are successful so far and we are not processing a
        * subrequest or an ErrorDocument.
        *
  -     * The order of the checks is important, since etag checks are supposed
  +     * The order of the checks is important, since ETag checks are supposed
        * to be more accurate than checks relative to the modification time.
  +     * However, not all documents are guaranteed to *have* ETags, and some
  +     * might have Last-Modified values w/o ETags, so this gets a little
  +     * complicated.
        */
  -    
  -    if (!is_HTTP_SUCCESS(r->status) || r->no_local_copy)
  +
  +    if (!is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
           return OK;
  +    }
  +
  +    mtime = (r->mtime != 0) ? r->mtime : time(NULL);
   
  -    /* If an If-Match request-header field was given and
  -     * if our ETag does not match any of the entity tags in that field
  -     * and the field value is not "*" (meaning match anything), then
  -     *    respond with a status of 412 (Precondition Failed).
  +    /*
  +     * If an If-Match request-header field was given
  +     * AND if our ETag does not match any of the entity tags in that field
  +     * AND the field value is not "*" (meaning match anything), then
  +     *     respond with a status of 412 (Precondition Failed).
        */
   
       if ((if_match = table_get(r->headers_in, "If-Match")) != NULL) {
  -        if ((if_match[0] != '*') && !find_token(r->pool, if_match, etag))
  -            return HTTP_PRECONDITION_FAILED;
  +     if ((etag == NULL) ||
  +         ((if_match[0] != '*') && !find_token(r->pool, if_match, etag))) {
  +         return HTTP_PRECONDITION_FAILED;
  +     }
       }
   
  -    /* Else if a valid If-Unmodified-Since request-header field was given
  -     * and the requested resource has been modified since the time
  +    /*
  +     * Else if a valid If-Unmodified-Since request-header field was given
  +     * AND the requested resource has been modified since the time
        * specified in this field, then the server MUST
  -     *    respond with a status of 412 (Precondition Failed).
  +     *     respond with a status of 412 (Precondition Failed).
        */
   
  -    else if ((if_unmodified = table_get(r->headers_in, 
"If-Unmodified-Since"))
  -             != NULL) {
  -        time_t ius = parseHTTPdate(if_unmodified);
  -
  -        if ((ius != BAD_DATE) && (mtime > ius))
  -            return HTTP_PRECONDITION_FAILED;
  +    else {
  +     if_unmodified = table_get(r->headers_in, "If-Unmodified-Since");
  +     if (if_unmodified != NULL) {
  +         time_t ius = parseHTTPdate(if_unmodified);
  +
  +         if ((ius != BAD_DATE) && (mtime > ius)) {
  +             return HTTP_PRECONDITION_FAILED;
  +         }
  +     }
       }
   
  -    /* If an If-None-Match request-header field was given and
  -     * if our ETag matches any of the entity tags in that field or
  -     * if the field value is "*" (meaning match anything), then
  +    /*
  +     * If an If-None-Match request-header field was given
  +     * AND if our ETag matches any of the entity tags in that field
  +     * OR if the field value is "*" (meaning match anything), then
        *    if the request method was GET or HEAD, the server SHOULD
        *       respond with a 304 (Not Modified) response.
        *    For all other request methods, the server MUST
        *       respond with a status of 412 (Precondition Failed).
        */
   
  -    if ((if_nonematch = table_get(r->headers_in, "If-None-Match")) != NULL) {
  -        if ((if_nonematch[0] == '*') || 
find_token(r->pool,if_nonematch,etag))
  -            return (r->method_number == M_GET) ? HTTP_NOT_MODIFIED
  -                                               : HTTP_PRECONDITION_FAILED;
  +    if_nonematch = table_get(r->headers_in, "If-None-Match");
  +    if (if_nonematch != NULL) {
  +     int rstatus;
  +
  +     if ((if_nonematch[0] == '*')
  +         || ((etag != NULL) && find_token(r->pool, if_nonematch, etag))) {
  +         rstatus = (r->method_number == M_GET)
  +                     ? HTTP_NOT_MODIFIED
  +                     : HTTP_PRECONDITION_FAILED;
  +         return rstatus;
  +     }
       }
   
  -    /* Else if a valid If-Modified-Since request-header field was given
  -     * and it is a GET or HEAD request
  -     * and the requested resource has not been modified since the time
  +    /*
  +     * Else if a valid If-Modified-Since request-header field was given
  +     * AND it is a GET or HEAD request
  +     * AND the requested resource has not been modified since the time
        * specified in this field, then the server MUST
        *    respond with a status of 304 (Not Modified).
        * A date later than the server's current request time is invalid.
  @@ -452,11 +464,63 @@
                 table_get(r->headers_in, "If-Modified-Since")) != NULL)) {
           time_t ims = parseHTTPdate(if_modified_since);
   
  -        if ((ims >= mtime) && (ims <= r->request_time))
  +        if ((ims >= mtime) && (ims <= r->request_time)) {
               return HTTP_NOT_MODIFIED;
  +     }
       }
  -
       return OK;
  +}
  +
  +/*
  + * Construct an entity tag (ETag) from resource information.  If it's a real
  + * file, build in some of the file characteristics.  If the modification time
  + * is newer than (request-time minus 1 second), mark the ETag as weak - it
  + * could be modified again in as short an interval.  We rationalize the
  + * modification time we're given to keep it from being in the future.
  + */
  +API_EXPORT(void) set_etag(request_rec *r)
  +{
  +    char *etag, weak_etag[MAX_STRING_LEN];
  +
  +    /*
  +     * Make an ETag header out of various pieces of information. We use
  +     * the last-modified date and, if we have a real file, the
  +     * length and inode number - note that this doesn't have to match
  +     * the content-length (i.e. includes), it just has to be unique
  +     * for the file.
  +     *
  +     * If the request was made within a second of the last-modified date,
  +     * we send a weak tag instead of a strong one, since it could
  +     * be modified again later in the second, and the validation
  +     * would be incorrect.
  +     */
  +
  +    if (r->finfo.st_mode != 0) {
  +        ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx-%lx-%lx\"", 
  +                 (unsigned long)r->finfo.st_ino,
  +                 (unsigned long)r->finfo.st_size,
  +                 (unsigned long)r->mtime);
  +    }
  +    else {
  +        ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx\"",
  +                 (unsigned long)r->mtime);
  +    }
  +
  +    etag = weak_etag + ((r->request_time - r->mtime > 1) ? 2 : 0);
  +    table_set(r->headers_out, "ETag", etag);
  +}
  +
  +/*
  + * This function sets the Last-Modified output header field to the value
  + * of the mtime field in the request structure - rationalized to keep it from
  + * being in the future.
  + */
  +API_EXPORT(void) set_last_modified(request_rec *r)
  +{
  +    time_t mod_time = rationalize_mtime(r, r->mtime);
  +
  +    table_set(r->headers_out, "Last-Modified",
  +           gm_timestr_822(r->pool, mod_time));
   }
   
   /* Get a line of protocol input, including any continuation lines
  
  
  
  1.27      +3 -1      apachen/src/main/http_protocol.h
  
  Index: http_protocol.h
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/http_protocol.h,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -u -r1.26 -r1.27
  --- http_protocol.h   1997/08/18 07:19:36     1.26
  +++ http_protocol.h   1997/09/12 18:56:04     1.27
  @@ -95,7 +95,9 @@
   
   API_EXPORT(int) set_content_length (request_rec *r, long length);
   int set_keepalive (request_rec *r);
  -API_EXPORT(int) set_last_modified (request_rec *r, time_t mtime);
  +API_EXPORT(time_t) rationalize_mtime(request_rec *r, time_t mtime);
  +API_EXPORT(void) set_etag(request_rec *r);
  +API_EXPORT(void) set_last_modified(request_rec *r);
   
   /* Other ways to send stuff at the client.  All of these keep track
    * of bytes_sent automatically.  This indirection is intended to make
  
  
  
  1.71      +38 -2     apachen/src/main/util_script.c
  
  Index: util_script.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/util_script.c,v
  retrieving revision 1.70
  retrieving revision 1.71
  diff -u -r1.70 -r1.71
  --- util_script.c     1997/08/31 21:28:54     1.70
  +++ util_script.c     1997/09/12 18:56:05     1.71
  @@ -323,12 +323,13 @@
   }
   
   
  -static int scan_script_header_err_core (request_rec *r, char *buffer,
  +static int scan_script_header_err_core(request_rec *r, char *buffer,
       int (*getsfunc)(char *, int, void *), void *getsfunc_data)
   {
       char x[MAX_STRING_LEN];
       char *w, *l;
       int p;
  +    int cgi_status = HTTP_OK;
   
       if (buffer) *buffer = '\0';
       w = buffer ? buffer : x;
  @@ -352,9 +353,26 @@
            else w[p-1] = '\0';
        }
   
  +        /*
  +      * If we've finished reading the headers, check to make sure any
  +      * HTTP/1.1 conditions are met.  If so, we're done; normal processing
  +      * will handle the script's output.  If not, just return the error.
  +      * The appropriate thing to do would be to send the script process a
  +      * SIGPIPE to let it know we're ignoring it, close the channel to the
  +      * script process, and *then* return the failed-to-meet-condition
  +      * error.  Otherwise we'd be waiting for the script to finish
  +      * blithering before telling the client the output was no good.
  +      * However, we don't have the information to do that, so we have to
  +      * leave it to an upper layer.
  +      */
           if (w[0] == '\0') {
  +         int cond_status = OK;
  +
            kill_timeout (r);
  -         return OK;
  +         if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) {
  +             cond_status = meets_conditions(r);
  +         }
  +         return cond_status;
        }
                                      
        /* if we see a bogus header don't ignore it. Shout and scream */
  @@ -399,6 +417,24 @@
           }   
           else if(!strcasecmp(w,"Transfer-Encoding")) {
            table_set (r->headers_out, w, l);
  +        }   
  +/*
  + * If the script gave us a Last-Modified header, we can't just pass it on
  + * blindly because of restrictions on future values.
  + */
  +        else if (!strcasecmp(w, "Last-Modified")) {
  +         time_t mtime = parseHTTPdate(l);
  +
  +         update_mtime(r, mtime);
  +         set_last_modified(r);
  +        }   
  +/*
  + * If the script returned a specific status, that's what we'll use - 
otherwise
  + * we assume 200 OK.
  + */
  +        else if (!strcasecmp(w, "Status")) {
  +         table_set (r->headers_out, w, l);
  +         cgi_status = atoi(l);
           }   
   
   /* The HTTP specification says that it is legal to merge duplicate
  
  
  
  1.50      +8 -3      apachen/src/modules/standard/mod_include.c
  
  Index: mod_include.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/modules/standard/mod_include.c,v
  retrieving revision 1.49
  retrieving revision 1.50
  diff -u -r1.49 -r1.50
  --- mod_include.c     1997/09/05 23:11:14     1.49
  +++ mod_include.c     1997/09/12 18:56:10     1.50
  @@ -2008,13 +2008,18 @@
           return FORBIDDEN;
       }
   
  -    if (*state == xbithack_full
  +    if ((*state == xbithack_full)
   #if !defined(__EMX__) && !defined(WIN32)
       /*  OS/2 dosen't support Groups. */
           && (r->finfo.st_mode & S_IXGRP)
   #endif
  -        && (errstatus = set_last_modified (r, r->finfo.st_mtime)))
  -        return errstatus;
  +       ) {
  +     update_mtime(r, r->finfo.st_mtime);
  +     set_last_modified(r);
  +    }
  +    if ((errstatus = meets_conditions(r)) != OK) {
  +     return errstatus;
  +    }
   
       send_http_header(r);
   
  
  
  

Reply via email to