Re: X-Vary-Options patch

From: Mark Nottingham <mnot_at_yahoo-inc.com>
Date: Sat, 7 Jun 2008 11:10:03 +1000

Squid devs: Did this make its way into 2.7?

Tim: AIUI, the following header:
   X-Vary-Options: Accept-Encoding; list-contains=gzip
will bucket the cache for this URI into two entries; those whose
Accept-Encoding contains the list value "gzip", and those that don't.
Is that correct?

Also, I'm assuming the origin would still need to send
   Vary: Accept-Encoding
to work properly with downstream caches.

Cheers,

On 26/02/2008, at 3:30 PM, Adrian Chadd wrote:

> G'day,
>
> I'm happy to commit this to Squid-2.HEAD as-is. Can you throw it in
> a Bugzilla report and spit me the number?
>
> Thanks,
>
>
>
> Adrian
>
> On Fri, Feb 08, 2008, Tim Starling wrote:
>> There are two major sources of suboptimal hit rate on Wikipedia which
>> relate to the Vary header:
>>
>> * In Accept-Encoding, we only care whether "gzip" is present or
>> not, but
>> IE and Firefox use different whitespace conventions and so each get
>> separate entries in the cache
>> * We only care whether the user is logged in or not. Other cookies,
>> such
>> as pure-JavaScript cookies used by client-side code to store
>> preferences, unnecessarily degrade our hit rate.
>>
>> There have been other patches related to this problem, but as far
>> as I'm
>> aware, they're all special-case, site-specific hacks. My patch adds
>> an
>> X-Vary-Options response header (hereafter XVO), and thus gives the
>> origin server fine control over cache variance. In the patch, the XVO
>> header overrides the Vary header, so the Vary header can still be
>> sent
>> as usual for compatibility with caches that don't support this
>> feature.
>>
>> The format of the XVO header is inspired by the format of the Accept
>> header. As in Vary, XVO is separated by commas into parts which
>> relate
>> to different request headers. Then those parts are further
>> separated by
>> semicolons. The first semicolon-separated part is the request header
>> name, and subsequent parts give name/value pairs separated by equals
>> signs, defining options relating to the variance of that header.
>>
>> Two option names are currently defined:
>>
>> list-contains: splits the request header into comma-separated parts
>> and varies depending on whether the resulting list contains the
>> option value
>> string-contains: performs a simple search of the request header and
>> varies depending on whether it matches.
>>
>> Multiple such options per header are allowed.
>>
>> So for example:
>>
>> X-Vary-Options: Cookie; string-contains=UserID;
>> string-contains=_session, Accept-Encoding; list-contains=gzip
>>
>> This would vary the cache on three tests:
>> * whether the Cookie header contains the string "UserID"
>> * whether the Cookie header contains the string "_session"
>> * whether the Accept-Encoding header, interpreted as a comma-
>> separated
>> list, contains the item "gzip"
>>
>> The patch refactors all references to the Vary and X-Accelerator-Vary
>> headers into the functions httpHeaderHasVary() and
>> httpHeaderGetVary()
>> in HttpHeader.c. It then adds X-Vary-Options to these functions,
>> interpreting it as a string rather than a list to avoid inappropriate
>> splitting on whitespace. It puts the resulting combined header into
>> X-Vary-Options instead of Vary in the base vary marker object,
>> again to
>> avoid inappropriate list-style interpretation. httpMakeVaryMark()
>> then
>> interprets this combined header in the way described above.
>>
>> The added features of the patch are conditional, and are enabled by
>> the
>> configure option --enable-vary-options. Autoconf and automake will
>> need
>> to be run after applying this patch.
>>
>> The interpretation of some non-standards-compliant Vary headers
>> (those
>> containing semicolons) is changed slightly by this patch regardless
>> of
>> --enable-vary-options.
>>
>> The patch is attached and also available at:
>> http://noc.wikimedia.org/~tstarling/patches/vary_options_upstream.patch
>>
>> For your review and consideration.
>>
>> -- Tim Starling
>
>> diff -Xdiffx -ru squid-2.6.18.orig/configure.in squid-2.6.18/
>> configure.in
>> --- squid-2.6.18.orig/configure.in 2008-01-10 23:34:23.000000000
>> +1100
>> +++ squid-2.6.18/configure.in 2008-02-07 19:43:23.000000000 +1100
>> @@ -1507,6 +1507,16 @@
>> fi
>> ])
>>
>> +dnl Enable vary options
>> +AC_ARG_ENABLE(vary_options,
>> +[ --enable-vary-options
>> + Enable support for the X-Vary-Options
>> header.],
>> +[ if test "$enableval" = "yes" ; then
>> + echo "Enabling support for vary options"
>> + AC_DEFINE(VARY_OPTIONS, 1, [Enable support for the X-Vary-
>> Options header])
>> + fi
>> +])
>> +
>> AC_ARG_ENABLE(follow-x-forwarded-for,
>> [ --enable-follow-x-forwarded-for
>> Enable support for following the X-
>> Forwarded-For
>> diff -Xdiffx -ru squid-2.6.18.orig/src/client_side.c squid-2.6.18/
>> src/client_side.c
>> --- squid-2.6.18.orig/src/client_side.c 2008-02-07
>> 19:28:38.000000000 +1100
>> +++ squid-2.6.18/src/client_side.c 2008-02-08 14:39:38.000000000
>> +1100
>> @@ -735,10 +735,7 @@
>> request_t *request = http->request;
>> const char *etag = httpHeaderGetStr(&mem->reply->header,
>> HDR_ETAG);
>> const char *vary = request->vary_headers;
>> - int has_vary = httpHeaderHas(&entry->mem_obj->reply-
>> >header, HDR_VARY);
>> -#if X_ACCELERATOR_VARY
>> - has_vary |= httpHeaderHas(&entry->mem_obj->reply->header,
>> HDR_X_ACCELERATOR_VARY);
>> -#endif
>> + int has_vary = httpHeaderHasVary(&entry->mem_obj->reply-
>> >header);
>> if (has_vary)
>> vary = httpMakeVaryMark(request, mem->reply);
>>
>> @@ -4948,10 +4945,7 @@
>> varyEvaluateMatch(StoreEntry * entry, request_t * request)
>> {
>> const char *vary = request->vary_headers;
>> - int has_vary = httpHeaderHas(&entry->mem_obj->reply->header,
>> HDR_VARY);
>> -#if X_ACCELERATOR_VARY
>> - has_vary |= httpHeaderHas(&entry->mem_obj->reply->header,
>> HDR_X_ACCELERATOR_VARY);
>> -#endif
>> + int has_vary = httpHeaderHasVary(&entry->mem_obj->reply-
>> >header);
>> if (!has_vary || !entry->mem_obj->vary_headers) {
>> if (vary) {
>> /* Oops... something odd is going on here.. */
>> diff -Xdiffx -ru squid-2.6.18.orig/src/enums.h squid-2.6.18/src/
>> enums.h
>> --- squid-2.6.18.orig/src/enums.h 2008-02-07 19:28:38.000000000
>> +1100
>> +++ squid-2.6.18/src/enums.h 2008-02-07 21:35:18.000000000 +1100
>> @@ -256,6 +256,9 @@
>> #if X_ACCELERATOR_VARY
>> HDR_X_ACCELERATOR_VARY,
>> #endif
>> +#if VARY_OPTIONS
>> + HDR_X_VARY_OPTIONS,
>> +#endif
>> HDR_X_ERROR_URL, /* errormap, requested URL */
>> HDR_X_ERROR_STATUS, /* errormap, received HTTP
>> status line */
>> HDR_FRONT_END_HTTPS,
>> diff -Xdiffx -ru squid-2.6.18.orig/src/http.c squid-2.6.18/src/http.c
>> --- squid-2.6.18.orig/src/http.c 2008-02-07 19:28:38.000000000
>> +1100
>> +++ squid-2.6.18/src/http.c 2008-02-08 14:48:44.000000000 +1100
>> @@ -353,20 +353,29 @@
>> String vstr = StringNull;
>>
>> stringClean(&vstr);
>> - hdr = httpHeaderGetList(&reply->header, HDR_VARY);
>> - if (strBuf(hdr))
>> - strListAdd(&vary, strBuf(hdr), ',');
>> - stringClean(&hdr);
>> -#if X_ACCELERATOR_VARY
>> - hdr = httpHeaderGetList(&reply->header, HDR_X_ACCELERATOR_VARY);
>> - if (strBuf(hdr))
>> - strListAdd(&vary, strBuf(hdr), ',');
>> - stringClean(&hdr);
>> -#endif
>> + vary = httpHeaderGetVary(&reply->header);
>> + debug(11,3) ("httpMakeVaryMark: Vary: %s\n", strBuf(vary));
>> +
>> while (strListGetItem(&vary, ',', &item, &ilen, &pos)) {
>> - char *name = xmalloc(ilen + 1);
>> - xstrncpy(name, item, ilen + 1);
>> - Tolower(name);
>> + const char *sc_item, *sc_pos = NULL;
>> + int sc_ilen;
>> + String str_item;
>> + char *name = NULL;
>> + String value_spec = StringNull;
>> + int need_value = 1;
>> +
>> + stringLimitInit(&str_item, item, ilen);
>> +
>> + /* Get the header name */
>> + if (strListGetItem(&str_item, ';', &sc_item, &sc_ilen,
>> &sc_pos)) {
>> + name = xmalloc(sc_ilen + 1);
>> + xstrncpy(name, sc_item, sc_ilen + 1);
>> + Tolower(name);
>> + } else {
>> + name = xmalloc(1);
>> + *name = '\0';
>> + }
>> +
>> if (strcmp(name, "accept-encoding") == 0) {
>> aclCheck_t checklist;
>> memset(&checklist, 0, sizeof(checklist));
>> @@ -381,22 +390,76 @@
>> if (strcmp(name, "*") == 0) {
>> /* Can not handle "Vary: *" efficiently, bail out making
>> the response not cached */
>> safe_free(name);
>> + stringClean(&str_item);
>> stringClean(&vary);
>> stringClean(&vstr);
>> break;
>> }
>> - strListAdd(&vstr, name, ',');
>> +
>> + /* Fetch the header string */
>> hdr = httpHeaderGetByName(&request->header, name);
>> - safe_free(name);
>> - value = strBuf(hdr);
>> - if (value) {
>> - value = rfc1738_escape_part(value);
>> - stringAppend(&vstr, "=\"", 2);
>> - stringAppend(&vstr, value, strlen(value));
>> - stringAppend(&vstr, "\"", 1);
>> +
>> + /* Process the semicolon-separated options */
>> +#ifdef VARY_OPTIONS
>> + while (strListGetItem(&str_item, ';', &sc_item, &sc_ilen,
>> &sc_pos)) {
>> + char *opt_name = xmalloc(sc_ilen + 1);
>> + char *opt_value;
>> + char *eqpos;
>> + xstrncpy(opt_name, sc_item, sc_ilen + 1);
>> + eqpos = strchr(opt_name, '=');
>> + if (!eqpos) {
>> + opt_value = NULL;
>> + } else {
>> + *eqpos = '\0';
>> + opt_value = eqpos + 1;
>> + }
>> + Tolower(opt_name);
>> +
>> + if (strcmp(opt_name, "list-contains") == 0 && opt_value) {
>> + if (strBuf(hdr) && strListIsMember(&hdr, opt_value,
>> ',')) {
>> + opt_value = rfc1738_escape_part(opt_value);
>> + strListAdd(&value_spec, "list-contains[\"", ';');
>> + stringAppend(&value_spec, opt_value,
>> strlen(opt_value));
>> + stringAppend(&value_spec, "\"]", 2);
>> + }
>> + need_value = 0;
>> + } else if (strcmp(opt_name, "string-contains") == 0 &&
>> opt_value) {
>> + if (strBuf(hdr) && strIsSubstr(&hdr, opt_value)) {
>> + opt_value = rfc1738_escape_part(opt_value);
>> + strListAdd(&value_spec, "string-contains[\"", ';');
>> + stringAppend(&value_spec, opt_value,
>> strlen(opt_value));
>> + stringAppend(&value_spec, "\"]", 2);
>> + }
>> + need_value = 0;
>> + } else {
>> + debug(11,3) ("httpMakeVaryMark: unrecognised vary
>> option: %s\n", opt_name);
>> + }
>> + safe_free(opt_name);
>> }
>> +#endif
>> +
>> + if (need_value) {
>> + value = strBuf(hdr);
>> + if (value) {
>> + value = rfc1738_escape_part(value);
>> + strListAdd(&value_spec, "\"", ';');
>> + stringAppend(&value_spec, value, strlen(value));
>> + stringAppend(&value_spec, "\"", 1);
>> + }
>> + }
>> +
>> + strListAdd(&vstr, name, ',');
>> + stringAppend(&vstr, "=", 1);
>> + if (strBuf(value_spec)) {
>> + stringAppend(&vstr, strBuf(value_spec),
>> strLen(value_spec));
>> + }
>> +
>> stringClean(&hdr);
>> + stringClean(&value_spec);
>> + stringClean(&str_item);
>> + safe_free(name);
>> }
>> +
>> safe_free(request->vary_hdr);
>> safe_free(request->vary_headers);
>> if (strBuf(vary) && strBuf(vstr)) {
>> @@ -514,11 +577,7 @@
>> /* non-chunked. Handle as one single big chunk (-1 if
>> terminated by EOF) */
>> httpState->chunk_size = httpReplyBodySize(httpState-
>> >orig_request->method, reply);
>> }
>> - if (httpHeaderHas(&reply->header, HDR_VARY)
>> -#if X_ACCELERATOR_VARY
>> - || httpHeaderHas(&reply->header, HDR_X_ACCELERATOR_VARY)
>> -#endif
>> - ) {
>> + if (httpHeaderHasVary(&reply->header)) {
>> const char *vary = NULL;
>> if (Config.onoff.cache_vary)
>> vary = httpMakeVaryMark(httpState->orig_request, reply);
>> diff -Xdiffx -ru squid-2.6.18.orig/src/HttpHeader.c squid-2.6.18/
>> src/HttpHeader.c
>> --- squid-2.6.18.orig/src/HttpHeader.c 2007-12-21
>> 20:56:53.000000000 +1100
>> +++ squid-2.6.18/src/HttpHeader.c 2008-02-08 14:49:24.000000000
>> +1100
>> @@ -133,6 +133,9 @@
>> #if X_ACCELERATOR_VARY
>> {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr},
>> #endif
>> +#if VARY_OPTIONS
>> + {"X-Vary-Options", HDR_X_VARY_OPTIONS, ftStr},
>> +#endif
>> {"X-Error-URL", HDR_X_ERROR_URL, ftStr},
>> {"X-Error-Status", HDR_X_ERROR_STATUS, ftInt},
>> {"Front-End-Https", HDR_FRONT_END_HTTPS, ftStr},
>> @@ -210,6 +213,9 @@
>> #if X_ACCELERATOR_VARY
>> HDR_X_ACCELERATOR_VARY,
>> #endif
>> +#if VARY_OPTIONS
>> + HDR_X_VARY_OPTIONS,
>> +#endif
>> HDR_X_SQUID_ERROR
>> };
>>
>> @@ -1185,6 +1191,54 @@
>> return tot;
>> }
>>
>> +/* Get the combined Vary headers as a String
>> + * Returns StringNull if there are no vary headers
>> + */
>> +String httpHeaderGetVary(const HttpHeader * hdr)
>> +{
>> + String hdrString = StringNull;
>> +#if VARY_OPTIONS
>> + HttpHeaderEntry *e;
>> + if ((e = httpHeaderFindEntry(hdr, HDR_X_VARY_OPTIONS))) {
>> + stringInit(&hdrString, strBuf(e->value));
>> + return hdrString;
>> + }
>> +#endif
>> +
>> + hdrString = httpHeaderGetList(hdr, HDR_VARY);
>> +#if X_ACCELERATOR_VARY
>> + {
>> + String xavString = StringNull;
>> + xavString = httpHeaderGetList(hdr, HDR_X_ACCELERATOR_VARY);
>> + if (strBuf(xavString))
>> + strListAdd(&hdrString, strBuf(xavString), ',');
>> + stringClean(&xavString);
>> + }
>> +#endif
>> + return hdrString;
>> +}
>> +
>> +/*
>> + * Returns TRUE if at least one of the vary headers are present
>> + */
>> +int httpHeaderHasVary(const HttpHeader * hdr)
>> +{
>> +#if VARY_OPTIONS
>> + if (httpHeaderHas(hdr, HDR_X_VARY_OPTIONS)) {
>> + return TRUE;
>> + }
>> +#endif
>> +#if X_ACCELERATOR_VARY
>> + if (httpHeaderHas(hdr, HDR_X_ACCELERATOR_VARY)) {
>> + return TRUE;
>> + }
>> +#endif
>> + if (httpHeaderHas(hdr, HDR_VARY)) {
>> + return TRUE;
>> + }
>> + return FALSE;
>> +}
>> +
>> /*
>> * HttpHeaderEntry
>> */
>> @@ -1438,3 +1492,5 @@
>> assert(id >= 0 && id < HDR_ENUM_END);
>> return strBuf(Headers[id].name);
>> }
>> +
>> +
>> diff -Xdiffx -ru squid-2.6.18.orig/src/HttpReply.c squid-2.6.18/src/
>> HttpReply.c
>> --- squid-2.6.18.orig/src/HttpReply.c 2006-06-11 10:28:19.000000000
>> +1000
>> +++ squid-2.6.18/src/HttpReply.c 2008-02-08 14:42:04.000000000
>> +1100
>> @@ -325,8 +325,7 @@
>> return squid_curtime;
>> }
>> }
>> - if (Config.onoff.vary_ignore_expire &&
>> - httpHeaderHas(&rep->header, HDR_VARY)) {
>> + if (Config.onoff.vary_ignore_expire && httpHeaderHasVary(&rep-
>> >header)) {
>> const time_t d = httpHeaderGetTime(&rep->header, HDR_DATE);
>> const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES);
>> if (d == e)
>> diff -Xdiffx -ru squid-2.6.18.orig/src/protos.h squid-2.6.18/src/
>> protos.h
>> --- squid-2.6.18.orig/src/protos.h 2008-02-07 19:28:38.000000000
>> +1100
>> +++ squid-2.6.18/src/protos.h 2008-02-08 14:46:21.000000000 +1100
>> @@ -444,6 +444,8 @@
>> extern squid_off_t httpHeaderGetSize(const HttpHeader * hdr,
>> http_hdr_type id);
>> extern time_t httpHeaderGetTime(const HttpHeader * hdr,
>> http_hdr_type id);
>> extern TimeOrTag httpHeaderGetTimeOrTag(const HttpHeader * hdr,
>> http_hdr_type id);
>> +extern String httpHeaderGetVary(const HttpHeader * hdr);
>> +extern int httpHeaderHasVary(const HttpHeader * hdr);
>> extern HttpHdrCc *httpHeaderGetCc(const HttpHeader * hdr);
>> extern HttpHdrRange *httpHeaderGetRange(const HttpHeader * hdr);
>> extern HttpHdrContRange *httpHeaderGetContRange(const HttpHeader *
>> hdr);
>> diff -Xdiffx -ru squid-2.6.18.orig/src/store.c squid-2.6.18/src/
>> store.c
>> --- squid-2.6.18.orig/src/store.c 2008-02-07 19:28:38.000000000
>> +1100
>> +++ squid-2.6.18/src/store.c 2008-02-08 14:55:06.000000000 +1100
>> @@ -721,7 +721,12 @@
>> 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);
>> +#if VARY_OPTIONS
>> + /* Can't put a string into a list header */
>> + httpHeaderPutStr(&state->e->mem_obj->reply->header,
>> HDR_X_VARY_OPTIONS, vary);
>> +#else
>> httpHeaderPutStr(&state->e->mem_obj->reply->header, HDR_VARY,
>> vary);
>> +#endif
>> storeSetPublicKey(state->e);
>> if (!state->oe) {
>> /* New entry, create new unique ID */
>> @@ -1039,20 +1044,8 @@
>> }
>> newkey = storeKeyPublicByRequest(mem->request);
>> if (mem->vary_headers && !EBIT_TEST(e->flags,
>> KEY_EARLY_PUBLIC)) {
>> - String vary = StringNull;
>> vary_id_t vary_id;
>> - 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
>> + String vary = httpHeaderGetVary(&mem->reply->header);
>> /* Create or update the vary object */
>> vary_id = storeAddVary(mem->url, mem->log_url, mem-
>> >method, newkey, httpHeaderGetStr(&mem->reply->header, HDR_ETAG),
>> strBuf(vary), mem->vary_headers, mem->vary_encoding);
>> if (vary_id.create_time) {
>>
>
>
> --
> - Xenion - http://www.xenion.com.au/ - VPS Hosting - Commercial
> Squid Support -
> - $25/pm entry-level VPSes w/ capped bandwidth charges available in
> WA -

--
Mark Nottingham       mnot_at_yahoo-inc.com
Received on Sat Jun 07 2008 - 01:10:45 MDT

This archive was generated by hypermail 2.2.0 : Sat Jun 07 2008 - 12:00:04 MDT