// nothing to do because we are using temporary buffers
// parsing; TODO: do not set until we parse, see ICAPOptXact
- icapReply = HTTPMSGLOCK(new HttpReply);
+ icapReply = new HttpReply;
icapReply->protoPrefix = "ICAP/"; // TODO: make an IcapReply class?
debugs(93,7, HERE << "initialized." << status());
{
Must(!state.serviceWaiting);
debugs(93, 7, HERE << "will wait for the ICAP service" << status());
- state.serviceWaiting = true;
typedef NullaryMemFunT<ModXact> Dialer;
AsyncCall::Pointer call = JobCallback(93,5,
Dialer, this, Adaptation::Icap::ModXact::noteServiceReady);
service().callWhenReady(call);
+ state.serviceWaiting = true; // after callWhenReady() which may throw
}
void Adaptation::Icap::ModXact::noteServiceReady()
Must(state.writing == State::writingHeaders);
// determine next step
- if (preview.enabled())
- state.writing = preview.done() ? State::writingPaused : State::writingPreview;
- else if (virginBody.expected())
+ if (preview.enabled()) {
+ if (preview.done())
+ decideWritingAfterPreview("zero-size");
+ else
+ state.writing = State::writingPreview;
+ } else if (virginBody.expected()) {
state.writing = State::writingPrime;
- else {
+ } else {
stopWriting(true);
return;
}
// change state once preview is written
- if (preview.done()) {
- debugs(93, 7, HERE << "wrote entire Preview body" << status());
+ if (preview.done())
+ decideWritingAfterPreview("body");
+}
- if (preview.ieof())
- stopWriting(true);
- else
- state.writing = State::writingPaused;
- }
+/// determine state.writing after we wrote the entire preview
+void Adaptation::Icap::ModXact::decideWritingAfterPreview(const char *kind)
+{
+ if (preview.ieof()) // nothing more to write
+ stopWriting(true);
+ else if (state.parsing == State::psIcapHeader) // did not get a reply yet
+ state.writing = State::writingPaused; // wait for the ICAP server reply
+ else
+ stopWriting(true); // ICAP server reply implies no post-preview writing
+
+ debugs(93, 6, HERE << "decided on writing after " << kind << " preview" <<
+ status());
}
void Adaptation::Icap::ModXact::writePrimeBody()
// allocate the adapted message and copy metainfo
Must(!adapted.header);
- HttpMsg *newHead = NULL;
+ {
+ HttpMsg::Pointer newHead;
if (const HttpRequest *oldR = dynamic_cast<const HttpRequest*>(oldHead)) {
- HttpRequest *newR = new HttpRequest;
+ HttpRequest::Pointer newR(new HttpRequest);
newR->canonical = oldR->canonical ?
xstrdup(oldR->canonical) : NULL; // parse() does not set it
newHead = newR;
} else if (dynamic_cast<const HttpReply*>(oldHead)) {
- HttpReply *newRep = new HttpReply;
- newHead = newRep;
+ newHead = new HttpReply;
}
- Must(newHead);
+ Must(newHead != NULL);
+
newHead->inheritProperties(oldHead);
adapted.setHeader(newHead);
+ }
// parse the buffer back
http_status error = HTTP_STATUS_NONE;
- Must(newHead->parse(&httpBuf, true, &error));
+ Must(adapted.header->parse(&httpBuf, true, &error));
- Must(newHead->hdr_sz == httpBuf.contentSize()); // no leftovers
+ Must(adapted.header->hdr_sz == httpBuf.contentSize()); // no leftovers
httpBuf.clean();
debugs(93, 7, HERE << "cloned virgin message " << oldHead << " to " <<
- newHead);
+ adapted.header);
// setup adapted body pipe if needed
if (oldHead->body_pipe != NULL) {
mustStop("adapted body consumer aborted");
}
+Adaptation::Icap::ModXact::~ModXact()
+{
+ delete bodyParser;
+}
+
// internal cleanup
void Adaptation::Icap::ModXact::swanSong()
{
icapBuf.Printf("%s=%d, ", section, (int) httpBuf.contentSize());
// begin cloning
- HttpMsg *headClone = NULL;
+ HttpMsg::Pointer headClone;
if (const HttpRequest* old_request = dynamic_cast<const HttpRequest*>(head)) {
- HttpRequest* new_request = new HttpRequest;
- assert(old_request->canonical);
+ HttpRequest::Pointer new_request(new HttpRequest);
+ Must(old_request->canonical);
urlParse(old_request->method, old_request->canonical, new_request);
new_request->http_ver = old_request->http_ver;
headClone = new_request;
} else if (const HttpReply *old_reply = dynamic_cast<const HttpReply*>(head)) {
- HttpReply* new_reply = new HttpReply;
+ HttpReply::Pointer new_reply(new HttpReply);
new_reply->sline = old_reply->sline;
headClone = new_reply;
}
-
- Must(headClone);
+ Must(headClone != NULL);
headClone->inheritProperties(head);
HttpHeaderPos pos = HttpHeaderInitPos;
// pack polished HTTP header
packHead(httpBuf, headClone);
- delete headClone;
+ // headClone unlocks and, hence, deletes the message we packed
}
void Adaptation::Icap::ModXact::packHead(MemBuf &httpBuf, const HttpMsg *head)
// comm module read a portion of the ICAP response for us
void Adaptation::Icap::OptXact::handleCommRead(size_t)
{
- if (HttpMsg *r = parseResponse()) {
+ if (parseResponse()) {
icap_tio_finish = current_time;
setOutcome(xoOpt);
- sendAnswer(r);
- icapReply = HTTPMSGLOCK(dynamic_cast<HttpReply*>(r));
+ sendAnswer(icapReply);
Must(done()); // there should be nothing else to do
return;
}
scheduleRead();
}
-HttpMsg *Adaptation::Icap::OptXact::parseResponse()
+bool Adaptation::Icap::OptXact::parseResponse()
{
debugs(93, 5, HERE << "have " << readBuf.contentSize() << " bytes to parse" <<
status());
debugs(93, 5, HERE << "\n" << readBuf.content());
- HttpReply *r = HTTPMSGLOCK(new HttpReply);
+ HttpReply::Pointer r(new HttpReply);
r->protoPrefix = "ICAP/"; // TODO: make an IcapReply class?
- if (!parseHttpMsg(r)) { // throws on errors
- HTTPMSGUNLOCK(r);
- return 0;
- }
+ if (!parseHttpMsg(r)) // throws on errors
+ return false;
if (httpHeaderHasConnDir(&r->header, "close"))
reuseConnection = false;
- return r;
+ icapReply = r;
+ return true;
}
void Adaptation::Icap::OptXact::swanSong()
serversFree(&servers);
+ doneWithRetries();
+
HTTPMSGUNLOCK(request);
if (err)
if (shutting_down)
return false;
+ if (!self) { // we have aborted before the server called us back
+ debugs(17, 5, HERE << "not retrying because of earlier abort");
+ // we will be destroyed when the server clears its Pointer to us
+ return false;
+ }
+
if (entry->store_status != STORE_PENDING)
return false;
void
FwdState::retryOrBail()
{
- if (!self) { // we have aborted before the server called us back
- debugs(17, 5, HERE << "not retrying because of earlier abort");
- // we will be destroyed when the server clears its Pointer to us
- return;
- }
-
if (checkRetry()) {
int originserver = (servers->_peer == NULL);
debugs(17, 3, "fwdServerClosed: re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
return;
}
- if (!err && shutting_down) {
+ // TODO: should we call completed() here and move doneWithRetries there?
+ doneWithRetries();
+
+ if (self != NULL && !err && shutting_down) {
errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
}
self = NULL; // refcounted
}
+// If the Server quits before nibbling at the request body, the body sender
+// will not know (so that we can retry). Call this if we will not retry. We
+// will notify the sender so that it does not get stuck waiting for space.
+void
+FwdState::doneWithRetries()
+{
+ if (request && request->body_pipe != NULL)
+ request->body_pipe->expectNoConsumption();
+}
+
// called by the server that failed after calling unregister()
void
FwdState::handleUnregisteredServerEnd()