From: rousskov <> Date: Thu, 4 Jun 1998 04:32:56 +0000 (+0000) Subject: - added ETag.c X-Git-Tag: SQUID_3_0_PRE1~3183 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a9771e5195a68c460a54856a84cb6271cb02fc46;p=thirdparty%2Fsquid.git - added ETag.c - added processing of If-Range headers --- diff --git a/ChangeLog b/ChangeLog index c71a0a27d0..ab8fb0f982 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,9 +3,12 @@ potentially cachable Range request for a not cached object, Squid requests the whole object from origin server and then replies with specified range(s) to the client. Multi-range requests are - supported. Limitations: If-Range header is not supported and - ignored; Multi-range requests with out of order or overlapping - ranges are not supported. + supported. If-Range requests are supported. Limitations: + Multi-range requests with out of order or overlapping ranges are not + supported. + - Made md5.c use standard memcpy and memset if they are avaliable. + - Memory pools will now shrink if Squid is run-time reconfigured with + smaller value of memory_pools_limit tag. Changes to squid-1.2.beta22 (June 1, 1998): diff --git a/src/ETag.cc b/src/ETag.cc new file mode 100644 index 0000000000..f197e2060f --- /dev/null +++ b/src/ETag.cc @@ -0,0 +1,64 @@ + +/* + * $Id: ETag.cc,v 1.1 1998/06/03 22:32:57 rousskov Exp $ + * + * DEBUG: section 7? HTTP ETag + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "squid.h" + +/* + * Note: ETag is not an http "field" like, for example HttpHdrRange. ETag is a + * field-value that maybe used in many http fields. + */ + +/* parses a string as weak or strong entity-tag; returns true on success */ +/* note: we do not duplicate "str"! */ +int +etagParseInit(ETag *etag, const char *str) +{ + int len; + assert(etag && str); + etag->str = NULL; + etag->weak = !strncmp(str, "W/", 2); + if (etag->weak) + str += 2; + /* check format (quoted-string) */ + len = strlen(str); + if (len >= 2 && str[0] == '"' && str[len-1] == '"') + etag->str = str; + return etag->str != NULL; +} + +/* returns true if etags are equal */ +int +etagIsEqual(const ETag *tag1, const ETag *tag2) +{ + assert(tag1 && tag2); + assert(!tag1->weak && !tag2->weak); /* weak comparison not implemented yet */ + return !strcmp(tag1->str, tag2->str); +} diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc index 6d566f3391..ee456cd34b 100644 --- a/src/HttpHeader.cc +++ b/src/HttpHeader.cc @@ -1,6 +1,6 @@ /* - * $Id: HttpHeader.cc,v 1.42 1998/06/03 15:52:15 rousskov Exp $ + * $Id: HttpHeader.cc,v 1.43 1998/06/03 22:32:58 rousskov Exp $ * * DEBUG: section 55 HTTP Header * AUTHOR: Alex Rousskov @@ -94,12 +94,12 @@ static const HttpHeaderFieldAttrs HeadersAttrs[] = {"Content-Range", HDR_CONTENT_RANGE, ftPContRange}, {"Content-Type", HDR_CONTENT_TYPE, ftStr}, {"Date", HDR_DATE, ftDate_1123}, - {"ETag", HDR_ETAG, ftStr}, /* for now */ + {"ETag", HDR_ETAG, ftETag}, {"Expires", HDR_EXPIRES, ftDate_1123}, {"From", HDR_FROM, ftStr}, {"Host", HDR_HOST, ftStr}, {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123}, - {"If-Range", HDR_IF_RANGE, ftDate_1123}, /* for now (ftDate_1123 or ftStr!) */ + {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag}, {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123}, {"Link", HDR_LINK, ftStr}, {"Location", HDR_LOCATION, ftStr}, @@ -824,6 +824,41 @@ httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *authSche return base64_decode(field); } +ETag +httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id) +{ + ETag etag = { NULL, -1 }; + HttpHeaderEntry *e; + assert(Headers[id].type == ftETag); /* must be of an appropriate type */ + if ((e = httpHeaderFindEntry(hdr, id))) + etagParseInit(&etag, strBuf(e->value)); + return etag; +} + +TimeOrTag +httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id) +{ + TimeOrTag tot; + HttpHeaderEntry *e; + assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */ + memset(&tot, 0, sizeof(tot)); + if ((e = httpHeaderFindEntry(hdr, id))) { + const char *str = strBuf(e->value); + /* try as an ETag */ + if (etagParseInit(&tot.tag, str)) { + tot.valid = tot.tag.str != NULL; + tot.time = -1; + } else { + /* or maybe it is time? */ + tot.time = parse_rfc1123(str); + tot.valid = tot.time >= 0; + tot.tag.str = NULL; + } + } + assert(tot.time < 0 || !tot.tag.str); /* paranoid */ + return tot; +} + /* * HttpHeaderEntry */ diff --git a/src/HttpHeaderTools.cc b/src/HttpHeaderTools.cc index 801e0b730e..efefb5fb5d 100644 --- a/src/HttpHeaderTools.cc +++ b/src/HttpHeaderTools.cc @@ -1,5 +1,5 @@ /* - * $Id: HttpHeaderTools.cc,v 1.18 1998/06/02 21:38:06 rousskov Exp $ + * $Id: HttpHeaderTools.cc,v 1.19 1998/06/03 22:32:59 rousskov Exp $ * * DEBUG: section 66 HTTP Header Tools * AUTHOR: Alex Rousskov @@ -154,6 +154,7 @@ httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, size_t ent_len) httpHdrContRangeDestroy(cr); } + /* * return true if a given directive is found in at least one of the "connection" header-fields * note: if HDR_PROXY_CONNECTION is present we ignore HDR_CONNECTION diff --git a/src/Makefile.in b/src/Makefile.in index f2ae25a6f8..07a2146cfe 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ # # Makefile for the Squid Object Cache server # -# $Id: Makefile.in,v 1.151 1998/05/28 22:57:55 wessels Exp $ +# $Id: Makefile.in,v 1.152 1998/06/03 22:32:59 rousskov Exp $ # # Uncomment and customize the following to suit your needs: # @@ -92,6 +92,7 @@ OBJS = \ disk.o \ dns.o \ errorpage.o \ + ETag.o \ event.o \ fd.o \ filemap.o \ diff --git a/src/client_side.cc b/src/client_side.cc index a2a0078a3f..3d44d58989 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $Id: client_side.cc,v 1.327 1998/06/02 23:29:03 rousskov Exp $ + * $Id: client_side.cc,v 1.328 1998/06/03 22:33:00 rousskov Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -1057,6 +1057,36 @@ clientBuildReplyHeader(clientHttpRequest * http, } #endif +/* returns true if If-Range specs match reply, false otherwise */ +static int +clientIfRangeMatch(clientHttpRequest * http, HttpReply * rep) +{ + const TimeOrTag spec = httpHeaderGetTimeOrTag(&http->request->header, HDR_IF_RANGE); + /* check for parsing falure */ + if (!spec.valid) + return 0; + /* got an ETag? */ + if (spec.tag.str) { + ETag rep_tag = httpHeaderGetETag(&rep->header, HDR_ETAG); + debug(33,3) ("clientIfRangeMatch: ETags: %s and %s\n", + spec.tag.str, rep_tag.str ? rep_tag.str : ""); + if (!rep_tag.str) + return 0; /* entity has no etag to compare with! */ + if (spec.tag.weak || rep_tag.weak) { + debug(33,1) ("clientIfRangeMatch: Weak ETags are not in If-Range: %s ? %s\n", + spec.tag.str, rep_tag.str); + return 0; /* must use strong validator for sub-range requests */ + } + return etagIsEqual(&rep_tag, &spec.tag); + } + /* got modification time? */ + if (spec.time >= 0) { + return http->entry->lastmod <= spec.time; + } + assert(0); /* should not happen */ + return 0; +} + /* adds appropriate Range headers if needed */ static void clientBuildRangeHeader(clientHttpRequest * http, HttpReply * rep) @@ -1077,6 +1107,8 @@ clientBuildRangeHeader(clientHttpRequest * http, HttpReply * rep) if (rep->content_length != http->entry->mem_obj->reply->content_length) range_err = "INCONSISTENT length"; /* a bug? */ else + if (httpHeaderHas(&http->request->header, HDR_IF_RANGE) && !clientIfRangeMatch(http, rep)) + range_err = "If-Range match failed"; if (!httpHdrRangeCanonize(http->request->range, rep->content_length)) range_err = "canonization failed"; else @@ -1092,6 +1124,8 @@ clientBuildRangeHeader(clientHttpRequest * http, HttpReply * rep) debug(33, 2) ("clientBuildRangeHeader: range spec count: %d clen: %d\n", spec_count, rep->content_length); assert(spec_count > 0); + /* ETags should not be returned with Partial Content replies? */ + httpHeaderDelById(hdr, HDR_ETAG); /* append appropriate header(s) */ if (spec_count == 1) { HttpHdrRangePos pos = HttpHdrRangeInitPos; diff --git a/src/enums.h b/src/enums.h index a3780bbdab..acffe8b553 100644 --- a/src/enums.h +++ b/src/enums.h @@ -258,9 +258,11 @@ typedef enum { ftInt, ftStr, ftDate_1123, + ftETag, ftPCc, + ftPContRange, ftPRange, - ftPContRange + ftDate_1123_or_ETag } field_type; /* possible owners of http header */ diff --git a/src/http.cc b/src/http.cc index ed7e7a0cee..318ceb5a2a 100644 --- a/src/http.cc +++ b/src/http.cc @@ -1,6 +1,6 @@ /* - * $Id: http.cc,v 1.280 1998/06/02 21:46:02 rousskov Exp $ + * $Id: http.cc,v 1.281 1998/06/03 22:33:02 rousskov Exp $ * * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) * AUTHOR: Harvest Derived @@ -658,6 +658,7 @@ httpBuildRequestHeader(request_t * request, } break; case HDR_RANGE: + case HDR_IF_RANGE: if (!filter_range) httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); break; diff --git a/src/protos.h b/src/protos.h index 3d562af27f..7a88ca9a73 100644 --- a/src/protos.h +++ b/src/protos.h @@ -240,6 +240,10 @@ extern int httpAnonHdrAllowed(http_hdr_type hdr_id); extern int httpAnonHdrDenied(http_hdr_type hdr_id); extern void httpBuildRequestHeader(request_t *, request_t *, StoreEntry *, HttpHeader *, int, int); +/* ETag */ +extern int etagParseInit(ETag *etag, const char *str); +extern int etagIsEqual(const ETag *tag1, const ETag *tag2); + /* Http Status Line */ /* init/clean */ extern void httpStatusLineInit(HttpStatusLine * sline); @@ -350,12 +354,14 @@ extern void httpHeaderPutTime(HttpHeader * hdr, http_hdr_type type, time_t time) extern void httpHeaderPutStr(HttpHeader * hdr, http_hdr_type type, const char *str); extern void httpHeaderPutAuth(HttpHeader * hdr, const char *authScheme, const char *realm); extern void httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc); -extern void httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr); -extern void httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range); +extern void httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange *cr); +extern void httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange *range); extern void httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value); extern int httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id); extern time_t httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id); +extern TimeOrTag httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id); extern HttpHdrCc *httpHeaderGetCc(const HttpHeader * hdr); +extern ETag httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id); extern HttpHdrRange *httpHeaderGetRange(const HttpHeader * hdr); extern HttpHdrContRange *httpHeaderGetContRange(const HttpHeader * hdr); extern const char *httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id); diff --git a/src/structs.h b/src/structs.h index d421fe808d..f5aa4158f6 100644 --- a/src/structs.h +++ b/src/structs.h @@ -409,6 +409,16 @@ struct _dwrite_q { FREE *free_func; }; + +/* ETag support is rudimantal; + * this struct is likely to change + * Note: "str" points to memory in HttpHeaderEntry (for now) + * so ETags should be used as tmp variables only (for now) */ +struct _ETag { + const char *str; /* quoted-string */ + int weak; /* true if it is a weak validator */ +}; + struct _fde { unsigned int type; unsigned int open; @@ -522,6 +532,13 @@ struct _HttpHdrContRange { size_t elength; /* entity length, not content length */ }; +/* some fields can hold either time or etag specs (e.g. If-Range) */ +struct _TimeOrTag { + ETag tag; /* entity tag */ + time_t time; + int valid; /* true if struct is usable */ +}; + /* data for iterating thru range specs */ struct _HttpHdrRangeIter { HttpHdrRangeSpec spec; diff --git a/src/typedefs.h b/src/typedefs.h index b88b17fd54..a82a2ae3d7 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -44,6 +44,7 @@ typedef struct _dread_ctrl dread_ctrl; typedef struct _dnsserver_t dnsserver_t; typedef struct _dnsStatData dnsStatData; typedef struct _dwrite_q dwrite_q; +typedef struct _ETag ETag; typedef struct _fde fde; typedef struct _fileMap fileMap; typedef struct _fqdncache_entry fqdncache_entry; @@ -60,6 +61,7 @@ typedef struct _HttpHdrRangeSpec HttpHdrRangeSpec; typedef struct _HttpHdrRange HttpHdrRange; typedef struct _HttpHdrRangeIter HttpHdrRangeIter; typedef struct _HttpHdrContRange HttpHdrContRange; +typedef struct _TimeOrTag TimeOrTag; typedef struct _HttpHeaderEntry HttpHeaderEntry; typedef struct _HttpHeaderFieldStat HttpHeaderFieldStat; typedef struct _HttpBody HttpBody;