# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: atcs.matthew@gmail.com-20091106225203-4i1s6bhkpd0nd4rp # target_branch: file:///home/lytithwyn/squid-repo/trunk/ # testament_sha1: a76d3bba71e0948160b4218656b1a9927e005128 # timestamp: 2009-11-06 17:53:57 -0500 # base_revision_id: kinkie@squid-cache.org-20091106162216-\ # ucnpf7lk1ane5rhw # # Begin patch === modified file 'src/HttpHdrRange.cc' --- src/HttpHdrRange.cc 2009-01-31 17:23:17 +0000 +++ src/HttpHdrRange.cc 2009-10-13 21:10:05 +0000 @@ -530,17 +530,17 @@ * grabbing the needed range elements from the origin. */ bool -HttpHdrRange::offsetLimitExceeded() const +HttpHdrRange::offsetLimitExceeded(int64_t limit) const { if (NULL == this) /* not a range request */ return false; - if (Config.rangeOffsetLimit == 0) + if (limit == 0) /* disabled */ return true; - if (-1 == Config.rangeOffsetLimit) + if (-1 == limit) /* forced */ return false; @@ -548,7 +548,7 @@ /* tail request */ return true; - if (Config.rangeOffsetLimit >= firstOffset()) + if (limit >= firstOffset()) /* below the limit */ return false; === modified file 'src/HttpHeaderRange.h' --- src/HttpHeaderRange.h 2009-03-31 12:39:30 +0000 +++ src/HttpHeaderRange.h 2009-10-13 21:10:05 +0000 @@ -103,7 +103,7 @@ bool willBeComplex() const; int64_t firstOffset() const; int64_t lowestOffset(int64_t) const; - bool offsetLimitExceeded() const; + bool offsetLimitExceeded(int64_t) const; bool contains(HttpHdrRangeSpec& r) const; Vector specs; === modified file 'src/Makefile.am' --- src/Makefile.am 2009-11-06 11:58:03 +0000 +++ src/Makefile.am 2009-11-06 22:52:03 +0000 @@ -403,6 +403,8 @@ PeerSelectState.h \ PingData.h \ protos.h \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ redirect.cc \ referer.cc \ refresh.cc \ @@ -1162,6 +1164,8 @@ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ redirect.cc \ referer.cc \ refresh.cc \ @@ -1337,6 +1341,8 @@ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ redirect.cc \ referer.cc \ refresh.cc \ @@ -1486,6 +1492,8 @@ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ redirect.cc \ referer.cc \ refresh.cc \ @@ -1625,6 +1633,8 @@ peer_sourcehash.cc \ peer_userhash.cc \ pconn.cc \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ redirect.cc \ referer.cc \ refresh.cc \ @@ -1778,6 +1788,8 @@ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ redirect.cc \ referer.cc \ refresh.cc \ @@ -1873,6 +1885,8 @@ HttpHdrScTarget.cc url.cc \ StatHist.cc HttpHdrRange.cc ETag.cc tests/stub_errorpage.cc \ tests/stub_HttpRequest.cc tests/stub_access_log.cc \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ refresh.cc \ tests/stub_store_client.cc \ tests/stub_tools.cc \ @@ -2137,6 +2151,8 @@ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ + rangeOffsetLimit.h \ + rangeOffsetLimit.cc \ redirect.cc \ referer.cc \ refresh.cc \ === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2009-10-31 11:53:09 +0000 +++ src/cache_cf.cc 2009-11-04 17:42:37 +0000 @@ -121,6 +121,7 @@ static void update_maxobjsize(void); static void configDoConfigure(void); static void parse_refreshpattern(refresh_t **); +static void parse_rangeoffsetlimit(range_offset_t **); static int parseTimeUnits(const char *unit); static void parseTimeLine(time_t * tptr, const char *units); static void parse_ushort(u_short * var); @@ -2448,6 +2449,90 @@ } static void +dump_rangeoffsetlimit(StoreEntry * entry, const char *name, range_offset_t * head) +{ + while (head != NULL) { + storeAppendPrintf(entry, "%s %d %s %s\n", + name, + (int)head->limit, + head->flags.icase ? " -i" : null_string, + head->pattern); + storeAppendPrintf(entry, "\n"); + head = head->next; + } +} + +static void +parse_rangeoffsetlimit(range_offset_t ** head) +{ + char *token; + int64_t offset; + char *pattern = xstrdup(".*");; + + range_offset_t *t; + regex_t comp; + int errcode; + int flags = REG_EXTENDED | REG_NOSUB; + int gotPattern = 0; + + offset = (int64_t) GetInteger(); + + if((token = strtok(NULL, w_space)) != NULL) { + if (strcmp(token, "-i") == 0) { + flags |= REG_ICASE; + } else if (strcmp(token, "+i") == 0) { + flags &= ~REG_ICASE; + } else { + safe_free(pattern); + pattern = xstrdup(token); + gotPattern = 1; + } + } + + if(((token = strtok(NULL, w_space)) != NULL) && !gotPattern) { + safe_free(pattern); + pattern = xstrdup(token); + gotPattern = 1; + } else if((token != NULL) && gotPattern) { + debugs(22, 0, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); + debugs(22, 0, "parse_rangeoffsetlimit: extra parameter '" << token); + } + + if ((errcode = regcomp(&comp, pattern, flags)) != 0) { + char errbuf[256]; + regerror(errcode, &comp, errbuf, sizeof errbuf); + debugs(22, 0, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); + debugs(22, 0, "parse_rangeoffsetlimit: Invalid regular expression '" << pattern << "': " << errbuf); + return; + } + + t = (range_offset_t*)xcalloc(1, sizeof(range_offset_t)); + t->pattern = (char *) xstrdup(pattern); + t->compiled_pattern = comp; + t->limit = offset; + if (flags & REG_ICASE) + t->flags.icase = 1; + + t->next = NULL; + while (*head) + head = &(*head)->next; + *head = t; + safe_free(pattern); +} + +static void +free_rangeoffsetlimit(range_offset_t ** head) +{ + range_offset_t *t; + while ((t = *head) != NULL) { + *head = t->next; + safe_free(t->pattern); + regfree(&t->compiled_pattern); + safe_free(t); + } +} + +static void dump_string(StoreEntry * entry, const char *name, char *var) { if (var != NULL) === modified file 'src/cf.data.depend' --- src/cf.data.depend 2009-08-04 02:07:56 +0000 +++ src/cf.data.depend 2009-10-13 21:10:05 +0000 @@ -42,6 +42,7 @@ peer_access cache_peer acl QosConfig refreshpattern +rangeoffsetlimit removalpolicy size_t IpAddress_list === modified file 'src/cf.data.pre' --- src/cf.data.pre 2009-11-06 11:58:03 +0000 +++ src/cf.data.pre 2009-11-06 22:52:03 +0000 @@ -3442,28 +3442,48 @@ DOC_END NAME: range_offset_limit -COMMENT: (bytes) -TYPE: b_int64_t +COMMENT: (bytes) [-i] [pattern] +TYPE: rangeoffsetlimit LOC: Config.rangeOffsetLimit DEFAULT: 0 KB DOC_START - Sets a upper limit on how far into the the file a Range request - may be to cause Squid to prefetch the whole file. If beyond this - limit Squid forwards the Range request as it is and the result - is NOT cached. + usage: (bytes) [-i] [pattern] + + Sets a upper limit on how far (number of bytes) into the the file + a Range request may be to cause Squid to prefetch the whole file. + If beyond this limit Squid forwards the Range request as it is and + the result is NOT cached. This is to stop a far ahead range request (lets say start at 17MB) from making Squid fetch the whole object up to that point before sending anything to the client. - - A value of 0 causes Squid to never fetch more than the + + Multiple range_offset_limit lines may be specified, and they will + be searched from top to bottom on each request until a match is found. + The first match found will be used. If no line matches a request, the + default limit of 0 bytes will be used. + + 'bytes' is the limit specified as a number of bytes. + + A byte value of 0 causes Squid to never fetch more than the client requested. (default) - A value of -1 causes Squid to always fetch the object from the + A byte value of -1 causes Squid to always fetch the object from the beginning so it may cache the result. (2.0 style) + + NP: Using -1 as the byte value here will override any quick_abort settings + that may otherwise apply to the range request. The range request will + be fully fetched from start to finish regardless of the client + actions. This affects bandwidth usage. + + '-i' is an optional modifier to make the pattern case insensitive. + + 'pattern' is an optional parameter that will cause the limit to be + applied only to requests where the url matches pattern. If no pattern + is supplied, the implied pattern will be ".*" (match anything). - NP: Using -1 here will override any quick_abort settings that may - otherwise apply to the range request. The range request will + NP: Using -1 as the byte value here will override any quick_abort settings + that may otherwise apply to the range request. The range request will be fully fetched from start to finish regardless of the client actions. This affects bandwidth usage. DOC_END === modified file 'src/client_side.cc' --- src/client_side.cc 2009-11-01 00:13:06 +0000 +++ src/client_side.cc 2009-11-04 17:42:37 +0000 @@ -104,6 +104,7 @@ #include "MemBuf.h" #include "SquidTime.h" #include "ChunkedCodingParser.h" +#include "rangeOffsetLimit.h" #include "eui/Config.h" #if LINGERING_CLOSE @@ -1107,6 +1108,8 @@ assert(request->range); /* check if we still want to do ranges */ + int64_t roffLimit = getRangeOffsetLimit(request->canonical); + if (!rep) range_err = "no [parse-able] reply"; else if ((rep->sline.status != HTTP_OK) && (rep->sline.status != HTTP_PARTIAL_CONTENT)) @@ -1127,7 +1130,7 @@ range_err = "canonization failed"; else if (http->request->range->isComplex()) range_err = "too complex range header"; - else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded()) + else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded(roffLimit)) range_err = "range outside range_offset_limit"; /* get rid of our range specs on error */ === modified file 'src/http.cc' --- src/http.cc 2009-11-03 10:01:15 +0000 +++ src/http.cc 2009-11-04 17:42:37 +0000 @@ -1896,8 +1896,10 @@ * the server and fetch only the requested content) */ + int64_t roffLimit = getRangeOffsetLimit(orig_request->canonical); + if (NULL == orig_request->range || !orig_request->flags.cachable - || orig_request->range->offsetLimitExceeded() || orig_request->flags.connection_auth) + || orig_request->range->offsetLimitExceeded(roffLimit) || orig_request->flags.connection_auth) result = false; debugs(11, 8, "decideIfWeDoRanges: range specs: " << === modified file 'src/http.h' --- src/http.h 2009-07-02 16:36:36 +0000 +++ src/http.h 2009-10-13 21:10:05 +0000 @@ -39,6 +39,7 @@ #include "forward.h" #include "Server.h" #include "ChunkedCodingParser.h" +#include "rangeOffsetLimit.h" class HttpStateData : public ServerStateData { === added file 'src/rangeOffsetLimit.cc' --- src/rangeOffsetLimit.cc 1970-01-01 00:00:00 +0000 +++ src/rangeOffsetLimit.cc 2009-10-23 15:32:25 +0000 @@ -0,0 +1,15 @@ +#include "rangeOffsetLimit.h" +#include "stdlib.h" + +const int64_t +getRangeOffsetLimit(const char * uri) +{ + + const range_offset_t *R; + for (R = Config.rangeOffsetLimit; R; R = R->next) { + if (!regexec(&(R->compiled_pattern), uri, 0, 0, 0)) { + return R->limit; + } + } + return (int64_t)0; +} === added file 'src/rangeOffsetLimit.h' --- src/rangeOffsetLimit.h 1970-01-01 00:00:00 +0000 +++ src/rangeOffsetLimit.h 2009-10-13 21:10:05 +0000 @@ -0,0 +1,8 @@ +#ifndef SQUID_RANGEOFFSETLIMIT_H +#define SQUID_RANGEOFFSETLIMIT_H + +#include "squid.h" + +const int64_t getRangeOffsetLimit(const char * uri); + +#endif === modified file 'src/store_client.cc' --- src/store_client.cc 2009-08-23 09:30:49 +0000 +++ src/store_client.cc 2009-10-13 21:10:05 +0000 @@ -47,6 +47,7 @@ #endif #include "HttpRequest.h" #include "MemBuf.h" +#include "rangeOffsetLimit.h" /* * NOTE: 'Header' refers to the swapfile metadata header. @@ -796,7 +797,9 @@ return 0; } - if ( Config.rangeOffsetLimit < 0 && mem->request && mem->request->range ) { + int64_t roffLimit = getRangeOffsetLimit(mem->url); + + if ( roffLimit < 0 && mem->request && mem->request->range ) { /* Don't abort if the admin has configured range_ofset -1 to download fully for caching. */ debugs(90, 3, "CheckQuickAbort2: NO admin configured range replies to full-download"); return 0; === modified file 'src/structs.h' --- src/structs.h 2009-09-25 11:09:37 +0000 +++ src/structs.h 2009-10-23 15:54:40 +0000 @@ -548,7 +548,7 @@ } comm_incoming; int max_open_disk_fds; int uri_whitespace; - int64_t rangeOffsetLimit; + range_offset_t *rangeOffsetLimit; #if MULTICAST_MISS_STREAM struct { @@ -1326,4 +1326,16 @@ customlog_type type; }; +struct _range_offset_t { + const char *pattern; + regex_t compiled_pattern; + int64_t limit; + range_offset_t * next; + + struct{ + unsigned int icase:1; + } flags; +}; + + #endif /* SQUID_STRUCTS_H */ === modified file 'src/typedefs.h' --- src/typedefs.h 2009-07-27 21:50:59 +0000 +++ src/typedefs.h 2009-10-13 21:10:05 +0000 @@ -153,6 +153,8 @@ typedef struct _refresh_t refresh_t; +typedef struct _range_offset_t range_offset_t; + typedef struct _CommWriteStateData CommWriteStateData; typedef struct _StatCounters StatCounters; # Begin bundle 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 SKcKEgvFDQHg