This patch is generated from the varyetag branch of HEAD in squid3 Wed Sep 29 00:44:44 2004 GMT See http://devel.squid-cache.org/ Index: squid3/doc/debug-sections.txt diff -u squid3/doc/debug-sections.txt:1.6 squid3/doc/debug-sections.txt:1.6.2.1 --- squid3/doc/debug-sections.txt:1.6 Tue Jul 22 19:12:50 2003 +++ squid3/doc/debug-sections.txt Thu Jun 3 15:50:05 2004 @@ -96,3 +96,4 @@ section 90 Store Client section 91 Http Surrogate-Control Header section 92 Store File System +section 93 MMSquid Project Index: squid3/src/CharHashTable.cc diff -u /dev/null squid3/src/CharHashTable.cc:1.1.2.2 --- /dev/null Tue Sep 28 17:49:22 2004 +++ squid3/src/CharHashTable.cc Sun Jul 4 09:56:50 2004 @@ -0,0 +1,307 @@ +/* + * $Id: squid3-varyetag-HEAD,v 1.3 2004/09/29 00:56:20 hno Exp $ + * + * 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" +#include "CharHashTable.h" + +/* + * Creates new CharHashTable. + * It's contents is packed in the argument. + * Format of packed: "L LK LV LK LV ...", + * where L is the length of the whole packed string minus sizeof(int) + * (it is the length of "LK LV LK LV ..." part only), + * LK is the length of the key of a pair and the key-string itself, + * LV is the length of the value of a pair and the value-string itself. + * + * Doesn't free packed. + */ +CharHashTable::CharHashTable(char *packed) : map(hash_create((HASHCMP *) strcmp,CHARHASHTABLE_DEFAULT_SIZE,hash4)) { + + debug(93, 9) ("CharHashTable::CharHashTable(%p): constructor\n", packed); + + // if there are no packed pairs to add to fresh and empty contents + if (!packed) + return; + + // counter to traverse packed string + int pos = 0; + + // length of packed string - first sizeof(int) bytes of packed string + int length; + + // temporal length for keys and values + int len; + + // temporal... + char *key, *element; + + // get packed string length + xmemcpy(&length, &packed[pos], sizeof(int)); + + // int was read + pos += sizeof(int); + + // read (key, element) as long as the whole packed is read + while (pos < length + 4) { + + // the length of the key + xmemcpy(&len, &packed[pos], sizeof(int)); + pos += sizeof(int); + + // after the key length should be the key itself or the element LV + assert(pos < length + 4); + + // key itself + key = (char*)xmalloc(len+1); + xmemcpy(key, &packed[pos], len); + key[len] = 0; + pos += len; + + // after the key should be the element LV + assert(pos < length + 4); + + // the length of the value + xmemcpy(&len, &packed[pos], sizeof(int)); + pos += sizeof(int); + + // if value is non empty there should be the element now + assert(pos < length + 4 || len == 0); + + // element itself + element = (char*)xmalloc(len+1); + xmemcpy(element, &packed[pos], len); + element[len] = 0; + pos += len; + + // save the pair (copies the strings) + put(key, element); + + // freedom for all + safe_free(key); + safe_free(element); + } +} + +/* + * Frees table pair... to be used with hashFreeItems. + */ +void +CharHashTable::elementFree(void *e) { + CharHashTableElement *chtel = (CharHashTableElement*)e; + assert(chtel); + + debug(93, 9) ("CharHashTable::elementFree(): key: %s, element: %s\n", + (char*)chtel->key, (char*)chtel->element); + + assert(chtel->key); + safe_free(chtel->key); + delete chtel; +} + +/* + * Frees the whole table. + */ +CharHashTable::~CharHashTable() { + + debug(93, 9) ("~CharHashTable: size=%d\n", map->count); + assert(map); + + hashFreeItems(map, elementFree); + hashFreeMemory(map); + +} + +/* + * Retrieves element identified by the given key or NULL. + * Doesn't copy the returned element. + */ +char * +CharHashTable::get(char *key) { + CharHashTableElement *hte = static_cast(hash_lookup(map, key)); + + if (hte) { + debug(93, 9) ("CharHashTable::get key=[%s] element=[%s]\n", key, hte->element); + return hte->element; + } + debug(93, 9) ("CharHashTable::get key=[%s] element=[NULL]\n", key); + return NULL; +} + +/* + * Retrieves concatenation of the keys. + */ +char * +CharHashTable::keys() { + CharHashTableElement *hte; + hash_first(map); + int size = 0; + int pos = 0; + + while ((hte = (CharHashTableElement*)hash_next(map))) { + size += (strlen((char*)hte->key)+1); // 1 for , + debug(93, 9) ("CharHashTable::keys: key=[%s]\n", + (char*)hte->key); + } + if (!size) { + debug(93, 9) ("CharHashTable::keys: result is empty!\n"); + return NULL; + } + char* result = (char*)xmalloc(size); + + hash_first(map); + + while ((hte = (CharHashTableElement*)hash_next(map))) { + xmemcpy(result+pos,(char*)hte->key, strlen((char*)hte->key)); + pos += (strlen((char*)hte->key)); // 1 for , + result[pos++]= ','; + debug(93, 9) ("CharHashTable::keys: key=[%s]\n", (char*)hte->key); + } + result[size-1] = 0; + debug(93, 9) ("CharHashTable::keys: result=[%s]\n", result); + return result; +} + +/* + * Puts the given pair into table. + * Copies both key and element. + */ +void +CharHashTable::put(char *key, char *element) { + + debug(93, 9) ("CharHashTable::put key=[%s] element=[%s]\n", key, element); + + // new element + CharHashTableElement *hte = (CharHashTableElement*)xmalloc(sizeof(CharHashTableElement)); + hte->key = xstrdup(key); + hte->element = xstrdup(element); + + hash_join(map, hte); +} + +/* + * Removes pair identified by the given key. + * Doesn't free the given key. + * + * XXX does it free the stored key? + */ +void +CharHashTable::remove(char *key) { + CharHashTableElement *hte = static_cast(hash_lookup(map, key)); + + debug(93, 9) ("CharHashTable::remove key=[%s], hte=%p\n", key, hte); + + if (hte) { + hash_remove_link(map, hte); + delete hte; + } + +} + +/* + * Packs table into char *. + * On packed_length returns the length + * of the whole char *. + * + * The resulting string is to be passed to constructor + * in order to restore the table. + * + * It's format: "L LK LV LK LV ..." + */ +char * +CharHashTable::pack(int *packed_length) { + + // temporal... + CharHashTableElement *hte; + + // length of the "LK LV LK LV ..." to be stored in the first + // sizeof(int) bytes of resulting string + int length = 0; + + hash_first(map); + + // sum up all the length needed for each "LK LV" + while ((hte = (CharHashTableElement*)hash_next(map))) { + length += (2*sizeof(int)); + length += (strlen((char*)hte->key) + strlen((char*)hte->element)); + } + + // return the whole length... + *packed_length = (length + sizeof(int)); + + char *packed = (char*)xmalloc(*packed_length); + + // the leading "L" + xmemcpy(packed, &length, sizeof(int)); + + // counter... traverses the packed string as the packing proceeds + int pos = sizeof(int); + + hash_first(map); + + // pack all pairs + while ((hte = (CharHashTableElement*)hash_next(map))) { + // temporal for key's and element's lengths + int len; + + // the length of the key + len = strlen((char*)hte->key); + xmemcpy(&packed[pos], &len, sizeof(int)); + pos += sizeof(int); + + // the key itself + xmemcpy(&packed[pos], hte->key, len); + pos += len; + + // the length of the element + len = strlen(hte->element); + xmemcpy(&packed[pos], &len, sizeof(int)); + pos += sizeof(int); + + // the element itself + xmemcpy(&packed[pos], hte->element, len); + pos += len; + } + + return packed; +} + +/* + * Dumps all the pairs with debugs + */ +void +CharHashTable::dump() { + CharHashTableElement *hte; + hash_first(map); + + while ((hte = (CharHashTableElement*)hash_next(map))) { + debug(93, 9) ("CharHashTable::dump: key=[%s] element=[%s]\n", + (char*)hte->key, (char*)hte->element); + } +} Index: squid3/src/CharHashTable.h diff -u /dev/null squid3/src/CharHashTable.h:1.1.2.1 --- /dev/null Tue Sep 28 17:49:22 2004 +++ squid3/src/CharHashTable.h Thu Jun 3 15:52:27 2004 @@ -0,0 +1,115 @@ +/* + * $Id: squid3-varyetag-HEAD,v 1.3 2004/09/29 00:56:20 hno Exp $ + * + * 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. + */ + +#ifndef SQUID_CHARHASHTABLE_H +#define SQUID_CHARHASHTABLE_H + +#define CHARHASHTABLE_DEFAULT_SIZE 32 + +/* + * Hashtable to store pair of char *. + * Based on hash_table from hash_table.cc. + */ +class CharHashTable +{ + +public: + /* + * Creates empty hashtable. + */ + CharHashTable() : map(hash_create((HASHCMP *) strcmp,CHARHASHTABLE_DEFAULT_SIZE,hash4)) {} + + /* + * Creates new hashtable filled with pairs encoded in the pack argument. + */ + CharHashTable(char *packed); + + /* + * Destroys hashtable along with it's elements. + */ + ~CharHashTable(); + + /* + * Retrieves element associated with the given key. + */ + char * get(char *key); + + /* + * Retrieves concatenation of the keys from hash table. + */ + char * keys(); + + /* + * Associates the given key with the given element. + * Copies both of them. + */ + void put(char *key, char *element); + + /* + * Encodes all the associations into a string. + * On packed_length the length of packed string is returned. + */ + char * pack(int *packed_length); + + /* + * Removes pair identified by the given key. + * Doesn't free the key. + */ + void remove(char *key); + + /* + * To be used with hashFreeItems. + */ + static void elementFree(void *); + + /* + * Debugs all the pairs + */ + void dump(); + +protected: + + /* + * Backing hashtable. + */ + hash_table *map; + +}; + +/* + * Hashtable element. + */ +struct CharHashTableElement : public hash_link +{ + ~CharHashTableElement() { safe_free(element); } + char *element; +}; + +#endif /* SQUID_CHARHASHTABLE_H*/ Index: squid3/src/HttpHeader.cc diff -u squid3/src/HttpHeader.cc:1.14 squid3/src/HttpHeader.cc:1.14.2.1 --- squid3/src/HttpHeader.cc:1.14 Mon Sep 29 19:12:40 2003 +++ squid3/src/HttpHeader.cc Thu Jun 3 15:50:06 2004 @@ -1162,6 +1162,22 @@ return etag; } +/* + * Extracts body of If-None-Match, returns empty String if there is no If-None-Match + */ +String +httpHeaderGetINM(const HttpHeader *hdr) { + assert(hdr); + + String inm; + + inm = httpHeaderGetStrOrList(hdr, HDR_IF_NONE_MATCH); + + debug(93, 5) ("httpHeaderGetINM: INM: %s\n", inm.buf()); + + return inm; +} + TimeOrTag httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id) { Index: squid3/src/HttpHeaderTools.cc diff -u squid3/src/HttpHeaderTools.cc:1.14 squid3/src/HttpHeaderTools.cc:1.14.2.1 --- squid3/src/HttpHeaderTools.cc:1.14 Sat Sep 20 19:13:17 2003 +++ squid3/src/HttpHeaderTools.cc Thu Jun 3 15:50:06 2004 @@ -318,6 +318,60 @@ return len > 0; } +/* + * creates array of char* and fills it with elements of str + * elements are separated by ',' + * len becomes number of elements + * XXX len and returned number arent the same? + */ +int +fillArrayWithStr(const String *str, char ***array, int *len) { + + assert(str); + + // for strListGetItem + const char *pos = NULL; + int ilen; + + // separate element + const char *item; + + // iterates the elements + int count = 0; + + // to start with + *len = 0; + + + // element number + while(strListGetItem(str, ',', &item, &ilen, &pos)) + (*len)++; + + //XXX shouldn't we safe_free? + *array = NULL; + + // if there are elements + if (*len) + *array = new (char *)[*len]; + + // for strListGetItem + const char *pos2 = NULL; + + //copy the elements + while(strListGetItem(str, ',', &item, &ilen, &pos2)) { + + debug(93,9) ("fillArrayWithStr: count=%d len=%d\n", count, *len); + assert(count < *len); + + (*array)[count] = (char*)xmalloc(ilen + 1); + xstrncpy((*array)[count++], item, ilen + 1); + + debug(93,3) ("fillArrayWithStr: item found %s\n", item); + } + + return count; +} + /* handy to printf prefixes of potentially very long buffers */ const char * getStringPrefix(const char *str, const char *end) Index: squid3/src/HttpReply.cc diff -u squid3/src/HttpReply.cc:1.17 squid3/src/HttpReply.cc:1.17.2.2 --- squid3/src/HttpReply.cc:1.17 Tue Dec 23 19:12:59 2003 +++ squid3/src/HttpReply.cc Sat Jun 5 18:18:51 2004 @@ -87,7 +87,7 @@ return rep; } -HttpReply::HttpReply() : hdr_sz (0), content_length (0), date (0), last_modified (0), expires (0), cache_control (NULL), surrogate_control (NULL), content_range (NULL), keep_alive (0), pstate(psReadyToParseStartLine), header (hoReply) +HttpReply::HttpReply() : hdr_sz (0), content_length (0), date (0), last_modified (0), expires (0), cache_control (NULL), surrogate_control (NULL), content_range (NULL), keep_alive (0), saved304 (0), pstate(psReadyToParseStartLine), header (hoReply) { assert(this); httpBodyInit(&body); @@ -221,7 +221,7 @@ HttpReply * httpReplyMake304 (const HttpReply * rep) { - static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER}; + static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_ETAG, /* eof */ HDR_OTHER}; HttpReply *rv; int t; @@ -238,8 +238,11 @@ /* rv->content_range */ /* rv->keep_alive */ HttpVersion ver(1,0); + + // XXX was "" OK? httpStatusLineSet(&rv->sline, ver, - HTTP_NOT_MODIFIED, ""); + // HTTP_NOT_MODIFIED, ""); + HTTP_NOT_MODIFIED, "Not Modified"); for (t = 0; ImsEntries[t] != HDR_OTHER; ++t) if ((e = httpHeaderFindEntry(&rep->header, ImsEntries[t]))) @@ -324,11 +327,13 @@ httpReplyValidatorsMatch(HttpReply const * rep, HttpReply const * otherRep) { String one,two; + debug(93, 9) ("httpReplyValidatorsMatch: start\n"); assert (rep && otherRep); /* Numbers first - easiest to check */ /* Content-Length */ /* TODO: remove -1 bypass */ + debug(93, 9) ("httpReplyValidatorsMatch: %d %d\n", rep->content_length, otherRep->content_length); if (rep->content_length != otherRep->content_length && rep->content_length > -1 && otherRep->content_length > -1) @@ -339,12 +344,17 @@ two = httpHeaderGetStrOrList(&otherRep->header, HDR_ETAG); + debug(93, 9) ("httpReplyValidatorsMatch: %s %s!\n",one.buf(), two.buf()); + if (one.buf() || two.buf()) { if (!one.buf() || !two.buf() || strcasecmp (one.buf(), two.buf())) { + debug(93, 9) ("httpReplyValidatorsMatch: no etags!\n"); one.clean(); two.clean(); return 0; } + } + debug(93, 9) ("httpReplyValidatorsMatch: %ld %ld\n",rep->last_modified, otherRep->last_modified); if (rep->last_modified != otherRep->last_modified) return 0; @@ -353,11 +363,21 @@ two = httpHeaderGetStrOrList(&otherRep->header, HDR_CONTENT_MD5); + + + debug(93, 9) ("httpReplyValidatorsMatch: md5: %s, %s\n", one.buf(), two.buf()); + + // if any of MD5 is not NULL we are able to compare 'em + if (one.buf() || two.buf()) { + if (!one.buf() || !two.buf() || strcasecmp (one.buf(), two.buf())) { one.clean(); two.clean(); return 0; } + } + + debug(93, 9) ("httpReplyValidatorsMatch: true\n"); return 1; } @@ -366,9 +386,11 @@ void httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply const * freshRep) { + debug(93, 9) ("httpReplyUpdateOnNotModified: start\n"); assert(rep && freshRep); /* Can not update modified headers that don't match! */ assert (httpReplyValidatorsMatch(rep, freshRep)); + debug(93, 9) ("httpReplyUpdateOnNotModified: after assert(httpReplyValidatorsMatch)\n"); /* clean cache */ httpReplyHdrCacheClean(rep); /* update raw headers */ @@ -376,6 +398,7 @@ (const HttpHeaderMask *) &Denied304HeadersMask); /* init cache */ httpReplyHdrCacheInit(rep); + debug(93, 9) ("httpReplyUpdateOnNotModified: end\n"); } Index: squid3/src/HttpReply.h diff -u squid3/src/HttpReply.h:1.7 squid3/src/HttpReply.h:1.7.2.1 --- squid3/src/HttpReply.h:1.7 Mon Sep 1 19:12:39 2003 +++ squid3/src/HttpReply.h Thu Jun 3 15:50:06 2004 @@ -102,6 +102,9 @@ HttpHdrContRange *content_range; short int keep_alive; + /* for handleConditionalMiss */ + int saved304; + /* public, readable */ HttpMsgParseState pstate; /* the current parsing state */ Index: squid3/src/HttpRequest.cc diff -u squid3/src/HttpRequest.cc:1.14 squid3/src/HttpRequest.cc:1.14.2.2 --- squid3/src/HttpRequest.cc:1.14 Mon Sep 1 19:12:39 2003 +++ squid3/src/HttpRequest.cc Sat Jun 5 18:18:51 2004 @@ -137,6 +137,8 @@ safe_free(req->vary_headers); + safe_free(req->real_vary_headers); + req->urlpath.clean(); httpHeaderClean(&req->header); @@ -155,6 +157,26 @@ req->extacl_log.clean(); + // clear inm information + if (req->inmlen) { + + assert(req->inm); + + for(int i=0; i < req->inmlen; i++) { + + assert(req->inm[i]); + safe_free(req->inm[i]); + + } + debug(93, 9) ("requestDestroy\n"); + delete req->inm; + } + + //clear etag information + if (req->etag) { + delete req->etag; + } + delete req; } Index: squid3/src/HttpRequest.h diff -u squid3/src/HttpRequest.h:1.9 squid3/src/HttpRequest.h:1.9.2.1 --- squid3/src/HttpRequest.h:1.9 Thu Oct 16 19:12:32 2003 +++ squid3/src/HttpRequest.h Thu Jun 3 15:50:06 2004 @@ -80,6 +80,16 @@ HttpVersion http_ver; time_t ims; int imslen; + + // etags from client INM + char **inm; + + // number of etags in client INM + int inmlen; + + // whole INM string + String *etag; + int max_forwards; /* these in_addr's could probably be sockaddr_in's */ @@ -96,6 +106,7 @@ char *peer_login; /* Configured peer login:password */ time_t lastmod; /* Used on refreshes */ const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ + const char *real_vary_headers; /* original vary_headers (before vary mappings) */ char *peer_domain; /* Configured peer forceddomain */ String tag; /* Internal tag for this request */ String extacl_user; /* User name returned by extacl lookup */ Index: squid3/src/Makefile.am diff -u squid3/src/Makefile.am:1.50 squid3/src/Makefile.am:1.50.2.1 --- squid3/src/Makefile.am:1.50 Mon Oct 20 19:12:44 2003 +++ squid3/src/Makefile.am Thu Jun 3 15:50:06 2004 @@ -316,6 +316,8 @@ client_side_request.h \ clientStream.cc \ clientStream.h \ + CharHashTable.cc \ + CharHashTable.h \ comm.cc \ comm.h \ comm_select.cc \ @@ -552,6 +554,8 @@ client_side_request.h \ clientStream.cc \ clientStream.h \ + CharHashTable.cc \ + CharHashTable.h \ comm.cc \ comm.h \ comm_select.cc \ Index: squid3/src/MemObject.h diff -u squid3/src/MemObject.h:1.8 squid3/src/MemObject.h:1.8.2.1 --- squid3/src/MemObject.h:1.8 Sun Aug 10 19:13:02 2003 +++ squid3/src/MemObject.h Thu Jun 3 15:50:06 2004 @@ -38,6 +38,8 @@ #include "stmem.h" #include "CommRead.h" +#include "CharHashTable.h" + typedef void STMCB (void *data, StoreIOBuffer wroteBuffer); class store_client; @@ -135,8 +137,13 @@ unsigned int chksum; #endif + // the vary key for variant const char *vary_headers; + // information of variants for base vary entry + CharHashTable *etag_mapping; + CharHashTable *vary_headers_mapping; + void delayRead(DeferredRead const &); void kickReads(); Index: squid3/src/StoreMetaVary.cc diff -u squid3/src/StoreMetaVary.cc:1.4 squid3/src/StoreMetaVary.cc:1.4.2.1 --- squid3/src/StoreMetaVary.cc:1.4 Mon Aug 4 19:12:48 2003 +++ squid3/src/StoreMetaVary.cc Thu Jun 3 15:50:06 2004 @@ -37,6 +37,7 @@ #include "StoreMetaVary.h" #include "Store.h" #include "MemObject.h" +#include "CharHashTable.h" MemPool *StoreMetaVary::pool = NULL; @@ -63,15 +64,204 @@ { assert (getType() == STORE_META_VARY_HEADERS); - if (!e->mem_obj->vary_headers) { - /* XXX separate this mutator from the query */ - /* Assume the object is OK.. remember the vary request headers */ - e->mem_obj->vary_headers = xstrdup((char *)value); - return true; + //unpacking + return storeMetaVaryUnpack(e->mem_obj, (char*)value); + +} + +bool +storeMetaVaryUnpack(MemObject *mem_obj, char* value) { + + debug(93, 9) ("\t*** storeMetaVaryUnpack: value: %p mem_obj: %p\n", value, mem_obj); + assert(value && mem_obj); + + // vary_headers length + int vary_headers_len; + + xmemcpy(&vary_headers_len, value, sizeof(int)); + + debug(93, 9) ("\t** storeMetaVaryUnpack: vary_headers_len: %d\n", vary_headers_len); + + char *vary_headers = NULL; + + if (vary_headers_len) { + // unpack vary_headers + vary_headers = (char*)xmalloc(vary_headers_len); + + xmemcpy(vary_headers, &value[sizeof(int)], vary_headers_len); + } + + + // packed etag_mapping length + int etag_mapping_packed_len; + + xmemcpy(&etag_mapping_packed_len, &value[sizeof(int) + vary_headers_len], sizeof(int)); + + debug(93, 9) ("\t** storeMetaVaryUnpack: etag_mapping_packed_len: %d\n", etag_mapping_packed_len); + + char *etag_mapping_packed = NULL; + + if (etag_mapping_packed_len) { + + // get packed etag_mapping + etag_mapping_packed = (char*)xmalloc(etag_mapping_packed_len + sizeof(int)); + + xmemcpy(etag_mapping_packed, + &value[sizeof(int) + vary_headers_len], + etag_mapping_packed_len + sizeof(int)); + } + + // packed vary_headers_mapping length + int vary_headers_mapping_packed_len; + + xmemcpy(&vary_headers_mapping_packed_len, + &value[sizeof(int) + vary_headers_len + sizeof(int) + etag_mapping_packed_len], + sizeof(int)); + + debug(93, 9) ("\t** storeMetaVaryUnpack: vary_headers_mapping_packed_len: %d\n", vary_headers_mapping_packed_len); + + char *vary_headers_mapping_packed = NULL; + + if (vary_headers_mapping_packed_len) { + + // get packed vary_headers_mapping + vary_headers_mapping_packed = (char*)xmalloc(vary_headers_mapping_packed_len + sizeof(int)); + + xmemcpy(vary_headers_mapping_packed, + &value[sizeof(int) + vary_headers_len + sizeof(int) + etag_mapping_packed_len], + vary_headers_mapping_packed_len + sizeof(int)); + + } + + debug(93, 9) ("\t*** storeMetaVaryUnpack: %d %d %d\n", vary_headers_mapping_packed_len, etag_mapping_packed_len, vary_headers_len); + + + // save data in mem_obj + + if (!mem_obj->vary_headers && vary_headers) { + mem_obj->vary_headers = xstrdup(vary_headers); + } + + // vary_headers means this is a variant, not a base object + if (!vary_headers) { + + // unpack mappings + mem_obj->etag_mapping = new CharHashTable(etag_mapping_packed); + mem_obj->vary_headers_mapping = new CharHashTable(vary_headers_mapping_packed); + } - if (strcmp(e->mem_obj->vary_headers, (char *)value) != 0) + + safe_free(etag_mapping_packed); + safe_free(vary_headers_mapping_packed); + + // decide if stuff unpacked correctly + // XXX check also mappings + if (mem_obj->vary_headers) { + + if (vary_headers) { + + if (strcmp(mem_obj->vary_headers, vary_headers) != 0) { + safe_free(vary_headers); + debug(93, 9) ("storeMetaVaryUnpack: end - false\n"); + return false; + } + + } else { + safe_free(vary_headers); + debug(93, 9) ("storeMetaVaryUnpack: end - false\n"); return false; + } + } + safe_free(vary_headers); + + debug(93, 9) ("storeMetaVaryUnpack: end - true\n"); return true; } + +/* + * Packs vary_headers or mappings + * rlen becomes length of the whole packed string (returned) + * + * LVH VH LEM VEM LVHM VVHM + */ +char * +storeMetaVaryPack(MemObject *mem_obj, int *rlen) { + + assert(mem_obj && rlen); + + debug(93, 9) ("storeMetaVaryPack: start\n"); + + // packed string to be returned + char *packed = NULL; + + // never all three + assert(!(mem_obj->vary_headers && mem_obj->etag_mapping && mem_obj->vary_headers_mapping)); + + debug(93, 9) ("storeMetaVaryPack: %p %p %p\n", mem_obj->vary_headers, mem_obj->etag_mapping, mem_obj->vary_headers_mapping); + + // which do we have? + int hasMappings = mem_obj->vary_headers_mapping && mem_obj->etag_mapping; + int hasVaryHeaders = (int)mem_obj->vary_headers; + + if (hasMappings || hasVaryHeaders) { + + // mappings lengths + int etag_mapping_len = sizeof(int); + int vary_headers_mapping_len = sizeof(int); + + // strings for packed mappings + char *etag_mapping_packed = NULL; + char *vary_headers_mapping_packed = NULL; + + // pack if we are to + if (hasMappings) { + etag_mapping_packed = mem_obj->etag_mapping->pack(&etag_mapping_len); + vary_headers_mapping_packed = mem_obj->vary_headers_mapping->pack(&vary_headers_mapping_len); + } + + debug(93, 9) ("storeMetaVaryPack: %d %d\n", etag_mapping_len, vary_headers_mapping_len); + + // vary_headers length + int vary_headers_len = 0; + + // pack if we are to + if (hasVaryHeaders) + vary_headers_len = strlen(mem_obj->vary_headers) + 1; + + // total length + int len = sizeof(int) + vary_headers_len + etag_mapping_len + vary_headers_mapping_len; + + // final allocation + packed = (char*)xmalloc(len); + + // LVH + xmemcpy(packed, &vary_headers_len, sizeof(int)); + + //VH + if (hasVaryHeaders) + xmemcpy(&packed[sizeof(int)], mem_obj->vary_headers, vary_headers_len); + + + if (hasMappings) { + debug(93, 9) ("storeMetaVaryPack: has mappings\n"); + // LEM VEM + xmemcpy(&packed[sizeof(int)+vary_headers_len], etag_mapping_packed, etag_mapping_len); + //LVH VVHM + xmemcpy(&packed[sizeof(int)+vary_headers_len + etag_mapping_len], + vary_headers_mapping_packed, vary_headers_mapping_len); + } else { + debug(93, 9) ("storeMetaVaryPack: has no mappings\n"); + int zero = 0; + xmemcpy(&packed[sizeof(int) + vary_headers_len], &zero, sizeof(int)); + xmemcpy(&packed[sizeof(int) + vary_headers_len + sizeof(int)], &zero, sizeof(int)); + } + + + *rlen = len; + } + + debug(93, 9) ("storeMetaVaryPack: end\n"); + return packed; +} Index: squid3/src/StoreMetaVary.h diff -u squid3/src/StoreMetaVary.h:1.4 squid3/src/StoreMetaVary.h:1.4.2.1 --- squid3/src/StoreMetaVary.h:1.4 Mon Aug 4 19:12:48 2003 +++ squid3/src/StoreMetaVary.h Thu Jun 3 15:50:06 2004 @@ -35,6 +35,7 @@ #define SQUID_STOREMETAVARY_H #include "StoreMeta.h" +#include "MemObject.h" class StoreMetaVary : public StoreMeta { @@ -51,4 +52,7 @@ static MemPool *pool; }; +bool storeMetaVaryUnpack(MemObject *, char*); +char *storeMetaVaryPack(MemObject *, int *); + #endif /* SQUID_STOREMETAVARY_H */ Index: squid3/src/client_side.cc diff -u squid3/src/client_side.cc:1.60 squid3/src/client_side.cc:1.60.2.2 --- squid3/src/client_side.cc:1.60 Sun Apr 4 19:00:27 2004 +++ squid3/src/client_side.cc Sat Jun 5 18:18:51 2004 @@ -596,8 +596,11 @@ openReference = NULL; clientdbEstablished(peer.sin_addr, -1); /* decrement */ assert(areAllContextsForThisConnection()); + debug(93, 9) ("ConnStateData::close before freeAllContexts()\n"); freeAllContexts(); + debug(93, 9) ("ConnStateData::close before authenticateOnCloseConnection()\n"); authenticateOnCloseConnection(this); + debug(93, 9) ("ConnStateData::close finished\n"); } bool @@ -801,6 +804,8 @@ if (!multipartRangeRequest()) { size_t length = lengthToSend(bodyData.range()); noteSentBodyBytes (length); + if (flags.saved304) + length = 0; comm_write(fd(), bodyData.data, length, clientWriteBodyComplete, this); return; @@ -1165,10 +1170,14 @@ void ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData) { + // ranges prepareReply(rep); + /* init mb; put status line and headers if any */ assert (rep); + MemBuf mb = httpReplyPack(rep); + /* Save length of headers for persistent conn checks */ http->out.headers_sz = mb.size; #if HEADERS_LOG @@ -1181,6 +1190,8 @@ size_t length = lengthToSend(bodyData.range()); noteSentBodyBytes (length); + debug(93, 9) ("sendStartOfMessage: length = %d\n", length); + if (!flags.saved304) memBufAppend(&mb, bodyData.data, length); } else { packRange(bodyData, &mb); @@ -1194,7 +1205,8 @@ } /* - * Write a chunk of data to a client socket. If the reply is present, send the reply headers down the wire too, + * Write a chunk of data to a client socket. If the reply is present, send + * the reply headers down the wire too, * and clean them up when finished. * Pre-condition: * The request is one backed by a connection, not an internal request. @@ -1231,6 +1243,10 @@ return; } + int saved304 = rep ? rep->saved304 : 0; + if (saved304) + context->flags.saved304 = 1; + if (!context->startOfOutput()) context->sendBody(rep, recievedData); else { @@ -1522,6 +1538,7 @@ void ClientSocketContext::writeComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag) { + StoreEntry *entry = http->storeEntry(); http->out.size += size; assert(fd > -1); @@ -2292,6 +2309,7 @@ context->mayUseConnection(true); clientAccessCheck(http); + } static void @@ -2836,6 +2854,7 @@ clientdbEstablished(details->peer.sin_addr, 1); incoming_sockets_accepted++; + } #if USE_SSL @@ -3078,7 +3097,12 @@ int varyEvaluateMatch(StoreEntry * entry, HttpRequest * request) { + + // vary_headers from Vary of base entry and request headers + // set after finding of Vary base entry const char *vary = request->vary_headers; + + // reply has Vary header int has_vary = httpHeaderHas(&entry->getReply()->header, HDR_VARY); #if X_ACCELERATOR_VARY @@ -3086,17 +3110,21 @@ httpHeaderHas(&entry->getReply()->header, HDR_X_ACCELERATOR_VARY); #endif + // mem_obj has vary_headers if reply is a variant if (!has_vary || !entry->mem_obj->vary_headers) { + + // we were looking for a variant and we haven't found one if (vary) { /* Oops... something odd is going on here.. */ - debug(33, - 1) + debug(33, 1) ("varyEvaluateMatch: Oops. Not a Vary object on second attempt, '%s' '%s'\n", entry->mem_obj->url, vary); safe_free(request->vary_headers); + safe_free(request->real_vary_headers); return VARY_CANCEL; } + // there never was any Vary/variants if (!has_vary) { /* This is not a varying object */ return VARY_NONE; @@ -3107,28 +3135,60 @@ */ vary = httpMakeVaryMark(request, entry->getReply()); + assert(entry->mem_obj->vary_headers_mapping); + assert(entry->mem_obj->etag_mapping); + + // always should be vary if (vary) { - request->vary_headers = xstrdup(vary); + + // saving vary_headers + request->real_vary_headers = xstrdup(vary); + request->vary_headers = entry->mem_obj->vary_headers_mapping->get((char *)request->real_vary_headers); + + // if there is no mapping + if (!request->vary_headers) + request->vary_headers = xstrdup(request->real_vary_headers); + else + request->vary_headers = xstrdup(request->vary_headers); + + return VARY_OTHER; } else { /* Ouch.. we cannot handle this kind of variance */ /* XXX This cannot really happen, but just to be complete */ return VARY_CANCEL; } + } else { + // reply has Vary header and reply is a variant + // XXX we're not sure if each variant has to have Vary header + + // first time we know the reply is a variant if (!vary) { + + // get variants key vary = httpMakeVaryMark(request, entry->getReply()); - if (vary) + // should always be vary + if (vary) { + request->vary_headers = xstrdup(vary); - } + request->real_vary_headers = xstrdup(vary); + + } else { - if (!vary) { /* Ouch.. we cannot handle this kind of variance */ /* XXX This cannot really happen, but just to be complete */ return VARY_CANCEL; - } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) { + } + + } + + + // check if this is the correct variant + if (strcmp(vary, entry->mem_obj->vary_headers) == 0) { return VARY_MATCH; + } else { /* Oops.. we have already been here and still haven't * found the requested variant. Bail out Index: squid3/src/client_side.h diff -u squid3/src/client_side.h:1.7 squid3/src/client_side.h:1.7.4.1 --- squid3/src/client_side.h:1.7 Thu Aug 14 19:12:38 2003 +++ squid3/src/client_side.h Thu Jun 3 15:50:06 2004 @@ -71,6 +71,10 @@ int parsed_ok: 1; /* Was this parsed correctly? */ + +int saved304: + 1; /* handleConditionalMiss path? */ + } flags; Index: squid3/src/client_side_reply.cc diff -u squid3/src/client_side_reply.cc:1.48 squid3/src/client_side_reply.cc:1.48.2.4 --- squid3/src/client_side_reply.cc:1.48 Sun Apr 4 19:00:27 2004 +++ squid3/src/client_side_reply.cc Sun Jul 4 09:56:59 2004 @@ -77,7 +77,7 @@ cbdataReferenceDone(http); } -clientReplyContext::clientReplyContext(clientHttpRequest *clientContext) : http (cbdataReference(clientContext)), old_entry (NULL), old_sc(NULL), deleting(false) +clientReplyContext::clientReplyContext(clientHttpRequest *clientContext) : http (cbdataReference(clientContext)), old_entry (NULL), old_sc(NULL), vary_entry(NULL), vary_sc(NULL), deleting(false) {} /* create an error in the store awaiting the client side to read it. */ @@ -219,6 +219,25 @@ storeClientCopy(sc, http->storeEntry(), tempBuffer, SendMoreData, this); } +/* + * copy of triggerInitialStoreRead but takes also a callback to call back at the end + */ +void +clientReplyContext::triggerInitialStoreReadClone(STCB * callback) +{ + + StoreIOBuffer otherTempBuffer; + /* when confident, 0 becomes reqofs, and then this factors into + * startSendProcess + */ + assert(reqofs == 0); + otherTempBuffer.offset = 0; + otherTempBuffer.length = next()->readBuffer.length; + otherTempBuffer.data = next()->readBuffer.data; + debug(93, 9) ("triggerInitialStoreReadClone: sc = %p, callback: %p\n", sc, callback); + storeClientCopy(sc, http->storeEntry(), otherTempBuffer, callback, this); +} + /* there is an expired entry in the store. * setup a temporary buffer area and perform an IMS to the origin */ @@ -228,7 +247,13 @@ char *url = http->uri; StoreEntry *entry = NULL; debug(88, 3)("clientReplyContext::processExpired: '%s'", http->uri); - assert(http->storeEntry()->lastmod >= 0); + + // get ETag from reply + const HttpHeader *rep_hdr = &http->storeEntry()->getReply()->header; + ETag etag = httpHeaderGetETag(rep_hdr, HDR_ETAG); + + // Last-Modified or ETag + assert(http->storeEntry()->lastmod >= 0 || etag.str); /* * check if we are allowed to contact other servers * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return @@ -257,11 +282,68 @@ sc->setDelayId(DelayId::DelayClient(http)); #endif + // old_entry may be a variant + const char *vary_headers = old_entry->mem_obj->vary_headers; + + // vary_entry is base vary object + vary_entry = NULL; + + if (vary_headers) { + + vary_entry = storeGetPublic(old_entry->mem_obj->url, old_entry->mem_obj->method); + + } + + debug(93, 9) ("processExpired: vary_entry = %p\n", vary_entry); + + // if Vary + if (vary_entry) { + + // collect etags of all the variants to send to server + + // we should have mem_obj because base object was swapped in in cacheHit + assert(vary_entry->mem_obj); + + // get all the etags + char * etag_keys = vary_entry->mem_obj->etag_mapping->keys(); + debug (93, 9) ("processExpired: inm=[%s]\n", etag_keys); + + flags.varyvalidation = 1; + + // save them + if (etag_keys) { + + http->request->lastmod = -1; + http->request->etag = new String(etag_keys); + + } else { + + // the variants has no etags -> try to validate with IMS + http->request->etag = NULL; http->request->lastmod = old_entry->lastmod; - debug(88, 5)("clientReplyContext::processExpired : lastmod %ld", + + } + + } else { + + // no vary, just casual single etag + + if (etag.str) + http->request->etag = new String(etag.str); + else + http->request->etag = NULL; + + http->request->lastmod = old_entry->lastmod; + + } + + debug(88, 5)("clientReplyContext::processExpired : lastmod %ld\n", (long int) entry->lastmod); http->storeEntry(entry); assert(http->out.offset == 0); + + debug(93, 9) ("processExpired: old_entry = %p old_entry->mem_obj = %p\n", old_entry, old_entry->mem_obj); + fwdStart(http->getConn().getRaw() != NULL ? http->getConn()->fd : -1, http->storeEntry(), http->request); /* Register with storage manager to receive updates when data comes in. */ @@ -299,24 +381,21 @@ return false; } - /* If the client did not send IMS in the request, then it + /* if client did not send INM in the request, then it * must get the old object, not this "Not Modified" reply * REGARDLESS of validation */ - if (!http->request->flags.ims) { - debug(88, 5) ("clientGetsOldEntry: YES, no client IMS\n"); + if (!http->request->flags.inm && !http->request->flags.ims) { + + debug(93, 5) ("clientGetsOldEntry: YES, no client INM, nor IMS\n"); return true; + } - /* If key metadata in the reply are not consistent with the - * old entry, we must use the new reply. - * Note: this means that the server is sending garbage replies - * in that it has sent an IMS that is incompatible with our request!? - */ - /* This is a duplicate call through the HandleIMS code path. - * Can we guarantee we don't need it elsewhere? - */ + + // to sum up: 304 and client's request was conditional if (!httpReplyValidatorsMatch(http->storeEntry()->getReply(), old_entry->getReply())) { + debug(88, 5) ("clientGetsOldEntry: NO, Old object has been invalidated" "by the new one\n"); return false; @@ -324,11 +403,42 @@ /* If the client IMS time is prior to the entry LASTMOD time we * need to send the old object */ + + if (http->request->flags.ims) { if (old_entry->modifiedSince(http->request)) { debug(88, 5) ("clientGetsOldEntry: YES, modified since %ld\n", (long int) http->request->ims); return true; } + } + + + // IMS(passed)+INM + if (http->request->flags.inm) { + + // check if one of the etags from reply matches client's one + + const HttpHeader *hdr = &http->storeEntry()->getReply()->header; + ETag etag = httpHeaderGetETag(hdr, HDR_ETAG); + const char *etag_str = etag.str; + int llen = 0; + char *etag_inm; + + debug(93,5) ("clientGetsOldEntry: etag from reply %s\n",etag_str); + + while(etag_str && llen < http->request->inmlen) { + etag_inm = http->request->inm[llen++]; + + debug(93,5) ("clientGetsOldEntry: parsing client INM: %s\n",etag_inm); + + if (strcmp(etag_str,etag_inm)) + continue; + + return false; + } + + return true; + } debug(88, 5) ("clientGetsOldEntry: NO, new one is fine\n"); return false; @@ -414,6 +524,10 @@ * headers have been loaded from disk. */ http->logType = LOG_TCP_REFRESH_HIT; + http_status status = http->storeEntry()->getReply()->sline.status; + + debug(93, 3) ("handleIMSGiveClientUpdatedOldEntry: reply=%d\n", status); + if (httpReplyValidatorsMatch(http->storeEntry()->getReply(), old_entry->getReply())) { int unlink_request = 0; @@ -452,6 +566,8 @@ * is a 304 that is incompatible with our cached entities. */ + debug(93, 9) ("handleIMSGiveClientNewEntry: newReply = %p oldReply = %p\n",http->storeEntry()->getReply(), old_entry->getReply()); + if (http->request->flags.ims) { /* The client asked for a IMS, and can deal * with any reply @@ -505,13 +621,118 @@ } } +/* + * see variantUpdateSend + */ +void +clientReplyContext::VariantUpdateSend(void *data, StoreIOBuffer result) +{ + clientReplyContext *context = (clientReplyContext *)data; + context->variantUpdateSend(result); +} + +/* + * for now does nothing :( + * should update variant headers after revalidation + */ +void +clientReplyContext::variantUpdateSend(StoreIOBuffer result) +{ + sendMoreData(result); +} + +/* + * see fakeEntryToVariant + */ +void +clientReplyContext::FakeEntryToVariant(void *data, StoreIOBuffer result) +{ + clientReplyContext *context = (clientReplyContext *)data; + context->fakeEntryToVariant(result); +} + +/* + * Decide which variant to send to client upon receiving Not Modified after Vary INM + * etag based + */ +void +clientReplyContext::fakeEntryToVariant(StoreIOBuffer result) +{ + // assumptions: + // - tempBufferVary - result saved in handleIMSReply + // - vary_entry - base vary object + // - vary_sc + // - old_entry is stale entry or save new entry for update + + // 304's headers + const HttpHeader *hdr = &old_entry->getReply()->header; + ETag etag = httpHeaderGetETag(hdr, HDR_ETAG); + const char *etag_str = etag.str; + + assert(vary_entry->mem_obj); + + // vary_headers of the variant pointed to by etag_str + + const char *vary_headers = vary_entry->mem_obj->etag_mapping->get + ((char*)etag_str); + + if (vary_headers) { + // we have such a variant + + // update for storeGetPublicByRequest + http->request->vary_headers = xstrdup(vary_headers); + StoreEntry *e = storeGetPublicByRequest(http->request); + + + if (e) { + // XXX: what with old_entry?? + // clearly we don't need it anymore + // mmatusiak + if (old_sc) { + storeUnregister(old_sc, old_entry, this); + } + + if (vary_sc) { + storeUnregister(vary_sc, vary_entry, this); + } + + storeLockObject(e); + sc = storeClientListAdd(e, this); + reqofs = 0; + reqsize = 0; + http->storeEntry(e); + + StoreIOBuffer tempBuffer; + tempBuffer.offset = reqofs; + tempBuffer.length = next()->readBuffer.length; + tempBuffer.data = next()->readBuffer.data; + // XXX: we should update first + storeClientCopy(sc, http->storeEntry(), tempBuffer, SendMoreData, this); + + } else { + // here we didn't find variant, we send server response or stale entry + debug(93, 9) ("fakeEntryToVariant: sending supposed garbage - no variant found\n"); + restoreState(); + sendMoreData(tempBufferVary); + + } + + } else { + // we haven't found correct etag->vary_headers mapping, so we must send client + // server response (probably garbage) or stale entry + debug(93, 9) ("fakeEntryToVariant: sending supposed garbage - no mapping\n"); + restoreState(); + sendMoreData(tempBufferVary); + } +} + void clientReplyContext::handleIMSReply(StoreIOBuffer result) { if (deleting) return; - debug(88, 3) ("clientHandleIMSReply: %s, %lu bytes\n", + debug(88, 3) ("handleIMSReply: %s, %lu bytes\n", storeUrl(http->storeEntry()), (long unsigned) result.length); @@ -525,6 +746,79 @@ reqsize = result.length + reqofs; http_status status = http->storeEntry()->getReply()->sline.status; + debug(88, 3) ("handleIMSReply: reply=%d\n", status); + + if (flags.varyvalidation) { + debug(88, 9) ("handleIMSReply: variant handling\n"); + + // we don't need old_entry anymore + if (old_entry) { + storeUnregister(old_sc, old_entry, this); + + // how to drop it? + storeUnlockObject(old_entry); + old_entry = NULL; + old_sc = NULL; + } + + // XXX: it's not sufficient (but for now, we can leave this) + // mmatusiak + // if other than HTTP_NOT_MODIFIED send response to client + if (status != HTTP_NOT_MODIFIED) { + debug(88, 9) ("handleIMSReply: new variant, proceed to sendMoreData()\n"); + + sendMoreData(result); + + return; + } + + // XXX: what if entry got aborted? + // mmatusiak + StoreEntry *entry = http->storeEntry(); + + // we go HTTP_NOT_MODIFIED and we can go on with vary stuff + + // base vary object + vary_entry = storeGetPublic(entry->mem_obj->url, entry->mem_obj->method); + + if (vary_entry) { + + // save for fakeEntryToVariant + saveState(); + tempBufferVary = result; + + vary_sc = NULL; + + if (!vary_entry->mem_obj) { + // "fake" (base) has no memobj -> swap it in + debug(88, 9) ("handleIMSReply: vary_entry has no mem_obj\n"); + + storeCreateMemObject(vary_entry, http->uri, http->log_uri); + + vary_sc = storeClientListAdd(vary_entry, this); + + // XXX: we need this only to swapin mem_obj we don't actualy need the reply + // so i thought that small buffer will be sufficient + // mmatusiak + StoreIOBuffer tempBuffer2(HTTP_REQBUF_SZ, 0, (char*)xmalloc(HTTP_REQBUF_SZ)); + storeClientCopy(vary_sc, vary_entry, tempBuffer2, FakeEntryToVariant, this); + } else { + // "fake" (base) has memobj + debug(88, 9) ("handleIMSReply: vary_entry has mem_obj\n"); + fakeEntryToVariant(result); + } + + } else { + + debug(88, 9) ("handleIMSReply: no base entry found, sendMoreData()\n"); + sendMoreData(result); + } + + return; + } + + // when no 'vary' was detected before we can process as always + // hovewer this implementation is broken, see httpReplyValidatorsMatch() issues if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) cleanUpAfterIMSCheck(); @@ -561,6 +855,8 @@ StoreEntry *e = http->storeEntry(); + debug(93, 9) ("cacheHit: entry = %p\n", e); + HttpRequest *r = http->request; debug(88, 3) ("clientCacheHit: %s, %ud bytes\n", http->uri, (unsigned int)result.length); @@ -627,22 +923,35 @@ case VARY_NONE: /* No variance detected. Continue as normal */ + flags.varyother = 0; break; case VARY_MATCH: /* This is the correct entity for this request. Continue */ debug(88, 2) ("clientProcessHit: Vary MATCH!\n"); + flags.varyother = 0; break; case VARY_OTHER: + /* This is not the correct entity for this request. We need * to requery the cache. */ + + flags.varyother = 1; + + // XXX won't it remove base object from memory removeClientStoreReference(&sc, http); + e = NULL; + /* Note: varyEvalyateMatch updates the request with vary information * so we only get here once. (it also takes care of cancelling loops) */ + + debug(93, 9) ("cacheHit: r->vary_headers = %s, r->real_vary_headers = %s\n", + r->vary_headers, r->real_vary_headers); + debug(88, 2) ("clientProcessHit: Vary detected!\n"); clientGetMoreData(ourNode, http); return; @@ -650,6 +959,7 @@ case VARY_CANCEL: /* varyEvaluateMatch found a object loop. Process as miss */ debug(88, 1) ("clientProcessHit: Vary object loop!\n"); + flags.varyother = 0; processMiss(); return; } @@ -661,10 +971,15 @@ return; } + if (storeCheckNegativeHit(e)) { + http->logType = LOG_TCP_NEGATIVE_HIT; sendMoreData(result); + } else if (!Config.onoff.offline && refreshCheckHTTP(e, r) && !http->flags.internal) { + + debug(88, 5) ("clientCacheHit: in refreshCheck() block\n"); /* * We hold a stale copy; it needs to be validated @@ -678,9 +993,15 @@ */ r->flags.need_validation = 1; - if (e->lastmod < 0) { + // ETag check... + const HttpHeader *rep_hdr = &e->getReply()->header; + ETag etag = httpHeaderGetETag(rep_hdr, HDR_ETAG); + + debug(93, 9) ("cacheHit: ETag: %s\n", etag.str); + + if (e->lastmod < 0 && !etag.str) { /* - * Previous reply didn't have a Last-Modified header, + * Previous reply didn't have a Last-Modified header or ETag, * we cannot revalidate it. */ http->logType = LOG_TCP_MISS; @@ -707,9 +1028,9 @@ http->logType = LOG_TCP_MISS; processMiss(); } - } else if (r->flags.ims) { + } else if (r->flags.ims || r->flags.inm) { /* - * Handle If-Modified-Since requests from the client + * Handle If-Modified-Since and If-None-Match requests from the client */ if (e->getReply()->sline.status != HTTP_OK) { @@ -717,17 +1038,67 @@ e->getReply()->sline.status); http->logType = LOG_TCP_MISS; processMiss(); - } else if (e->modifiedSince(http->request)) { + return; + + } + + // 304 vs 200 decisions + + // IMS based + int modifiedSince = r->flags.ims ? e->modifiedSince(http->request) : false; + + // INM based + int etagNoneMatch = r->flags.inm; + + if (r->flags.inm) { + + // r->inm is client ETags array + + // reply's ETag + const HttpHeader *hdr = &e->getReply()->header; + ETag etag = httpHeaderGetETag(hdr, HDR_ETAG); + const char *etag_str = etag.str; + + debug(93,5) ("cacheHit: etag from stored reply %s\n",etag_str); + + + // check if any of client's ETags matches + char *etag_inm; + int llen = 0; + + while(etag.str && llen < r->inmlen) { + + etag_inm = r->inm[llen++]; + + debug(93,5) ("cacheHit: parsing client INM: %s\n",etag_inm); + + if (!strcmp(etag_str,etag_inm)) { + etagNoneMatch = false; + break; + } + } + } + + if (modifiedSince || etagNoneMatch) { + // one of validators doesn't match -> send 200 OK http->logType = LOG_TCP_IMS_HIT; sendMoreData(result); + } else { + // both validators match -> send 304 Not Modified + time_t const timestamp = e->timestamp; + HttpReply *temprep = httpReplyMake304 (e->getReply()); http->logType = LOG_TCP_IMS_HIT; + removeClientStoreReference(&sc, http); + createStoreEntry(http->request->method, request_flags()); + e = http->storeEntry(); + /* * Copy timestamp from the original entry so the 304 * reply has a meaningful Age: header. @@ -735,6 +1106,7 @@ e->timestamp = timestamp; httpReplySwapOut (temprep, e); e->complete(); + /* TODO: why put this in the store and then serialise it and then parse it again. * Simply mark the request complete in our context and * write the reply struct to the client side @@ -782,6 +1154,20 @@ removeClientStoreReference(&sc, http); } + // vary check + vary_entry = NULL; + + // there was no variant matching client's request + // the flag was set in cacheHit with VARY_OTHER. + + if (flags.varyother) { + // base vary object + vary_entry = storeGetPublic(urlCanonical(r), r->method); + } + + + debug(93,9) ("processMiss: varyother: %d, vary_entry: %p\n", flags.varyother, vary_entry); + if (r->method == METHOD_PURGE) { purgeRequest(); return; @@ -805,9 +1191,40 @@ triggerInitialStoreRead(); return; } else { + assert(http->out.offset == 0); + debug(93, 9) ("processMiss: before createStoreEntry()\n"); createStoreEntry(r->method, r->flags); - triggerInitialStoreRead(); + + debug(93, 9) ("processMiss: entry = %p\n", http->storeEntry()); + + // vary procesing start... + + if (vary_entry) { + + http->request->lastmod = -1; + + assert(vary_entry->mem_obj); + + // get all the variants' etags + char * etag_keys = vary_entry->mem_obj->etag_mapping->keys(); + debug (93, 9) ("processMiss: inm=[%s]\n", etag_keys); + + // and use 'em + if (etag_keys) { + + http->request->etag = new String(etag_keys); + + triggerInitialStoreReadClone(HandleIMSReply); + + flags.varyvalidation = 1; + + } else + triggerInitialStoreReadClone(HandleConditionalMiss); + + } else + triggerInitialStoreReadClone(HandleConditionalMiss); + if (http->redirect.status) { HttpReply *rep = httpReplyCreate(); @@ -821,9 +1238,11 @@ http->redirect.location); httpReplySwapOut(rep, http->storeEntry()); http->storeEntry()->complete(); + return; } + if (http->flags.internal) r->protocol = PROTO_INTERNAL; @@ -1459,6 +1878,11 @@ return; } + // if handleConditionalMiss decided to send 304 + if (saved304) { + holdingReply = saved304; + } + /* enforce 1.0 reply version */ holdingReply->sline.version = HttpVersion(1,0); @@ -1664,6 +2088,7 @@ reqofs = 0; /* guarantee nothing has been sent yet! */ assert(http->out.size == 0); + storeLockObject(http->storeEntry()); assert(http->out.offset == 0); tempBuffer.offset = reqofs; tempBuffer.length = getNextNode()->readBuffer.length; @@ -1833,6 +2258,7 @@ } headers_sz = rep->hdr_sz; + debug(93, 9) ("\t --- processReplyAccess: headers_sz: %d\n", headers_sz); ACLChecklist *replyChecklist; replyChecklist = clientAclChecklistCreate(Config.accessList.reply, http); replyChecklist->reply = rep; @@ -1882,7 +2308,7 @@ assert(body_size >= 0); debug(88,3) - ("clientReplyContext::sendMoreData: Appending %d bytes after %d bytes of headers\n", + ("clientReplyContext::processReplyAccessResult: Appending %d bytes after %d bytes of headers\n", (int) body_size, rep->hdr_sz); #if ESI @@ -1896,7 +2322,11 @@ } #endif - if (http->request->method == METHOD_HEAD) { + + debug(93, 9) ("processReplyAccessResult: saved304: %p\n", + saved304); + // method was head or we're sending 304 so we don't have the body + if ((http->request->method == METHOD_HEAD) /*|| saved304*/) { /* do not forward body for HEAD replies */ body_size = 0; http->flags.done_copying = 1; @@ -1930,12 +2360,284 @@ } /* TODO??: move the data in the buffer back by the request header size */ - clientStreamCallback((clientStreamNode *)http->client_stream.head->data, - http, rep, tempBuffer); + clientStreamCallback((clientStreamNode *)http->client_stream.head->data, http, rep, tempBuffer); return; } +/* + * see handleConditionalMiss + */ +void +clientReplyContext::HandleConditionalMiss(void *data, StoreIOBuffer result) +{ + clientReplyContext *context = static_cast(data); + context->handleConditionalMiss(result); +} + +/* + * decides whether to send 304 or 200 + * when there is no cached reply and client's request was conditional + * + * in case the request isnot conditional passes to sendMoreData + */ +void +clientReplyContext::handleConditionalMiss(StoreIOBuffer result) +{ + + + HttpRequest *r = http->request; + StoreEntry *e = http->storeEntry(); + assert(r); + + debug(93, 9) ("handleConditionalMiss: ims: %d, inm: %d\n", + r->flags.ims, r->flags.inm); + + // if the request isnot conditional + if (!r->flags.ims && !r->flags.inm) { + sendMoreData(result); + return; + } + + + debug(93, 9) ("handleConditionalMiss: Reply code %d\n", + e->getReply()->sline.status); + + if (e->getReply()->sline.status != HTTP_OK ) { + + // not 200... passing + + sendMoreData(result); + return; + + } + + // check if validators match, the same as in cacheHit + int modifiedSince = r->flags.ims ? e->modifiedSince(http->request) : false; + int etagNoneMatch = r->flags.inm; + + if (r->flags.inm) { + const HttpHeader *hdr = &e->getReply()->header; + ETag etag = httpHeaderGetETag(hdr, HDR_ETAG); + const char *etag_str = etag.str; + char *etag_inm; + int llen = 0; + + debug(93,5) ("handleConditionalMiss: etag from stored reply %s\n",etag_str); + + while(etag.str && llen < r->inmlen) { + etag_inm = r->inm[llen++]; + if (!strcmp(etag_str,etag_inm)) { + etagNoneMatch = false; + break; + } + } + } + + debug(93, 9) ("handleConditionalMiss: modifiedSince: %d, etagNoneMatch: %d\n", + modifiedSince, etagNoneMatch); + + if (modifiedSince || etagNoneMatch) { + // one of validators doesn't match -> send 200 OK + sendMoreData(result); + return; + + } + + // both validators match -> send 304 Not Modified + + // magic trick + saved304 = httpReplyMake304 (e->getReply()); + saved304->saved304 = 1; + + // swap current 200 out -> clearly wrong + //e->complete(); + + // fill result with new saved304 reply + // XXX unnecessary? + //Packer p; + //packerToStoreIOBufferInit(&p, &result); + //httpReplyPackHeadersInto(saved304, &p); + //packerClean(&p); + + //saved304->hdr_sz = result.length; + + + sendMoreData(result); +} + + +/* + * see updateFakeEntry + */ +void +clientReplyContext::UpdateFakeEntry(void *data, StoreIOBuffer result) +{ + clientReplyContext *context = static_cast(data); + context->updateFakeEntry(result); +} + +/* + * updates Vary mappings when result passes through sendMoreData + */ +void clientReplyContext::updateFakeEntry(StoreIOBuffer result) +{ + StoreEntry *entry = http->storeEntry(); + + ETag etag = httpHeaderGetETag(&entry->mem_obj->getReply()->header, HDR_ETAG); + + // this is wrong when Vary has changed + const char *vary_headers = http->request->real_vary_headers; + + assert(vary_entry); + + // get the mappings from base vary object + CharHashTable *vhm = vary_entry->mem_obj->vary_headers_mapping; + CharHashTable *em = vary_entry->mem_obj->etag_mapping; + + // tells if the code below changes the mappings + int changed = 1; + + if (!vhm->get((char*)vary_headers)) { + + if (etag.str) { + // there is ETag + + if (em->get + ((char*)etag.str)) { + + vhm->put((char*)vary_headers, (char*)em->get + ((char*)etag.str)); + } + else { + vhm->put((char*)vary_headers, (char*)vary_headers); + em->put((char*)etag.str, (char*)vary_headers); + } + } else { + // no ETag + vhm->put((char*)vary_headers, (char*)vary_headers); + } + + } + else { + + if (etag.str) { + // there is ETag + + if (em->get + ((char*)etag.str)) { + if (strcmp(vhm->get + ((char*)vary_headers), em->get + ((char*)etag.str))) { + + vhm->remove + ((char*)vary_headers); + + vhm->put((char*)vary_headers, em->get + ((char*)etag.str)); + } + else + changed = 0; + } + else { + + vhm->remove + ((char*)vary_headers); + + vhm->put((char*)vary_headers, (char*)vary_headers); + + em->put((char*)etag.str, (char*)vary_headers); + } + } else + changed = 0; + } + + // XXX if (changed) swap out + debug(93, 9) ("updateFakeEntry: changed: %d\n", changed); + vhm->dump(); + em->dump(); + + // MM 2004.06.29 + if (changed) { + + String vary = httpHeaderGetList(&vary_entry->mem_obj->getReply()->header, + HDR_VARY); + + // kill old base + storeRelease(vary_entry); + debug(93, 9) ("\t --> updateFakeEntry: vary_entry: %p, mem: %p\n", + vary_entry, vary_entry->mem_obj); + + debug(93, 9) ("updateFakeEntry: url: %s, log_url: %s, method: %d\n", + entry->mem_obj->url, entry->mem_obj->log_url, + http->request->method); + + debug(93, 9) ("updateFakeEntry: url: %s, log_url: %s, method: %d\n", + vary_entry->mem_obj->url, vary_entry->mem_obj->log_url, + http->request->method); + + // new base + vary_entry = storeCreateEntry(vary_entry->mem_obj->url, + vary_entry->mem_obj->log_url, + http->request->flags, + http->request->method); + debug(93, 9) ("updateFakeEntry: vary_entry: %p\n", vary_entry); + HttpVersion version(1, 0); + httpReplySetHeaders((HttpReply *)vary_entry->getReply(), version, HTTP_OK, "Internal marker object", + "x-squid-internal/vary", -1, -1, squid_curtime + 100000); + + debug(93, 9) ("updateFakeEntry: Internal marker object set\n"); + debug(93, 9) ("updateFakeEntry: vary: %s\n", vary.buf()); + if (vary.size()) { + httpHeaderPutStr((HttpHeader *)&vary_entry->getReply()->header, HDR_VARY, vary.buf()); + vary.clean(); + } + + // mappings + vary_entry->mem_obj->etag_mapping = em; + vary_entry->mem_obj->vary_headers_mapping = vhm; + + debug(93, 9) ("updateFakeEntry: etag_mapping and vary_headers_mapping set\n"); + +#if X_ACCELERATOR_VARY + vary = httpHeaderGetList(&mem->getReply()->header, HDR_X_ACCELERATOR_VARY); + + if (vary.buf()) { + /* Again, we own this structure layout */ + httpHeaderPutStr((HttpHeader *)&vary_entry->getReply()->header, HDR_X_ACCELERATOR_VARY, + vary.buf()); + vary.clean(); + } + +#endif + storeSetPublicKey(vary_entry); + debug(93, 9) ("updateFakeEntry: storeSetPublicKey\n"); + + storeBuffer(vary_entry); + + debug(93, 9) ("updateFakeEntry: storeBuffer\n"); + + /* TODO: remove this when the metadata is separated */ + { + Packer p; + packerToStoreInit(&p, vary_entry); + httpReplyPackHeadersInto(vary_entry->getReply(), &p); + packerClean(&p); + } + + storeBufferFlush(vary_entry); + storeTimestampsSet(vary_entry); + vary_entry->complete(); + storeUnlockObject(vary_entry); + + } + + // set in sendMoreData to avoid reentering + if (flags.varycomplete == 1) + sendMoreData(tempBufferVary); +} + + void clientReplyContext::sendMoreData (StoreIOBuffer result) { @@ -1944,6 +2646,69 @@ StoreEntry *entry = http->storeEntry(); + + // this is wrong when Vary has changed + const char *vary_headers = http->request->real_vary_headers; + + // if base vary object should be updated + if (vary_headers && !flags.varycomplete) { + + // get base + vary_entry = storeGetPublic(entry->mem_obj->url, entry->mem_obj->method); + + + if (vary_entry) { + + if (!vary_entry->mem_obj) { + debug(93, 9) ("sendMoreData: base vary object has no mem_obj\n"); + + // swap the mem_obj in + + storeCreateMemObject(vary_entry, http->uri, http->log_uri); + + vary_sc = storeClientListAdd(vary_entry, this); + + tempBufferVary = result; + StoreIOBuffer tempBuffer2(HTTP_REQBUF_SZ, 0, (char*)xmalloc(HTTP_REQBUF_SZ)); + + flags.varycomplete = 1; + + // swap mem_obj in and go to updateFakeEntry + storeClientCopy(vary_sc, vary_entry, tempBuffer2, UpdateFakeEntry, this); + + return; + } else { + + flags.varycomplete = 2; + + // go to updateFakeEntry + updateFakeEntry(result); + } + + } else { + + flags.varycomplete = 2; + debug(93, 9) ("sendMoreData: no base vary entry found!\n"); + ; + + } + + } else if (vary_headers && flags.varycomplete == 1) { + + // after updateFakeEntry + + // unregister store client from base vary entry + // and continue + + assert(vary_entry); + + flags.varycomplete = 2; + + storeUnregister(vary_sc, vary_entry, this); + } + + debug(93, 9) ("sendMoreData: after vary processing\n"); + ConnStateData::Pointer conn = http->getConn(); int fd = conn.getRaw() != NULL ? conn->fd : -1; @@ -2091,6 +2856,7 @@ if (http->request == NULL) http->request = requestLink(requestCreate(m, PROTO_NONE, null_string)); + debug(93, 9) ("createStoreEntry: before storeCreateEntry()\n"); StoreEntry *e = storeCreateEntry(http->uri, http->log_uri, flags, m); sc = storeClientListAdd(e, this); Index: squid3/src/client_side_reply.h diff -u squid3/src/client_side_reply.h:1.6 squid3/src/client_side_reply.h:1.6.4.2 --- squid3/src/client_side_reply.h:1.6 Sun Aug 10 19:13:02 2003 +++ squid3/src/client_side_reply.h Sat Jun 5 18:18:52 2004 @@ -51,7 +51,11 @@ void operator delete (void *address); static STCB CacheHit; static STCB HandleIMSReply; + static STCB HandleConditionalMiss; static STCB SendMoreData; + static STCB UpdateFakeEntry; + static STCB FakeEntryToVariant; + static STCB VariantUpdateSend; clientReplyContext(clientHttpRequest *); ~clientReplyContext(); @@ -108,7 +112,12 @@ int storelogiccomplete: 1; - +int varycomplete: + 2; +int varyother: + 1; +int varyvalidation: + 1; int complete: 1; /* we have read all we can from upstream */ bool headersSent; @@ -129,6 +138,8 @@ void startSendProcess(); StoreIOBuffer holdingBuffer; HttpReply *holdingReply; + + HttpReply *saved304; void processReplyAccess(); static PF ProcessReplyAccessResult; void processReplyAccessResult(bool accessAllowed); @@ -141,8 +152,15 @@ void processOnlyIfCachedMiss(); void cacheHit(StoreIOBuffer result); void handleIMSReply(StoreIOBuffer result); + + void updateFakeEntry(StoreIOBuffer result); + void fakeEntryToVariant(StoreIOBuffer result); + void variantUpdateSend(StoreIOBuffer result); + void handleConditionalMiss(StoreIOBuffer result); + void sendMoreData(StoreIOBuffer result); void triggerInitialStoreRead(); + void triggerInitialStoreReadClone(STCB* callback); void cleanUpAfterIMSCheck(); void handlePartialIMSHeaders(); void handleIMSGiveClientUpdatedOldEntry(); @@ -153,6 +171,13 @@ StoreEntry *old_entry; store_client *old_sc; /* ... for entry to be validated */ + + //base vary StoreEntry + StoreEntry *vary_entry; + store_client *vary_sc; + // buffer for base vary object updates + StoreIOBuffer tempBufferVary; + bool deleting; }; Index: squid3/src/client_side_request.cc diff -u squid3/src/client_side_request.cc:1.28 squid3/src/client_side_request.cc:1.28.2.1 --- squid3/src/client_side_request.cc:1.28 Wed Apr 7 18:59:44 2004 +++ squid3/src/client_side_request.cc Thu Jun 3 15:50:07 2004 @@ -548,6 +548,19 @@ const char *str; #endif + + // check for INM + String inm = httpHeaderGetINM(req_hdr); + debug(93, 1) ("clientInterpretRequestHeaders: INM: %s\n", inm.buf()); + + // parse INM into ETag array + if (fillArrayWithStr(&inm, &request->inm, &request->inmlen)) + request->flags.inm = 1; + + debug(93, 9) ("clientInterpretRequestHeaders: inmlen=%d flags.inm=%d\n",request->inmlen, request->flags.inm); + + + // check for IMS request->imslen = -1; request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE); Index: squid3/src/http.cc diff -u squid3/src/http.cc:1.33 squid3/src/http.cc:1.33.2.3 --- squid3/src/http.cc:1.33 Sun Apr 4 19:00:28 2004 +++ squid3/src/http.cc Sun Jul 4 09:56:59 2004 @@ -221,9 +221,14 @@ assert(e->mem_obj); - if (e->mem_obj->request) + // XXX: here we deal with bad situation + // mmatusiak + if (e->mem_obj->request) { + const char *old_vary_headers = e->mem_obj->request->vary_headers; + e->mem_obj->request->vary_headers = e->mem_obj->request->real_vary_headers; pe = storeGetPublicByRequest(e->mem_obj->request); - else + e->mem_obj->request->vary_headers = old_vary_headers; + } else pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method); if (pe != NULL) { @@ -235,6 +240,10 @@ * Also remove any cached HEAD response in case the object has * changed. */ + + // XXX: here we deal with bad situation + // but for this time we just ignore HEAD... + // mmatusiak if (e->mem_obj->request) pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_HEAD); else @@ -601,6 +610,8 @@ debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", entry->getMD5Text()); + debug(93, 9) ("processReplyHeader: reply got, header being parsed\n"); + if (reply_hdr == NULL) reply_hdr = (char *)memAllocate(MEM_8K_BUF); @@ -698,6 +709,8 @@ if (vary) { entry->mem_obj->vary_headers = xstrdup(vary); + /* XXX: ^^^ possible memory leak */ + /* Kill the old base object if a change in variance is detected */ httpMakePublic(entry); } else { @@ -937,9 +950,12 @@ * we want to process the reply headers. */ /* doesn't return */ + { processReplyHeader(buf, len); + } else { fwdComplete(fwd); + do_next_read = 0; comm_close(fd); } @@ -972,6 +988,7 @@ void HttpStateData::processReplyData(const char *buf, size_t len) { + if (reply_hdr_state < 2) { do_next_read = 1; maybeReadData(); @@ -1002,7 +1019,12 @@ tempBuffer.length = len; tempBuffer.offset = currentOffset; currentOffset += len; + + // store_status may be STORE_OK if we passed through handleConditionalMiss + // and decided to send 304 there + //if (entry->store_status == STORE_PENDING) entry->write(tempBuffer); + } if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { @@ -1016,11 +1038,13 @@ switch (persistentConnStatus()) { case INCOMPLETE_MSG: + debug(93, 9) ("processReplyData: persistentConnStatus(): INCOMPLETE_MSG\n"); /* Wait for EOF condition */ do_next_read = 1; break; case COMPLETE_PERSISTENT_MSG: + debug(93, 9) ("processReplyData: persistentConnStatus(): COMPLETE_PERSISTENT_MSG\n"); /* yes we have to clear all these! */ commSetTimeout(fd, -1, NULL, NULL); do_next_read = 0; @@ -1043,6 +1067,7 @@ return; case COMPLETE_NONPERSISTENT_MSG: + debug(93, 9) ("processReplyData: persistentConnStatus(): COMPLETE_NONPERSISTENT_MSG\n"); /* close the connection ourselves */ /* yes - same as for a complete persistent conn here */ commSetTimeout(fd, -1, NULL, NULL); @@ -1057,6 +1082,7 @@ return; } + debug(93, 9) ("processReplyData: before maybeReadData\n"); maybeReadData(); } @@ -1137,9 +1163,15 @@ assert (hdr_out->owner == hoRequest); /* append our IMS header */ + // add If-Modified-Since if needed if (request->lastmod > -1 && request->method == METHOD_GET) httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, request->lastmod); + // add If-None-Match if needed, etags prepared earlier + if (request->etag && request->method == METHOD_GET) { + httpHeaderPutStr(hdr_out, HDR_IF_NONE_MATCH, request->etag->buf()); + } + bool we_do_ranges = decideIfWeDoRanges (orig_request); String strConnection (httpHeaderGetList(hdr_in, HDR_CONNECTION)); @@ -1406,12 +1438,9 @@ break; + // don't pass clients IMS or INM case HDR_IF_MODIFIED_SINCE: - /* append unless we added our own; - * note: at most one client's ims header can pass through */ - - if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE)) - httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + case HDR_IF_NONE_MATCH: break; Index: squid3/src/protos.h diff -u squid3/src/protos.h:1.41 squid3/src/protos.h:1.41.2.2 --- squid3/src/protos.h:1.41 Sun Apr 4 19:00:28 2004 +++ squid3/src/protos.h Sun Jun 6 17:35:41 2004 @@ -163,6 +163,7 @@ SQUIDCEXTERN void packerToStoreInit(Packer * p, StoreEntry * e); SQUIDCEXTERN void packerToMemInit(Packer * p, MemBuf * mb); + SQUIDCEXTERN void packerClean(Packer * p); SQUIDCEXTERN void packerAppend(Packer * p, const char *buf, int size); #if STDC_HEADERS @@ -415,6 +416,10 @@ SQUIDCEXTERN const char *httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id); SQUIDCEXTERN const char *httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id); SQUIDCEXTERN const char *httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme); + +extern int fillArrayWithStr(const String *, char ***, int *); +extern String httpHeaderGetINM(const HttpHeader * hdr); + extern String httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id); extern String httpHeaderGetStrOrList(const HttpHeader * hdr, http_hdr_type id); extern String httpHeaderGetByName(const HttpHeader * hdr, const char *name); Index: squid3/src/store.cc diff -u squid3/src/store.cc:1.25 squid3/src/store.cc:1.25.2.2 --- squid3/src/store.cc:1.25 Mon Sep 29 19:12:40 2003 +++ squid3/src/store.cc Sat Jun 5 18:18:52 2004 @@ -310,8 +310,19 @@ { storeSetMemStatus(e, NOT_IN_MEMORY); MemObject *mem = e->mem_obj; + + // since MemObject holds additional vary information, this has to be done + if (mem) { + + if (mem->etag_mapping) + delete mem->etag_mapping; + + if (mem->vary_headers_mapping) + delete mem->vary_headers_mapping; + e->mem_obj = NULL; delete mem; + } } static void @@ -565,6 +576,8 @@ const cache_key *newkey; MemObject *mem = e->mem_obj; + debug(93, 9) ("storeSetPrivateKey: start\n"); + if (e->key && EBIT_TEST(e->flags, KEY_PRIVATE)) return; /* is already private */ @@ -594,6 +607,8 @@ const cache_key *newkey; MemObject *mem = e->mem_obj; + debug(93, 9) ("storeSetPublicKey: start\n"); + if (e->key && !EBIT_TEST(e->flags, KEY_PRIVATE)) return; /* is already public */ @@ -619,6 +634,7 @@ assert(!EBIT_TEST(e->flags, RELEASE_REQUEST)); + debug(93, 9) ("storeSetPublicKey: later...\n"); if (mem->request) { StoreEntry *pe; HttpRequest *request = mem->request; @@ -626,12 +642,17 @@ if (!mem->vary_headers) { /* First handle the case where the object no longer varies */ safe_free(request->vary_headers); + safe_free(request->real_vary_headers); + } else { - if (request->vary_headers && strcmp(request->vary_headers, mem->vary_headers) != 0) { + + if (request->real_vary_headers && strcmp(request->real_vary_headers, mem->vary_headers) != 0) { /* Oops.. the variance has changed. Kill the base object * to record the new variance key */ safe_free(request->vary_headers); /* free old "bad" variance key */ + safe_free(request->real_vary_headers); /* free old "bad" variance key */ + pe = storeGetPublic(mem->url, mem->method); if (pe) @@ -662,6 +683,19 @@ vary.clean(); } + // add mappings + pe->mem_obj->etag_mapping = new CharHashTable(); + pe->mem_obj->vary_headers_mapping = new CharHashTable(); + + // fill the mappings + pe->mem_obj->vary_headers_mapping->put((char*)request->vary_headers, (char*)request->vary_headers); + ETag etag = httpHeaderGetETag(&mem->getReply()->header, HDR_ETAG); + // XXX ^^^ memory leak? + + if (etag.str) { + pe->mem_obj->etag_mapping->put((char*)etag.str, (char*)request->vary_headers); + } + #if X_ACCELERATOR_VARY vary = httpHeaderGetList(&mem->getReply()->header, HDR_X_ACCELERATOR_VARY); @@ -690,11 +724,25 @@ storeUnlockObject(pe); } + + debug(93, 9) ("storeSetPublicKey: mem has request\n"); + + const char *old_vary_headers = mem->request->vary_headers; + + if (mem->request->real_vary_headers) { + mem->request->vary_headers = mem->request->real_vary_headers; + } + debug(93, 9) ("storeSetPublicKey: vary_headers = %s\n", mem->request->vary_headers); newkey = storeKeyPublicByRequest(mem->request); - } else + + mem->request->vary_headers = old_vary_headers; + } else { + debug(93, 9) ("storeSetPublicKey: mem has no request\n"); newkey = storeKeyPublic(mem->url, mem->method); + } if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) { + debug(93, 9) ("storeSetPublicKey: entry found = %p\n", e2); debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url); storeSetPrivateKey(e2); storeRelease(e2); Index: squid3/src/store_client.cc diff -u squid3/src/store_client.cc:1.18 squid3/src/store_client.cc:1.18.2.1 --- squid3/src/store_client.cc:1.18 Mon Oct 20 19:12:45 2003 +++ squid3/src/store_client.cc Sat Jun 5 18:18:52 2004 @@ -636,6 +636,9 @@ int storeUnregister(store_client * sc, StoreEntry * e, void *data) { + + debug(93, 9) ("storeUnregister: entry = %p\n", e); + MemObject *mem = e->mem_obj; #if STORE_CLIENT_LIST_DEBUG Index: squid3/src/store_key_md5.cc diff -u squid3/src/store_key_md5.cc:1.5 squid3/src/store_key_md5.cc:1.5.2.1 --- squid3/src/store_key_md5.cc:1.5 Sun Aug 10 19:13:03 2003 +++ squid3/src/store_key_md5.cc Sat Jun 5 18:18:52 2004 @@ -144,6 +144,9 @@ MD5Update(&M, &m, sizeof(m)); MD5Update(&M, (unsigned char *) url, strlen(url)); + // XXX: suspection, should be real_vary_headers? + // no, this breaks some other stuff + // mmatusiak if (request->vary_headers) MD5Update(&M, (unsigned char *) request->vary_headers, strlen(request->vary_headers)); Index: squid3/src/store_swapmeta.cc diff -u squid3/src/store_swapmeta.cc:1.5 squid3/src/store_swapmeta.cc:1.5.2.1 --- squid3/src/store_swapmeta.cc:1.5 Mon Aug 4 19:12:49 2003 +++ squid3/src/store_swapmeta.cc Thu Jun 3 15:50:07 2004 @@ -39,6 +39,8 @@ #include "StoreMeta.h" #include "StoreMetaUnpacker.h" +#include "StoreMetaVary.h" + void storeSwapTLVFree(tlv * n) { @@ -60,7 +62,11 @@ tlv *TLV = NULL; /* we'll return this */ tlv **T = &TLV; const char *url; - const char *vary; + + // packed vary stuff + const char *packed_vary; + int packed_vary_len = 0; + assert(e->mem_obj != NULL); assert(e->swap_status == SWAPOUT_WRITING); url = storeUrl(e); @@ -89,10 +95,11 @@ } T = StoreMeta::Add(T, t); - vary = e->mem_obj->vary_headers; + // pack vary stuff + packed_vary = storeMetaVaryPack(e->mem_obj, &packed_vary_len); - if (vary) { - t =StoreMeta::Factory(STORE_META_VARY_HEADERS, strlen(vary) + 1, vary); + if (packed_vary) { + t =StoreMeta::Factory(STORE_META_VARY_HEADERS, packed_vary_len, packed_vary); if (!t) { storeSwapTLVFree(TLV); Index: squid3/src/store_swapout.cc diff -u squid3/src/store_swapout.cc:1.10 squid3/src/store_swapout.cc:1.10.2.1 --- squid3/src/store_swapout.cc:1.10 Wed Feb 11 19:09:05 2004 +++ squid3/src/store_swapout.cc Thu Jun 3 15:50:07 2004 @@ -172,6 +172,7 @@ void storeSwapOut(StoreEntry * e) { + if (!e->mem_obj) return; Index: squid3/src/structs.h diff -u squid3/src/structs.h:1.51 squid3/src/structs.h:1.51.2.1 --- squid3/src/structs.h:1.51 Sun Apr 4 19:00:28 2004 +++ squid3/src/structs.h Thu Jun 3 15:50:07 2004 @@ -1412,6 +1412,9 @@ unsigned int ims: 1; +unsigned int inm: + 1; + unsigned int auth: 1;