This is the second half of the patch for Bug #2001, adding support for
large response headers.
What this patch does is to make client_side_reply clone the already
parsed HTTP response and skip over the ascii HTTP header received from
the store, similar to the change Adrian did earlier in Squid-2 but for
other reasons.
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [EMAIL PROTECTED]
# xk0b7qlireb5frg2
# target_branch: file:///data/bzr/squid3/trunk/
# testament_sha1: a3df357ae862b61434bb557e1faf88f8ae965680
# timestamp: 2008-03-30 17:29:53 +0200
# base_revision_id: [EMAIL PROTECTED]
# so853tluhwlx6qc5
#
# Begin patch
=== modified file 'src/HttpReply.cc'
--- src/HttpReply.cc 2008-02-09 01:27:59 +0000
+++ src/HttpReply.cc 2008-03-15 19:09:52 +0000
@@ -551,3 +551,17 @@
}
}
}
+
+HttpReply *
+HttpReply::clone() const
+{
+ HttpReply *rep = new HttpReply();
+ rep->header.append(&header);
+ rep->hdrCacheInit();
+ rep->hdr_sz = hdr_sz;
+ rep->http_ver = http_ver;
+ rep->pstate = pstate;
+ rep->protocol = protocol;
+ rep->sline = sline;
+ return rep;
+}
=== modified file 'src/HttpReply.h'
--- src/HttpReply.h 2008-03-16 00:50:50 +0000
+++ src/HttpReply.h 2008-03-16 00:53:56 +0000
@@ -127,6 +127,11 @@
void packHeadersInto(Packer * p) const;
+ /// Clone this reply.
+ /// Could be done as a copy-contructor but we do not want to
+ /// accidently copy a HttpReply..
+ HttpReply *clone() const;
+
private:
/* initialize */
void init();
=== modified file 'src/client_side_reply.cc'
--- src/client_side_reply.cc 2008-03-20 23:20:58 +0000
+++ src/client_side_reply.cc 2008-03-30 13:34:01 +0000
@@ -352,68 +352,47 @@
sendClientOldEntry();
}
- // we have a partial reply from the origin
- else if (STORE_PENDING == http->storeEntry()->store_status && 0 == status) {
- // header is too large, send old entry
-
- if (reqsize >= HTTP_REQBUF_SZ) {
- debugs(88, 3, "handleIMSReply: response from origin is too large '" << http->storeEntry()->url() << "', sending old entry to client" );
- http->logType = LOG_TCP_REFRESH_FAIL;
- sendClientOldEntry();
- }
-
- // everything looks fine, we're just waiting for more data
- else {
- debugs(88, 3, "handleIMSReply: incomplete headers for '" << http->storeEntry()->url() << "', waiting for more data" );
- reqofs = reqsize;
- waitForMoreData();
- }
- }
-
- // we have a reply from the origin
+ HttpReply *old_rep = (HttpReply *) old_entry->getReply();
+
+ // origin replied 304
+
+ if (status == HTTP_NOT_MODIFIED) {
+ http->logType = LOG_TCP_REFRESH_UNMODIFIED;
+
+ // update headers on existing entry
+ HttpReply *old_rep = (HttpReply *) old_entry->getReply();
+ old_rep->updateOnNotModified(http->storeEntry()->getReply());
+ old_entry->timestampsSet();
+
+ // if client sent IMS
+
+ if (http->request->flags.ims) {
+ // forward the 304 from origin
+ debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and forwarding 304 to client");
+ sendClientUpstreamResponse();
+ } else {
+ // send existing entry, it's still valid
+ debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and sending " <<
+ old_rep->sline.status << " to client");
+ sendClientOldEntry();
+ }
+ }
+
+ // origin replied with a non-error code
+ else if (status > HTTP_STATUS_NONE && status < HTTP_INTERNAL_SERVER_ERROR) {
+ // forward response from origin
+ http->logType = LOG_TCP_REFRESH_MODIFIED;
+ debugs(88, 3, "handleIMSReply: origin replied " << status << ", replacing existing entry and forwarding to client");
+ sendClientUpstreamResponse();
+ }
+
+ // origin replied with an error
else {
- HttpReply *old_rep = (HttpReply *) old_entry->getReply();
-
- // origin replied 304
-
- if (status == HTTP_NOT_MODIFIED) {
- http->logType = LOG_TCP_REFRESH_UNMODIFIED;
-
- // update headers on existing entry
- HttpReply *old_rep = (HttpReply *) old_entry->getReply();
- old_rep->updateOnNotModified(http->storeEntry()->getReply());
- old_entry->timestampsSet();
-
- // if client sent IMS
-
- if (http->request->flags.ims) {
- // forward the 304 from origin
- debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and forwarding 304 to client");
- sendClientUpstreamResponse();
- } else {
- // send existing entry, it's still valid
- debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and sending " <<
- old_rep->sline.status << " to client");
- sendClientOldEntry();
- }
- }
-
- // origin replied with a non-error code
- else if (status > HTTP_STATUS_NONE && status < HTTP_INTERNAL_SERVER_ERROR) {
- // forward response from origin
- http->logType = LOG_TCP_REFRESH_MODIFIED;
- debugs(88, 3, "handleIMSReply: origin replied " << status << ", replacing existing entry and forwarding to client");
- sendClientUpstreamResponse();
- }
-
- // origin replied with an error
- else {
- // ignore and let client have old entry
- http->logType = LOG_TCP_REFRESH_FAIL;
- debugs(88, 3, "handleIMSReply: origin replied with error " <<
- status << ", sending old entry (" << old_rep->sline.status << ") to client");
- sendClientOldEntry();
- }
+ // ignore and let client have old entry
+ http->logType = LOG_TCP_REFRESH_FAIL;
+ debugs(88, 3, "handleIMSReply: origin replied with error " <<
+ status << ", sending old entry (" << old_rep->sline.status << ") to client");
+ sendClientOldEntry();
}
}
@@ -472,33 +451,6 @@
/* update size of the request */
reqsize = result.length + reqofs;
- if (e->getReply()->sline.status == 0) {
- /*
- * we don't have full reply headers yet; either wait for more or
- * punt to clientProcessMiss.
- */
-
- if (e->mem_status == IN_MEMORY || e->store_status == STORE_OK) {
- processMiss();
- } else if (result.length + reqofs >= HTTP_REQBUF_SZ
- && http->out.offset == 0) {
- processMiss();
- } else {
- debugs(88, 3, "clientCacheHit: waiting for HTTP reply headers");
- reqofs += result.length;
- assert(reqofs <= HTTP_REQBUF_SZ);
- /* get the next users' buffer */
- StoreIOBuffer tempBuffer;
- tempBuffer.offset = http->out.offset + reqofs;
- tempBuffer.length = next()->readBuffer.length - reqofs;
- tempBuffer.data = next()->readBuffer.data + reqofs;
- storeClientCopy(sc, e,
- tempBuffer, CacheHit, this);
- }
-
- return;
- }
-
/*
* Got the headers, now grok them
*/
@@ -1390,32 +1342,14 @@
void
-clientReplyContext::buildReply(const char *buf, size_t size)
+clientReplyContext::cloneReply()
{
- size_t k = headersEnd(buf, size);
-
- if (!k)
- return;
-
assert(reply == NULL);
- HttpReply *rep = new HttpReply;
+ HttpReply *rep = http->storeEntry()->getReply()->clone();
reply = HTTPMSGLOCK(rep);
- if (!reply->parseCharBuf(buf, k)) {
- /* parsing failure, get rid of the invalid reply */
- HTTPMSGUNLOCK(reply);
-
- if (http->request->range) {
- debugs(0,0,HERE << "look for bug here");
- /* this will fail and destroy request->range */
- // clientBuildRangeHeader(http, reply);
- }
-
- return;
- }
-
/* enforce 1.0 reply version */
reply->sline.version = HttpVersion(1,0);
@@ -1721,32 +1655,6 @@
}
void
-clientReplyContext::waitForMoreData ()
-{
- debugs(88, 5, "clientReplyContext::waitForMoreData: Waiting for more data to parse reply headers in client side.");
- /* We don't have enough to parse the metadata yet */
- /* TODO: the store should give us out of band metadata and
- * obsolete this routine
- */
- /* wait for more to arrive */
- startSendProcess();
-}
-
-void
-clientReplyContext::startSendProcess()
-{
- debugs(88, 5, "clientReplyContext::startSendProcess: triggering store read to SendMoreData");
- assert(reqofs <= HTTP_REQBUF_SZ);
- /* TODO: copy into the supplied buffer */
- StoreIOBuffer tempBuffer;
- tempBuffer.offset = reqofs;
- tempBuffer.length = next()->readBuffer.length - reqofs;
- tempBuffer.data = next()->readBuffer.data + reqofs;
- storeClientCopy(sc, http->storeEntry(),
- tempBuffer, SendMoreData, this);
-}
-
-void
clientReplyContext::sendBodyTooLargeError()
{
IPAddress tmp_noaddr; tmp_noaddr.SetNoAddr(); // TODO: make a global const
@@ -1835,8 +1743,10 @@
http->loggingEntry(http->storeEntry());
ssize_t body_size = reqofs - reply->hdr_sz;
-
- assert(body_size >= 0);
+ if (body_size < 0) {
+ reqofs = reply->hdr_sz;
+ body_size = 0;
+ }
debugs(88, 3, "clientReplyContext::sendMoreData: Appending " <<
(int) body_size << " bytes after " << reply->hdr_sz <<
@@ -1866,7 +1776,7 @@
StoreIOBuffer tempBuffer;
char *buf = next()->readBuffer.data;
- char *body_buf = buf + reply->hdr_sz;
+ char *body_buf = buf + reply->hdr_sz - next()->readBuffer.offset;
//Server side may disable ranges under some circumstances.
@@ -1910,23 +1820,11 @@
char *body_buf = buf;
- /* This is always valid until we get the headers as metadata from
- * storeClientCopy.
- * Then it becomes reqofs == next->readBuffer.offset()
- */
- assert(reqofs == 0 || flags.storelogiccomplete);
-
- if (flags.headersSent && buf != result.data) {
+ if (buf != result.data) {
/* we've got to copy some data */
assert(result.length <= next()->readBuffer.length);
xmemcpy(buf, result.data, result.length);
body_buf = buf;
- } else if (!flags.headersSent &&
- buf + reqofs !=result.data) {
- /* we've got to copy some data */
- assert(result.length + reqofs <= next()->readBuffer.length);
- xmemcpy(buf + reqofs, result.data, result.length);
- body_buf = buf;
}
/* We've got the final data to start pushing... */
@@ -1965,38 +1863,23 @@
return;
}
- buildReply(buf, reqofs);
-
- if (reply) {
-
- /* handle headers */
-
- if (Config.onoff.log_mime_hdrs) {
- size_t k;
-
- if ((k = headersEnd(buf, reqofs))) {
- safe_free(http->al.headers.reply);
- http->al.headers.reply = (char *)xcalloc(k + 1, 1);
- xstrncpy(http->al.headers.reply, buf, k);
- }
- }
-
- holdingBuffer = result;
- processReplyAccess();
- return;
-
- } else if (reqofs < HTTP_REQBUF_SZ && entry->store_status == STORE_PENDING) {
- waitForMoreData();
- return;
- } else {
- debugs(88, 0, "clientReplyContext::sendMoreData: Unable to parse reply headers within a single HTTP_REQBUF_SZ length buffer");
- StoreIOBuffer tempBuffer;
- tempBuffer.flags.error = 1;
- /* XXX FIXME: make an html error page here */
- sendStreamError(tempBuffer);
- return;
+ cloneReply();
+
+ /* handle headers */
+
+ if (Config.onoff.log_mime_hdrs) {
+ size_t k;
+
+ if ((k = headersEnd(buf, reqofs))) {
+ safe_free(http->al.headers.reply);
+ http->al.headers.reply = (char *)xcalloc(k + 1, 1);
+ xstrncpy(http->al.headers.reply, buf, k);
+ }
}
- fatal ("clientReplyContext::sendMoreData: Unreachable code reached \n");
+
+ holdingBuffer = result;
+ processReplyAccess();
+ return;
}
=== modified file 'src/client_side_reply.h'
--- src/client_side_reply.h 2008-03-16 22:10:18 +0000
+++ src/client_side_reply.h 2008-03-22 01:47:25 +0000
@@ -122,15 +122,13 @@
bool errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const ;
void sendStreamError(StoreIOBuffer const &result);
void pushStreamData(StoreIOBuffer const &result, char *source);
- void waitForMoreData ();
clientStreamNode * next() const;
- void startSendProcess();
StoreIOBuffer holdingBuffer;
HttpReply *reply;
void processReplyAccess();
static PF ProcessReplyAccessResult;
void processReplyAccessResult(bool accessAllowed);
- void buildReply(const char *buf, size_t size);
+ void cloneReply();
void buildReplyHeader ();
bool alwaysAllowResponse(http_status sline) const;
int checkTransferDone();
# Begin bundle
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