This patch is generated from the etag-2_4 branch of vary-2_4 in squid Tue Aug 17 18:55:45 2004 GMT See http://devel.squid-cache.org/ Index: squid/include/version.h diff -u squid/include/version.h:1.3.4.1.4.4 squid/include/version.h:1.3.4.1.4.2.2.2 --- squid/include/version.h:1.3.4.1.4.4 Tue Oct 16 14:59:04 2001 +++ squid/include/version.h Tue Oct 16 14:58:56 2001 @@ -4,7 +4,7 @@ * SQUID_VERSION - String for version id of this distribution */ #ifndef SQUID_VERSION -#define SQUID_VERSION "2.4.STABLE2-vary" +#define SQUID_VERSION "2.4.STABLE2-etag" #endif #ifndef SQUID_RELEASE_TIME Index: squid/src/ETag.c diff -u squid/src/ETag.c:1.3.4.1 squid/src/ETag.c:removed --- squid/src/ETag.c:1.3.4.1 Mon Jan 15 14:49:17 2001 +++ squid/src/ETag.c Tue Aug 17 11:58:28 2004 @@ -1,68 +0,0 @@ - -/* - * $Id: squid-etag-2_4-vary-2_4,v 1.2 2004/09/29 00:22:50 hno Exp $ - * - * DEBUG: none ETag parsing support - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111, 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); -} Index: squid/src/HttpHeader.c diff -u squid/src/HttpHeader.c:1.3.4.2.2.3 squid/src/HttpHeader.c:1.3.4.2.2.3.4.2 --- squid/src/HttpHeader.c:1.3.4.2.2.3 Thu Feb 1 05:59:31 2001 +++ squid/src/HttpHeader.c Fri Jun 1 09:18:26 2001 @@ -86,7 +86,7 @@ {"Content-Type", HDR_CONTENT_TYPE, ftStr}, {"Cookie", HDR_COOKIE, ftStr}, {"Date", HDR_DATE, ftDate_1123}, - {"ETag", HDR_ETAG, ftETag}, + {"ETag", HDR_ETAG, ftStr}, {"Expires", HDR_EXPIRES, ftDate_1123}, {"From", HDR_FROM, ftStr}, {"Host", HDR_HOST, ftStr}, @@ -898,18 +898,6 @@ 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) { @@ -920,17 +908,20 @@ 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; + if (*str == '"' || (str[0] == 'W' && str[1] == '/')) { + tot.tag = str; tot.time = -1; + tot.valid = 1; } else { /* or maybe it is time? */ tot.time = parse_rfc1123(str); - tot.valid = tot.time >= 0; - tot.tag.str = NULL; + if (tot.time >= 0) + tot.valid = 1; + tot.tag = NULL; } + } else { + tot.time = -1; } - assert(tot.time < 0 || !tot.tag.str); /* paranoid */ return tot; } Index: squid/src/HttpReply.c diff -u squid/src/HttpReply.c:1.3.4.3.2.1 squid/src/HttpReply.c:1.3.4.3.2.1.4.1 --- squid/src/HttpReply.c:1.3.4.3.2.1 Thu Jan 18 07:29:04 2001 +++ squid/src/HttpReply.c Thu May 31 08:39:12 2001 @@ -209,7 +209,7 @@ httpPacked304Reply(const HttpReply * rep) { static const http_hdr_type ImsEntries[] = - {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER}; + {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_ETAG, /* eof */ HDR_OTHER}; int t; MemBuf mb; Packer p; Index: squid/src/HttpRequest.c diff -u squid/src/HttpRequest.c:1.3.4.1.2.1 squid/src/HttpRequest.c:1.3.4.1.2.1.4.1 --- squid/src/HttpRequest.c:1.3.4.1.2.1 Tue Jan 16 09:07:10 2001 +++ squid/src/HttpRequest.c Thu May 31 08:39:12 2001 @@ -64,6 +64,13 @@ httpHdrCcDestroy(req->cache_control); if (req->range) httpHdrRangeDestroy(req->range); + if (req->vary) { + if (req->etags == &req->vary->etags) + req->etags = NULL; + storeLocateVaryDone(req->vary); + } + assert(req->etags == NULL); + safe_free(req->etag); memFree(req, MEM_REQUEST_T); } Index: squid/src/Makefile.in diff -u squid/src/Makefile.in:1.3.4.1.2.1.2.2 squid/src/Makefile.in:1.3.4.1.2.1.2.2.2.1 --- squid/src/Makefile.in:1.3.4.1.2.1.2.2 Fri Apr 13 12:10:53 2001 +++ squid/src/Makefile.in Fri Jun 1 09:14:56 2001 @@ -107,7 +107,6 @@ disk.o \ @DNS_OBJS@ \ errorpage.o \ - ETag.o \ event.o \ fd.o \ filemap.o \ Index: squid/src/client_side.c diff -u squid/src/client_side.c:1.5.2.5.2.9.2.2 squid/src/client_side.c:1.5.2.5.2.9.2.2.2.3 --- squid/src/client_side.c:1.5.2.5.2.9.2.2 Thu May 31 08:24:16 2001 +++ squid/src/client_side.c Tue Jun 12 03:02:13 2001 @@ -116,6 +116,7 @@ static log_type clientProcessRequest2(clientHttpRequest * http); static int clientReplyBodyTooLarge(int clen); static int clientRequestBodyTooLarge(int clen); +static int varyEvaluateMatch(StoreEntry * entry, request_t * request); static int checkAccelOnly(clientHttpRequest * http) @@ -340,11 +341,136 @@ } static void +clientHandleETagMiss(clientHttpRequest *http) +{ + StoreEntry *entry = http->entry; + MemObject *mem = entry->mem_obj; + request_t *request = http->request; + + if (mem->reply) { + const char *etag = httpHeaderGetStr(&mem->reply->header, HDR_ETAG); + if (etag) { + /* This has to match storeSetPublicKey, except for the key which is NULL */ + String vary = StringNull; + String varyhdr; + varyhdr = httpHeaderGetList(&mem->reply->header, HDR_VARY); + if (strBuf(varyhdr)) + strListAdd(&vary, strBuf(varyhdr), ','); + stringClean(&varyhdr); +#if X_ACCELERATOR_VARY + /* This needs to match the order in http.c:httpMakeVaryMark */ + varyhdr = httpHeaderGetList(&mem->reply->header, HDR_X_ACCELERATOR_VARY); + if (strBuf(varyhdr)) + strListAdd(&vary, strBuf(varyhdr), ','); + stringClean(&varyhdr); +#endif + storeAddVary(mem->url, mem->log_url, mem->method, NULL, httpHeaderGetStr(&mem->reply->header, HDR_ETAG), strBuf(vary), httpMakeVaryMark(request, mem->reply)); + stringClean(&vary); + } + } + request->done_etag = 1; + if (request->vary) { + storeLocateVaryDone(request->vary); + request->vary = NULL; + request->etags = NULL; /* pointed into request->vary */ + } + safe_free(request->etag); + safe_free(request->vary_headers); + storeUnregister(http->sc, entry, http); + storeUnlockObject(entry); + clientProcessRequest(http); +} + +static void +clientHandleETagReply(void *data, char *buf, ssize_t size) +{ + clientHttpRequest *http = data; + StoreEntry *entry = http->entry; + MemObject *mem; + const char *url = storeUrl(entry); + http_status status; + debug(33, 3) ("clientHandleETagReply: %s, %d bytes\n", url, (int) size); + if (entry == NULL) { + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientHandleETagMiss(http); + } + if (size < 0 && !EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientHandleETagMiss(http); + } + mem = entry->mem_obj; + status = mem->reply->sline.status; + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + debug(33, 3) ("clientHandleETagReply: ABORTED '%s'\n", url); + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientHandleETagMiss(http); + return; + } + if (STORE_PENDING == entry->store_status && 0 == status) { + debug(33, 3) ("clientHandleETagReply: Incomplete headers for '%s'\n", url); + if (size >= CLIENT_SOCK_SZ) { + /* will not get any bigger than that */ + debug(33, 3) ("clientHandleETagReply: Reply is too large '%s'\n", url); + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientHandleETagMiss(http); + } else { + storeClientCopy(http->sc, entry, + http->out.offset + size, + http->out.offset, + CLIENT_SOCK_SZ, + buf, + clientHandleETagReply, + http); + } + return; + } + if (HTTP_NOT_MODIFIED == mem->reply->sline.status) { + /* Remember the ETag and restart */ + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientHandleETagMiss(http); + } else { + /* Send the new object to the client */ + clientSendMoreData(data, buf, size); + } +} + +static void +clientProcessETag(clientHttpRequest *http) +{ + char *url = http->uri; + StoreEntry *entry = NULL; + debug(33, 3) ("clientProcessETag: '%s'\n", http->uri); + entry = storeCreateEntry(url, + http->log_uri, + http->request->flags, + http->request->method); + http->sc = storeClientListAdd(entry, http); +#if DELAY_POOLS + /* delay_id is already set on original store client */ + delaySetStoreClient(http->sc, delayClient(http->request)); +#endif + http->entry = entry; + http->out.offset = 0; + fwdStart(http->conn->fd, http->entry, http->request); + /* Register with storage manager to receive updates when data comes in. */ + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) + debug(33, 0) ("clientProcessETag: found ENTRY_ABORTED object\n"); + storeClientCopy(http->sc, entry, + http->out.offset, + http->out.offset, + CLIENT_SOCK_SZ, + memAllocate(MEM_CLIENT_SOCK_BUF), + clientHandleETagReply, + http); +} + +static void clientProcessExpired(void *data) { clientHttpRequest *http = data; char *url = http->uri; StoreEntry *entry = NULL; + const char *etag; debug(33, 3) ("clientProcessExpired: '%s'\n", http->uri); assert(http->entry->lastmod >= 0); /* @@ -379,6 +505,9 @@ debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry->lastmod); http->entry = entry; http->out.offset = 0; + etag = httpHeaderGetStr(&http->old_entry->mem_obj->reply->header, HDR_ETAG); + if (etag) + http->request->etag = xstrdup(etag); fwdStart(http->conn->fd, http->entry, http->request); /* Register with storage manager to receive updates when data comes in. */ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) @@ -412,6 +541,20 @@ debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", status); return 0; } + /* If the ETag matches the clients If-None-Match, then return + * the servers 304 reply + */ + if (httpHeaderHas(&new_entry->mem_obj->reply->header, HDR_ETAG) && + httpHeaderHas(&request->header, HDR_IF_NONE_MATCH)) { + const char *etag = httpHeaderGetStr(&new_entry->mem_obj->reply->header, HDR_ETAG); + String etags = httpHeaderGetList(&request->header, HDR_IF_NONE_MATCH); + int etag_match = strListIsMember(&etags, etag, ','); + stringClean(&etags); + if (etag_match) { + debug(33, 5) ("clientGetsOldEntry: NO, client If-None-Match\n"); + return 0; + } + } /* If the client did not send IMS in the request, then it * must get the old object, not this "Not Modified" reply */ if (!request->flags.ims) { @@ -1109,22 +1252,22 @@ if (!spec.valid) return 0; /* got an ETag? */ - if (spec.tag.str) { - ETag rep_tag = httpHeaderGetETag(&rep->header, HDR_ETAG); + if (spec.tag) { + const char *rep_tag = httpHeaderGetStr(&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) + spec.tag, rep_tag ? rep_tag : ""); + if (!rep_tag) return 0; /* entity has no etag to compare with! */ - if (spec.tag.weak || rep_tag.weak) { + if (spec.tag[0] == 'W' || rep_tag[0] == 'W') { debug(33, 1) ("clientIfRangeMatch: Weak ETags are not allowed in If-Range: %s ? %s\n", - spec.tag.str, rep_tag.str); + spec.tag, rep_tag); return 0; /* must use strong validator for sub-range requests */ } - return etagIsEqual(&rep_tag, &spec.tag); + return strcmp(rep_tag, spec.tag) == 0; } /* got modification time? */ - if (spec.time >= 0) { - return http->entry->lastmod <= spec.time; + else if (spec.time >= 0) { + return http->entry->lastmod == spec.time; } assert(0); /* should not happen */ return 0; @@ -1377,6 +1520,31 @@ } /* + * clientProcessVary is called when it is detected that a object + * varies and we need to get the correct variant + */ +static void +clientProcessVary(VaryData *vary, void *data) +{ + clientHttpRequest *http = data; + if (!vary) { + clientProcessRequest(http); + return; + } + if (vary->key) { + debug(33, 2) ("clientProcessVary: HIT key=%s etag=%s\n", vary->key, vary->etag); + } else { + int i; + debug(33, 2) ("clientProcessVary MISS\n"); + for (i=0; ietags.count; i++) { + debug(33, 3) ("ETag: %s\n", vary->etags.items[i]); + } + } + http->request->vary = vary; + clientProcessRequest(http); +} + +/* * clientCacheHit should only be called until the HTTP reply headers * have been parsed. Normally this should be a single call, but * it might take more than one. As soon as we have the headers, @@ -1390,6 +1558,7 @@ StoreEntry *e = http->entry; MemObject *mem; request_t *r = http->request; + int is_modified = -1; debug(33, 3) ("clientCacheHit: %s, %d bytes\n", http->uri, (int) size); if (http->entry == NULL) { memFree(buf, MEM_CLIENT_SOCK_BUF); @@ -1448,20 +1617,28 @@ debug(33, 2) ("clientProcessHit: Vary MATCH!\n"); break; case VARY_OTHER: - /* This is not the correct entity for this request. We need - * to requery the cache. - */ - memFree(buf, MEM_CLIENT_SOCK_BUF); - http->entry = NULL; - storeUnregister(http->sc, e, http); - http->sc = NULL; - storeUnlockObject(e); - /* Note: varyEvalyateMatch updates the request with vary information - * so we only get here once. (it also takes care of cancelling loops) - */ - debug(33, 2) ("clientProcessHit: Vary detected!\n"); - clientProcessRequest(http); - return; + { + /* This is not the correct entity for this request. We need + * to requery the cache. + */ + store_client *sc = http->sc; + http->entry = NULL; /* saved in e */ + memFree(buf, MEM_CLIENT_SOCK_BUF); + /* Warning: storeUnregister may abort the object so we must + * call storeLocateVary before unregistering, and + * storeLocateVary may complete immediately so we cannot + * rely on the http structure for this... + */ + http->sc = NULL; + storeLocateVary(e, e->mem_obj->reply->hdr_sz, r->vary_headers, clientProcessVary, http); + storeUnregister(sc, e, http); + storeUnlockObject(e); + /* Note: varyEvalyateMatch updates the request with vary information + * so we only get here once. (it also takes care of cancelling loops) + */ + debug(33, 2) ("clientProcessHit: Vary detected!\n"); + return; + } case VARY_CANCEL: /* varyEvaluateMatch found a object loop. Process as miss */ debug(33, 1) ("clientProcessHit: Vary object loop!\n"); @@ -1481,7 +1658,10 @@ if (checkNegativeHit(e)) { http->log_type = LOG_TCP_NEGATIVE_HIT; clientSendMoreData(data, buf, size); - } else if (r->method == METHOD_HEAD) { + return; + } +#if THIS_IS_FALSE + if (r->method == METHOD_HEAD) { /* * RFC 2068 seems to indicate there is no "conditional HEAD" * request. We cannot validate a cached object for a HEAD @@ -1490,7 +1670,36 @@ if (e->mem_status == IN_MEMORY) http->log_type = LOG_TCP_MEM_HIT; clientSendMoreData(data, buf, size); - } else if (refreshCheckHTTP(e, r) && !http->flags.internal) { + return; + } +#endif + if (httpHeaderHas(&r->header, HDR_IF_MATCH)) { + String req_etags; + const char *rep_etag = httpHeaderGetStr(&e->mem_obj->reply->header, HDR_ETAG); + int has_etag; + if (!rep_etag) { + /* The cached object does not have a entity tag. This cannot + * be a hit for the requested object. + */ + http->log_type = LOG_TCP_MISS; + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientProcessMiss(http); + return; + } + req_etags = httpHeaderGetList(&http->request->header, HDR_IF_MATCH); + has_etag = strListIsMember(&req_etags, rep_etag, ','); + stringClean(&req_etags); + if (!has_etag) { + /* The entity tags does not match. This cannot be a + * hit for this object. Qyery the origin. + */ + http->log_type = LOG_TCP_MISS; + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientProcessMiss(http); + return; + } + } + if (refreshCheckHTTP(e, r) && !http->flags.internal) { debug(33, 5) ("clientCacheHit: in refreshCheck() block\n"); /* * We hold a stale copy; it needs to be validated @@ -1533,7 +1742,39 @@ clientProcessMiss(http); } memFree(buf, MEM_CLIENT_SOCK_BUF); - } else if (r->flags.ims) { + return; + } + if (httpHeaderHas(&r->header, HDR_IF_NONE_MATCH)) { + String req_etags; + const char *rep_etag = httpHeaderGetStr(&e->mem_obj->reply->header, HDR_ETAG); + int has_etag; + if (mem->reply->sline.status != HTTP_OK) { + debug(33, 4) ("clientCacheHit: Reply code %d != 200\n", + mem->reply->sline.status); + memFree(buf, MEM_CLIENT_SOCK_BUF); + http->log_type = LOG_TCP_MISS; + clientProcessMiss(http); + return; + } + if (!rep_etag) { + /* The cached object does not have a entity tag, but the client + * obviously thinks there should be one... Query the origin to + * be on the safe side. + */ + http->log_type = LOG_TCP_MISS; + memFree(buf, MEM_CLIENT_SOCK_BUF); + clientProcessMiss(http); + return; + } + req_etags = httpHeaderGetList(&http->request->header, HDR_IF_NONE_MATCH); + has_etag = strListIsMember(&req_etags, rep_etag, ','); + stringClean(&req_etags); + if (has_etag) { + http->log_type = LOG_TCP_IMS_HIT; + is_modified = 0; + } + } + if (is_modified != 0 && r->flags.ims) { /* * Handle If-Modified-Since requests from the client */ @@ -1543,39 +1784,43 @@ memFree(buf, MEM_CLIENT_SOCK_BUF); http->log_type = LOG_TCP_MISS; clientProcessMiss(http); + return; } else if (modifiedSince(e, http->request)) { http->log_type = LOG_TCP_IMS_HIT; clientSendMoreData(data, buf, size); - } else { - time_t timestamp = e->timestamp; - MemBuf mb = httpPacked304Reply(e->mem_obj->reply); - http->log_type = LOG_TCP_IMS_HIT; - memFree(buf, MEM_CLIENT_SOCK_BUF); - storeUnregister(http->sc, e, http); - http->sc = NULL; - storeUnlockObject(e); - e = clientCreateStoreEntry(http, http->request->method, null_request_flags); - /* - * Copy timestamp from the original entry so the 304 - * reply has a meaningful Age: header. - */ - e->timestamp = timestamp; - http->entry = e; - httpReplyParse(e->mem_obj->reply, mb.buf, mb.size); - storeAppend(e, mb.buf, mb.size); - memBufClean(&mb); - storeComplete(e); + return; } - } else { + is_modified = 0; + } + if (is_modified == 0) { + time_t timestamp = e->timestamp; + MemBuf mb = httpPacked304Reply(e->mem_obj->reply); + http->log_type = LOG_TCP_IMS_HIT; + memFree(buf, MEM_CLIENT_SOCK_BUF); + storeUnregister(http->sc, e, http); + http->sc = NULL; + storeUnlockObject(e); + e = clientCreateStoreEntry(http, http->request->method, null_request_flags); /* - * plain ol' cache hit + * Copy timestamp from the original entry so the 304 + * reply has a meaningful Age: header. */ - if (e->mem_status == IN_MEMORY) - http->log_type = LOG_TCP_MEM_HIT; - else if (Config.onoff.offline) - http->log_type = LOG_TCP_OFFLINE_HIT; - clientSendMoreData(data, buf, size); + e->timestamp = timestamp; + http->entry = e; + httpReplyParse(e->mem_obj->reply, mb.buf, mb.size); + storeAppend(e, mb.buf, mb.size); + memBufClean(&mb); + storeComplete(e); + return; } + /* + * plain ol' cache hit + */ + if (e->mem_status == IN_MEMORY) + http->log_type = LOG_TCP_MEM_HIT; + else if (Config.onoff.offline) + http->log_type = LOG_TCP_OFFLINE_HIT; + clientSendMoreData(data, buf, size); } /* put terminating boundary for multiparts */ @@ -2121,6 +2366,16 @@ if (NULL == e) { /* this object isn't in the cache */ debug(33, 3) ("clientProcessRequest2: storeGet() MISS\n"); + if (r->vary) { + if (r->done_etag) { + debug(33, 1) ("clientProcessRequest2: ETag loop\n"); + } else if (r->etags) { + debug(33, 2) ("clientProcessRequest2: ETag miss\n"); + r->etags = NULL; + } else if (r->vary->etags.count > 0) { + r->etags = &r->vary->etags; + } + } return LOG_TCP_MISS; } if (Config.onoff.offline) { @@ -2299,9 +2554,9 @@ return; } assert(http->out.offset == 0); - http->entry = clientCreateStoreEntry(http, r->method, r->flags); if (http->redirect.status) { HttpReply *rep = httpReplyCreate(); + http->entry = clientCreateStoreEntry(http, r->method, r->flags); #if LOG_TCP_REDIRECTS http->log_type = LOG_TCP_REDIRECT; #endif @@ -2312,6 +2567,11 @@ storeComplete(http->entry); return; } + if (r->etags) { + clientProcessETag(http); + return; + } + http->entry = clientCreateStoreEntry(http, r->method, r->flags); if (http->flags.internal) r->protocol = PROTO_INTERNAL; fwdStart(http->conn->fd, http->entry, r); @@ -3187,7 +3447,7 @@ NHttpSockets = 0; } -int +static int varyEvaluateMatch(StoreEntry *entry, request_t *request) { const char *vary = request->vary_headers; @@ -3225,6 +3485,7 @@ /* Ouch.. we cannot handle this kind of variance */ /* XXX This cannot really happen, but just to be complete */ return VARY_CANCEL; +#if NOT_FOR_ETAGS } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) { return VARY_MATCH; } else { @@ -3234,6 +3495,10 @@ debug(33, 1) ("varyEvaluateMatch: Oops. Not a Vary match on second attempt, '%s' '%s'\n", entry->mem_obj->url, vary); return VARY_CANCEL; +#else + } else { + return VARY_MATCH; +#endif } } } Index: squid/src/http.c diff -u squid/src/http.c:1.4.4.2.2.9.2.3 squid/src/http.c:1.4.4.2.2.9.4.3 --- squid/src/http.c:1.4.4.2.2.9.2.3 Tue Oct 16 14:57:08 2001 +++ squid/src/http.c Tue Oct 16 14:58:56 2001 @@ -134,8 +134,6 @@ int remove = 0; int forbidden = 0; StoreEntry *pe; - if (!EBIT_TEST(e->flags, KEY_PRIVATE)) - return; switch (status) { case HTTP_OK: case HTTP_NON_AUTHORITATIVE_INFORMATION: @@ -167,14 +165,16 @@ } if (!remove && !forbidden) return; - assert(e->mem_obj); - if (e->mem_obj->request) - pe = storeGetPublicByRequest(e->mem_obj->request); - else - pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method); - if (pe != NULL) { - assert(e != pe); - storeRelease(pe); + if (EBIT_TEST(e->flags, KEY_PRIVATE)) { + assert(e->mem_obj); + if (e->mem_obj->request) + pe = storeGetPublicByRequest(e->mem_obj->request); + else + pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method); + if (pe != NULL) { + assert(e != pe); + storeRelease(pe); + } } /* * Also remove any cached HEAD response in case the object has @@ -184,8 +184,7 @@ pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_HEAD); else pe = storeGetPublic(e->mem_obj->url, METHOD_HEAD); - if (pe != NULL) { - assert(e != pe); + if (pe != NULL && e != pe) { storeRelease(pe); } if (forbidden) @@ -326,9 +325,8 @@ * Returns false if the variance cannot be stored */ const char * -httpMakeVaryMark(request_t * request, HttpReply *reply) +httpMakeVaryMark(const request_t * request, HttpReply *reply) { - int ok = 1; String vary, hdr; const char *pos = NULL; const char *item; @@ -375,7 +373,7 @@ } stringClean(&vary); #endif - debug(11, 0) ("httpMakeVaryMark: %d / %s\n", ok, strBuf(vstr)); + debug(11, 2) ("httpMakeVaryMark: %d / %s\n", strBuf(vstr)); return strBuf(vstr); } @@ -427,8 +425,6 @@ storeTimestampsSet(entry); /* Check if object is cacheable or not based on reply code */ debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status); - if (neighbors_do_private_keys) - httpMaybeRemovePublic(entry, reply->sline.status); switch (httpCachableReply(httpState)) { case 1: if (httpHeaderHas(&reply->header, HDR_VARY) @@ -464,6 +460,8 @@ else if (EBIT_TEST(reply->cache_control->mask, CC_MUST_REVALIDATE)) EBIT_SET(entry->flags, ENTRY_REVALIDATE); } + if (neighbors_do_private_keys) + httpMaybeRemovePublic(entry, reply->sline.status); if (httpState->flags.keepalive) if (httpState->peer) httpState->peer->stats.n_keepalives_sent++; @@ -737,6 +735,13 @@ /* append our IMS header */ if (request->lastmod > -1 && request->method == METHOD_GET) httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, request->lastmod); + if (request->etag) + httpHeaderPutStr(hdr_out, HDR_IF_NONE_MATCH, request->etag); + else if (request->etags) { + int i; + for (i = 0; i < request->etags->count; i++) + httpHeaderPutStr(hdr_out, HDR_IF_NONE_MATCH, request->etags->items[i]); + } /* decide if we want to do Ranges ourselves * (and fetch the whole object now) Index: squid/src/protos.h diff -u squid/src/protos.h:1.4.4.4.2.4.2.3 squid/src/protos.h:1.4.4.4.2.4.2.2.2.3 --- squid/src/protos.h:1.4.4.4.2.4.2.3 Tue Oct 16 14:57:08 2001 +++ squid/src/protos.h Tue Oct 16 14:58:56 2001 @@ -285,11 +285,7 @@ extern int httpAnonHdrDenied(http_hdr_type hdr_id); extern void httpBuildRequestHeader(request_t *, request_t *, StoreEntry *, HttpHeader *, int, http_state_flags); extern void httpBuildVersion(http_version_t * version, unsigned int major, unsigned int minor); -extern const char *httpMakeVaryMark(request_t *request, HttpReply *reply); - -/* ETag */ -extern int etagParseInit(ETag * etag, const char *str); -extern int etagIsEqual(const ETag * tag1, const ETag * tag2); +extern const char *httpMakeVaryMark(const request_t * request, HttpReply * reply); /* Http Status Line */ /* init/clean */ @@ -415,7 +411,6 @@ 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); @@ -1230,6 +1225,6 @@ extern unsigned int url_checksum(const char *url); #endif -/* Vary support functions */ -int varyEvaluateMatch(StoreEntry *entry, request_t *req); - +void storeLocateVaryDone(VaryData *data); +void storeLocateVary(StoreEntry * e, int offset, const char *vary_data, STLVCB *callback, void *cbdata); +void storeAddVary(const char *url, const char *log_url, const method_t method, const cache_key *key, const char *etag, const char *vary, const char *vary_headers); Index: squid/src/store.c diff -u squid/src/store.c:1.4.4.3.2.8.2.2 squid/src/store.c:1.4.4.3.2.8.2.2.2.4 --- squid/src/store.c:1.4.4.3.2.8.2.2 Thu Mar 29 04:25:58 2001 +++ squid/src/store.c Fri Jun 1 09:14:56 2001 @@ -329,6 +329,13 @@ StoreEntry * storeGetPublicByRequestMethod(request_t *req, const method_t method) { + if (req->vary) { + /* Varying objects... */ + if (req->vary->key) + return storeGet(storeKeyScan(req->vary->key)); + else + return NULL; + } return storeGet(storeKeyPublicByRequestMethod(req, method)); } @@ -374,6 +381,423 @@ storeHashInsert(e, newkey); } +typedef struct { + StoreEntry *oe; + StoreEntry *e; + store_client *sc; + char *url; + char *key; + char *vary_headers; + char *etag; + int offset; + int seen_offset; + char *buf; + int done:1; + struct { + char *key; + char *etag; + int this_key:1; + int key_used:1; + int ignore:1; + } current; +} AddVaryState; +static MemPool *AddVaryState_pool = NULL; + +static void free_AddVaryState(void *data, int dummy) +{ + AddVaryState *state = data; + debug(11, 2) ("free_AddVaryState: %p\n", data); + if (!state->done && state->key) { + storeAppendPrintf(state->e, "Key: %s\n", state->key); + if (state->etag) + storeAppendPrintf(state->e, "ETag: %s\n", state->etag); + storeAppendPrintf(state->e, "VaryData: %s\n", state->vary_headers); + } + storeBufferFlush(state->e); + storeTimestampsSet(state->e); + storeComplete(state->e); + storeTimestampsSet(state->e); + storeUnlockObject(state->e); + state->e = NULL; + if (state->oe) { + storeUnlockObject(state->oe); + state->oe = NULL; + } + safe_free(state->url); + safe_free(state->key); + safe_free(state->vary_headers); + safe_free(state->etag); + safe_free(state->current.key); + safe_free(state->current.etag); + if (state->buf) { + memFree(state->buf, MEM_CLIENT_SOCK_BUF); + state->buf = NULL; + } + memPoolFree(AddVaryState_pool, state); +} + +static void storeAddVaryFlush(AddVaryState *state) +{ + if (state->current.ignore || state->current.key_used) { + /* do nothing */ + } else if (state->current.this_key) { + if (state->current.key) + storeAppendPrintf(state->e, "Key: %s\n", state->current.key); + else + storeAppendPrintf(state->e, "Key: %s\n", state->key); + if (state->etag) + storeAppendPrintf(state->e, "ETag: %s\n", state->etag); + storeAppendPrintf(state->e, "VaryData: %s\n", state->vary_headers); + state->done = 1; + state->current.key_used = 1; + } else if (state->current.key) { + storeAppendPrintf(state->e, "Key: %s\n", state->current.key); + safe_free(state->current.key); + if (state->current.etag) { + storeAppendPrintf(state->e, "ETag: %s\n", state->current.etag); + safe_free(state->current.etag); + } + state->current.key_used = 1; + } +} + +static int inline +strmatchbeg(const char *search, const char *match, int maxlen) +{ + int mlen = strlen(match); + if (maxlen < mlen) + return -1; + return strncmp(search, match, mlen); +} + +static int inline +strmatch(const char *search, const char *match, int maxlen) +{ + int mlen = strlen(match); + if (maxlen < mlen) + return -1; + return strncmp(search, match, maxlen); +} + +static void +storeReadVary(void *data, char *buf, ssize_t size) +{ + AddVaryState *state = data; + int l = size; + char *e; + char *p = buf; + debug(11, 3) ("storeReadVary: %p offset=%d seen_offset=%d size=%d\n", data, state->offset, state->seen_offset, size); + if (size <= 0) { + storeUnregister(state->sc, state->oe, state); + state->sc = NULL; + cbdataFree(state); + debug(11, 2) ("storeReadVary: DONE\n"); + return; + } + state->seen_offset += size; + while ((e = memchr(p, '\n', l)) != NULL) { + int l2; + char *p2; + if (strmatchbeg(p, "Key: ", l) == 0) { + /* key field */ + p2 = p + 5; + l2 = e - p2; + if (state->current.this_key) { + storeAddVaryFlush(state); + } + safe_free(state->current.key); + safe_free(state->current.etag); + memset(&state->current, 0, sizeof(state->current)); + state->current.key = xmalloc(l2+1); + memcpy(state->current.key, p2, l2); + state->current.key[l2] = '\0'; + if (state->key) { + if (strcmp(state->current.key, state->key) == 0) { + state->current.this_key = 1; + } + } + debug(11, 3) ("storeReadVary: Key: %s%s\n", state->current.key, state->current.this_key ? " (THIS)" : ""); + } else if (strmatchbeg(p, "ETag: ", l) == 0) { + /* etag field */ + p2 = p + 6; + l2 = e - p2; + safe_free(state->current.etag); + state->current.etag = xmalloc(l2+1); + memcpy(state->current.etag, p2, l2); + state->current.etag[l2] = '\0'; + if (state->etag && strcmp(state->current.etag, state->etag) == 0) { + if (!state->key) { + state->current.this_key = 1; + } else { + const cache_key *oldkey = storeKeyScan(state->current.key); + if (strmatch(p2, state->key, l) != 0) { + StoreEntry *old_e = storeGet(oldkey); + if (old_e) + storeRelease(old_e); + safe_free(state->current.key); + state->current.key = xstrdup(state->key); + state->current.this_key = 1; + } + } + } else if (state->current.this_key) { + state->current.ignore = 1; + } + debug(11, 2) ("storeReadVary: ETag: %s%s%s\n", state->current.etag, state->current.this_key ? " (THIS)" : "", state->current.ignore ? " (IGNORE)" : ""); + } else if (!state->current.ignore && strmatchbeg(p, "VaryData: ", l) == 0) { + /* vary field */ + p2 = p + 10; + l2 = e - p2; + storeAddVaryFlush(state); + if (strmatch(p2, state->vary_headers, l2) != 0) { + storeAppend(state->e, p, e - p + 1); + debug(11, 3) ("storeReadVary: %s\n", p); + } + } + e += 1; + l -= e - p; + p = e; + if (l == 0) + break; + assert(l>0); + assert(p < (buf+size)); + } + state->offset += p - buf; + debug(11, 3) ("storeReadVary: %p offset=%d seen_offset=%d\n", data, state->offset, state->seen_offset); + storeClientCopy(state->sc, state->oe, + state->seen_offset, + state->offset, + CLIENT_SOCK_SZ, + state->buf, + storeReadVary, + state); +} + +/* + * Adds/updates a Vary record. + * For updates only one of key or etag needs to be specified + * At leas one of key or etag must be specified, preferably both. + */ +void +storeAddVary(const char *url, const char *log_url, const method_t method, const cache_key *key, const char *etag, const char *vary, const char *vary_headers) +{ + AddVaryState *state; + http_version_t version; + request_flags flags = null_request_flags; + if (!AddVaryState_pool) + AddVaryState_pool = memPoolCreate("AddVaryState", sizeof(AddVaryState)); + state = memPoolAlloc(AddVaryState_pool); + cbdataAdd(state, free_AddVaryState, -1); + state->url = xstrdup(url); + if (key) + state->key = xstrdup(storeKeyText(key)); + state->vary_headers = xstrdup(vary_headers); + if (etag) + state->etag = xstrdup(etag); + state->oe = storeGetPublic(url, method); + debug(11, 2) ("storeAddVary: %s (%s) %s %s\n", + state->url, state->key, state->vary_headers, state->etag); + if (state->oe) + storeLockObject(state->oe); + flags.cachable = 1; + state->e = storeCreateEntry(url, log_url, flags, method); + httpBuildVersion(&version, 1, 0); + httpReplySetHeaders(state->e->mem_obj->reply, version, HTTP_OK, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000); + httpHeaderPutStr(&state->e->mem_obj->reply->header, HDR_VARY, vary); + storeSetPublicKey(state->e); + httpReplySwapOut(state->e->mem_obj->reply, state->e); + if (state->oe) { + /* Here we need to tack on the old etag/vary information, and we should + * merge, clean up etc + * + * Suggestion: + * swap in the old file, looking for ETag, Key and VaryData. If a match is + * found then + * - on ETag, update the key, and expire the old object if different + * - on Key, drop the old data if ETag is different, else nothing + * - on VaryData, remove the line if a different key. If this makes + * the searched key "empty" then expire it and remove it from the + * map + * - VaryData is added last in the Key record it corresponds to (after + * modifications above) + */ + /* Swap in the dummy Vary object */ + if (!state->oe->mem_obj) { + storeCreateMemObject(state->oe, state->url, log_url); + state->oe->mem_obj->method = method; + } + state->sc = storeClientListAdd(state->oe, state); + state->buf = memAllocate(MEM_CLIENT_SOCK_BUF); + debug(11, 3) ("storeAddVary: %p\n", state); + storeClientCopy(state->sc, state->oe, 0, 0, + CLIENT_SOCK_SZ, + state->buf, + storeReadVary, + state); + return; + } else { + cbdataFree(state); + } +} + +static MemPool *VaryData_pool = NULL; + +void storeLocateVaryDone(VaryData *data) +{ + int i; + safe_free(data->key); + data->etag = NULL; /* points to an entry in etags */ + for (i=0 ; i < data->etags.count; i++) { + safe_free(data->etags.items[i]); + } + arrayClean(&data->etags); + memPoolFree(VaryData_pool, data); +} + +typedef struct { + VaryData *data; + STLVCB *callback; + void *callback_data; + StoreEntry *e; + store_client *sc; + char *buf; + char *vary_data; + int offset, seen_offset; + struct { + char *key; + char *etag; + } current; +} LocateVaryState; +static MemPool *LocateVaryState_pool = NULL; + +static void +free_LocateVaryState(void *data, int dummy) +{ + memPoolFree(LocateVaryState_pool, data); +} + +static void +storeLocateVaryCallback(LocateVaryState *state) +{ + if (cbdataValid(state->callback_data)) { + VaryData *data = state->data; + if (data->key || data->etags.count) { + state->callback(data, state->callback_data); + state->data = NULL; /* now owned by the caller */ + } else { + state->callback(NULL, state->callback_data); + } + } + cbdataUnlock(state->callback_data); + if (state->data) { + storeLocateVaryDone(state->data); + state->data = NULL; + } + state->current.etag = NULL; /* shared by data->entries[x] */ + safe_free(state->vary_data); + safe_free(state->current.key); + if (state->sc) { + storeUnregister(state->sc, state->e, state); + state->sc = NULL; + } + if (state->e) { + storeUnlockObject(state->e); + state->e = NULL; + } + if (state->buf) + memFree(state->buf, MEM_4K_BUF); + cbdataFree(state); + debug(11, 2) ("storeLocateVary: DONE\n"); +} + +static void +storeLocateVaryRead(void *data, char *buf, ssize_t size) +{ + LocateVaryState *state = data; + char *e; + char *p = buf; + int l = size; + debug(11, 3) ("storeLocateVaryRead: %s %p offset=%d seen_offset=%d size=%d\n", state->vary_data, data, state->offset, state->seen_offset, size); + if (size <= 0) { + storeLocateVaryCallback(state); + return; + } + state->seen_offset += size; + while ((e = memchr(p, '\n', l)) != NULL) { + int l2; + char *p2; + if (strmatchbeg(p, "Key: ", l) == 0) { + /* key field */ + p2 = p + 5; + l2 = e - p2; + safe_free(state->current.key); + state->current.etag = NULL; + safe_free(state->current.etag); + memset(&state->current, 0, sizeof(state->current)); + state->current.key = xmalloc(l2+1); + memcpy(state->current.key, p2, l2); + state->current.key[l2] = '\0'; + debug(11, 3) ("storeLocateVaryRead: Key: %s\n", state->current.key); + } else if (strmatchbeg(p, "ETag: ", l) == 0) { + /* etag field */ + char *etag; + p2 = p + 6; + l2 = e - p2; + etag = xmalloc(l2+1); + memcpy(etag, p2, l2); + etag[l2] = '\0'; + state->current.etag = etag; + arrayAppend(&state->data->etags, etag); + debug(11, 3) ("storeLocateVaryRead: ETag: %s\n", etag); + } else if (strmatchbeg(p, "VaryData: ", l) == 0) { + /* vary field */ + p2 = p + 10; + l2 = e - p2; + if (strmatch(p2, state->vary_data, l2) == 0) { + /* A matching vary header found */ + safe_free(state->data->key); + state->data->key = xstrdup(state->current.key); + state->data->etag = state->current.etag; + debug(11, 2) ("storeLocateVaryRead: MATCH! %s %s\n", state->current.key, state->current.etag); + } + } + e += 1; + l -= e - p; + p = e; + if (l == 0) + break; + assert(l>0); + assert(p < (buf+size)); + } + state->offset += p - buf; + debug(11, 3) ("storeReadVary: %p offset=%d seen_offset=%d\n", data, state->offset, state->seen_offset); + storeClientCopy(state->sc, state->e, state->offset, state->seen_offset, 4096, state->buf, storeLocateVaryRead, state); +} + +void +storeLocateVary(StoreEntry * e, int offset, const char *vary_data, STLVCB *callback, void *cbdata) +{ + LocateVaryState *state; + debug(11, 2) ("storeLocateVary: %s\n", vary_data); + if (!LocateVaryState_pool) { + LocateVaryState_pool = memPoolCreate("LocateVaryState", sizeof(LocateVaryState)); + } + if (!VaryData_pool) + VaryData_pool = memPoolCreate("VaryData", sizeof(VaryData)); + state = memPoolAlloc(LocateVaryState_pool); + cbdataAdd(state, free_LocateVaryState, -1); + state->vary_data = xstrdup(vary_data); + state->data = memPoolAlloc(VaryData_pool); + state->e = e; + storeLockObject(state->e); + state->callback_data = cbdata; + cbdataLock(cbdata); + state->callback = callback; + state->buf = memAllocate(MEM_4K_BUF); + state->sc = storeClientListAdd(state->e, state); + state->offset = state->seen_offset = offset; + storeClientCopy(state->sc, state->e, state->offset, state->seen_offset, 4096, state->buf, storeLocateVaryRead, state); +} + void storeSetPublicKey(StoreEntry * e) { @@ -419,36 +843,27 @@ if (!request->vary_headers) request->vary_headers = xstrdup(httpMakeVaryMark(request, mem->reply)); } - if (mem->vary_headers && !storeGetPublic(mem->url, mem->method)) { - /* Create "vary" base object */ - http_version_t version; - String vary; - pe = storeCreateEntry(mem->url, mem->log_url, request->flags, request->method); - httpBuildVersion(&version, 1, 0); - httpReplySetHeaders(pe->mem_obj->reply, version, HTTP_OK, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000); - vary = httpHeaderGetList(&mem->reply->header, HDR_VARY); - if (strBuf(vary)) { - httpHeaderPutStr(&pe->mem_obj->reply->header, HDR_VARY, strBuf(vary)); - stringClean(&vary); - } + newkey = storeKeyPublicByRequest(mem->request); + if (mem->vary_headers) { + String vary = StringNull; + String varyhdr; + varyhdr = httpHeaderGetList(&mem->reply->header, HDR_VARY); + if (strBuf(varyhdr)) + strListAdd(&vary, strBuf(varyhdr), ','); + stringClean(&varyhdr); #if X_ACCELERATOR_VARY - vary = httpHeaderGetList(&mem->reply->header, HDR_X_ACCELERATOR_VARY); - if (strBuf(vary)) { - httpHeaderPutStr(&pe->mem_obj->reply->header, HDR_X_ACCELERATOR_VARY, strBuf(vary)); - stringClean(&vary); - } + /* This needs to match the order in http.c:httpMakeVaryMark */ + varyhdr = httpHeaderGetList(&mem->reply->header, HDR_X_ACCELERATOR_VARY); + if (strBuf(varyhdr)) + strListAdd(&vary, strBuf(varyhdr), ','); + stringClean(&varyhdr); #endif - storeSetPublicKey(pe); - httpReplySwapOut(pe->mem_obj->reply, pe); - storeBufferFlush(pe); - storeTimestampsSet(pe); - storeComplete(pe); - storeUnlockObject(pe); + storeAddVary(mem->url, mem->log_url, mem->method, newkey, httpHeaderGetStr(&mem->reply->header, HDR_ETAG), strBuf(vary), mem->vary_headers); + stringClean(&vary); } - newkey = storeKeyPublicByRequest(mem->request); - } - else + } else { newkey = storeKeyPublic(mem->url, mem->method); + } if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) { debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url); storeSetPrivateKey(e2); Index: squid/src/structs.h diff -u squid/src/structs.h:1.6.2.5.2.3.2.2 squid/src/structs.h:1.6.2.5.2.3.2.2.2.1 --- squid/src/structs.h:1.6.2.5.2.3.2.2 Fri Apr 13 12:10:53 2001 +++ squid/src/structs.h Thu May 31 08:39:12 2001 @@ -710,7 +710,7 @@ /* some fields can hold either time or etag specs (e.g. If-Range) */ struct _TimeOrTag { - ETag tag; /* entity tag */ + const char *tag; /* entity tag */ time_t time; int valid; /* true if struct is usable */ }; @@ -1468,6 +1468,10 @@ char *peer_login; /* Configured peer login:password */ time_t lastmod; /* Used on refreshes */ char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ + VaryData *vary; + Array *etags; /* possible known entity tags (Vary MISS) */ + char *etag; /* current entity tag, cache validation */ + unsigned int done_etag:1; /* We have done clientProcessETag on this, don't attempt it again */ }; struct _cachemgr_passwd { @@ -1930,3 +1934,10 @@ char *name; void (*parse) (SwapDir * sd, const char *option, const char *value, int reconfiguring); }; + +struct _VaryData { + cache_key *key; + char *etag; + Array etags; +}; + Index: squid/src/typedefs.h diff -u squid/src/typedefs.h:1.3.4.2.4.1 squid/src/typedefs.h:1.3.4.2.4.1.2.1 --- squid/src/typedefs.h:1.3.4.2.4.1 Wed Mar 7 07:59:32 2001 +++ squid/src/typedefs.h Thu May 31 08:39:12 2001 @@ -314,4 +314,7 @@ typedef int STDIRSELECT(const StoreEntry *); +typedef struct _VaryData VaryData; +typedef void STLVCB(VaryData *vary, void *cbdata); + #endif /* _TYPEDEFS_H_ */