diff -rNU 20 ../squid-3.2.11-o/src/client_side.cc ./src/client_side.cc --- ../squid-3.2.11-o/src/client_side.cc 2013-04-30 06:47:06.000000000 +0200 +++ ./src/client_side.cc 2014-04-09 15:14:50.000000000 +0200 @@ -1249,43 +1249,41 @@ /* hits only - upstream peer determines correct behaviour on misses, and client_side_reply determines * hits candidates */ else if (logTypeIsATcpHit(http->logType) && http->request->header.has(HDR_IF_RANGE) && !clientIfRangeMatch(http, rep)) range_err = "If-Range match failed"; else if (!http->request->range->canonize(rep)) 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(roffLimit)) range_err = "range outside range_offset_limit"; /* get rid of our range specs on error */ if (range_err) { /* XXX We do this here because we need canonisation etc. However, this current * code will lead to incorrect store offset requests - the store will have the * offset data, but we won't be requesting it. * So, we can either re-request, or generate an error */ - debugs(33, 3, "clientBuildRangeHeader: will not do ranges: " << range_err << "."); - delete http->request->range; - http->request->range = NULL; + http->request->ignoreRange(range_err); } else { /* XXX: TODO: Review, this unconditional set may be wrong. - TODO: review. */ httpStatusLineSet(&rep->sline, rep->sline.version, HTTP_PARTIAL_CONTENT, NULL); // web server responded with a valid, but unexpected range. // will (try-to) forward as-is. //TODO: we should cope with multirange request/responses bool replyMatchRequest = rep->content_range != NULL ? request->range->contains(rep->content_range->spec) : true; const int spec_count = http->request->range->specs.count; int64_t actual_clen = -1; debugs(33, 3, "clientBuildRangeHeader: range spec count: " << spec_count << " virgin clen: " << rep->content_length); assert(spec_count > 0); /* append appropriate header(s) */ if (spec_count == 1) { if (!replyMatchRequest) { diff -rNU 20 ../squid-3.2.11-o/src/client_side_reply.cc ./src/client_side_reply.cc --- ../squid-3.2.11-o/src/client_side_reply.cc 2013-04-30 06:47:06.000000000 +0200 +++ ./src/client_side_reply.cc 2014-04-09 15:21:57.000000000 +0200 @@ -102,40 +102,43 @@ err_type err, http_status status, const HttpRequestMethod& method, char const *uri, Ip::Address &addr, HttpRequest * failedrequest, const char *unparsedrequest, #if USE_AUTH Auth::UserRequest::Pointer auth_user_request #else void* #endif ) { ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest); if (unparsedrequest) errstate->request_hdrs = xstrdup(unparsedrequest); if (status == HTTP_NOT_IMPLEMENTED && http->request) /* prevent confusion over whether we default to persistent or not */ http->request->flags.proxy_keepalive = 0; http->al->http.code = errstate->httpStatus; + if (http->request) + http->request->ignoreRange("responding with a Squid-generated error"); + createStoreEntry(method, request_flags()); #if USE_AUTH errstate->auth_user_request = auth_user_request; #endif assert(errstate->callback_data == NULL); errorAppendEntry(http->storeEntry(), errstate); /* Now the caller reads to get this */ } void clientReplyContext::removeStoreReference(store_client ** scp, StoreEntry ** ep) { StoreEntry *e; store_client *sc_tmp = *scp; if ((e = *ep) != NULL) { *ep = NULL; storeUnregister(sc_tmp, e, this); *scp = NULL; diff -rNU 20 ../squid-3.2.11-o/src/client_side_request.cc ./src/client_side_request.cc --- ../squid-3.2.11-o/src/client_side_request.cc 2014-04-09 14:58:52.000000000 +0200 +++ ./src/client_side_request.cc 2014-04-09 15:22:41.000000000 +0200 @@ -1097,42 +1097,41 @@ clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data; /* XXX: This is suboptimal. We should give the stream the range set, * and thereby let the top of the stream set the offset when the * size becomes known. As it is, we will end up requesting from 0 * for evey -X range specification. * RBC - this may be somewhat wrong. We should probably set the range * iter up at this point. */ node->readBuffer.offset = request->range->lowestOffset(0); http->range_iter.pos = request->range->begin(); http->range_iter.valid = true; } } /* Only HEAD and GET requests permit a Range or Request-Range header. * If these headers appear on any other type of request, delete them now. */ else { req_hdr->delById(HDR_RANGE); req_hdr->delById(HDR_REQUEST_RANGE); - delete request->range; - request->range = NULL; + request->ignoreRange("neither HEAD nor GET"); } if (req_hdr->has(HDR_AUTHORIZATION)) request->flags.auth = 1; clientCheckPinning(http); if (request->login[0] != '\0') request->flags.auth = 1; if (req_hdr->has(HDR_VIA)) { String s = req_hdr->getList(HDR_VIA); /* * ThisCache cannot be a member of Via header, "1.1 ThisCache" can. * Note ThisCache2 has a space prepended to the hostname so we don't * accidentally match super-domains. */ if (strListIsSubstr(&s, ThisCache2, ',')) { debugObj(33, 1, "WARNING: Forwarding loop detected for:\n", diff -rNU 20 ../squid-3.2.11-o/src/http.cc ./src/http.cc --- ../squid-3.2.11-o/src/http.cc 2013-04-30 06:47:06.000000000 +0200 +++ ./src/http.cc 2014-04-09 15:23:31.000000000 +0200 @@ -1710,42 +1710,41 @@ assert (hdr_out->owner == hoRequest); /* append our IMS header */ if (request->lastmod > -1) hdr_out->putTime(HDR_IF_MODIFIED_SINCE, request->lastmod); bool we_do_ranges = decideIfWeDoRanges (request); String strConnection (hdr_in->getList(HDR_CONNECTION)); while ((e = hdr_in->getEntry(&pos))) copyOneHeaderFromClientsideRequestToUpstreamRequest(e, strConnection, request, hdr_out, we_do_ranges, flags); /* Abstraction break: We should interpret multipart/byterange responses * into offset-length data, and this works around our inability to do so. */ if (!we_do_ranges && request->multipartRangeRequest()) { /* don't cache the result */ request->flags.cachable = 0; /* pretend it's not a range request */ - delete request->range; - request->range = NULL; + request->ignoreRange("want to request the whole object"); request->flags.range = 0; } /* append Via */ if (Config.onoff.via) { String strVia; strVia = hdr_in->getList(HDR_VIA); snprintf(bbuf, BBUF_SZ, "%d.%d %s", request->http_ver.major, request->http_ver.minor, ThisCache); strListAdd(&strVia, bbuf, ','); hdr_out->putStr(HDR_VIA, strVia.termedBuf()); strVia.clean(); } if (request->flags.accelerated) { /* Append Surrogate-Capabilities */ String strSurrogate(hdr_in->getList(HDR_SURROGATE_CAPABILITY)); #if USE_SQUID_ESI snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", Config.Accel.surrogate_id); diff -rNU 20 ../squid-3.2.11-o/src/HttpRequest.cc ./src/HttpRequest.cc --- ../squid-3.2.11-o/src/HttpRequest.cc 2013-04-30 06:47:06.000000000 +0200 +++ ./src/HttpRequest.cc 2014-04-09 15:04:05.000000000 +0200 @@ -674,31 +674,46 @@ return rangeOffsetLimit; rangeOffsetLimit = 0; // default value for rangeOffsetLimit ACLFilledChecklist ch(NULL, this, NULL); ch.src_addr = client_addr; ch.my_addr = my_addr; for (acl_size_t *l = Config.rangeOffsetLimit; l; l = l -> next) { /* if there is no ACL list or if the ACLs listed match use this limit value */ if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) { debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit); rangeOffsetLimit = l->size; // may be -1 break; } } return rangeOffsetLimit; } +void +HttpRequest::ignoreRange(const char *reason) +{ + if (range) { + debugs(73, 3, static_cast<void*>(range) << " for " << reason); + delete range; + range = NULL; + } + // Some callers also reset isRanged but it may not be safe for all callers: + // isRanged is used to determine whether a weak ETag comparison is allowed, + // and that check should not ignore the Range header if it was present. + // TODO: Some callers also delete HDR_RANGE, HDR_REQUEST_RANGE. Should we? +} + + bool HttpRequest::canHandle1xx() const { // old clients do not support 1xx unless they sent Expect: 100-continue // (we reject all other HDR_EXPECT values so just check for HDR_EXPECT) if (http_ver <= HttpVersion(1,0) && !header.has(HDR_EXPECT)) return false; // others must support 1xx control messages return true; } diff -rNU 20 ../squid-3.2.11-o/src/HttpRequest.h ./src/HttpRequest.h --- ../squid-3.2.11-o/src/HttpRequest.h 2013-04-30 06:47:06.000000000 +0200 +++ ./src/HttpRequest.h 2014-04-09 15:04:42.000000000 +0200 @@ -223,40 +223,43 @@ void pack(Packer * p); static void httpRequestPack(void *obj, Packer *p); static HttpRequest * CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method); static HttpRequest * CreateFromUrl(char * url); ConnStateData *pinnedConnection() { if (clientConnectionManager.valid() && clientConnectionManager->pinning.pinned) return clientConnectionManager.get(); return NULL; } /** * The client connection manager, if known; * Used for any response actions needed directly to the client. * ie 1xx forwarding or connection pinning state changes */ CbcPointer<ConnStateData> clientConnectionManager; + /// forgets about the cached Range header (for a reason) + void ignoreRange(const char *reason); + int64_t getRangeOffsetLimit(); /* the result of this function gets cached in rangeOffsetLimit */ private: const char *packableURI(bool full_uri) const; mutable int64_t rangeOffsetLimit; /* caches the result of getRangeOffsetLimit */ protected: virtual void packFirstLineInto(Packer * p, bool full_uri) const; virtual bool sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error); virtual void hdrCacheInit(); virtual bool inheritProperties(const HttpMsg *aMsg); }; MEMPROXY_CLASS_INLINE(HttpRequest);