/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "format/Token.h"
#include "FwdState.h"
#include "globals.h"
-#include "globals.h"
+#include "http/Stream.h"
#include "HttpHeaderTools.h"
#include "HttpReply.h"
#include "HttpRequest.h"
reply(NULL),
old_entry(NULL),
old_sc(NULL),
- deleting(false)
+ deleting(false),
+ collapsedRevalidation(crNone)
{
*tempbuf = 0;
}
clientReplyContext::processExpired()
{
const char *url = storeId();
- StoreEntry *entry = NULL;
debugs(88, 3, "clientReplyContext::processExpired: '" << http->uri << "'");
assert(http->storeEntry()->lastmod >= 0);
/*
return;
}
+ http->logType = LOG_TCP_REFRESH;
http->request->flags.refresh = true;
#if STORE_CLIENT_LIST_DEBUG
/* Prevent a race with the store client memory free routines
#endif
/* Prepare to make a new temporary request */
saveState();
- entry = storeCreateEntry(url,
- http->log_uri, http->request->flags, http->request->method);
- /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
+
+ // TODO: support collapsed revalidation for Vary-controlled entries
+ const bool collapsingAllowed = Config.onoff.collapsed_forwarding &&
+ !Store::Root().smpAware() &&
+ http->request->vary_headers.isEmpty();
+
+ StoreEntry *entry = nullptr;
+ if (collapsingAllowed) {
+ if ((entry = storeGetPublicByRequest(http->request, ksRevalidation)))
+ entry->lock("clientReplyContext::processExpired#alreadyRevalidating");
+ }
+
+ if (entry) {
+ debugs(88, 5, "collapsed on existing revalidation entry: " << *entry);
+ collapsedRevalidation = crSlave;
+ } else {
+ entry = storeCreateEntry(url,
+ http->log_uri, http->request->flags, http->request->method);
+ /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
+
+ if (collapsingAllowed) {
+ debugs(88, 5, "allow other revalidation requests to collapse on " << *entry);
+ Store::Root().allowCollapsing(entry, http->request->flags,
+ http->request->method);
+ collapsedRevalidation = crInitiator;
+ } else {
+ collapsedRevalidation = crNone;
+ }
+ }
+
sc = storeClientListAdd(entry, this);
#if USE_DELAY_POOLS
/* delay_id is already set on original store client */
assert(http->out.offset == 0);
assert(http->request->clientConnectionManager == http->getConn());
- /*
- * A refcounted pointer so that FwdState stays around as long as
- * this clientReplyContext does
- */
- Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL;
- FwdState::Start(conn, http->storeEntry(), http->request, http->al);
-
+ if (collapsedRevalidation != crSlave) {
+ /*
+ * A refcounted pointer so that FwdState stays around as long as
+ * this clientReplyContext does
+ */
+ Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL;
+ FwdState::Start(conn, http->storeEntry(), http->request, http->al);
+ }
/* Register with storage manager to receive updates when data comes in. */
if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
sendClientOldEntry();
}
- HttpReply *old_rep = (HttpReply *) old_entry->getReply();
+ const HttpReply *old_rep = old_entry->getReply();
// origin replied 304
if (status == Http::scNotModified) {
http->request->flags.staleIfHit = false; // old_entry is no longer stale
// update headers on existing entry
- old_rep->updateOnNotModified(http->storeEntry()->getReply());
- old_entry->timestampsSet();
+ Store::Root().updateOnNotModified(old_entry, *http->storeEntry());
// if client sent IMS
// forward response from origin
http->logType = LOG_TCP_REFRESH_MODIFIED;
debugs(88, 3, "handleIMSReply: origin replied " << status << ", replacing existing entry and forwarding to client");
+
+ if (collapsedRevalidation)
+ http->storeEntry()->clearPublicKeyScope();
+
sendClientUpstreamResponse();
}
}
/* And for Vary, release the base URI if none of the headers was included in the request */
-
- if (http->request->vary_headers
- && !strstr(http->request->vary_headers, "=")) {
+ if (!http->request->vary_headers.isEmpty()
+ && http->request->vary_headers.find('=') != SBuf::npos) {
// XXX: performance regression, c_str() reallocates
SBuf tmp(http->request->effectiveRequestUri());
StoreEntry *entry = storeGetPublic(tmp.c_str(), Http::METHOD_GET);
if (curReply->content_length < 0)
return 0;
- int64_t expectedLength = curReply->content_length + http->out.headers_sz;
+ uint64_t expectedLength = curReply->content_length + http->out.headers_sz;
if (http->out.size < expectedLength)
return 0;
return STREAM_FAILED;
}
+ if (EBIT_TEST(http->storeEntry()->flags, ENTRY_BAD_LENGTH)) {
+ debugs(88, 5, "clientReplyStatus: truncated response body");
+ return STREAM_UNPLANNED_COMPLETE;
+ }
+
if (!done) {
debugs(88, 5, "clientReplyStatus: closing, !done, but read 0 bytes");
return STREAM_FAILED;
*/
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
hdr->delById(Http::HdrType::DATE);
- hdr->insertTime(Http::HdrType::DATE, squid_curtime);
+ hdr->putTime(Http::HdrType::DATE, squid_curtime);
} else if (http->getConn() && http->getConn()->port->actAsOrigin) {
// Swap the Date: header to current time if we are simulating an origin
HttpHeaderEntry *h = hdr->findEntry(Http::HdrType::DATE);
if (h)
hdr->putExt("X-Origin-Date", h->value.termedBuf());
hdr->delById(Http::HdrType::DATE);
- hdr->insertTime(Http::HdrType::DATE, squid_curtime);
+ hdr->putTime(Http::HdrType::DATE, squid_curtime);
h = hdr->findEntry(Http::HdrType::EXPIRES);
if (h && http->storeEntry()->expires >= 0) {
hdr->putExt("X-Origin-Expires", h->value.termedBuf());
hdr->delById(Http::HdrType::EXPIRES);
- hdr->insertTime(Http::HdrType::EXPIRES, squid_curtime + http->storeEntry()->expires - http->storeEntry()->timestamp);
+ hdr->putTime(Http::HdrType::EXPIRES, squid_curtime + http->storeEntry()->expires - http->storeEntry()->timestamp);
}
if (http->storeEntry()->timestamp <= squid_curtime) {
// put X-Cache-Age: instead of Age:
*/
if ( !hdr->has(Http::HdrType::DATE) ) {
if (!http->storeEntry())
- hdr->insertTime(Http::HdrType::DATE, squid_curtime);
+ hdr->putTime(Http::HdrType::DATE, squid_curtime);
else if (http->storeEntry()->timestamp > 0)
- hdr->insertTime(Http::HdrType::DATE, http->storeEntry()->timestamp);
+ hdr->putTime(Http::HdrType::DATE, http->storeEntry()->timestamp);
else {
debugs(88,DBG_IMPORTANT,"BUG 3279: HTTP reply without Date:");
/* dump something useful about the problem */
}
// Decide if we send chunked reply
- if (maySendChunkedReply &&
- request->flags.proxyKeepalive &&
- reply->bodySize(request->method) < 0) {
+ if (maySendChunkedReply && reply->bodySize(request->method) < 0) {
debugs(88, 3, "clientBuildReplyHeader: chunked reply");
request->flags.chunkedReply = true;
hdr->putStr(Http::HdrType::TRANSFER_ENCODING, "chunked");
/* TODO: else case: drop any controls intended specifically for our surrogate ID */
}
- httpHdrMangleList(hdr, request, ROR_REPLY);
+ httpHdrMangleList(hdr, request, http->al, ROR_REPLY);
}
void
StoreEntry *entry = http->storeEntry();
- ConnStateData * conn = http->getConn();
+ if (ConnStateData * conn = http->getConn()) {
+ if (!conn->isOpen()) {
+ debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
+ return;
+ }
+ if (conn->pinning.zeroReply) {
+ debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
+ return;
+ }
- // too late, our conn is closing
- // TODO: should we also quit?
- if (conn == NULL) {
- debugs(33,3, "not sending more data to a closed connection" );
- return;
- }
- if (!conn->isOpen()) {
- debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
- return;
- }
- if (conn->pinning.zeroReply) {
- debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
- return;
+ if (reqofs==0 && !http->logType.isTcpHit()) {
+ if (Ip::Qos::TheConfig.isHitTosActive()) {
+ Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
+ }
+ if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
+ Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
+ }
+ }
+
+ debugs(88, 5, conn->clientConnection <<
+ " '" << entry->url() << "'" <<
+ " out.offset=" << http->out.offset);
}
char *buf = next()->readBuffer.data;
memcpy(buf, result.data, result.length);
}
- if (reqofs==0 && !http->logType.isTcpHit() && Comm::IsConnOpen(conn->clientConnection)) {
- if (Ip::Qos::TheConfig.isHitTosActive()) {
- Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
- }
- if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
- Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
- }
- }
-
/* We've got the final data to start pushing... */
flags.storelogiccomplete = 1;
debugs(88, 5, "clientReplyContext::sendMoreData: " << http->uri << ", " <<
reqofs << " bytes (" << result.length <<
" new bytes)");
- debugs(88, 5, "clientReplyContext::sendMoreData:"
- << conn->clientConnection <<
- " '" << entry->url() << "'" <<
- " out.offset=" << http->out.offset);
/* update size of the request */
reqsize = reqofs;
cloneReply();
+#if USE_DELAY_POOLS
+ if (sc)
+ sc->setDelayId(DelayId::DelayClient(http,reply));
+#endif
+
/* handle headers */
if (Config.onoff.log_mime_hdrs) {