# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: henrik@henriknordstrom.net-20080330134254-\ # xk0b7qlireb5frg2 # target_branch: file:///data/bzr/squid3/trunk/ # testament_sha1: a3df357ae862b61434bb557e1faf88f8ae965680 # timestamp: 2008-03-30 17:29:53 +0200 # base_revision_id: henrik@henriknordstrom.net-20080328204641-\ # 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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