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);