--------------------- PatchSet 2163 Date: 2005/11/21 21:05:50 Author: dwsquid Branch: squid3-icap Tag: (none) Log: Reorganized ICAP code -- moved to src/ICAP subdirectory and renamed some classes. Members: src/ChunkedCodingParser.cc:1.1.2.5->1.1.2.6(DEAD) src/ChunkedCodingParser.h:1.1.2.3->1.1.2.4(DEAD) src/ClientRequestContext.h:1.1.2.2->1.1.2.3 src/ICAPAnchor.cc:1.1.2.32->1.1.2.33(DEAD) src/ICAPAnchor.h:1.1.2.9->1.1.2.10(DEAD) src/ICAPClient.cc:1.1.2.9->1.1.2.10(DEAD) src/ICAPClient.h:1.1.2.8->1.1.2.9(DEAD) src/ICAPClientSideHook.cc:1.1.2.10->1.1.2.11(DEAD) src/ICAPClientSideHook.h:1.1.2.4->1.1.2.5(DEAD) src/ICAPConfig.cc:1.1.2.13->1.1.2.14(DEAD) src/ICAPConfig.h:1.1.2.8->1.1.2.9(DEAD) src/ICAPElements.cc:1.1.2.2->1.1.2.3(DEAD) src/ICAPElements.h:1.1.2.2->1.1.2.3(DEAD) src/ICAPModXact.cc:1.1.2.2->1.1.2.3(DEAD) src/ICAPModXact.h:1.1.2.1->1.1.2.2(DEAD) src/ICAPOptXact.cc:1.1.2.7->1.1.2.8(DEAD) src/ICAPOptXact.h:1.1.2.3->1.1.2.4(DEAD) src/ICAPOptions.cc:1.1.2.7->1.1.2.8(DEAD) src/ICAPOptions.h:1.1.2.6->1.1.2.7(DEAD) src/ICAPServiceRep.cc:1.1.2.10->1.1.2.11(DEAD) src/ICAPServiceRep.h:1.1.2.7->1.1.2.8(DEAD) src/ICAPXaction.cc:1.1.2.52->1.1.2.53(DEAD) src/ICAPXaction.h:1.1.2.28->1.1.2.29(DEAD) src/Makefile.am:1.60.4.18->1.60.4.19 src/MsgPipe.cc:1.1.2.14->1.1.2.15(DEAD) src/MsgPipe.h:1.1.2.8->1.1.2.9(DEAD) src/MsgPipeData.h:1.1.2.11->1.1.2.12(DEAD) src/MsgPipeEnd.h:1.1.2.3->1.1.2.4(DEAD) src/MsgPipeSink.h:1.1.2.3->1.1.2.4(DEAD) src/MsgPipeSource.h:1.1.2.3->1.1.2.4(DEAD) src/TextException.cc:1.1.2.3->1.1.2.4(DEAD) src/TextException.h:1.1.2.2->1.1.2.3(DEAD) src/cache_cf.cc:1.52.2.8->1.52.2.9 src/client_side_request.cc:1.34.4.16->1.34.4.17 src/client_side_request.h:1.17.12.5->1.17.12.6 src/http.cc:1.49.2.53->1.49.2.54 src/http.h:1.11.4.15->1.11.4.16 src/ICAP/ChunkedCodingParser.cc:1.1->1.1.2.1 src/ICAP/ChunkedCodingParser.h:1.1->1.1.2.1 src/ICAP/ICAPClient.cc:1.1->1.1.2.1 src/ICAP/ICAPClient.h:1.1->1.1.2.1 src/ICAP/ICAPClientReqmodPrecache.cc:1.1->1.1.2.1 src/ICAP/ICAPClientReqmodPrecache.h:1.1->1.1.2.1 src/ICAP/ICAPClientRespmodPrecache.cc:1.1->1.1.2.1 src/ICAP/ICAPClientRespmodPrecache.h:1.1->1.1.2.1 src/ICAP/ICAPClientStream.cc:1.1->1.1.2.1 src/ICAP/ICAPClientStream.h:1.1->1.1.2.1 src/ICAP/ICAPConfig.cc:1.1->1.1.2.1 src/ICAP/ICAPConfig.h:1.1->1.1.2.1 src/ICAP/ICAPElements.cc:1.1->1.1.2.1 src/ICAP/ICAPElements.h:1.1->1.1.2.1 src/ICAP/ICAPModXact.cc:1.1->1.1.2.1 src/ICAP/ICAPModXact.h:1.1->1.1.2.1 src/ICAP/ICAPOptXact.cc:1.1->1.1.2.1 src/ICAP/ICAPOptXact.h:1.1->1.1.2.1 src/ICAP/ICAPOptions.cc:1.1->1.1.2.1 src/ICAP/ICAPOptions.h:1.1->1.1.2.1 src/ICAP/ICAPServiceRep.cc:1.1->1.1.2.1 src/ICAP/ICAPServiceRep.h:1.1->1.1.2.1 src/ICAP/ICAPXaction.cc:1.1->1.1.2.1 src/ICAP/ICAPXaction.h:1.1->1.1.2.1 src/ICAP/Makefile.am:1.1->1.1.2.1 src/ICAP/MsgPipe.cc:1.1->1.1.2.1 src/ICAP/MsgPipe.h:1.1->1.1.2.1 src/ICAP/MsgPipeData.h:1.1->1.1.2.1 src/ICAP/MsgPipeEnd.h:1.1->1.1.2.1 src/ICAP/MsgPipeSink.h:1.1->1.1.2.1 src/ICAP/MsgPipeSource.h:1.1->1.1.2.1 src/ICAP/TextException.cc:1.1->1.1.2.1 src/ICAP/TextException.h:1.1->1.1.2.1 --- squid3/src/ChunkedCodingParser.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,245 +0,0 @@ -#include "squid.h" -#include "Parsing.h" -#include "TextException.h" -#include "ChunkedCodingParser.h" -#include "MemBuf.h" - -ChunkedCodingParser::Step ChunkedCodingParser::psChunkBeg = &ChunkedCodingParser::parseChunkBeg; -ChunkedCodingParser::Step ChunkedCodingParser::psChunkBody = &ChunkedCodingParser::parseChunkBody; -ChunkedCodingParser::Step ChunkedCodingParser::psChunkEnd = &ChunkedCodingParser::parseChunkEnd; -ChunkedCodingParser::Step ChunkedCodingParser::psTrailer = &ChunkedCodingParser::parseTrailer; -ChunkedCodingParser::Step ChunkedCodingParser::psMessageEnd = &ChunkedCodingParser::parseMessageEnd; - -ChunkedCodingParser::ChunkedCodingParser() -{ - reset(); -} - -void ChunkedCodingParser::reset() -{ - theStep = psChunkBeg; - theChunkSize = theLeftBodySize = 0; - doNeedMoreData = false; - sawIeof = false; - theIn = theOut = NULL; -} - -bool ChunkedCodingParser::parse(MemBuf *rawData, MemBuf *parsedContent) -{ - Must(rawData && parsedContent); - theIn = rawData; - theOut = parsedContent; - - // we must reset this all the time so that mayContinue() lets us - // output more content if we stopped due to needsMoreSpace() before - doNeedMoreData = !theIn->hasContent(); - - while (mayContinue()) { - (this->*theStep)(); - } - - return theStep == psMessageEnd; -} - -bool ChunkedCodingParser::needsMoreData() const -{ - return doNeedMoreData; -} - -bool ChunkedCodingParser::needsMoreSpace() const -{ - assert(theOut); - return theStep == psChunkBody && !theOut->hasPotentialSpace(); -} - -bool ChunkedCodingParser::mayContinue() const -{ - return !needsMoreData() && !needsMoreSpace() && theStep != psMessageEnd; -} - -void ChunkedCodingParser::parseChunkBeg() -{ - Must(theChunkSize <= 0); // Should(), really - - size_t crlfBeg = 0; - size_t crlfEnd = 0; - - if (findCrlf(crlfBeg, crlfEnd)) { - debugs(99,5, "found chunk-size end: " << crlfBeg << "-" << crlfEnd); - int size = -1; - const char *p = 0; - - if (StringToInt(theIn->content(), size, &p, 16)) { - if (size < 0) { - throw TexcHere("negative chunk size"); - return; - } - - // check for ieof chunk extension in the last-chunk - if (size == 0 && p && *p++ == ';') { - const char *e = theIn->content() + crlfBeg; // end of extension - - while (p < e && isspace(*p)) - ++p; // skip space - - sawIeof = e - p >= 4 && - strncmp(p, "ieof", 4) == 0 && - isspace(p[4]); - } - - theIn->consume(crlfEnd); - theChunkSize = theLeftBodySize = size; - debugs(99,5, "found chunk: " << theChunkSize); - theStep = theChunkSize == 0 ? psTrailer : psChunkBody; - return; - } - - throw TexcHere("corrupted chunk size"); - } - - doNeedMoreData = true; -} - -void ChunkedCodingParser::parseChunkBody() -{ - Must(theLeftBodySize > 0); // Should, really - - const size_t availSize = XMIN(theLeftBodySize, (size_t)theIn->contentSize()); - const size_t safeSize = XMIN(availSize, (size_t)theOut->potentialSpaceSize()); - - doNeedMoreData = availSize < theLeftBodySize; - // and we may also need more space - - theOut->append(theIn->content(), safeSize); - theIn->consume(safeSize); - theLeftBodySize -= safeSize; - - if (theLeftBodySize == 0) - theStep = psChunkEnd; - else - Must(needsMoreData() || needsMoreSpace()); -} - -void ChunkedCodingParser::parseChunkEnd() -{ - Must(theLeftBodySize == 0); // Should(), really - - size_t crlfBeg = 0; - size_t crlfEnd = 0; - - if (findCrlf(crlfBeg, crlfEnd)) { - if (crlfBeg != 0) { - throw TexcHere("found data bewteen chunk end and CRLF"); - return; - } - - theIn->consume(crlfEnd); - theChunkSize = 0; // done with the current chunk - theStep = psChunkBeg; - return; - } - - doNeedMoreData = true; -} - -void ChunkedCodingParser::parseTrailer() -{ - Must(theChunkSize == 0); // Should(), really - - while (mayContinue()) - parseTrailerHeader(); -} - -void ChunkedCodingParser::parseTrailerHeader() -{ - size_t crlfBeg = 0; - size_t crlfEnd = 0; - - if (findCrlf(crlfBeg, crlfEnd)) { - if (crlfBeg > 0) - - ; //theTrailer.append(theIn->content(), crlfEnd); - - theIn->consume(crlfEnd); - - if (crlfBeg == 0) - theStep = psMessageEnd; - - return; - } - - doNeedMoreData = true; -} - -void ChunkedCodingParser::parseMessageEnd() -{ - // termination step, should not be called - Must(false); // Should(), really -} - -// finds next CRLF -bool ChunkedCodingParser::findCrlf(size_t &crlfBeg, size_t &crlfEnd) -{ - // XXX: This code was copied, with permission, from another software. - // There is a similar and probably better code inside httpHeaderParse - // but it seems difficult to isolate due to parsing-unrelated bloat. - // Such isolation should probably be done before this class is used - // for handling of traffic "more external" than ICAP. - - const char *buf = theIn->content(); - size_t size = theIn->contentSize(); - - ssize_t crOff = -1; - bool quoted = false; - bool slashed = false; - - for (size_t i = 0; i < size; ++i) { - if (slashed) { - slashed = false; - continue; - } - - const char c = buf[i]; - - // handle quoted strings - if (quoted) { - if (c == '\\') - slashed = true; - else - if (c == '"') - quoted = false; - - continue; - } else - if (c == '"') { - quoted = true; - crOff = -1; - continue; - } - - if (crOff < 0) { // looking for the first CR or LF - - if (c == '\n') { - crlfBeg = i; - crlfEnd = ++i; - return true; - } - - if (c == '\r') - crOff = i; - } else { // skipping CRs, looking for the first LF - - if (c == '\n') { - crlfBeg = crOff; - crlfEnd = ++i; - return true; - } - - if (c != '\r') - crOff = -1; - } - } - - return false; -} - --- squid3/src/ChunkedCodingParser.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,90 +0,0 @@ - -/* - * $Id: ChunkedCodingParser.h,v 1.1.2.3 2005/09/01 20:58:10 rousskov Exp $ - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_CHUNKEDCODINGPARSER_H -#define SQUID_CHUNKEDCODINGPARSER_H - -#include "RefCount.h" - -// ChunkedCodingParser is an incremental parser for chunked transfer coding -// used by HTTP and ICAP. The parser shovels content bytes from the raw -// input buffer into the content output buffer, both caller-supplied. -// Ignores chunk extensions except for ICAP's ieof. -// Has a trailer-handling placeholder. - -class ChunkedCodingParser -{ - -public: - ChunkedCodingParser(); - - void reset(); - - // true = complete success; false == needs more data - bool parse(MemBuf *rawData, MemBuf *parsedContent); // throws on error - - bool needsMoreData() const; - bool needsMoreSpace() const; - bool sawIeof; // saw ieof chunk extension after a 0-size chunk - -private: - typedef void (ChunkedCodingParser::*Step)(); - -private: - bool mayContinue() const; - - void parseChunkBeg(); - void parseChunkBody(); - void parseChunkEnd(); - void parseTrailer(); - void parseTrailerHeader(); - void parseMessageEnd(); - - bool findCrlf(size_t &crlfBeg, size_t &crlfEnd); - -private: - static Step psChunkBeg; - static Step psChunkBody; - static Step psChunkEnd; - static Step psTrailer; - static Step psMessageEnd; - - MemBuf *theIn; - MemBuf *theOut; - - Step theStep; - size_t theChunkSize; - size_t theLeftBodySize; - bool doNeedMoreData; -}; - -#endif /* SQUID_CHUNKEDCODINGPARSER_H */ Index: squid3/src/ClientRequestContext.h =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/ClientRequestContext.h,v retrieving revision 1.1.2.2 retrieving revision 1.1.2.3 diff -u -r1.1.2.2 -r1.1.2.3 --- squid3/src/ClientRequestContext.h 3 Oct 2005 16:10:01 -0000 1.1.2.2 +++ squid3/src/ClientRequestContext.h 21 Nov 2005 21:05:51 -0000 1.1.2.3 @@ -1,5 +1,5 @@ #if ICAP_CLIENT -#include "ICAPServiceRep.h" +#include "ICAP/ICAPServiceRep.h" #endif class ClientRequestContext : public RefCountable --- squid3/src/ICAPAnchor.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,246 +0,0 @@ -#include "squid.h" -#include "http.h" -#include "MsgPipe.h" -#include "MsgPipeData.h" -#include "MsgPipeSource.h" -#include "MsgPipeSink.h" -#include "HttpRequest.h" -#include "HttpReply.h" -#include "ICAPAnchor.h" -#include "ICAPClient.h" -#include "ICAPServiceRep.h" - -#include "LeakFinder.h" - -CBDATA_CLASS_INIT(ICAPAnchor); - -extern LeakFinder *MsgPipeLeaker; - -ICAPAnchor::ICAPAnchor(ICAPServiceRep::Pointer aService): service(aService), httpState(NULL), virgin(NULL), adapted(NULL) -{ - debug(93,5)("ICAPAnchor constructed, this=%p\n", this); -} - -ICAPAnchor::~ICAPAnchor() -{ - stop(notifyNone); - cbdataReferenceDone(httpState); - debug(93,5)("ICAPAnchor destructed, this=%p\n", this); - - if (virgin != NULL) - freeVirgin(); - - if (adapted != NULL) - freeAdapted(); - - service = NULL; -} - -void ICAPAnchor::startRespMod(HttpStateData *anHttpState, HttpRequest *request, HttpReply *reply) -{ - httpState = cbdataReference(anHttpState); - - virgin = new MsgPipe("virgin"); // this is the place to create a refcount ptr - leakTouch(virgin.getRaw(), MsgPipeLeaker); - virgin->source = this; - virgin->data = new MsgPipeData; - virgin->data->cause = requestLink(request); - virgin->data->header = reply; - virgin->data->body = new MemBuf; - virgin->data->body->init(ICAP::MsgPipeBufSizeMin, ICAP::MsgPipeBufSizeMax); - - adapted = new MsgPipe("adapted"); - leakTouch(adapted.getRaw(), MsgPipeLeaker); - adapted->sink = this; -#if ICAP_ANCHOR_LOOPBACK - - adapted->data = new MsgPipeData; - adapted->data->cause = request; // should not hurt -#else - - ICAPInitXaction(service, virgin, adapted); -#endif - - virgin->sendSourceStart(); // we may have virgin data to provide - adapted->sendSinkNeed(); // we want adapted response, eventially -} - -void ICAPAnchor::sendMoreData(StoreIOBuffer buf) -{ - debug(93,5)("ICAPAnchor::sendMoreData() called\n"); - //buf.dump(); - /* - * The caller is responsible for not giving us more data - * than will fit in body MemBuf. Caller should use - * potentialSpaceSize() to find out how much we can hold. - */ - leakTouch(virgin.getRaw(), MsgPipeLeaker); - virgin->data->body->append(buf.data, buf.length); - virgin->sendSourceProgress(); -} - -int -ICAPAnchor::potentialSpaceSize() -{ - if (virgin == NULL) - return 0; - - leakTouch(virgin.getRaw(), MsgPipeLeaker); - - return (int) virgin->data->body->potentialSpaceSize(); -} - -// HttpStateData says we have the entire HTTP message -void ICAPAnchor::doneSending() -{ - debug(93,5)("ICAPAnchor::doneSending() called\n"); - -#if ICAP_ANCHOR_LOOPBACK - /* simple assignments are not the right way to do this */ - adapted->data->header = virgin->data->header; - adapted->data->body = virgin->data->body; - noteSourceFinish(adapted); - return; -#else - - leakTouch(virgin.getRaw(), MsgPipeLeaker); - virgin->sendSourceFinish(); -#endif -} - -// HttpStateData tells us to abort -void ICAPAnchor::ownerAbort() -{ - debug(93,5)("ICAPAnchor::ownerAbort() called\n"); - stop(notifyIcap); -} - -// ICAP client needs more virgin response data -void ICAPAnchor::noteSinkNeed(MsgPipe *p) -{ - debug(93,5)("ICAPAnchor::noteSinkNeed() called\n"); - - leakTouch(virgin.getRaw(), MsgPipeLeaker); - - if (virgin->data->body->potentialSpaceSize()) - httpState->icapSpaceAvailable(); -} - -// ICAP client aborting -void ICAPAnchor::noteSinkAbort(MsgPipe *p) -{ - debug(93,5)("ICAPAnchor::noteSinkAbort() called\n"); - stop(notifyOwner); -} - -// ICAP client starts sending adapted response -// ICAP client has received new HTTP headers (if any) at this point -void ICAPAnchor::noteSourceStart(MsgPipe *p) -{ - debug(93,5)("ICAPAnchor::noteSourceStart() called\n"); - leakTouch(adapted.getRaw(), MsgPipeLeaker); - - HttpReply *reply = dynamic_cast(adapted->data->header); - assert(reply); // check that ICAP xaction created the right object - httpState->takeAdaptedHeaders(reply); - - assert(reply == adapted->data->header); - adapted->data->header = NULL; - - noteSourceProgress(p); -} - -// ICAP client sends more data -void ICAPAnchor::noteSourceProgress(MsgPipe *p) -{ - debug(93,5)("ICAPAnchor::noteSourceProgress() called\n"); - //tell HttpStateData to store a fresh portion of the adapted response - - leakTouch(p, MsgPipeLeaker); - - if (p->data->body->hasContent()) { - httpState->takeAdaptedBody(p->data->body); - } -} - -// ICAP client is done sending adapted response -void ICAPAnchor::noteSourceFinish(MsgPipe *p) -{ - debug(93,5)("ICAPAnchor::noteSourceFinish() called\n"); - //tell HttpStateData that we expect no more response data - leakTouch(p, MsgPipeLeaker); - httpState->doneAdapting(); - stop(notifyNone); -} - -// ICAP client is aborting -void ICAPAnchor::noteSourceAbort(MsgPipe *p) -{ - debug(93,5)("ICAPAnchor::noteSourceAbort() called\n"); - leakTouch(p, MsgPipeLeaker); - stop(notifyOwner); -} - -// internal cleanup -void ICAPAnchor::stop(Notify notify) -{ - if (virgin != NULL) { - leakTouch(virgin.getRaw(), MsgPipeLeaker); - - if (notify == notifyIcap) - virgin->sendSourceAbort(); - else - virgin->source = NULL; - - freeVirgin(); - } - - if (adapted != NULL) { - leakTouch(adapted.getRaw(), MsgPipeLeaker); - - if (notify == notifyIcap) - adapted->sendSinkAbort(); - else - adapted->sink = NULL; - - freeAdapted(); - } - - if (httpState) { - if (notify == notifyOwner) - // tell HttpStateData that we are aborting prematurely - httpState->abortAdapting(); - - cbdataReferenceDone(httpState); - - // httpState is now NULL, will not call it any more - } -} - -void ICAPAnchor::freeVirgin() -{ - requestUnlink(virgin->data->cause); - virgin->data->cause = NULL; - virgin->data->header = NULL; - leakTouch(virgin.getRaw(), MsgPipeLeaker); - virgin = NULL; // refcounted -} - -void ICAPAnchor::freeAdapted() -{ - /* - * Note on adapted->data->header. ICAPXaction-side created it - * but gave control of it to us. Normally we give it to - * HttpStateData::takeAdaptedHeader. If not, we have to - * make sure it gets deleted; - */ - - if (adapted->data->header != NULL) { - debug(93,3)("hey, adapted->data->header is still set!\n"); - delete adapted->data->header; - adapted->data->header = NULL; - } - - leakTouch(adapted.getRaw(), MsgPipeLeaker); - adapted = NULL; // refcounted -} --- squid3/src/ICAPAnchor.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,92 +0,0 @@ - -/* - * $Id: ICAPAnchor.h,v 1.1.2.9 2005/09/29 18:00:44 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPANCHOR_H -#define SQUID_ICAPANCHOR_H - -#include "MsgPipe.h" -#include "MsgPipeSource.h" -#include "MsgPipeSink.h" -#include "ICAPServiceRep.h" - -/* The ICAP Anchor implements message pipe sink and source interfaces. It - * helps HttpStateData to marshall the incoming/virgin HTTP message (being - * recieved from the HTTP server) to Squid's ICAP client module, using the - * MsgPipe interface. The same interface is used to get the adapted HTTP - * message back from the ICAP client. HttpStateData is the "owner" of the - * ICAPAnchor. - */ - -class HttpRequest; - -class HttpReply; - -class ICAPAnchor: public MsgPipeSource, public MsgPipeSink -{ - -public: - ICAPAnchor(ICAPServiceRep::Pointer); - virtual ~ICAPAnchor(); - - // synchronous calls called by HttpStateData - void startRespMod(HttpStateData *anHttpState, HttpRequest *request, HttpReply *reply); - void sendMoreData(StoreIOBuffer buf); - void doneSending(); - void ownerAbort(); - int potentialSpaceSize(); /* how much data can we accept? */ - - // pipe source methods; called by ICAP while receiving the virgin message - virtual void noteSinkNeed(MsgPipe *p); - virtual void noteSinkAbort(MsgPipe *p); - - // pipe sink methods; called by ICAP while sending the adapted message - virtual void noteSourceStart(MsgPipe *p); - virtual void noteSourceProgress(MsgPipe *p); - virtual void noteSourceFinish(MsgPipe *p); - virtual void noteSourceAbort(MsgPipe *p); - -public: - ICAPServiceRep::Pointer service; - HttpStateData *httpState; - MsgPipe::Pointer virgin; - MsgPipe::Pointer adapted; - -private: - typedef enum { notifyNone, notifyOwner, notifyIcap } Notify; - void stop(Notify notify); - void freeVirgin(); - void freeAdapted(); - CBDATA_CLASS2(ICAPAnchor); -}; - -#endif /* SQUID_ICAPANCHOR_H */ --- squid3/src/ICAPClient.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,37 +0,0 @@ -#include "squid.h" -#include "ICAPModXact.h" -#include "ICAPClient.h" -#include "http.h" - -void ICAPInitModule() -{ - /* - * ICAP's MsgPipe buffer needs to be at least as large - * as the HTTP read buffer. Otherwise HTTP may take - * data from the network that won't fit into the MsgPipe, - * which leads to a runtime assertion. - */ - assert(ICAP::MsgPipeBufSizeMax >= SQUID_TCP_SO_RCVBUF); -} - -void ICAPCleanModule() -{} - -// initialize ICAP-specific ends of message pipes -void ICAPInitXaction(ICAPServiceRep::Pointer service, MsgPipe::Pointer virgin, MsgPipe::Pointer adapted) -{ - ICAPModXact::Pointer x = new ICAPModXact; - debugs(93,5, "ICAPInitXaction: " << x.getRaw()); - x->init(service, virgin, adapted, x); - // if we want to do something to the transaction after it is done, - // we need to keep a pointer to it -} - -// declared in ICAPModXact.h (ick?) -void ICAPNoteXactionDone(ICAPModXact::Pointer x) -{ - // nothing to be done here? - // refcounting will delete the transaction - // as soon as the last pointer to it is gone - debugs(93,5, "ICAPNoteXactionDone: " << x.getRaw()); -} --- squid3/src/ICAPClient.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,50 +0,0 @@ - -/* - * $Id: ICAPClient.h,v 1.1.2.8 2005/10/17 23:11:42 rousskov Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPCLIENT_H -#define SQUID_ICAPCLIENT_H - -#include "MsgPipe.h" // TODO: move; needed for ICAPInitXaction() -#include "ICAPServiceRep.h" // TODO: move; needed for ICAPInitXaction() - -// ICAP-related things needed by code unaware of ICAP internals. - -extern void ICAPInitModule(); -extern void ICAPCleanModule(); - -// let ICAP initialize ICAP-specific ends of message pipes - -class MsgPipe; -extern void ICAPInitXaction(ICAPServiceRep::Pointer, MsgPipe::Pointer virgin, MsgPipe::Pointer adapted); - -#endif /* SQUID_ICAPCLIENT_H */ --- squid3/src/ICAPClientSideHook.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,211 +0,0 @@ -#include "squid.h" -#include "client_side_request.h" -#include "ClientRequestContext.h" -#include "MsgPipe.h" -#include "MsgPipeData.h" -#include "MsgPipeSource.h" -#include "MsgPipeSink.h" -#include "HttpRequest.h" -#include "ICAPClientSideHook.h" -#include "ICAPServiceRep.h" -#include "ICAPClient.h" - -#include "LeakFinder.h" - -extern LeakFinder *MsgPipeLeaker; - -CBDATA_CLASS_INIT(ICAPClientSideHook); - -ICAPClientSideHook::ICAPClientSideHook(ICAPServiceRep::Pointer aService): service(aService), http(NULL), virgin(NULL), adapted(NULL) -{ - debug(93,3)("ICAPClientSideHook constructed, this=%p\n", this); -} - -ICAPClientSideHook::~ICAPClientSideHook() -{ - stop(notifyNone); - cbdataReferenceDone(http); - debug(93,3)("ICAPClientSideHook destructed, this=%p\n", this); - - if (virgin != NULL) - freeVirgin(); - - if (adapted != NULL) - freeAdapted(); -} - -void ICAPClientSideHook::startReqMod(ClientHttpRequest *aHttp, HttpRequest *request) -{ - debug(93,3)("ICAPClientSideHook::startReqMod() called\n"); - http = cbdataReference(aHttp); - - virgin = new MsgPipe("virgin"); // this is the place to create a refcount ptr - leakTouch(virgin.getRaw(), MsgPipeLeaker); - virgin->source = this; - virgin->data = new MsgPipeData; - virgin->data->cause = NULL; - virgin->data->header = requestLink(request); - virgin->data->body = new MemBuf; - virgin->data->body->init(ICAP::MsgPipeBufSizeMin, ICAP::MsgPipeBufSizeMax); - - adapted = new MsgPipe("adapted"); - leakTouch(adapted.getRaw(), MsgPipeLeaker); - adapted->sink = this; - - ICAPInitXaction(service, virgin, adapted); - - virgin->sendSourceStart(); // we may have virgin data to provide - adapted->sendSinkNeed(); // we want adapted response, eventially -} - -void ICAPClientSideHook::sendMoreData(StoreIOBuffer buf) -{ - debug(93,3)("ICAPClientSideHook::sendMoreData() called\n"); - //buf.dump(); - /* - * The caller is responsible for not giving us more data - * than will fit in body MemBuf. Caller should use - * potentialSpaceSize() to find out how much we can hold. - */ - leakTouch(virgin.getRaw(), MsgPipeLeaker); - virgin->data->body->append(buf.data, buf.length); - virgin->sendSourceProgress(); -} - -int -ICAPClientSideHook::potentialSpaceSize() -{ - if (virgin == NULL) - return 0; - - leakTouch(virgin.getRaw(), MsgPipeLeaker); - - return (int) virgin->data->body->potentialSpaceSize(); -} - -// ClientHttpRequest says we have the entire HTTP message -void ICAPClientSideHook::doneSending() -{ - debug(93,3)("ICAPClientSideHook::doneSending() called\n"); - leakTouch(virgin.getRaw(), MsgPipeLeaker); - - virgin->sendSourceFinish(); -} - -// ClientHttpRequest tells us to abort -void ICAPClientSideHook::ownerAbort() -{ - debug(93,3)("ICAPClientSideHook::ownerAbort() called\n"); - stop(notifyIcap); -} - -// ICAP client needs more virgin response data -void ICAPClientSideHook::noteSinkNeed(MsgPipe *p) -{ - debug(93,3)("ICAPClientSideHook::noteSinkNeed() called\n"); - - leakTouch(virgin.getRaw(), MsgPipeLeaker); - - if (virgin->data->body->potentialSpaceSize()) - http->icapSpaceAvailable(); -} - -// ICAP client aborting -void ICAPClientSideHook::noteSinkAbort(MsgPipe *p) -{ - debug(93,3)("ICAPClientSideHook::noteSinkAbort() called\n"); - stop(notifyOwner); -} - -// ICAP client starts sending adapted response -// ICAP client has received new HTTP headers (if any) at this point -void ICAPClientSideHook::noteSourceStart(MsgPipe *p) -{ - debug(93,3)("ICAPClientSideHook::noteSourceStart() called\n"); - leakTouch(adapted.getRaw(), MsgPipeLeaker); - http->takeAdaptedHeaders(adapted->data->header); - noteSourceProgress(p); -} - -// ICAP client sends more data -void ICAPClientSideHook::noteSourceProgress(MsgPipe *p) -{ - debug(93,3)("ICAPClientSideHook::noteSourceProgress() called\n"); - //tell ClientHttpRequest to store a fresh portion of the adapted response - - leakTouch(p, MsgPipeLeaker); - - if (p->data->body->hasContent()) { - http->takeAdaptedBody(p->data->body); - } -} - -// ICAP client is done sending adapted response -void ICAPClientSideHook::noteSourceFinish(MsgPipe *p) -{ - debug(93,3)("ICAPClientSideHook::noteSourceFinish() called\n"); - //tell ClientHttpRequest that we expect no more response data - leakTouch(p, MsgPipeLeaker); - http->doneAdapting(); - stop(notifyNone); -} - -// ICAP client is aborting -void ICAPClientSideHook::noteSourceAbort(MsgPipe *p) -{ - debug(93,3)("ICAPClientSideHook::noteSourceAbort() called\n"); - leakTouch(p, MsgPipeLeaker); - stop(notifyOwner); -} - -// internal cleanup -void ICAPClientSideHook::stop(Notify notify) -{ - if (virgin != NULL) { - leakTouch(virgin.getRaw(), MsgPipeLeaker); - - if (notify == notifyIcap) - virgin->sendSourceAbort(); - else - virgin->source = NULL; - - freeVirgin(); - } - - if (adapted != NULL) { - leakTouch(adapted.getRaw(), MsgPipeLeaker); - - if (notify == notifyIcap) - adapted->sendSinkAbort(); - else - adapted->sink = NULL; - - freeAdapted(); - } - - if (http) { - if (notify == notifyOwner) - // tell ClientHttpRequest that we are aborting prematurely - http->abortAdapting(); - - cbdataReferenceDone(http); - - // http is now NULL, will not call it any more - } -} - -void ICAPClientSideHook::freeVirgin() -{ - // virgin->data->cause should be NULL; - requestUnlink(dynamic_cast(virgin->data->header)); - virgin->data->header = NULL; - leakTouch(virgin.getRaw(), MsgPipeLeaker); - virgin = NULL; // refcounted -} - -void ICAPClientSideHook::freeAdapted() -{ - adapted->data->header = NULL; // we don't own it - leakTouch(adapted.getRaw(), MsgPipeLeaker); - adapted = NULL; // refcounted -} --- squid3/src/ICAPClientSideHook.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,91 +0,0 @@ - -/* - * $Id: ICAPClientSideHook.h,v 1.1.2.4 2005/10/03 16:10:01 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPCLIENTSIDEHOOK_H -#define SQUID_ICAPCLIENTSIDEHOOK_H - -#include "MsgPipe.h" -#include "MsgPipeSource.h" -#include "MsgPipeSink.h" - -/* The ICAP ClientSideHook implements message pipe sink and source interfaces. It - * helps client-side to marshall the incoming/virgin HTTP message (being - * recieved from the HTTP client) to Squid's ICAP client module, using the - * MsgPipe interface. The same interface is used to get the adapted HTTP - * message back from the ICAP client. client-side is the "owner" of the - * ICAPClientSideHook. - */ - -class HttpRequest; - -class ClientRequestContext; - -class ICAPClientSideHook: public MsgPipeSource, public MsgPipeSink -{ - -public: - ICAPClientSideHook(ICAPServiceRep::Pointer); - virtual ~ICAPClientSideHook(); - - // synchronous calls called by ClientHttpRequest - void startReqMod(ClientHttpRequest *, HttpRequest *); - void sendMoreData(StoreIOBuffer buf); - void doneSending(); - void ownerAbort(); - int potentialSpaceSize(); /* how much data can we accept? */ - - // pipe source methods; called by ICAP while receiving the virgin message - virtual void noteSinkNeed(MsgPipe *p); - virtual void noteSinkAbort(MsgPipe *p); - - // pipe sink methods; called by ICAP while sending the adapted message - virtual void noteSourceStart(MsgPipe *p); - virtual void noteSourceProgress(MsgPipe *p); - virtual void noteSourceFinish(MsgPipe *p); - virtual void noteSourceAbort(MsgPipe *p); - -public: - ICAPServiceRep::Pointer service; - ClientHttpRequest *http; - MsgPipe::Pointer virgin; - MsgPipe::Pointer adapted; - -private: - typedef enum { notifyNone, notifyOwner, notifyIcap } Notify; - void stop(Notify notify); - void freeVirgin(); - void freeAdapted(); - CBDATA_CLASS2(ICAPClientSideHook); -}; - -#endif /* SQUID_ICAPCLIENTSIDEHOOK_H */ --- squid3/src/ICAPConfig.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,380 +0,0 @@ - -/* - * $Id: ICAPConfig.cc,v 1.1.2.13 2005/11/04 20:29:06 dwsquid Exp $ - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - * - * Copyright (c) 2003, Robert Collins - */ - -#include "squid.h" - -#include "ConfigParser.h" -#include "ACL.h" -#include "Store.h" -#include "Array.h" // really Vector -#include "ICAPConfig.h" -#include "ICAPServiceRep.h" -#include "HttpRequest.h" -#include "ACLChecklist.h" - -ICAPConfig TheICAPConfig; - -ICAPServiceRep::Pointer -ICAPConfig::findService(const String& key) -{ - Vector::iterator iter = services.begin(); - - while (iter != services.end()) { - if (iter->getRaw()->key == key) - return *iter; - - ++iter; - } - - return NULL; -} - -ICAPClass * -ICAPConfig::findClass(const String& key) -{ - if (!key.size()) - return NULL; - - Vector::iterator iter = classes.begin(); - - while (iter != classes.end()) { - if ((*iter)->key == key) - return *iter; - - ++iter; - } - - return NULL; -} - -int -ICAPClass::prepare() -{ - int found = 0; - wordlist *service_names = NULL; - wordlist *iter; - - ConfigParser::ParseString(&key); - ConfigParser::ParseWordList(&service_names); - - for (iter = service_names; iter; iter = iter->next) { - ICAPServiceRep::Pointer match = TheICAPConfig.findService(iter->key); - - if (match != NULL) { - found = 1; - services += match; - } - } - - return found; -}; - -// ================================================================================ // - -CBDATA_CLASS_INIT(ICAPAccessCheck); - -ICAPAccessCheck::ICAPAccessCheck(ICAP::Method aMethod, - ICAP::VectPoint aPoint, - HttpRequest *aReq, - HttpReply *aRep, - ICAPAccessCheckCallback *aCallback, - void *aCallbackData) -{ - method = aMethod; - point = aPoint; - req = requestLink(aReq); - rep = aRep; - callback = aCallback; - callback_data = aCallbackData; - candidateClasses.clean(); - matchedClass.clean(); - acl_checklist = NULL; - debug(93,5)("ICAPAccessCheck constructed for %s %s\n", - ICAP::methodStr(method), - ICAP::vectPointStr(point)); -} - -ICAPAccessCheck::~ICAPAccessCheck() -{ - requestUnlink(req); -} - -/* - * Walk the ICAPAccess list and find all classes that have at least - * one service with matching method and vectoring point. - */ -void -ICAPAccessCheck::check() -{ - debug(93,3)("ICAPAccessCheck::check\n"); - Vector::iterator ci; - - for (ci = TheICAPConfig.classes.begin(); ci != TheICAPConfig.classes.end(); ++ci) { - - ICAPClass *theClass = *ci; - - Vector::iterator si; - - for (si = theClass->services.begin(); si != theClass->services.end(); ++si) { - ICAPServiceRep *theService = si->getRaw(); - - if (method != theService->method) - continue; - - if (point != theService->point) - continue; - - debug(93,1)("ICAPAccessCheck::check: class '%s' has candidate service '%s'\n", theClass->key.buf(), theService->key.buf()); - - candidateClasses += theClass->key; - - break; - } - } - - checkCandidates(); -} - -void -ICAPAccessCheck::checkCandidates() -{ - while (!candidateClasses.empty()) { - // It didn't really match yet, but we use the name anyway. - matchedClass = candidateClasses.shift(); - ICAPClass *theClass = TheICAPConfig.findClass(matchedClass); - - if (theClass == NULL) - // class apparently went away (reconfigure) - continue; - - // XXX we don't have access to conn->rfc931 here. - acl_checklist = aclChecklistCreate(theClass->accessList, req, dash_str); - - acl_checklist->nonBlockingCheck(ICAPAccessCheckCallbackWrapper, this); - - return; - } - - /* - * when there are no canidates, set matchedClass to NULL string - * and call the wrapper with answer = 1 - */ - debug(93,1)("ICAPAccessCheck::check: NO candidates or matches found\n"); - - matchedClass.clean(); - - ICAPAccessCheckCallbackWrapper(1, this); - - return; -} - -void -ICAPAccessCheck::ICAPAccessCheckCallbackWrapper(int answer, void *data) -{ - debug(93,5)("ICAPAccessCheckCallbackWrapper: answer=%d\n", answer); - ICAPAccessCheck *ac = (ICAPAccessCheck*)data; - - if (ac->matchedClass.size()) { - debug(93,5)("ICAPAccessCheckCallbackWrapper matchedClass = %s\n", - ac->matchedClass.buf()); - } - - if (!answer) { - ac->checkCandidates(); - return; - } - - /* - * We use an event here to break deep function call sequences - */ - eventAdd("ICAPAccessCheckCallbackEvent", - ICAPAccessCheckCallbackEvent, - ac, - 0.0, - 0, - true); -} - -void -ICAPAccessCheck::ICAPAccessCheckCallbackEvent(void *data) -{ - debug(93,5)("ICAPAccessCheckCallbackEvent\n"); - ICAPAccessCheck *ac = (ICAPAccessCheck*)data; - ac->do_callback(); - delete ac; -} - -void -ICAPAccessCheck::do_callback() -{ - debug(93,3)("ICAPAccessCheck::do_callback\n"); - - if (matchedClass.size()) { - debug(93,3)("ICAPAccessCheck::do_callback matchedClass = %s\n", matchedClass.buf()); - } - - ICAPClass *theClass = TheICAPConfig.findClass(matchedClass); - - if (theClass == NULL) { - callback(NULL, callback_data); - return; - } - - matchedClass.clean(); - - Vector::iterator i; - - for (i = theClass->services.begin(); i != theClass->services.end(); ++i) { - ICAPServiceRep *theService = i->getRaw(); - - if (method != theService->method) - continue; - - if (point != theService->point) - continue; - - callback(*i, callback_data); - - return; - } - - callback(NULL, callback_data); -} - -// ================================================================================ // - -void -ICAPConfig::parseICAPService() -{ - ICAPServiceRep::Pointer S = new ICAPServiceRep(); - - if (S->configure(S)) - services += S; - else - S->invalidate(); -}; - -void -ICAPConfig::freeICAPService() -{ - services.clean(); -}; - -void -ICAPConfig::dumpICAPService(StoreEntry *entry, const char *name) -{ - typedef Vector::iterator VI; - - for (VI i = services.begin(); i != services.end(); ++i) { - const ICAPServiceRep::Pointer &r = *i; - storeAppendPrintf(entry, "%s %s_%s %s %d %s\n", name, r->key.buf(), - r->methodStr(), r->vectPointStr(), r->bypass, r->uri.buf()); - } -}; - -void -ICAPConfig::parseICAPClass() -{ - ICAPClass *C = new ICAPClass(); - - if (C->prepare()) { - classes.push_back(C); - } else { - delete C; - } -}; - -void -ICAPConfig::freeICAPClass() -{ - classes.clean(); -}; - -void -ICAPConfig::dumpICAPClass(StoreEntry *entry, const char *name) -{ - Vector::iterator i = classes.begin(); - - while (i != classes.end()) { - storeAppendPrintf(entry, "%s %s\n", name, (*i)->key.buf()); - ++i; - } -}; - -void -ICAPConfig::parseICAPAccess() -{ - String aKey; - ConfigParser::ParseString(&aKey); - ICAPClass *theClass = TheICAPConfig.findClass(aKey); - - if (theClass == NULL) - fatalf("Did not find ICAP class '%s' referenced on line %d\n", - aKey.buf(), config_lineno); - - aclParseAccessLine(&theClass->accessList); -}; - -void -ICAPConfig::freeICAPAccess() -{ - (void) 0; -}; - -void -ICAPConfig::dumpICAPAccess(StoreEntry *entry, const char *name) -{ - LOCAL_ARRAY(char, nom, 64); - - Vector::iterator i = classes.begin(); - - while (i != classes.end()) { - snprintf(nom, 64, "%s %s", name, (*i)->key.buf()); - dump_acl_access(entry, nom, (*i)->accessList); - ++i; - } -}; - -ICAPConfig::~ICAPConfig() -{ - - // invalidate each service so that it can be deleted when refcount=0 - Vector::iterator si; - - for (si = services.begin(); si != services.end(); ++si) - (*si)->invalidate(); - - classes.clean(); - -}; --- squid3/src/ICAPConfig.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,125 +0,0 @@ - -/* - * $Id: ICAPConfig.h,v 1.1.2.8 2005/11/04 20:29:06 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - * - * Copyright (c) 2003, Robert Collins - */ - -#ifndef SQUID_ICAPCONFIG_H -#define SQUID_ICAPCONFIG_H - -#include "ICAPServiceRep.h" - -class acl_access; - -class ICAPConfig; - -class ICAPClass -{ - -public: - String key; - acl_access *accessList; - - Vector services; - - ICAPClass() : key(NULL) {}; - - int prepare(); -}; - -class ICAPAccessCheck -{ - -public: - typedef void ICAPAccessCheckCallback(ICAPServiceRep::Pointer match, void *data); - ICAPAccessCheck(ICAP::Method, ICAP::VectPoint, HttpRequest *, HttpReply *, ICAPAccessCheckCallback *, void *); - ~ICAPAccessCheck(); - -private: - ICAP::Method method; - ICAP::VectPoint point; - HttpRequest *req; - HttpReply *rep; - ICAPAccessCheckCallback *callback; - void *callback_data; - ACLChecklist *acl_checklist; - Vector candidateClasses; - String matchedClass; - void do_callback(); - -public: - void check(); - void checkCandidates(); - static void ICAPAccessCheckCallbackWrapper(int, void*); - static EVH ICAPAccessCheckCallbackEvent; - -private: - CBDATA_CLASS2(ICAPAccessCheck); -}; - -class ICAPConfig -{ - -public: - - int onoff; - int preview_enable; - int preview_size; - int check_interval; - int send_client_ip; - int auth_user; - char *auth_scheme; - - Vector services; - Vector classes; - - ICAPConfig() {}; - - ~ICAPConfig(); - - void parseICAPService(void); - void freeICAPService(void); - void dumpICAPService(StoreEntry *, const char *); - ICAPServiceRep::Pointer findService(const String&); - ICAPClass * ICAPConfig::findClass(const String& key); - - void parseICAPClass(void); - void freeICAPClass(void); - void dumpICAPClass(StoreEntry *, const char *); - - void parseICAPAccess(void); - void freeICAPAccess(void); - void dumpICAPAccess(StoreEntry *, const char *); - -}; - -#endif /* SQUID_ICAPCONFIG_H */ --- squid3/src/ICAPElements.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,52 +0,0 @@ -#include "squid.h" -#include "ICAPElements.h" - -const char *ICAP::crlf = "\r\n"; - - -const char * -ICAP::methodStr(ICAP::Method method) -{ - switch(method) { - - case ICAP::methodReqmod: - return "REQMOD"; - break; - - case ICAP::methodRespmod: - return "RESPMOD"; - break; - - case ICAP::methodOptions: - return "OPTIONS"; - break; - - default: - break; - } - - return "NONE"; -} - - -const char * -ICAP::vectPointStr(ICAP::VectPoint point) -{ - switch(point) { - - case ICAP::pointPreCache: - return "PRECACHE"; - break; - - case ICAP::pointPostCache: - return "POSTCACHE"; - break; - - default: - break; - } - - return "NONE"; -} - - --- squid3/src/ICAPElements.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,55 +0,0 @@ - -/* - * $Id: ICAPElements.h,v 1.1.2.2 2005/11/04 00:21:34 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPELEMENTS_H -#define SQUID_ICAPELEMENTS_H - -// ICAP-related things shared by many ICAP classes - -// A "fake" class to encapsulate ICAP-related declarations without -// adding namespaces to Squid. Eventually, namespaces should be added. - -struct ICAP -{ - typedef enum { methodNone, methodReqmod, methodRespmod, methodOptions } Method; - typedef enum { pointNone, pointPreCache, pointPostCache } VectPoint; - - // recommended initial size and max capacity for MsgPipe buffer - enum { MsgPipeBufSizeMin = (4*1024), MsgPipeBufSizeMax = SQUID_TCP_SO_RCVBUF }; - - static const char *crlf; - static const char *methodStr(ICAP::Method); - static const char *vectPointStr(ICAP::VectPoint); -}; - -#endif /* SQUID_ICAPCLIENT_H */ --- squid3/src/ICAPModXact.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,1201 +0,0 @@ -/* - * DEBUG: section 93 ICAP (RFC 3507) Client - */ - -#include "squid.h" -#include "comm.h" -#include "MsgPipe.h" -#include "MsgPipeData.h" -#include "HttpRequest.h" -#include "HttpReply.h" -#include "ICAPServiceRep.h" -#include "ICAPModXact.h" -#include "ICAPClient.h" -#include "ChunkedCodingParser.h" -#include "TextException.h" - -// flow and terminology: -// HTTP| --> receive --> encode --> write --> |network -// end | <-- send <-- parse <-- read <-- |end - -// TODO: doneSending()/doneReceving() data members should probably be in sync -// with this->adapted/virgin pointers. Make adapted/virgin methods? - -// TODO: replace gotEncapsulated() with something faster; we call it often - -CBDATA_CLASS_INIT(ICAPModXact); - -static const size_t TheBackupLimit = ICAP::MsgPipeBufSizeMax; - - -ICAPModXact::State::State() -{ - memset(this, sizeof(*this), 0); -} - -ICAPModXact::ICAPModXact(): ICAPXaction("ICAPModXact"), - self(NULL), virgin(NULL), adapted(NULL), - icapReply(NULL), virginConsumed(0), - bodyParser(NULL) -{} - -void ICAPModXact::init(ICAPServiceRep::Pointer &aService, MsgPipe::Pointer &aVirgin, MsgPipe::Pointer &anAdapted, Pointer &aSelf) -{ - assert(!self.getRaw() && !virgin.getRaw() && !adapted.getRaw()); - assert(aSelf.getRaw() && aVirgin.getRaw() && anAdapted.getRaw()); - - self = aSelf; - service(aService); - - virgin = aVirgin; - adapted = anAdapted; - - // receiving end - virgin->sink = this; // should be 'self' and refcounted - // virgin pipe data is initiated by the source - - // sending end - adapted->source = this; // should be 'self' and refcounted - adapted->data = new MsgPipeData; - - adapted->data->body = new MemBuf; // XXX: make body a non-pointer? - adapted->data->body->init(ICAP::MsgPipeBufSizeMin, ICAP::MsgPipeBufSizeMax); - // headers are initialized when we parse them - - // writing and reading ends are handled by ICAPXaction - - // encoding - // nothing to do because we are using temporary buffers - - // parsing - icapReply = new HttpReply; - icapReply->protoPrefix = "ICAP/"; // TODO: make an IcapReply class? - - // XXX: make sure stop() cleans all buffers -} - -// HTTP side starts sending virgin data -void ICAPModXact::noteSourceStart(MsgPipe *p) -{ - ICAPXaction_Enter(noteSourceStart); - - // make sure TheBackupLimit is in-sync with the buffer size - Must(TheBackupLimit <= static_cast(virgin->data->body->max_capacity)); - - estimateVirginBody(); // before virgin disappears! - - // it is an ICAP violation to send request to a service w/o known OPTIONS - - if (service().up()) - startWriting(); - else - waitForService(); - - // XXX: but this has to be here to catch other errors. Thus, if - // commConnectStart in startWriting fails, we may get here - //_after_ the object got destroyed. Somebody please fix commConnectStart! - ICAPXaction_Exit(); -} - -static -void ICAPModXact_noteServiceReady(void *data, ICAPServiceRep::Pointer &) -{ - ICAPModXact *x = static_cast(data); - assert(x); - x->noteServiceReady(); -} - -void ICAPModXact::waitForService() -{ - Must(!state.serviceWaiting); - debugs(93, 7, "ICAPModXact will wait for the ICAP service " << status()); - state.serviceWaiting = true; - service().callWhenReady(&ICAPModXact_noteServiceReady, this); -} - -void ICAPModXact::noteServiceReady() -{ - ICAPXaction_Enter(noteServiceReady); - - Must(state.serviceWaiting); - state.serviceWaiting = false; - startWriting(); // will throw if service is not up - - ICAPXaction_Exit(); -} - -void ICAPModXact::startWriting() -{ - Must(service().up()); - - state.writing = State::writingConnect; - openConnection(); - // put nothing here as openConnection calls commConnectStart - // and that may call us back without waiting for the next select loop -} - -// connection with the ICAP service established -void ICAPModXact::handleCommConnected() -{ - Must(state.writing == State::writingConnect); - - startReading(); // wait for early errors from the ICAP server - - MemBuf requestBuf; - requestBuf.init(); - - makeRequestHeaders(requestBuf); - debugs(93, 9, "ICAPModXact ICAP request prefix " << status() << ":\n" << - (requestBuf.terminate(), requestBuf.content())); - - // write headers - state.writing = State::writingHeaders; - scheduleWrite(requestBuf); -} - -void ICAPModXact::handleCommWrote(size_t) -{ - if (state.writing == State::writingHeaders) - handleCommWroteHeaders(); - else - handleCommWroteBody(); -} - -void ICAPModXact::handleCommWroteHeaders() -{ - Must(state.writing == State::writingHeaders); - - if (virginBody.expected()) { - state.writing = preview.enabled() ? - State::writingPreview : State::writingPrime; - virginWriteClaim.protectAll(); - writeMore(); - } else { - stopWriting(); - } -} - -void ICAPModXact::writeMore() -{ - if (writer) // already writing something - return; - - switch (state.writing) { - - case State::writingInit: // waiting for service OPTIONS - Must(state.serviceWaiting); - - case State::writingConnect: // waiting for the connection to establish - - case State::writingHeaders: // waiting for the headers to be written - - case State::writingPaused: // waiting for the ICAP server response - - case State::writingDone: // nothing more to write - return; - - case State::writingPreview: - writePriviewBody(); - return; - - case State::writingPrime: - writePrimeBody(); - return; - - default: - throw TexcHere("ICAPModXact in bad writing state"); - } -} - -void ICAPModXact::writePriviewBody() -{ - debugs(93, 8, "ICAPModXact will write Preview body " << status()); - Must(state.writing == State::writingPreview); - - MsgPipeData::Body *body = virgin->data->body; - const size_t size = XMIN(preview.debt(), (size_t)body->contentSize()); - writeSomeBody("preview body", size); - - // change state once preview is written - - if (preview.done()) { - debugs(93, 7, "ICAPModXact wrote entire Preview body " << status()); - - if (preview.ieof()) - stopWriting(); - else - state.writing = State::writingPaused; - } -} - -void ICAPModXact::writePrimeBody() -{ - Must(state.writing == State::writingPrime); - Must(virginWriteClaim.active()); - - MsgPipeData::Body *body = virgin->data->body; - const size_t size = body->contentSize(); - writeSomeBody("prime virgin body", size); - - if (state.doneReceiving) - stopWriting(); -} - -void ICAPModXact::writeSomeBody(const char *label, size_t size) -{ - Must(!writer && !state.doneWriting()); - debugs(93, 8, "ICAPModXact will write up to " << size << " bytes of " << - label); - - MemBuf writeBuf; // TODO: suggest a min size based on size and lastChunk - - writeBuf.init(); // note: we assume that last-chunk will fit - - const size_t writeableSize = claimSize(virginWriteClaim); - const size_t chunkSize = XMIN(writeableSize, size); - - if (chunkSize) { - debugs(93, 7, "ICAPModXact will write " << chunkSize << - "-byte chunk of " << label); - } else { - debugs(93, 7, "ICAPModXact has no writeable " << label << " content"); - } - - moveRequestChunk(writeBuf, chunkSize); - - const bool lastChunk = - (state.writing == State::writingPreview && preview.done()) || - (state.doneReceiving && claimSize(virginWriteClaim) <= 0); - - if (lastChunk && virginBody.expected()) { - debugs(93, 8, "ICAPModXact will write last-chunk of " << label); - addLastRequestChunk(writeBuf); - } - - debugs(93, 7, "ICAPModXact will write " << writeBuf.contentSize() - << " raw bytes of " << label); - - if (writeBuf.hasContent()) { - scheduleWrite(writeBuf); // comm will free the chunk - } else { - writeBuf.clean(); - } -} - -void ICAPModXact::moveRequestChunk(MemBuf &buf, size_t chunkSize) -{ - if (chunkSize > 0) { - openChunk(buf, chunkSize); - buf.append(claimContent(virginWriteClaim), chunkSize); - closeChunk(buf, false); - - virginWriteClaim.release(chunkSize); - virginConsume(); - } - - if (state.writing == State::writingPreview) - preview.wrote(chunkSize, state.doneReceiving); // even if wrote nothing -} - -void ICAPModXact::addLastRequestChunk(MemBuf &buf) -{ - openChunk(buf, 0); - closeChunk(buf, state.writing == State::writingPreview && preview.ieof()); -} - -void ICAPModXact::openChunk(MemBuf &buf, size_t chunkSize) -{ - buf.Printf("%x\r\n", chunkSize); -} - -void ICAPModXact::closeChunk(MemBuf &buf, bool ieof) -{ - if (ieof) - buf.append("; ieof", 6); - - buf.append(ICAP::crlf, 2); // chunk-terminating CRLF -} - -size_t ICAPModXact::claimSize(const MemBufClaim &claim) const -{ - Must(claim.active()); - const size_t start = claim.offset(); - const size_t end = virginConsumed + virgin->data->body->contentSize(); - Must(virginConsumed <= start && start <= end); - return end - start; -} - -const char *ICAPModXact::claimContent(const MemBufClaim &claim) const -{ - Must(claim.active()); - const size_t start = claim.offset(); - Must(virginConsumed <= start); - return virgin->data->body->content() + (start - virginConsumed); -} - -void ICAPModXact::virginConsume() -{ - MemBuf &buf = *virgin->data->body; - const size_t have = static_cast(buf.contentSize()); - const size_t end = virginConsumed + have; - size_t offset = end; - - if (virginWriteClaim.active()) - offset = XMIN(virginWriteClaim.offset(), offset); - - if (virginSendClaim.active()) - offset = XMIN(virginSendClaim.offset(), offset); - - Must(virginConsumed <= offset && offset <= end); - - if (const size_t size = offset - virginConsumed) { - debugs(93, 8, "ICAPModXact consumes " << size << " out of " << have << - " virgin body bytes"); - buf.consume(size); - virginConsumed += size; - - if (!state.doneReceiving) - virgin->sendSinkNeed(); - } -} - -void ICAPModXact::handleCommWroteBody() -{ - writeMore(); -} - -void ICAPModXact::stopWriting() -{ - if (state.writing == State::writingDone) - return; - - debugs(93, 7, "ICAPModXact will no longer write " << status()); - - state.writing = State::writingDone; - - virginWriteClaim.disable(); - - virginConsume(); - - // Comm does not have an interface to clear the writer, but - // writeMore() will not write if our write callback is called - // when state.writing == State::writingDone; -} - -void ICAPModXact::stopBackup() -{ - if (!virginSendClaim.active()) - return; - - debugs(93, 7, "ICAPModXact will no longer backup " << status()); - - virginSendClaim.disable(); - - virginConsume(); -} - -bool ICAPModXact::doneAll() const -{ - return ICAPXaction::doneAll() && !state.serviceWaiting && - state.doneReceiving && doneSending() && - doneReading() && state.doneWriting(); -} - -void ICAPModXact::startReading() -{ - Must(connection >= 0); - Must(!reader); - Must(adapted.getRaw()); - Must(adapted->data); - Must(adapted->data->body); - - // we use the same buffer for headers and body and then consume headers - readMore(); -} - -void ICAPModXact::readMore() -{ - if (reader || doneReading()) - return; - - // do not fill readBuf if we have no space to store the result - if (!adapted->data->body->hasPotentialSpace()) - return; - - if (readBuf.hasSpace()) - scheduleRead(); -} - -// comm module read a portion of the ICAP response for us -void ICAPModXact::handleCommRead(size_t) -{ - Must(!state.doneParsing()); - parseMore(); - readMore(); -} - -void ICAPModXact::echoMore() -{ - Must(state.sending == State::sendingVirgin); - Must(virginSendClaim.active()); - - MemBuf &from = *virgin->data->body; - MemBuf &to = *adapted->data->body; - - const size_t sizeMax = claimSize(virginSendClaim); - const size_t size = XMIN(static_cast(to.potentialSpaceSize()), - sizeMax); - debugs(93, 5, "ICAPModXact echos " << size << " out of " << sizeMax << - " bytes"); - - if (size > 0) { - to.append(claimContent(virginSendClaim), size); - virginSendClaim.release(size); - virginConsume(); - adapted->sendSourceProgress(); - } - - if (!from.hasContent() && state.doneReceiving) { - debugs(93, 5, "ICAPModXact echoed all " << status()); - stopSending(true); - } else { - debugs(93, 5, "ICAPModXact has " << from.contentSize() << " bytes " << - "and expects more to echo " << status()); - virgin->sendSinkNeed(); // TODO: timeout if sink is broken - } -} - -bool ICAPModXact::doneSending() const -{ - Must((state.sending == State::sendingDone) == (!adapted)); - return state.sending == State::sendingDone; -} - -void ICAPModXact::stopSending(bool nicely) -{ - if (doneSending()) - return; - - if (state.sending != State::sendingUndecided) { - debugs(93, 7, "ICAPModXact will no longer send " << status()); - - if (nicely) - adapted->sendSourceFinish(); - else - adapted->sendSourceAbort(); - } else { - debugs(93, 7, "ICAPModXact will not start sending " << status()); - adapted->sendSourceAbort(); // or the sink may wait forever - } - - state.sending = State::sendingDone; - - /* - * Note on adapted->data->header: we created the header, but allow the - * other side (ICAPAnchor) to take control of it. We won't touch it here - * and instead rely on the Anchor-side to make sure it is properly freed. - */ - adapted = NULL; // refcounted -} - -void ICAPModXact::stopReceiving() -{ - // stopSending NULLifies adapted but we do not NULLify virgin. - // This is assymetric because we want to keep virgin->data even - // though we are not expecting any more virgin->data->body. - // TODO: can we cache just the needed headers info instead? - - // If they closed first, there is not point (or means) to notify them. - - if (state.doneReceiving) - return; - - // There is no sendSinkFinished() to notify the other side. - debugs(93, 7, "ICAPModXact will not receive " << status()); - - state.doneReceiving = true; -} - -void ICAPModXact::parseMore() -{ - debugs(93, 5, "have " << readBuf.contentSize() << " bytes to parse" << - status()); - - if (state.parsingHeaders()) - parseHeaders(); - - if (state.parsing == State::psBody) - parseBody(); -} - -// note that allocation for echoing is done in handle204NoContent() -void ICAPModXact::maybeAllocateHttpMsg() -{ - if (adapted->data->header) // already allocated - return; - - if (gotEncapsulated("res-hdr")) { - adapted->data->header = new HttpReply; - } else if (gotEncapsulated("req-hdr")) { - adapted->data->header = new HttpRequest; - } else - throw TexcHere("Neither res-hdr nor req-hdr in maybeAllocateHttpMsg()"); -} - -void ICAPModXact::parseHeaders() -{ - Must(state.parsingHeaders()); - - if (state.parsing == State::psIcapHeader) - parseIcapHead(); - - if (state.parsing == State::psHttpHeader) - parseHttpHead(); - - if (state.parsingHeaders()) { // need more data - Must(mayReadMore()); - return; - } - - adapted->sendSourceStart(); - - if (state.sending == State::sendingVirgin) - echoMore(); -} - -void ICAPModXact::parseIcapHead() -{ - Must(state.sending == State::sendingUndecided); - - if (!parseHead(icapReply)) - return; - - switch (icapReply->sline.status) { - - case 100: - handle100Continue(); - break; - - case 200: - handle200Ok(); - break; - - case 204: - handle204NoContent(); - break; - - default: - handleUnknownScode(); - break; - } - - // handle100Continue() manages state.writing on its own. - // Non-100 status means the server needs no postPreview data from us. - if (state.writing == State::writingPaused) - stopWriting(); - - // TODO: Consider applying a Squid 2.5 patch to recognize 201 responses -} - -void ICAPModXact::handle100Continue() -{ - Must(state.writing == State::writingPaused); - Must(preview.enabled() && preview.done() && !preview.ieof()); - Must(virginSendClaim.active()); - - if (virginSendClaim.limited()) // preview only - stopBackup(); - - state.parsing = State::psHttpHeader; // eventually - - state.writing = State::writingPrime; - - writeMore(); -} - -void ICAPModXact::handle200Ok() -{ - state.parsing = State::psHttpHeader; - state.sending = State::sendingAdapted; - stopBackup(); -} - -void ICAPModXact::handle204NoContent() -{ - stopParsing(); - Must(virginSendClaim.active()); - virginSendClaim.protectAll(); // extends protection if needed - state.sending = State::sendingVirgin; - - // We want to clone the HTTP message, but we do not want - // to copy non-HTTP state parts that HttpMsg kids carry in them. - // Thus, we cannot use a smart pointer, copy constructor, or equivalent. - // Instead, we simply write the HTTP message and "clone" it by parsing. - - HttpMsg *oldHead = virgin->data->header; - debugs(93, 7, "ICAPModXact cloning virgin message " << oldHead); - - MemBuf httpBuf; - - // write the virgin message into a memory buffer - httpBuf.init(); - packHead(httpBuf, oldHead); - - // allocate the adapted message - HttpMsg *&newHead = adapted->data->header; - Must(!newHead); - - if (dynamic_cast(oldHead)) - newHead = new HttpRequest; - else - if (dynamic_cast(oldHead)) - newHead = new HttpReply; - - Must(newHead); - - // parse the buffer back - http_status error = HTTP_STATUS_NONE; - - Must(newHead->parse(&httpBuf, true, &error)); - - Must(newHead->hdr_sz == httpBuf.contentSize()); // no leftovers - - httpBuf.clean(); - - debugs(93, 7, "ICAPModXact cloned virgin message " << oldHead << " to " << newHead); -} - -void ICAPModXact::handleUnknownScode() -{ - stopParsing(); - stopBackup(); - // TODO: mark connection as "bad" - - // Terminate the transaction; we do not know how to handle this response. - throw TexcHere("Unsupported ICAP status code"); -} - -void ICAPModXact::parseHttpHead() -{ - if (gotEncapsulated("res-hdr") || gotEncapsulated("req-hdr")) { - maybeAllocateHttpMsg(); - - if (!parseHead(adapted->data->header)) - return; - } - - state.parsing = State::psBody; -} - -bool ICAPModXact::parseHead(HttpMsg *head) -{ - assert(head); - debugs(93, 5, "have " << readBuf.contentSize() << " head bytes to parse" << - "; state: " << state.parsing); - - http_status error = HTTP_STATUS_NONE; - const bool parsed = head->parse(&readBuf, commEof, &error); - Must(parsed || !error); // success or need more data - - if (!parsed) { // need more data - head->reset(); - return false; - } - - readBuf.consume(head->hdr_sz); - return true; -} - -void ICAPModXact::parseBody() -{ - Must(state.parsing == State::psBody); - - debugs(93, 5, "have " << readBuf.contentSize() << " body bytes to parse"); - - if (gotEncapsulated("res-body")) { - if (!parsePresentBody()) // need more body data - return; - } else { - debugs(93, 5, "not expecting a body"); - } - - stopParsing(); - stopSending(true); -} - -// returns true iff complete body was parsed -bool ICAPModXact::parsePresentBody() -{ - if (!bodyParser) - bodyParser = new ChunkedCodingParser; - - // the parser will throw on errors - const bool parsed = bodyParser->parse(&readBuf, adapted->data->body); - - adapted->sendSourceProgress(); // TODO: do not send if parsed nothing - - debugs(93, 5, "have " << readBuf.contentSize() << " body bytes after " << - "parse; parsed all: " << parsed); - - if (parsed) - return true; - - if (bodyParser->needsMoreData()) - Must(mayReadMore()); - - if (bodyParser->needsMoreSpace()) { - Must(!doneSending()); // can hope for more space - Must(adapted->data->body->hasContent()); // paranoid - // TODO: there should be a timeout in case the sink is broken. - } - - return false; -} - -void ICAPModXact::stopParsing() -{ - if (state.parsing == State::psDone) - return; - - debugs(93, 7, "ICAPModXact will no longer parse " << status()); - - delete bodyParser; - - bodyParser = NULL; - - state.parsing = State::psDone; -} - -// HTTP side added virgin body data -void ICAPModXact::noteSourceProgress(MsgPipe *p) -{ - ICAPXaction_Enter(noteSourceProgress); - - Must(!state.doneReceiving); - writeMore(); - - if (state.sending == State::sendingVirgin) - echoMore(); - - ICAPXaction_Exit(); -} - -// HTTP side sent us all virgin info -void ICAPModXact::noteSourceFinish(MsgPipe *p) -{ - ICAPXaction_Enter(noteSourceFinish); - - Must(!state.doneReceiving); - stopReceiving(); - - // push writer and sender in case we were waiting for the last-chunk - writeMore(); - - if (state.sending == State::sendingVirgin) - echoMore(); - - ICAPXaction_Exit(); -} - -// HTTP side is aborting -void ICAPModXact::noteSourceAbort(MsgPipe *p) -{ - ICAPXaction_Enter(noteSourceAbort); - - Must(!state.doneReceiving); - stopReceiving(); - mustStop("HTTP source quit"); - - ICAPXaction_Exit(); -} - -// HTTP side wants more adapted data and possibly freed some buffer space -void ICAPModXact::noteSinkNeed(MsgPipe *p) -{ - ICAPXaction_Enter(noteSinkNeed); - - if (state.sending == State::sendingVirgin) - echoMore(); - else - if (state.sending == State::sendingAdapted) - parseMore(); - else - Must(state.sending == State::sendingUndecided); - - ICAPXaction_Exit(); -} - -// HTTP side aborted -void ICAPModXact::noteSinkAbort(MsgPipe *p) -{ - ICAPXaction_Enter(noteSinkAbort); - - mustStop("HTTP sink quit"); - - ICAPXaction_Exit(); -} - -// internal cleanup -void ICAPModXact::doStop() -{ - ICAPXaction::doStop(); - - stopWriting(); - stopBackup(); - - if (icapReply) { - delete icapReply; - icapReply = NULL; - } - - stopSending(false); - - // see stopReceiving() for reasons it cannot NULLify virgin there - - if (virgin != NULL) { - if (!state.doneReceiving) - virgin->sendSinkAbort(); - else - virgin->sink = NULL; - - virgin = NULL; // refcounted - } - - if (self != NULL) { - Pointer s = self; - self = NULL; - ICAPNoteXactionDone(s); - /* this object may be destroyed when 's' is cleared */ - } -} - -void ICAPModXact::makeRequestHeaders(MemBuf &buf) -{ - const ICAPServiceRep &s = service(); - buf.Printf("%s %s ICAP/1.0\r\n", s.methodStr(), s.uri.buf()); - buf.Printf("Host: %s:%d\r\n", s.host.buf(), s.port); - buf.Printf("Encapsulated: "); - - MemBuf httpBuf; - httpBuf.init(); - - // build HTTP request header, if any - ICAP::Method m = s.method; - - if (ICAP::methodRespmod == m && virgin->data->cause) - encapsulateHead(buf, "req-hdr", httpBuf, virgin->data->cause); - else if (ICAP::methodReqmod == m) - encapsulateHead(buf, "req-hdr", httpBuf, virgin->data->header); - - if (ICAP::methodRespmod == m) - if (const MsgPipeData::Header *prime = virgin->data->header) - encapsulateHead(buf, "res-hdr", httpBuf, prime); - - if (!virginBody.expected()) - buf.Printf("null-body=%d", httpBuf.contentSize()); - else if (ICAP::methodReqmod == m) - buf.Printf("req-body=%d", httpBuf.contentSize()); - else - buf.Printf("res-body=%d", httpBuf.contentSize()); - - buf.append(ICAP::crlf, 2); // terminate Encapsulated line - - if (shouldPreview()) { - buf.Printf("Preview: %d\r\n", (int)preview.ad()); - virginSendClaim.protectUpTo(preview.ad()); - } - - if (shouldAllow204()) { - buf.Printf("Allow: 204\r\n"); - // be robust: do not rely on the expected body size - virginSendClaim.protectAll(); - } - - buf.append(ICAP::crlf, 2); // terminate ICAP header - - // start ICAP request body with encapsulated HTTP headers - buf.append(httpBuf.content(), httpBuf.contentSize()); - - httpBuf.clean(); -} - -void ICAPModXact::encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const HttpMsg *head) -{ - // update ICAP header - icapBuf.Printf("%s=%d,", section, httpBuf.contentSize()); - - // pack HTTP head - packHead(httpBuf, head); -} - -void ICAPModXact::packHead(MemBuf &httpBuf, const HttpMsg *head) -{ - Packer p; - packerToMemInit(&p, &httpBuf); - head->packInto(&p, true); - packerClean(&p); -} - -// decides whether to offer a preview and calculates its size -bool ICAPModXact::shouldPreview() -{ - size_t wantedSize; - - if (!service().wantsPreview(wantedSize)) { - debugs(93, 5, "ICAPModXact should not offer preview"); - return false; - } - - Must(wantedSize >= 0); - - // cannot preview more than we can backup - size_t ad = XMIN(wantedSize, TheBackupLimit); - - if (virginBody.expected() && virginBody.knownSize()) - ad = XMIN(ad, virginBody.size()); // not more than we have - else - ad = 0; // questionable optimization? - - debugs(93, 5, "ICAPModXact should offer " << ad << "-byte preview " << - "(service wanted " << wantedSize << ")"); - - preview.enable(ad); - - return preview.enabled(); -} - -// decides whether to allow 204 responses -bool ICAPModXact::shouldAllow204() -{ - if (!service().allows204()) - return false; - - if (!virginBody.expected()) - return true; // no body means no problems with supporting 204s. - - // if there is a body, make sure we can backup it all - - if (!virginBody.knownSize()) - return false; - - // or should we have a different backup limit? - // note that '<' allows for 0-termination of the "full" backup buffer - return virginBody.size() < TheBackupLimit; -} - -// returns a temporary string depicting transaction status, for debugging -void ICAPModXact::fillPendingStatus(MemBuf &buf) const -{ - if (state.serviceWaiting) - buf.append("U", 1); - - if (!state.doneWriting() && state.writing != State::writingInit) - buf.Printf("w(%d)", state.writing); - - if (preview.enabled()) { - if (!preview.done()) - buf.Printf("P(%d)", preview.debt()); - } - - if (virginSendClaim.active()) - buf.append("B", 1); - - if (!state.doneParsing() && state.parsing != State::psIcapHeader) - buf.Printf("p(%d)", state.parsing); - - if (!doneSending() && state.sending != State::sendingUndecided) - buf.Printf("S(%d)", state.sending); -} - -void ICAPModXact::fillDoneStatus(MemBuf &buf) const -{ - if (state.doneReceiving) - buf.append("R", 1); - - if (state.doneWriting()) - buf.append("w", 1); - - if (preview.enabled()) { - if (preview.done()) - buf.Printf("P%s", preview.ieof() ? "(ieof)" : ""); - } - - if (doneReading()) - buf.append("r", 1); - - if (state.doneParsing()) - buf.append("p", 1); - - if (doneSending()) - buf.append("S", 1); -} - -bool ICAPModXact::gotEncapsulated(const char *section) const -{ - return httpHeaderGetByNameListMember(&icapReply->header, "Encapsulated", - section, ',').size() > 0; -} - -// calculate whether there is a virgin HTTP body and -// whether its expected size is known -void ICAPModXact::estimateVirginBody() -{ - // note: defaults should be fine but will disable previews and 204s - - Must(virgin != NULL && virgin->data->header); - - method_t method; - - if (virgin->data->cause) - method = virgin->data->cause->method; - else - if (HttpRequest *req= dynamic_cast(virgin->data-> - header)) - method = req->method; - else - return; - - ssize_t size; - if (virgin->data->header->expectingBody(method, size)) { - virginBody.expect(size) - ; - debugs(93, 6, "ICAPModXact expects virgin body; size: " << size); - } else { - debugs(93, 6, "ICAPModXact does not expect virgin body"); - } -} - - -// TODO: Move SizedEstimate, MemBufBackup, and ICAPPreview elsewhere - -SizedEstimate::SizedEstimate() - : theData(dtUnexpected) -{} - -void SizedEstimate::expect(ssize_t aSize) -{ - theData = (aSize >= 0) ? aSize : (ssize_t)dtUnknown; -} - -bool SizedEstimate::expected() const -{ - return theData != dtUnexpected; -} - -bool SizedEstimate::knownSize() const -{ - Must(expected()); - return theData != dtUnknown; -} - -size_t SizedEstimate::size() const -{ - Must(knownSize()); - return static_cast(theData); -} - - - -MemBufClaim::MemBufClaim(): theStart(-1), theGoal(-1) -{} - -void MemBufClaim::protectAll() -{ - if (theStart < 0) - theStart = 0; - - theGoal = -1; // no specific goal -} - -void MemBufClaim::protectUpTo(size_t aGoal) -{ - if (theStart < 0) - theStart = 0; - - Must(aGoal >= 0); - - theGoal = (theGoal < 0) ? static_cast(aGoal) : - XMIN(static_cast(aGoal), theGoal); -} - -void MemBufClaim::disable() -{ - theStart = -1; -} - -void MemBufClaim::release(size_t size) -{ - Must(active()); - Must(size >= 0); - theStart += static_cast(size); - - if (limited() && theStart >= theGoal) - disable(); -} - -size_t MemBufClaim::offset() const -{ - Must(active()); - return static_cast(theStart); -} - -bool MemBufClaim::limited() const -{ - Must(active()); - return theGoal >= 0; -} - - -ICAPPreview::ICAPPreview(): theWritten(0), theAd(0), theState(stDisabled) -{} - -void ICAPPreview::enable(size_t anAd) -{ - // TODO: check for anAd not exceeding preview size limit - Must(anAd >= 0); - Must(!enabled()); - theAd = anAd; - theState = stWriting; -} - -bool ICAPPreview::enabled() const -{ - return theState != stDisabled; -} - -size_t ICAPPreview::ad() const -{ - Must(enabled()); - return theAd; -} - -bool ICAPPreview::done() const -{ - Must(enabled()); - return theState >= stIeof; -} - -bool ICAPPreview::ieof() const -{ - Must(enabled()); - return theState == stIeof; -} - -size_t ICAPPreview::debt() const -{ - Must(enabled()); - return done() ? 0 : (theAd - theWritten); -} - -void ICAPPreview::wrote(size_t size, bool sawEof) -{ - Must(enabled()); - theWritten += size; - - if (theWritten >= theAd) - theState = stDone; // sawEof is irrelevant - else - if (sawEof) - theState = stIeof; -} - --- squid3/src/ICAPModXact.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,279 +0,0 @@ - -/* - * $Id: ICAPModXact.h,v 1.1.2.1 2005/10/17 22:58:29 rousskov Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPMODXACT_H -#define SQUID_ICAPMODXACT_H - -#include "ICAPXaction.h" -#include "MsgPipe.h" -#include "MsgPipeSource.h" -#include "MsgPipeSink.h" - -/* ICAPModXact implements ICAP REQMOD and RESPMOD transaction using ICAPXaction - * as the base. It implements message pipe sink and source interfaces for - * communication with various HTTP "anchors" and "hooks". ICAPModXact receives - * virgin HTTP messages, communicates with the ICAP server, and sends the - * adapted messages back. ICAPClient is the "owner" of the ICAPModXact. */ - -class ChunkedCodingParser; - -// estimated future presence and size of something (e.g., HTTP body) - -class SizedEstimate -{ - -public: - SizedEstimate(); // not expected by default - void expect(ssize_t aSize); // expect with any, even unknown size - bool expected() const; - - /* other members can be accessed iff expected() */ - - bool knownSize() const; - size_t size() const; // can be accessed iff knownSize() - -private: - enum { dtUnexpected = -2, dtUnknown = -1 }; - ssize_t theData; // combines expectation and size info to save RAM -}; - -// Protects buffer area. If area size is unknown, protects buffer suffix. -// Only "released" data can be consumed by the caller. Used to maintain -// write, preview, and 204 promises for ICAPModXact virgin->data-body buffer. - -class MemBufClaim -{ - -public: - MemBufClaim(); - - void protectAll(); - void protectUpTo(size_t aGoal); - void disable(); - bool active() const { return theStart >= 0; } - - // methods below require active() - - void release(size_t size); // stop protecting size more bytes - size_t offset() const; // protected area start - bool limited() const; // protects up to a known size goal - -private: - ssize_t theStart; // left area border - ssize_t theGoal; // "end" maximum, if any -}; - -// maintains preview-related sizes - -class ICAPPreview -{ - -public: - ICAPPreview(); // disabled - void enable(size_t anAd); // enabled with advertised size - bool enabled() const; - - /* other members can be accessed iff enabled() */ - - size_t ad() const; // advertised preview size - size_t debt() const; // remains to write - bool done() const; // wrote everything - bool ieof() const; // premature EOF - - void wrote(size_t size, bool sawEof); - -private: - size_t theWritten; - size_t theAd; - enum State { stDisabled, stWriting, stIeof, stDone } theState; -}; - -class ICAPModXact: public ICAPXaction, public MsgPipeSource, public MsgPipeSink -{ - -public: - typedef RefCount Pointer; - -public: - ICAPModXact(); - - // called by ICAPClient - void init(ICAPServiceRep::Pointer&, MsgPipe::Pointer &aVirgin, MsgPipe::Pointer &anAdapted, Pointer &aSelf); - - // pipe source methods; called by Anchor while receiving the adapted msg - virtual void noteSinkNeed(MsgPipe *p); - virtual void noteSinkAbort(MsgPipe *p); - - // pipe sink methods; called by ICAP while sending the virgin message - virtual void noteSourceStart(MsgPipe *p); - virtual void noteSourceProgress(MsgPipe *p); - virtual void noteSourceFinish(MsgPipe *p); - virtual void noteSourceAbort(MsgPipe *p); - - // comm handlers - virtual void handleCommConnected(); - virtual void handleCommWrote(size_t size); - virtual void handleCommRead(size_t size); - void handleCommWroteHeaders(); - void handleCommWroteBody(); - - // service waiting - void noteServiceReady(); - -private: - void estimateVirginBody(); - - void waitForService(); - - // will not send anything [else] on the adapted pipe - bool doneSending() const; - - void startWriting(); - void writeMore(); - void writePriviewBody(); - void writePrimeBody(); - void writeSomeBody(const char *label, size_t size); - - void startReading(); - void readMore(); - virtual bool doneReading() const { return commEof || state.doneParsing(); } - - size_t claimSize(const MemBufClaim &claim) const; - const char *claimContent(const MemBufClaim &claim) const; - void makeRequestHeaders(MemBuf &buf); - void moveRequestChunk(MemBuf &buf, size_t chunkSize); - void addLastRequestChunk(MemBuf &buf); - void openChunk(MemBuf &buf, size_t chunkSize); - void closeChunk(MemBuf &buf, bool ieof); - void virginConsume(); - - bool shouldPreview(); - bool shouldAllow204(); - void prepBackup(size_t expectedSize); - void backup(const MemBuf &buf); - - void parseMore(); - - void parseHeaders(); - void parseIcapHead(); - void parseHttpHead(); - bool parseHead(HttpMsg *head); - - void parseBody(); - bool parsePresentBody(); - void maybeAllocateHttpMsg(); - - void handle100Continue(); - void handle200Ok(); - void handle204NoContent(); - void handleUnknownScode(); - - void echoMore(); - - virtual bool doneAll() const; - - virtual void doStop(); - void stopReceiving(); - void stopSending(bool nicely); - void stopWriting(); - void stopParsing(); - void stopBackup(); - - virtual void fillPendingStatus(MemBuf &buf) const; - virtual void fillDoneStatus(MemBuf &buf) const; - -private: - void packHead(MemBuf &httpBuf, const HttpMsg *head); - void encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const HttpMsg *head); - bool gotEncapsulated(const char *section) const; - - Pointer self; - MsgPipe::Pointer virgin; - MsgPipe::Pointer adapted; - - HttpReply *icapReply; - - SizedEstimate virginBody; - MemBufClaim virginWriteClaim; // preserve virgin data buffer for writing - MemBufClaim virginSendClaim; // ... for sending (previe and 204s) - size_t virginConsumed; // virgin data consumed so far - ICAPPreview preview; // use for creating (writing) the preview - - ChunkedCodingParser *bodyParser; // ICAP response body parser - - class State - { - - public: - State(); - - public: - - unsigned serviceWaiting: - 1; // waiting for the ICAPServiceRep preparing the ICAP service - - unsigned doneReceiving: - 1; // expect no new virgin info (from the virgin pipe) - - // will not write anything [else] to the ICAP server connection - bool doneWriting() const { return writing == writingDone; } - - // parsed entire ICAP response from the ICAP server - bool doneParsing() const { return parsing == psDone; } - - // is parsing ICAP or HTTP headers read from the ICAP server - bool parsingHeaders() const - { - return parsing == psIcapHeader || - parsing == psHttpHeader; - } - - enum Parsing { psIcapHeader, psHttpHeader, psBody, psDone } parsing; - - // measures ICAP request writing progress - enum Writing { writingInit, writingConnect, writingHeaders, - writingPreview, writingPaused, writingPrime, writingDone } writing; - - enum Sending { sendingUndecided, sendingVirgin, sendingAdapted, - sendingDone } sending; - } - - state; - - CBDATA_CLASS2(ICAPModXact); -}; - -// destroys the transaction; implemented in ICAPClient.cc (ick?) -extern void ICAPNoteXactionDone(ICAPModXact::Pointer x); - -#endif /* SQUID_ICAPMOD_XACT_H */ --- squid3/src/ICAPOptXact.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,111 +0,0 @@ -/* - * DEBUG: section 93 ICAP (RFC 3507) Client - */ - -#include "squid.h" -#include "comm.h" -#include "HttpReply.h" - -#include "ICAPOptXact.h" -#include "ICAPOptions.h" -#include "TextException.h" - -CBDATA_CLASS_INIT(ICAPOptXact); - -ICAPOptXact::ICAPOptXact(): ICAPXaction("ICAPOptXact"), options(NULL), - cb(NULL), cbData(NULL) - -{ - debug(93,9)("ICAPOptXact constructed, this=%p\n", this); -} - -ICAPOptXact::~ICAPOptXact() -{ - Must(!options); // the caller must set to NULL - debug(93,9)("ICAPOptXact destructed, this=%p\n", this); -} - -void ICAPOptXact::start(ICAPServiceRep::Pointer &aService, Callback *aCb, void *aCbData) -{ - service(aService); - - Must(!cb && aCb && aCbData); - cb = aCb; - cbData = cbdataReference(aCbData); - - openConnection(); -} - -void ICAPOptXact::handleCommConnected() -{ - scheduleRead(); - - MemBuf requestBuf; - requestBuf.init(); - makeRequest(requestBuf); - debugs(93, 9, "ICAPOptXact request " << status() << ":\n" << - (requestBuf.terminate(), requestBuf.content())); - - scheduleWrite(requestBuf); -} - -void ICAPOptXact::doStop() -{ - ICAPXaction::doStop(); - - if (Callback *call = cb) { - cb = NULL; - void *data = NULL; - - if (cbdataReferenceValidDone(cbData, &data)) { - (*call)(this, data); // will delete us - return; - } - } - - // get rid of options if we did call the callback - delete options; - - options = NULL; -} - -void ICAPOptXact::makeRequest(MemBuf &buf) -{ - const ICAPServiceRep &s = service(); - buf.Printf("OPTIONS %s ICAP/1.0\r\n", s.uri.buf()); - buf.Printf("Host: %s:%d\r\n", s.host.buf(), s.port); - buf.append(ICAP::crlf, 2); -} - -void ICAPOptXact::handleCommWrote(size_t size) -{ - debugs(93, 9, "ICAPOptXact finished writing " << size << - "-byte request " << status()); -} - -// comm module read a portion of the ICAP response for us -void ICAPOptXact::handleCommRead(size_t) -{ - if (parseResponse()) - Must(done()); // there should be nothing else to do - else - scheduleRead(); -} - -bool ICAPOptXact::parseResponse() -{ - HttpReply *r = new HttpReply; - r->protoPrefix = "ICAP/"; // TODO: make an IcapReply class? - - if (!parseHttpMsg(r)) { - delete r; - return false; - } - - options = new ICAPOptions; - - options->configure(r); - - delete r; - return true; -} --- squid3/src/ICAPOptXact.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,77 +0,0 @@ -/* - * $Id: ICAPOptXact.h,v 1.1.2.3 2005/10/17 22:58:29 rousskov Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPOPTXACT_H -#define SQUID_ICAPOPTXACT_H - -#include "ICAPXaction.h" - -class ICAPOptions; - -/* ICAPOptXact sends an ICAP OPTIONS request to the ICAP service, - * converts the response into ICAPOptions object, and notifies - * the caller via the callback. NULL options objects means the - * ICAP service could not be contacted or did not return any response */ - -class ICAPOptXact: public ICAPXaction -{ - -public: - typedef void Callback(ICAPOptXact*, void *data); - - ICAPOptXact(); - virtual ~ICAPOptXact(); - - void start(ICAPServiceRep::Pointer &aService, Callback *aCb, void *aCbData); - - ICAPOptions *options; // result for the caller to take/handle - -protected: - virtual void handleCommConnected(); - virtual void handleCommWrote(size_t size); - virtual void handleCommRead(size_t size); - - void makeRequest(MemBuf &buf); - bool parseResponse(); - - void startReading(); - - virtual void doStop(); - -private: - Callback *cb; - void *cbData; - - CBDATA_CLASS2(ICAPOptXact); -}; - -#endif /* SQUID_ICAPOPTXACT_H */ --- squid3/src/ICAPOptions.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,182 +0,0 @@ -#include "squid.h" -#include "HttpReply.h" -#include "ICAPOptions.h" -#include "TextException.h" - -ICAPOptions::ICAPOptions(): error("unconfigured"), method(ICAP::methodNone), - max_connections(-1), allow204(false), - preview(-1), ttl(-1), transfer_ext(NULL) -{ - transfers.preview = transfers.ignore = transfers.complete = NULL; - transfers.other = TRANSFER_NONE; -}; - -ICAPOptions::~ICAPOptions() -{ - delete transfers.preview; - delete transfers.ignore; - delete transfers.complete; - delete transfer_ext; -}; - -ICAPOptions::transfer_type ICAPOptions::getTransferExt(const char *s) -{ - - if (transfer_ext) { - List *data = transfer_ext; - - while (data) { - if (*(data->element.ext) == *s) { - return data->element.type; - } - - data = data->next; - } - } - - return TRANSFER_NONE; -} - -#if UNUSED_CODE -void ICAPOptions::insertTransferExt(const char *t, transfer_type t_type) -{ - List **Tail; - TransferPair t_ext; - - if (t == "*") { - transfers.other = t_type; - return; - } - - for (Tail = &transfer_ext; *Tail; Tail = &((*Tail)->next)) { - if (*(*Tail)->element.ext == *t) { - (*Tail)->element.type = t_type; - return; - } - } - - t_ext.ext = xstrdup(t); - t_ext.type = t_type; - List *q = new List(t_ext); - *(Tail) = q; - -}; - -List *ICAPOptions::parseExtFileList(const char *start, const char *end, transfer_type t_type) -{ - const String s = xstrndup(start, end - start - 1); - const char *item; - const char *pos = NULL; - char *fext = NULL; - int ilen; - String t = NULL; - - List **Tail; - List *H; - - for (Tail = &H; *Tail; Tail = &((*Tail)->next)) - - ; - while (strListGetItem(&s, ',', &item, &ilen, &pos)) { - fext = xstrndup(item, ilen + 1); - t = fext; - List *q = new List (t); - *(Tail) = q; - Tail = &q->next; - insertTransferExt(fext, t_type); - } - - return H; -} - -#endif - -bool ICAPOptions::valid() const -{ - return !error; -} - -bool ICAPOptions::fresh() const -{ - return squid_curtime <= expire(); -} - -time_t ICAPOptions::expire() const -{ - Must(valid()); - return ttl >= 0 ? timestamp + ttl : -1; -} - -void ICAPOptions::configure(const HttpReply *reply) -{ - error = NULL; // reset initial "unconfigured" value (or an old error?) - - const HttpHeader *h = &reply->header; - - if (reply->sline.status != 200) - error = "unsupported status code of OPTIONS response"; - - // Methods - if (httpHeaderGetByNameListMember(h, "Methods", "REQMOD", ',').size()) - cfgMethod(ICAP::methodReqmod); - - if (httpHeaderGetByNameListMember(h, "Methods", "RESPMOD", ',').size()) - cfgMethod(ICAP::methodRespmod); - - service = httpHeaderGetByName(h, "Service"); - - serviceId = httpHeaderGetByName(h, "ServiceId"); - - istag = httpHeaderGetByName(h, "ISTag"); - - if (httpHeaderGetByName(h, "Opt-body-type").size()) - error = "ICAP service returns unsupported OPTIONS body"; - - cfgIntHeader(h, "Max-Connections", max_connections); - - cfgIntHeader(h, "Options-TTL", ttl); - - timestamp = httpHeaderGetTime(h, HDR_DATE); - - if (timestamp < 0) - timestamp = squid_curtime; - - if (httpHeaderGetByNameListMember(h, "Allow", "204", ',').size()) - allow204 = true; - - cfgIntHeader(h, "Preview", preview); - -#if 0 - - if (!strncasecmp("Transfer-Preview", start, 16)) - headers->transfer_preview = parseExtFileList(value_start, end, TRANSFER_PREVIEW); - - if (!strncasecmp("Transfer-Ignore", start, 15)) - headers->transfer_ignore = parseExtFileList(value_start, end, TRANSFER_IGNORE); - - if (!strncasecmp("Transfer-Complete", start, 17)) - headers->transfer_complete = parseExtFileList(value_start, end, TRANSFER_COMPLETE); - -#endif -} - -void ICAPOptions::cfgMethod(ICAP::Method m) -{ - Must(m != ICAP::methodNone); - - if (method == ICAP::methodNone) - method = m; - else - error = "the service claims to support several request methods"; -} - -// TODO: HttpHeader should provide a general method for this type of conversion -void ICAPOptions::cfgIntHeader(const HttpHeader *h, const char *fname, int &value) -{ - const String s = httpHeaderGetByName(h, fname); - - if (s.size() && xisdigit(*s.buf())) - value = atoi(s.buf()); - else - value = -1; -} --- squid3/src/ICAPOptions.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,111 +0,0 @@ - -/* - * $Id: ICAPOptions.h,v 1.1.2.6 2005/11/03 00:28:58 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPOPTIONS_H -#define SQUID_ICAPOPTIONS_H - -#include "squid.h" -#include "List.h" -#include "ICAPClient.h" - -/* Maintains options supported by a given ICAP service. - * See RFC 3507, Section "4.10.2 OPTIONS Response". */ - -class ICAPOptions -{ - -public: - typedef void GetCallback(void *data, ICAPOptions *options); - static void Get(ICAPServiceRep::Pointer &service, GetCallback *cb, void *data); - -public: - ICAPOptions(); - ~ICAPOptions(); - - void configure(const HttpReply *reply); - - bool valid() const; - bool fresh() const; - time_t expire() const; - - typedef enum { TRANSFER_NONE, TRANSFER_PREVIEW, TRANSFER_IGNORE, TRANSFER_COMPLETE } transfer_type; - transfer_type getTransferExt(const char *); - -public: - const char *error; // human-readable information; set iff !valid() - - // ICAP server MUST supply this info - ICAP::Method method; - String istag; - - // ICAP server MAY supply this info. If not, Squid supplies defaults. - String service; - String serviceId; - int max_connections; - bool allow204; - int preview; - - // varios Transfer-* lists - - struct Transfers - { - List *preview; - List *ignore; - List *complete; - transfer_type other; // default X from Transfer-X: * - } - - transfers; - -protected: - int ttl; - time_t timestamp; - - // The list of pairs "file extension <-> transfer type" - - struct TransferPair - { - char *ext; - transfer_type type; - }; - - List *transfer_ext; - -private: - void cfgMethod(ICAP::Method m); - void cfgIntHeader(const HttpHeader *h, const char *fname, int &value); -}; - - - -#endif /* SQUID_ICAPOPTIONS_H */ --- squid3/src/ICAPServiceRep.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,382 +0,0 @@ -/* - * DEBUG: section 93 ICAP (RFC 3507) Client - */ - -#include "squid.h" -#include "TextException.h" -#include "ICAPServiceRep.h" -#include "ICAPOptions.h" -#include "ICAPOptXact.h" -#include "ConfigParser.h" - -CBDATA_CLASS_INIT(ICAPServiceRep); - -ICAPServiceRep::ICAPServiceRep(): method(ICAP::methodNone), - point(ICAP::pointNone), port(-1), bypass(false), unreachable(false), - theOptions(NULL), theState(stateInit), notifying(false), self(NULL) -{ -} - -ICAPServiceRep::~ICAPServiceRep() -{ - Must(!waiting()); - changeOptions(0); -} - -const char * -ICAPServiceRep::methodStr() const -{ - return ICAP::methodStr(method); -} - -ICAP::Method -ICAPServiceRep::parseMethod(const char *str) const -{ - if (!strncasecmp(str, "REQMOD", 6)) - return ICAP::methodReqmod; - - if (!strncasecmp(str, "RESPMOD", 7)) - return ICAP::methodRespmod; - - return ICAP::methodNone; -} - - -const char * -ICAPServiceRep::vectPointStr() const -{ - return ICAP::vectPointStr(point); -} - -ICAP::VectPoint -ICAPServiceRep::parseVectPoint(const char *service) const -{ - const char *t = service; - const char *q = strchr(t, '_'); - - if (q) - t = q + 1; - - if (!strcasecmp(t, "precache")) - return ICAP::pointPreCache; - - if (!strcasecmp(t, "postcache")) - return ICAP::pointPostCache; - - return ICAP::pointNone; -} - -bool -ICAPServiceRep::configure(Pointer &aSelf) -{ - assert(!self && aSelf != NULL); - self = aSelf; - - char *service_type = NULL; - - ConfigParser::ParseString(&key); - ConfigParser::ParseString(&service_type); - ConfigParser::ParseBool(&bypass); - ConfigParser::ParseString(&uri); - - debug(3, 5) ("ICAPService::parseConfigLine (line %d): %s %s %d\n", config_lineno, key.buf(), service_type, bypass); - - method = parseMethod(service_type); - point = parseVectPoint(service_type); - - debug(3, 5) ("ICAPService::parseConfigLine (line %d): service is %s_%s\n", config_lineno, methodStr(), vectPointStr()); - - if (uri.cmp("icap://", 7) != 0) { - debug(3, 0) ("ICAPService::parseConfigLine (line %d): wrong uri: %s\n", config_lineno, uri.buf()); - return false; - } - - const char *s = uri.buf() + 7; - - const char *e; - - bool have_port = false; - - if ((e = strchr(s, ':')) != NULL) { - have_port = true; - } else if ((e = strchr(s, '/')) != NULL) { - have_port = false; - } else { - return false; - } - - int len = e - s; - host.limitInit(s, len); - s = e; - - if (have_port) { - s++; - - if ((e = strchr(s, '/')) != NULL) { - char *t; - port = strtoul(s, &t, 0) % 65536; - - if (t != e) { - return false; - } - - s = e; - - if (s[0] != '/') { - return false; - } - } - } else { - - struct servent *serv = getservbyname("icap", "tcp"); - - if (serv) { - port = htons(serv->s_port); - } else { - port = 1344; - } - } - - s++; - e = strchr(s, '\0'); - len = e - s; - - if (len > 1024) { - debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); - } - - resource.limitInit(s, len + 1); - - if ((bypass != 0) && (bypass != 1)) { - return false; - } - - return true; - -}; - -void ICAPServiceRep::invalidate() -{ - assert(self != NULL); - self = NULL; // may destroy us and, hence, invalidate cbdata(this) - // TODO: it would be nice to invalidate cbdata(this) when not destroyed -} - -bool ICAPServiceRep::up() const -{ - return self != NULL && theState == stateUp; -} - -bool ICAPServiceRep::wantsPreview(size_t &wantedSize) const -{ - Must(up()); - - if (theOptions->preview < 0) - return false; - - wantedSize = theOptions->preview; - - return true; -} - -bool ICAPServiceRep::allows204() const -{ - Must(up()); - return true; // in the future, we may have ACLs to prevent 204s -} - - -static -void ICAPServiceRep_noteTimeToUpdate(void *data) -{ - ICAPServiceRep *service = static_cast(data); - Must(service); - service->noteTimeToUpdate(); -} - -void ICAPServiceRep::noteTimeToUpdate() -{ - if (!self || waiting()) { - debugs(93,5, "ICAPService ignores options update " << status()); - return; - } - - debugs(93,5, "ICAPService performs a regular options update " << status()); - startGettingOptions(); -} - -static -void ICAPServiceRep_noteTimeToNotify(void *data) -{ - ICAPServiceRep *service = static_cast(data); - Must(service); - service->noteTimeToNotify(); -} - -void ICAPServiceRep::noteTimeToNotify() -{ - Must(!notifying); - notifying = true; - debugs(93,7, "ICAPService notifies " << theClients.size() << " clients " << - status()); - - // note: we must notify even if we are invalidated - - Pointer us = NULL; - - while (!theClients.empty()) { - Client i = theClients.pop_back(); - us = i.service; // prevent callbacks from destroying us while we loop - - if (cbdataReferenceValid(i.data)) - (*i.callback)(i.data, us); - - cbdataReferenceDone(i.data); - } - - notifying = false; -} - -void ICAPServiceRep::callWhenReady(Callback *cb, void *data) -{ - Must(cb); - Must(self != NULL); - - Client i; - i.service = self; - i.callback = cb; - i.data = cbdataReference(data); - theClients.push_back(i); - - if (waiting() || notifying) - return; // do nothing, we will be picked up in noteTimeToNotify() - - if (needNewOptions()) - startGettingOptions(); - else - scheduleNotification(); -} - -void ICAPServiceRep::scheduleNotification() -{ - debugs(93,7, "ICAPService will notify " << theClients.size() << " clients"); - eventAdd("ICAPServiceRep::noteTimeToNotify", &ICAPServiceRep_noteTimeToNotify, this, 0, 0, true); -} - -bool ICAPServiceRep::waiting() const -{ - return theState == stateWait; -} - -bool ICAPServiceRep::needNewOptions() const -{ - return !theOptions || !theOptions->fresh(); -} - -void ICAPServiceRep::changeOptions(ICAPOptions *newOptions) -{ - debugs(93,9, "ICAPService changes options from " << theOptions << " to " << - newOptions); - delete theOptions; - theOptions = newOptions; -} - -static -void ICAPServiceRep_noteNewOptions(ICAPOptXact *x, void *data) -{ - ICAPServiceRep *service = static_cast(data); - Must(service); - service->noteNewOptions(x); -} - -void ICAPServiceRep::noteNewOptions(ICAPOptXact *x) -{ - Must(x); - Must(waiting()); - - theState = stateDown; // default in case we fail to set new options - - changeOptions(x->options); - x->options = NULL; - delete x; - - if (theOptions && theOptions->valid()) - theState = stateUp; - - debugs(93,6, "ICAPService got new options and is now " << - (up() ? "up" : "down")); - - scheduleUpdate(); - - scheduleNotification(); -} - -void ICAPServiceRep::startGettingOptions() -{ - debugs(93,6, "ICAPService will get new options " << status()); - theState = stateWait; - - ICAPOptXact *x = new ICAPOptXact; - x->start(self, &ICAPServiceRep_noteNewOptions, this); - // TODO: timeout incase ICAPOptXact never calls us back? -} - -void ICAPServiceRep::scheduleUpdate() -{ - int delay = -1; - - if (theOptions && theOptions->valid()) { - const time_t expire = theOptions->expire(); - - if (expire > squid_curtime) - delay = expire - squid_curtime; - else - if (expire >= 0) - delay = 1; // delay for expired or 'expiring now' options - else - delay = 60*60; // default for options w/o known expiration time - } else { - delay = 5*60; // delay for a down service - } - - if (delay <= 0) { - debugs(93,0, "internal error: ICAPServiceRep failed to compute options update schedule"); - delay = 5*60; // delay for an internal error - } - - // with zero delay, the state changes to stateWait before - // notifications are sent out to clients - assert(delay > 0); - - debugs(93,7, "ICAPService will update options in " << delay << " sec"); - - eventAdd("ICAPServiceRep::noteTimeToUpdate", - &ICAPServiceRep_noteTimeToUpdate, this, delay, 0, true); - - // XXX: prompt updates of valid options should not disable concurrent ICAP - // xactions. 'Wait' state should not mark the service 'down'! This will - // also remove 'delay == 0' as a special case above. -} - -const char *ICAPServiceRep::status() const -{ - if (!self) - return "[invalidated]"; - - switch (theState) { - - case stateInit: - return "[init]"; - - case stateWait: - return "[wait]"; - - case stateUp: - return "[up]"; - - case stateDown: - return "[down]"; - } - - return "[unknown]"; -} --- squid3/src/ICAPServiceRep.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,138 +0,0 @@ - -/* - * $Id: ICAPServiceRep.h,v 1.1.2.7 2005/10/17 23:30:37 rousskov Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPSERVICEREP_H -#define SQUID_ICAPSERVICEREP_H - -#include "ICAPElements.h" - -class ICAPOptions; - -class ICAPOptXact; - -/* The ICAP service representative maintains information about a single ICAP - service that Squid communicates with. The representative initiates OPTIONS - requests to the service to keep cached options fresh. One ICAP server may - host many ICAP services */ - -class ICAPServiceRep : public RefCountable -{ - -public: - typedef RefCount Pointer; - -public: - ICAPServiceRep(); - virtual ~ICAPServiceRep(); - - bool configure(Pointer &aSelf); // needs self pointer for ICAPOptXact - void invalidate(); // call when the service is no longer needed or valid - - const char *methodStr() const; - const char *vectPointStr() const; - - bool up() const; - - /* Service is "up" iff there is a fresh cached OPTIONS response. To - get an OPTIONS response, ICAPServiceRep does an OPTIONS - transaction. Failed transaction results in a "down" service. The - Callback is called if/once the service is in a steady ("up" or - "down") state. */ - typedef void Callback(void *data, Pointer &service); - void callWhenReady(Callback *cb, void *data); - - - // the methods below can only be called on an up() service - - bool wantsPreview(size_t &wantedSize) const; - bool allows204() const; - -public: - String key; - ICAP::Method method; - ICAP::VectPoint point; - String uri; // service URI - - // URI components - String host; - int port; - String resource; - - // non-options flags; TODO: check that both are used. - bool bypass; - bool unreachable; - -public: // treat these as private, they are for callbacks only - void noteTimeToUpdate(); - void noteTimeToNotify(); - void noteNewOptions(ICAPOptXact *x); - -private: - // stores Prepare() callback info - - struct Client - { - Pointer service; // one for each client to preserve service - Callback *callback; - void *data; - }; - - typedef Vector Clients; - Clients theClients; // all clients waiting for a call back - - ICAPOptions *theOptions; - - typedef enum { stateInit, stateWait, stateUp, stateDown } State; - State theState; - bool notifying; // may be true in any state except for the initial - -private: - ICAP::Method parseMethod(const char *) const; - ICAP::VectPoint parseVectPoint(const char *) const; - - bool waiting() const; - bool needNewOptions() const; - - void scheduleNotification(); - void changeOptions(ICAPOptions *newOptions); - void startGettingOptions(); - void scheduleUpdate(); - - const char *status() const; - - Pointer self; - CBDATA_CLASS2(ICAPServiceRep); -}; - - -#endif /* SQUID_ICAPSERVICEREP_H */ --- squid3/src/ICAPXaction.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,400 +0,0 @@ -/* - * DEBUG: section 93 ICAP (RFC 3507) Client - */ - -#include "squid.h" -#include "comm.h" -#include "HttpReply.h" -#include "ICAPXaction.h" -#include "ICAPClient.h" -#include "TextException.h" - -/* comm module handlers (wrappers around corresponding ICAPXaction methods */ - -// TODO: Teach comm module to call object methods directly - -//CBDATA_CLASS_INIT(ICAPXaction); - -static -ICAPXaction &ICAPXaction_fromData(void *data) -{ - ICAPXaction *x = static_cast(data); - assert(x); - return *x; -} - -static -void ICAPXaction_noteCommTimedout(int, void *data) -{ - ICAPXaction_fromData(data).noteCommTimedout(); -} - -static -void ICAPXaction_noteCommClosed(int, void *data) -{ - ICAPXaction_fromData(data).noteCommClosed(); -} - -static -void ICAPXaction_noteCommConnected(int, comm_err_t status, int xerrno, void *data) -{ - ICAPXaction_fromData(data).noteCommConnected(status); -} - -static -void ICAPXaction_noteCommWrote(int, char *, size_t size, comm_err_t status, void *data) -{ - ICAPXaction_fromData(data).noteCommWrote(status, size); -} - -static -void ICAPXaction_noteCommRead(int, char *, size_t size, comm_err_t status, int xerrno, void *data) -{ - ICAPXaction_fromData(data).noteCommRead(status, size); -} - -ICAPXaction::ICAPXaction(const char *aTypeName): - connection(-1), - commBuf(NULL), commBufSize(0), - commEof(false), - connector(NULL), reader(NULL), writer(NULL), closer(NULL), - typeName(aTypeName), - theService(NULL), - inCall(NULL) -{ - readBuf.init(SQUID_TCP_SO_RCVBUF, SQUID_TCP_SO_RCVBUF); - commBuf = (char*)memAllocBuf(SQUID_TCP_SO_RCVBUF, &commBufSize); - // make sure maximum readBuf space does not exceed commBuf size - Must(static_cast(readBuf.potentialSpaceSize()) <= commBufSize); -} - -ICAPXaction::~ICAPXaction() -{ - doStop(); - readBuf.clean(); - memFreeBuf(commBufSize, commBuf); -} - -// TODO: obey service-specific, OPTIONS-reported connection limit -void ICAPXaction::openConnection() -{ - const ICAPServiceRep &s = service(); - // TODO: check whether NULL domain is appropriate here - connection = pconnPop(s.host.buf(), s.port, NULL); - - if (connection < 0) { - connection = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, - COMM_NONBLOCKING, s.uri.buf()); - - if (connection < 0) - throw TexcHere("cannot connect to ICAP service " /* + uri */); - } - - debugs(93,3, typeName << " opens connection to " << s.host.buf() << ":" << s.port); - - commSetTimeout(connection, Config.Timeout.connect, - &ICAPXaction_noteCommTimedout, this); - - closer = &ICAPXaction_noteCommClosed; - comm_add_close_handler(connection, closer, this); - - connector = &ICAPXaction_noteCommConnected; - commConnectStart(connection, s.host.buf(), s.port, connector, this); -} - -void ICAPXaction::closeConnection() -{ - if (connection >= 0) { - commSetTimeout(connection, -1, NULL, NULL); - - if (closer) { - comm_remove_close_handler(connection, closer, this); - closer = NULL; - } - - cancelRead(); - - comm_close(connection); - - connector = NULL; - connection = -1; - } -} - -// connection with the ICAP service established -void ICAPXaction::noteCommConnected(comm_err_t commStatus) -{ - ICAPXaction_Enter(noteCommConnected); - - Must(connector); - connector = NULL; - Must(commStatus == COMM_OK); - - handleCommConnected(); - - ICAPXaction_Exit(); -} - -void ICAPXaction::scheduleWrite(MemBuf &buf) -{ - // comm module will free the buffer - writer = &ICAPXaction_noteCommWrote; - comm_old_write_mbuf(connection, &buf, writer, this); -} - -void ICAPXaction::noteCommWrote(comm_err_t commStatus, size_t size) -{ - ICAPXaction_Enter(noteCommWrote); - - Must(writer); - writer = NULL; - - Must(commStatus == COMM_OK); - - handleCommWrote(size); - - ICAPXaction_Exit(); -} - -// communication timeout with the ICAP service -void ICAPXaction::noteCommTimedout() -{ - ICAPXaction_Enter(noteCommTimedout); - - handleCommTimedout(); - - ICAPXaction_Exit(); -} - -void ICAPXaction::handleCommTimedout() -{ - mustStop("connection with ICAP service timed out"); -} - -// unexpected connection close while talking to the ICAP service -void ICAPXaction::noteCommClosed() -{ - closer = NULL; - ICAPXaction_Enter(noteCommClosed); - - handleCommClosed(); - - ICAPXaction_Exit(); -} - -void ICAPXaction::handleCommClosed() -{ - mustStop("ICAP service connection externally closed"); -} - -bool ICAPXaction::done() const -{ - if (stopReason != NULL) // mustStop() has been called - return true; - - return doneAll(); -} - -bool ICAPXaction::doneAll() const -{ - return !connector && !reader && !writer; -} - -void ICAPXaction::scheduleRead() -{ - Must(connection >= 0); - Must(!reader); - Must(readBuf.hasSpace()); - - reader = &ICAPXaction_noteCommRead; - /* - * See comments in ICAPXaction.h about why we use commBuf - * here instead of reading directly into readBuf.buf. - */ - - comm_read(connection, commBuf, readBuf.spaceSize(), reader, this); -} - -// comm module read a portion of the ICAP response for us -void ICAPXaction::noteCommRead(comm_err_t commStatus, size_t sz) -{ - ICAPXaction_Enter(noteCommRead); - - Must(reader); - reader = NULL; - - Must(commStatus == COMM_OK); - Must(sz >= 0); - - debugs(93, 5, "read " << sz << " bytes"); - - /* - * See comments in ICAPXaction.h about why we use commBuf - * here instead of reading directly into readBuf.buf. - */ - - if (sz > 0) - readBuf.append(commBuf, sz); - else - commEof = true; - - handleCommRead(sz); - - ICAPXaction_Exit(); -} - -void ICAPXaction::cancelRead() -{ - if (reader) { - // check callback presence because comm module removes - // fdc_table[].read.callback after the actual I/O but - // before we get the callback via a queued event. - // These checks try to mimic the comm_read_cancel() assertions. - - if (comm_has_pending_read(connection) && - !comm_has_pending_read_callback(connection)) - comm_read_cancel(connection, reader, this); - - reader = NULL; - } -} - -bool ICAPXaction::parseHttpMsg(HttpMsg *msg) -{ - debugs(93, 5, "have " << readBuf.contentSize() << " head bytes to parse"); - - http_status error = HTTP_STATUS_NONE; - const bool parsed = msg->parse(&readBuf, commEof, &error); - Must(parsed || !error); // success or need more data - - if (!parsed) { // need more data - Must(mayReadMore()); - msg->reset(); - return false; - } - - readBuf.consume(msg->hdr_sz); - return true; -} - -bool ICAPXaction::mayReadMore() const -{ - return !doneReading() && // will read more data - readBuf.hasSpace(); // have space for more data -} - -bool ICAPXaction::doneReading() const -{ - return commEof; -} - -void ICAPXaction::mustStop(const char *aReason) -{ - Must(inCall); // otherwise nobody will call doStop() - Must(!stopReason); - Must(aReason); - stopReason = aReason; - debugs(93, 5, typeName << " will stop, reason: " << stopReason); -} - -// internal cleanup -void ICAPXaction::doStop() -{ - debugs(93, 5, typeName << "::doStop " << status()); - - closeConnection(); // TODO: pconn support: close iff bad connection -} - -void ICAPXaction::service(ICAPServiceRep::Pointer &aService) -{ - Must(!theService); - Must(aService != NULL); - theService = aService; -} - -ICAPServiceRep &ICAPXaction::service() -{ - Must(theService != NULL); - return *theService; -} - -bool ICAPXaction::callStart(const char *method) -{ - debugs(93, 5, typeName << "::" << method << " called " << status()); - - if (inCall) { - // this may happen when we have bugs or when arguably buggy - // comm interface calls us while we are closing the connection - debugs(93, 5, typeName << "::" << inCall << " is in progress; " << - typeName << "::" << method << " cancels reentry."); - return false; - } - - inCall = method; - return true; -} - -void ICAPXaction::callException(const TextException &e) -{ - debugs(93, 4, typeName << "::" << inCall << " caught an exception: " << - e.message << ' ' << status()); - - if (!done()) - mustStop("exception"); -} - -void ICAPXaction::callEnd() -{ - if (done()) { - debugs(93, 5, "ICAPXaction::" << inCall << " ends xaction " << - status()); - doStop(); // may delete us - return; - } - - debugs(93, 6, typeName << "::" << inCall << " ended " << status()); - inCall = NULL; -} - -// returns a temporary string depicting transaction status, for debugging -const char *ICAPXaction::status() const -{ - static MemBuf buf; - buf.reset(); - - buf.append("[", 1); - - fillPendingStatus(buf); - buf.append("/", 1); - fillDoneStatus(buf); - - buf.append("]", 1); - - buf.terminate(); - - return buf.content(); -} - -void ICAPXaction::fillPendingStatus(MemBuf &buf) const -{ - if (connection >= 0) { - buf.Printf("Comm(%d", connection); - - if (writer) - buf.append("w", 1); - - if (reader) - buf.append("r", 1); - - buf.append(")", 1); - } -} - -void ICAPXaction::fillDoneStatus(MemBuf &buf) const -{ - if (connection >= 0 && commEof) - buf.Printf("Comm(%d)", connection); - - if (stopReason != NULL) - buf.Printf("Stopped"); -} --- squid3/src/ICAPXaction.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,164 +0,0 @@ - -/* - * $Id: ICAPXaction.h,v 1.1.2.28 2005/11/03 00:28:58 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_ICAPXACTION_H -#define SQUID_ICAPXACTION_H - -#include "MemBuf.h" -#include "ICAPServiceRep.h" - -class HttpMsg; - -class TextException; - -/* The ICAP Xaction implements message pipe sink and source interfaces. It - * receives virgin HTTP messages, communicates with the ICAP server, and sends - * the adapted messages back. ICAPClient is the "owner" of the ICAPXaction. */ - -// Note: ICAPXaction must be the first parent for object-unaware cbdata to work - -class ICAPXaction: public RefCountable -{ - -public: - typedef RefCount Pointer; - -public: - ICAPXaction(const char *aTypeName); - virtual ~ICAPXaction(); - - // comm handler wrappers, treat as private - void noteCommConnected(comm_err_t status); - void noteCommWrote(comm_err_t status, size_t sz); - void noteCommRead(comm_err_t status, size_t sz); - void noteCommTimedout(); - void noteCommClosed(); - -protected: - // Set or get service pointer; ICAPXaction cbdata-locks it. - void service(ICAPServiceRep::Pointer &aService); - ICAPServiceRep &service(); - - // comm hanndlers; called by comm handler wrappers - virtual void handleCommConnected() = 0; - virtual void handleCommWrote(size_t sz) = 0; - virtual void handleCommRead(size_t sz) = 0; - virtual void handleCommTimedout(); - virtual void handleCommClosed(); - - void openConnection(); - void closeConnection(); - void scheduleRead(); - void scheduleWrite(MemBuf &buf); - - void cancelRead(); - - bool parseHttpMsg(HttpMsg *msg); // true=success; false=needMore; throw=err - bool mayReadMore() const; - virtual bool doneReading() const; - - bool done() const; - virtual bool doneAll() const; - virtual void doStop(); - void mustStop(const char *reason); - - // returns a temporary string depicting transaction status, for debugging - const char *status() const; - virtual void fillPendingStatus(MemBuf &buf) const; - virtual void fillDoneStatus(MemBuf &buf) const; - -protected: - int connection; // FD of the ICAP server connection - - /* - * We have two read buffers. We would prefer to read directly - * into the MemBuf, but since comm_read isn't MemBuf-aware, and - * uses event-delayed callbacks, it leaves the MemBuf in an - * inconsistent state. There would be data in the buffer, but - * MemBuf.size won't be updated until the (delayed) callback - * occurs. To avoid that situation we use a plain buffer - * (commBuf) and then copy (append) its contents to readBuf in - * the callback. If comm_read ever becomes MemBuf-aware, we - * can eliminate commBuf and this extra buffer copy. - */ - MemBuf readBuf; - char *commBuf; - size_t commBufSize; - bool commEof; - - const char *stopReason; - - // asynchronous call maintenance - bool callStart(const char *method); - void callException(const TextException &e); - void callEnd(); - - // active (pending) comm callbacks for the ICAP server connection - CNCB *connector; - IOCB *reader; - CWCB *writer; - PF *closer; - - const char *typeName; // the type of the final class (child), for debugging - -private: - ICAPServiceRep::Pointer theService; - - const char *inCall; // name of the asynchronous call being executed, if any - - //CBDATA_CLASS2(ICAPXaction); -}; - -// call guards for all "asynchronous" note*() methods - -// asynchronous call entry: -// - open the try clause; -// - call callStart(). -#define ICAPXaction_Enter(method) \ - try { \ - if (!callStart(#method)) \ - return; - -// asynchronous call exit: -// - close the try clause; -// - catch exceptions; -// - let callEnd() handle transaction termination conditions -#define ICAPXaction_Exit() \ - } \ - catch (const TextException &e) { \ - callException(e); \ - } \ - callEnd(); - - -#endif /* SQUID_ICAPXACTION_H */ Index: squid3/src/Makefile.am =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/Makefile.am,v retrieving revision 1.60.4.18 retrieving revision 1.60.4.19 diff -u -r1.60.4.18 -r1.60.4.19 --- squid3/src/Makefile.am 7 Nov 2005 17:51:20 -0000 1.60.4.18 +++ squid3/src/Makefile.am 21 Nov 2005 21:05:51 -0000 1.60.4.19 @@ -1,7 +1,7 @@ # # Makefile for the Squid Object Cache server # -# $Id: Makefile.am,v 1.60.4.18 2005/11/07 17:51:20 dwsquid Exp $ +# $Id: Makefile.am,v 1.60.4.19 2005/11/21 21:05:51 dwsquid Exp $ # # Uncomment and customize the following to suit your needs: # @@ -25,6 +25,8 @@ TESTS=$(check_PROGRAMS) check_PROGRAMS= +SUBDIRS = fs repl auth + DELAY_POOL_ALL_SOURCE = \ CommonPool.h \ CompositePoolNode.h \ @@ -92,24 +94,11 @@ ESI_SOURCE = endif -ICAP_CLIENT_ALL_SOURCE = \ - ChunkedCodingParser.cc \ - ICAPAnchor.cc \ - ICAPClientSideHook.cc \ - ICAPClient.cc \ - ICAPElements.cc \ - ICAPXaction.cc \ - ICAPOptXact.cc \ - ICAPModXact.cc \ - ICAPServiceRep.cc \ - ICAPConfig.cc \ - ICAPOptions.cc \ - TextException.cc \ - MsgPipe.cc if USE_ICAP_CLIENT - ICAP_CLIENT_SOURCE = $(ICAP_CLIENT_ALL_SOURCE) + ICAP_LIBS = ICAP/libicap.a + SUBDIRS += ICAP else - ICAP_CLIENT_SOURCE = + ICAP_LIBS = endif if ENABLE_XPROF_STATS @@ -178,8 +167,6 @@ AM_CFLAGS = @SQUID_CFLAGS@ AM_CXXFLAGS = @SQUID_CXXFLAGS@ -SUBDIRS = fs repl auth - EXTRA_LIBRARIES = libAIO.a libBlocking.a libDiskDaemon.a libDiskThreads.a noinst_LIBRARIES = @DISK_LIBS@ @@ -574,6 +561,7 @@ @CRYPTLIB@ \ @REGEXLIB@ \ @SNMPLIB@ \ + ${ICAP_LIBS} \ @LIB_MALLOC@ \ @SSLLIB@ \ -lmiscutil \ --- squid3/src/MsgPipe.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,114 +0,0 @@ -#include "squid.h" -#include "MsgPipe.h" -#include "MsgPipeSource.h" -#include "MsgPipeSink.h" -#include "MsgPipeData.h" - -#include "LeakFinder.h" -LeakFinder *MsgPipeLeaker = new LeakFinder; - -CBDATA_CLASS_INIT(MsgPipe); - -// static event callback template -// XXX: refcounting needed to make sure destination still exists -#define MsgPipe_MAKE_CALLBACK(callName, destination) \ -static \ -void MsgPipe_send ## callName(void *p) { \ - MsgPipe *pipe = static_cast(p); \ - if (pipe && pipe->canSend(pipe->destination, #callName, false)) \ - pipe->destination->note##callName(pipe); \ -} - -// static event callbacks -MsgPipe_MAKE_CALLBACK(SourceStart, sink) -MsgPipe_MAKE_CALLBACK(SourceProgress, sink) -MsgPipe_MAKE_CALLBACK(SourceFinish, sink) -MsgPipe_MAKE_CALLBACK(SourceAbort, sink) -MsgPipe_MAKE_CALLBACK(SinkNeed, source) -MsgPipe_MAKE_CALLBACK(SinkAbort, source) - - -MsgPipe::MsgPipe(const char *aName): name(aName), - data(NULL), source(NULL), sink(NULL) -{ - leakAdd(this, MsgPipeLeaker); -} - -MsgPipe::~MsgPipe() -{ - delete data; - delete source; - delete sink; - leakFree(this, MsgPipeLeaker); -}; - -void MsgPipe::sendSourceStart() -{ - leakTouch(this, MsgPipeLeaker); - debug(99,5)("MsgPipe::sendSourceStart() called\n"); - sendLater("SourceStart", &MsgPipe_sendSourceStart, sink); -} - - - -void MsgPipe::sendSourceProgress() -{ - leakTouch(this, MsgPipeLeaker); - debug(99,5)("MsgPipe::sendSourceProgress() called\n"); - sendLater("SourceProgress", &MsgPipe_sendSourceProgress, sink); -} - -void MsgPipe::sendSourceFinish() -{ - leakTouch(this, MsgPipeLeaker); - debug(99,5)("MsgPipe::sendSourceFinish() called\n"); - sendLater("sendSourceFinish", &MsgPipe_sendSourceFinish, sink); - source = NULL; -} - -void MsgPipe::sendSourceAbort() -{ - leakTouch(this, MsgPipeLeaker); - debug(99,5)("MsgPipe::sendSourceAbort() called\n"); - sendLater("SourceAbort", &MsgPipe_sendSourceAbort, sink); - source = NULL; -} - - -void MsgPipe::sendSinkNeed() -{ - leakTouch(this, MsgPipeLeaker); - debug(99,5)("MsgPipe::sendSinkNeed() called\n"); - sendLater("SinkNeed", &MsgPipe_sendSinkNeed, source); -} - -void MsgPipe::sendSinkAbort() -{ - leakTouch(this, MsgPipeLeaker); - debug(99,5)("MsgPipe::sendSinkAbort() called\n"); - sendLater("SinkAbort", &MsgPipe_sendSinkAbort, source); - sink = NULL; -} - -void MsgPipe::sendLater(const char *callName, EVH * handler, MsgPipeEnd *destination) -{ - leakTouch(this, MsgPipeLeaker); - - if (canSend(destination, callName, true)) - eventAdd(callName, handler, this, 0.0, 0, true); -} - -bool MsgPipe::canSend(MsgPipeEnd *destination, const char *callName, bool future) -{ - leakTouch(this, MsgPipeLeaker); - const bool res = destination != NULL; - const char *verb = future ? - (res ? "will send " : "wont send ") : - (res ? "sends " : "ignores "); - debugs(99,5, "MsgPipe " << name << "(" << this << ") " << - verb << callName << " to the " << - (destination ? destination->kind() : "destination") << "(" << - destination << "); " << - "data: " << data << "; source: " << source << "; sink " << sink); - return res; -} --- squid3/src/MsgPipe.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,87 +0,0 @@ - -/* - * $Id: MsgPipe.h,v 1.1.2.8 2005/09/02 15:23:56 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_MSGPIPE_H -#define SQUID_MSGPIPE_H - - -// MsgPipe is a unidirectional communication channel for asynchronously -// transmitting potentially large messages. It aggregates the message -// being piped and pointers to the message sender and recepient. -// MsgPipe also provides convenience wrappers for asynchronous calls to -// recepient's and sender's note*() methods. - -class MsgPipeData; - -class MsgPipeEnd; - -class MsgPipeSource; - -class MsgPipeSink; - -class MsgPipe : public RefCountable -{ - -public: - typedef RefCount Pointer; - - MsgPipe(const char *aName = "anonym"); - ~MsgPipe(); - - // the pipe source calls these to notify the sink - void sendSourceStart(); - void sendSourceProgress(); - void sendSourceFinish(); - void sendSourceAbort(); - - // the pipe sink calls these to notify the source - void sendSinkNeed(); - void sendSinkAbort(); - - // private method exposed for the event handler only - bool canSend(MsgPipeEnd *destination, const char *callName, bool future); - -public: - const char *name; // unmanaged pointer used for debugging only - - MsgPipeData *data; - MsgPipeSource *source; - MsgPipeSink *sink; - -private: - void sendLater(const char *callName, EVH * handler, MsgPipeEnd *destination); - - CBDATA_CLASS2(MsgPipe); -}; - -#endif /* SQUID_MSGPIPE_H */ --- squid3/src/MsgPipeData.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,76 +0,0 @@ - -/* - * $Id: MsgPipeData.h,v 1.1.2.11 2005/09/18 04:12:42 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_MSGPIPEDATA_H -#define SQUID_MSGPIPEDATA_H - -#include "HttpMsg.h" -#include "MemBuf.h" - -// MsgPipeData contains information about the HTTP message being sent -// from the pipe source to the sink. Since the entire message body may be -// large, only partial information about the body is kept. For HTTP -// responses, request header information is also available as metadata. - -class HttpRequest; - -class MsgPipeData -{ - -public: - MsgPipeData(): header(0), body(0), cause(0) {}; - - ~MsgPipeData() - { - assert(NULL == cause); - assert(NULL == header); - - if (body) { - body->clean(); - delete body; - } - }; - -public: - typedef HttpMsg Header; - typedef MemBuf Body; - - // message being piped - Header *header; // parsed HTTP status/request line and headers - Body *body; // a buffer for decoded HTTP body piping - - // HTTP request header for piped responses (the cause of the response) - HttpRequest *cause; -}; - -#endif /* SQUID_MSGPIPEDATA_H */ --- squid3/src/MsgPipeEnd.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,50 +0,0 @@ - -/* - * $Id: MsgPipeEnd.h,v 1.1.2.3 2005/08/31 20:03:39 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_MSGPIPEEND_H -#define SQUID_MSGPIPEEND_H - -// MsgPipeEnd is a common part of the MsgPipeSource and MsgPipeSink interfaces. -// Mesage pipe ends must be refcounted so that the recepient does not disappear -// while a message is being [asynchoronously] delivered to it. - -class MsgPipeEnd: public RefCountable -{ - -public: - virtual ~MsgPipeEnd() {} - - virtual const char *kind() const = 0; // "sink" or "source", for debugging -}; - -#endif /* SQUID_MSGPIPEEND_H */ --- squid3/src/MsgPipeSink.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,56 +0,0 @@ - -/* - * $Id: MsgPipeSink.h,v 1.1.2.3 2005/08/26 20:28:41 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_MSGPIPESINK_H -#define SQUID_MSGPIPESINK_H - -#include "MsgPipeEnd.h" - -// MsgPipeSink is an interface for the recepient of a given message -// over a given message pipe. Use MsgPipe to call sink methods. - -class MsgPipe; - -class MsgPipeSink: public MsgPipeEnd -{ - -public: - virtual void noteSourceStart(MsgPipe *p) = 0; - virtual void noteSourceProgress(MsgPipe *p) = 0; - virtual void noteSourceFinish(MsgPipe *p) = 0; - virtual void noteSourceAbort(MsgPipe *p) = 0; - - virtual const char *kind() const { return "sink"; } -}; - -#endif /* SQUID_MSGPIPESINK_H */ --- squid3/src/MsgPipeSource.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,54 +0,0 @@ - -/* - * $Id: MsgPipeSource.h,v 1.1.2.3 2005/08/26 20:28:41 dwsquid Exp $ - * - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sinks; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#ifndef SQUID_MSGPIPESOURCE_H -#define SQUID_MSGPIPESOURCE_H - -#include "MsgPipeEnd.h" - -// MsgPipeSource is an interface for the sender of a given message -// over a given message pipe. Use MsgPipe to call source methods. - -class MsgPipe; - -class MsgPipeSource: public MsgPipeEnd -{ - -public: - virtual const char *kind() const { return "source"; } - - virtual void noteSinkNeed(MsgPipe *p) = 0; - virtual void noteSinkAbort(MsgPipe *p) = 0; -}; - -#endif /* SQUID_MSGPIPESOURCE_H */ --- squid3/src/TextException.cc Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,27 +0,0 @@ -#include "squid.h" -#include "TextException.h" - -TextException::TextException(const char *aMsg, const char *aFileName, int aLineNo): - message(xstrdup(aMsg)), theFileName(aFileName), theLineNo(aLineNo) -{} - -TextException::~TextException() -{ - xfree(message); -} - -void Throw(const char *message, const char *fileName, int lineNo) -{ - - // or should we let the exception recepient print the exception instead? - - if (fileName) { - debugs(0, 3, fileName << ':' << lineNo << ": exception" << - (message ? ": " : ".") << (message ? message : "")); - } else { - debugs(0, 3, "exception" << - (message ? ": " : ".") << (message ? message : "")); - } - - throw TextException(message, fileName, lineNo); -} --- squid3/src/TextException.h Wed Feb 14 13:35:38 2007 +++ /dev/null Wed Feb 14 13:33:00 2007 @@ -1,46 +0,0 @@ -#ifndef SQUID__TEXTEXCEPTION_H -#define SQUID__TEXTEXCEPTION_H - -// Origin: xstd/TextException - - -// simple exception to report custom errors -// we may want to change the interface to be able to report system errors - -class TextException -{ - -public: - TextException(const char *aMessage, const char *aFileName = 0, int aLineNo = -1); - ~TextException(); - - // ostream &print(ostream &os) const; - -public: - char *message; // read-only - -protected: - // optional location information - const char *theFileName; - int theLineNo; -}; - -//inline -//ostream &operator <<(ostream &os, const TextException &exx) { -// return exx.print(os); -//} - -#if !defined(TexcHere) -# define TexcHere(msg) TextException((msg), __FILE__, __LINE__) -#endif - -extern void Throw(const char *message, const char *fileName, int lineNo); - -// Must(condition) is like assert(condition) but throws an exception instead -#if !defined(Must) -# define Must(cond) ((cond) ? \ - (void)0 : \ - (void)Throw(#cond, __FILE__, __LINE__)) -#endif - -#endif /* SQUID__TEXTEXCEPTION_H */ Index: squid3/src/cache_cf.cc =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/cache_cf.cc,v retrieving revision 1.52.2.8 retrieving revision 1.52.2.9 diff -u -r1.52.2.8 -r1.52.2.9 --- squid3/src/cache_cf.cc 7 Nov 2005 17:51:20 -0000 1.52.2.8 +++ squid3/src/cache_cf.cc 21 Nov 2005 21:05:51 -0000 1.52.2.9 @@ -1,6 +1,6 @@ /* - * $Id: cache_cf.cc,v 1.52.2.8 2005/11/07 17:51:20 dwsquid Exp $ + * $Id: cache_cf.cc,v 1.52.2.9 2005/11/21 21:05:51 dwsquid Exp $ * * DEBUG: section 3 Configuration File Parsing * AUTHOR: Harvest Derived @@ -53,7 +53,7 @@ #endif #if ICAP_CLIENT -#include "ICAPConfig.h" +#include "ICAP/ICAPConfig.h" extern ICAPConfig TheICAPConfig; // for cf_parser.h static void parse_icap_service_type(ICAPConfig *); Index: squid3/src/client_side_request.cc =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/client_side_request.cc,v retrieving revision 1.34.4.16 retrieving revision 1.34.4.17 diff -u -r1.34.4.16 -r1.34.4.17 --- squid3/src/client_side_request.cc 7 Nov 2005 17:51:20 -0000 1.34.4.16 +++ squid3/src/client_side_request.cc 21 Nov 2005 21:05:51 -0000 1.34.4.17 @@ -1,6 +1,6 @@ /* - * $Id: client_side_request.cc,v 1.34.4.16 2005/11/07 17:51:20 dwsquid Exp $ + * $Id: client_side_request.cc,v 1.34.4.17 2005/11/21 21:05:51 dwsquid Exp $ * * DEBUG: section 85 Client-side Request Routines * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c) @@ -56,9 +56,9 @@ #include "ClientRequestContext.h" #if ICAP_CLIENT -#include "ICAPClientSideHook.h" -#include "ICAPElements.h" -#include "ICAPConfig.h" +#include "ICAP/ICAPClientReqmodPrecache.h" +#include "ICAP/ICAPElements.h" +#include "ICAP/ICAPConfig.h" static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data); #endif @@ -1092,7 +1092,7 @@ { debug(85,3)("ClientHttpRequest::doIcap() called\n"); assert(NULL == icap); - icap = new ICAPClientSideHook(service); + icap = new ICAPClientReqmodPrecache(service); (void) cbdataReference(icap); icap->startReqMod(this, request); icap->doneSending(); Index: squid3/src/client_side_request.h =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/client_side_request.h,v retrieving revision 1.17.12.5 retrieving revision 1.17.12.6 diff -u -r1.17.12.5 -r1.17.12.6 --- squid3/src/client_side_request.h 3 Oct 2005 16:10:01 -0000 1.17.12.5 +++ squid3/src/client_side_request.h 21 Nov 2005 21:05:51 -0000 1.17.12.6 @@ -1,6 +1,6 @@ /* - * $Id: client_side_request.h,v 1.17.12.5 2005/10/03 16:10:01 dwsquid Exp $ + * $Id: client_side_request.h,v 1.17.12.6 2005/11/21 21:05:51 dwsquid Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -40,9 +40,9 @@ #include "AccessLogEntry.h" #if ICAP_CLIENT -#include "ICAPServiceRep.h" +#include "ICAP/ICAPServiceRep.h" -class ICAPClientSideHook; +class ICAPClientReqmodPrecache; class HttpMsg; #endif @@ -155,7 +155,7 @@ #if ICAP_CLIENT public: - ICAPClientSideHook *icap; + ICAPClientReqmodPrecache *icap; int doIcap(ICAPServiceRep::Pointer); void icapSpaceAvailable(); void takeAdaptedHeaders(HttpMsg *); Index: squid3/src/http.cc =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/http.cc,v retrieving revision 1.49.2.53 retrieving revision 1.49.2.54 diff -u -r1.49.2.53 -r1.49.2.54 --- squid3/src/http.cc 7 Nov 2005 21:22:52 -0000 1.49.2.53 +++ squid3/src/http.cc 21 Nov 2005 21:05:51 -0000 1.49.2.54 @@ -1,6 +1,6 @@ /* - * $Id: http.cc,v 1.49.2.53 2005/11/07 21:22:52 dwsquid Exp $ + * $Id: http.cc,v 1.49.2.54 2005/11/21 21:05:51 dwsquid Exp $ * * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) * AUTHOR: Harvest Derived @@ -53,8 +53,8 @@ #include "DelayPools.h" #endif #if ICAP_CLIENT -#include "ICAPAnchor.h" -#include "ICAPConfig.h" +#include "ICAP/ICAPClientRespmodPrecache.h" +#include "ICAP/ICAPConfig.h" #endif CBDATA_CLASS_INIT(HttpStateData); @@ -2050,13 +2050,13 @@ { debug(11,5)("HttpStateData::doIcap() called\n"); assert(NULL == icap); - icap = new ICAPAnchor(service); + icap = new ICAPClientRespmodPrecache(service); (void) cbdataReference(icap); return 0; } /* - * Called by ICAPAnchor when it has space available for us. + * Called by ICAPClientRespmodPrecache when it has space available for us. */ void HttpStateData::icapSpaceAvailable() Index: squid3/src/http.h =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/http.h,v retrieving revision 1.11.4.15 retrieving revision 1.11.4.16 diff -u -r1.11.4.15 -r1.11.4.16 --- squid3/src/http.h 3 Oct 2005 15:49:56 -0000 1.11.4.15 +++ squid3/src/http.h 21 Nov 2005 21:05:51 -0000 1.11.4.16 @@ -1,6 +1,6 @@ /* - * $Id: http.h,v 1.11.4.15 2005/10/03 15:49:56 dwsquid Exp $ + * $Id: http.h,v 1.11.4.16 2005/11/21 21:05:51 dwsquid Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -36,11 +36,14 @@ #include "StoreIOBuffer.h" #include "comm.h" -#include "ICAPServiceRep.h" -class ICAPAnchor; +#if ICAP_CLIENT +#include "ICAP/ICAPServiceRep.h" + +class ICAPClientRespmodPrecache; class ICAPAccessCheck; +#endif class HttpStateData { @@ -83,7 +86,7 @@ void processSurrogateControl(HttpReply *); #if ICAP_CLIENT - ICAPAnchor *icap; + ICAPClientRespmodPrecache *icap; void icapAclCheckDone(ICAPServiceRep::Pointer); bool icapAccessCheckPending; #endif --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ChunkedCodingParser.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,245 @@ +#include "squid.h" +#include "Parsing.h" +#include "TextException.h" +#include "ChunkedCodingParser.h" +#include "MemBuf.h" + +ChunkedCodingParser::Step ChunkedCodingParser::psChunkBeg = &ChunkedCodingParser::parseChunkBeg; +ChunkedCodingParser::Step ChunkedCodingParser::psChunkBody = &ChunkedCodingParser::parseChunkBody; +ChunkedCodingParser::Step ChunkedCodingParser::psChunkEnd = &ChunkedCodingParser::parseChunkEnd; +ChunkedCodingParser::Step ChunkedCodingParser::psTrailer = &ChunkedCodingParser::parseTrailer; +ChunkedCodingParser::Step ChunkedCodingParser::psMessageEnd = &ChunkedCodingParser::parseMessageEnd; + +ChunkedCodingParser::ChunkedCodingParser() +{ + reset(); +} + +void ChunkedCodingParser::reset() +{ + theStep = psChunkBeg; + theChunkSize = theLeftBodySize = 0; + doNeedMoreData = false; + sawIeof = false; + theIn = theOut = NULL; +} + +bool ChunkedCodingParser::parse(MemBuf *rawData, MemBuf *parsedContent) +{ + Must(rawData && parsedContent); + theIn = rawData; + theOut = parsedContent; + + // we must reset this all the time so that mayContinue() lets us + // output more content if we stopped due to needsMoreSpace() before + doNeedMoreData = !theIn->hasContent(); + + while (mayContinue()) { + (this->*theStep)(); + } + + return theStep == psMessageEnd; +} + +bool ChunkedCodingParser::needsMoreData() const +{ + return doNeedMoreData; +} + +bool ChunkedCodingParser::needsMoreSpace() const +{ + assert(theOut); + return theStep == psChunkBody && !theOut->hasPotentialSpace(); +} + +bool ChunkedCodingParser::mayContinue() const +{ + return !needsMoreData() && !needsMoreSpace() && theStep != psMessageEnd; +} + +void ChunkedCodingParser::parseChunkBeg() +{ + Must(theChunkSize <= 0); // Should(), really + + size_t crlfBeg = 0; + size_t crlfEnd = 0; + + if (findCrlf(crlfBeg, crlfEnd)) { + debugs(99,5, "found chunk-size end: " << crlfBeg << "-" << crlfEnd); + int size = -1; + const char *p = 0; + + if (StringToInt(theIn->content(), size, &p, 16)) { + if (size < 0) { + throw TexcHere("negative chunk size"); + return; + } + + // check for ieof chunk extension in the last-chunk + if (size == 0 && p && *p++ == ';') { + const char *e = theIn->content() + crlfBeg; // end of extension + + while (p < e && isspace(*p)) + ++p; // skip space + + sawIeof = e - p >= 4 && + strncmp(p, "ieof", 4) == 0 && + isspace(p[4]); + } + + theIn->consume(crlfEnd); + theChunkSize = theLeftBodySize = size; + debugs(99,5, "found chunk: " << theChunkSize); + theStep = theChunkSize == 0 ? psTrailer : psChunkBody; + return; + } + + throw TexcHere("corrupted chunk size"); + } + + doNeedMoreData = true; +} + +void ChunkedCodingParser::parseChunkBody() +{ + Must(theLeftBodySize > 0); // Should, really + + const size_t availSize = XMIN(theLeftBodySize, (size_t)theIn->contentSize()); + const size_t safeSize = XMIN(availSize, (size_t)theOut->potentialSpaceSize()); + + doNeedMoreData = availSize < theLeftBodySize; + // and we may also need more space + + theOut->append(theIn->content(), safeSize); + theIn->consume(safeSize); + theLeftBodySize -= safeSize; + + if (theLeftBodySize == 0) + theStep = psChunkEnd; + else + Must(needsMoreData() || needsMoreSpace()); +} + +void ChunkedCodingParser::parseChunkEnd() +{ + Must(theLeftBodySize == 0); // Should(), really + + size_t crlfBeg = 0; + size_t crlfEnd = 0; + + if (findCrlf(crlfBeg, crlfEnd)) { + if (crlfBeg != 0) { + throw TexcHere("found data bewteen chunk end and CRLF"); + return; + } + + theIn->consume(crlfEnd); + theChunkSize = 0; // done with the current chunk + theStep = psChunkBeg; + return; + } + + doNeedMoreData = true; +} + +void ChunkedCodingParser::parseTrailer() +{ + Must(theChunkSize == 0); // Should(), really + + while (mayContinue()) + parseTrailerHeader(); +} + +void ChunkedCodingParser::parseTrailerHeader() +{ + size_t crlfBeg = 0; + size_t crlfEnd = 0; + + if (findCrlf(crlfBeg, crlfEnd)) { + if (crlfBeg > 0) + + ; //theTrailer.append(theIn->content(), crlfEnd); + + theIn->consume(crlfEnd); + + if (crlfBeg == 0) + theStep = psMessageEnd; + + return; + } + + doNeedMoreData = true; +} + +void ChunkedCodingParser::parseMessageEnd() +{ + // termination step, should not be called + Must(false); // Should(), really +} + +// finds next CRLF +bool ChunkedCodingParser::findCrlf(size_t &crlfBeg, size_t &crlfEnd) +{ + // XXX: This code was copied, with permission, from another software. + // There is a similar and probably better code inside httpHeaderParse + // but it seems difficult to isolate due to parsing-unrelated bloat. + // Such isolation should probably be done before this class is used + // for handling of traffic "more external" than ICAP. + + const char *buf = theIn->content(); + size_t size = theIn->contentSize(); + + ssize_t crOff = -1; + bool quoted = false; + bool slashed = false; + + for (size_t i = 0; i < size; ++i) { + if (slashed) { + slashed = false; + continue; + } + + const char c = buf[i]; + + // handle quoted strings + if (quoted) { + if (c == '\\') + slashed = true; + else + if (c == '"') + quoted = false; + + continue; + } else + if (c == '"') { + quoted = true; + crOff = -1; + continue; + } + + if (crOff < 0) { // looking for the first CR or LF + + if (c == '\n') { + crlfBeg = i; + crlfEnd = ++i; + return true; + } + + if (c == '\r') + crOff = i; + } else { // skipping CRs, looking for the first LF + + if (c == '\n') { + crlfBeg = crOff; + crlfEnd = ++i; + return true; + } + + if (c != '\r') + crOff = -1; + } + } + + return false; +} + --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ChunkedCodingParser.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,90 @@ + +/* + * $Id: ChunkedCodingParser.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_CHUNKEDCODINGPARSER_H +#define SQUID_CHUNKEDCODINGPARSER_H + +#include "RefCount.h" + +// ChunkedCodingParser is an incremental parser for chunked transfer coding +// used by HTTP and ICAP. The parser shovels content bytes from the raw +// input buffer into the content output buffer, both caller-supplied. +// Ignores chunk extensions except for ICAP's ieof. +// Has a trailer-handling placeholder. + +class ChunkedCodingParser +{ + +public: + ChunkedCodingParser(); + + void reset(); + + // true = complete success; false == needs more data + bool parse(MemBuf *rawData, MemBuf *parsedContent); // throws on error + + bool needsMoreData() const; + bool needsMoreSpace() const; + bool sawIeof; // saw ieof chunk extension after a 0-size chunk + +private: + typedef void (ChunkedCodingParser::*Step)(); + +private: + bool mayContinue() const; + + void parseChunkBeg(); + void parseChunkBody(); + void parseChunkEnd(); + void parseTrailer(); + void parseTrailerHeader(); + void parseMessageEnd(); + + bool findCrlf(size_t &crlfBeg, size_t &crlfEnd); + +private: + static Step psChunkBeg; + static Step psChunkBody; + static Step psChunkEnd; + static Step psTrailer; + static Step psMessageEnd; + + MemBuf *theIn; + MemBuf *theOut; + + Step theStep; + size_t theChunkSize; + size_t theLeftBodySize; + bool doNeedMoreData; +}; + +#endif /* SQUID_CHUNKEDCODINGPARSER_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClient.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,37 @@ +#include "squid.h" +#include "ICAPModXact.h" +#include "ICAPClient.h" +#include "http.h" + +void ICAPInitModule() +{ + /* + * ICAP's MsgPipe buffer needs to be at least as large + * as the HTTP read buffer. Otherwise HTTP may take + * data from the network that won't fit into the MsgPipe, + * which leads to a runtime assertion. + */ + assert(ICAP::MsgPipeBufSizeMax >= SQUID_TCP_SO_RCVBUF); +} + +void ICAPCleanModule() +{} + +// initialize ICAP-specific ends of message pipes +void ICAPInitXaction(ICAPServiceRep::Pointer service, MsgPipe::Pointer virgin, MsgPipe::Pointer adapted) +{ + ICAPModXact::Pointer x = new ICAPModXact; + debugs(93,5, "ICAPInitXaction: " << x.getRaw()); + x->init(service, virgin, adapted, x); + // if we want to do something to the transaction after it is done, + // we need to keep a pointer to it +} + +// declared in ICAPModXact.h (ick?) +void ICAPNoteXactionDone(ICAPModXact::Pointer x) +{ + // nothing to be done here? + // refcounting will delete the transaction + // as soon as the last pointer to it is gone + debugs(93,5, "ICAPNoteXactionDone: " << x.getRaw()); +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClient.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,50 @@ + +/* + * $Id: ICAPClient.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPCLIENT_H +#define SQUID_ICAPCLIENT_H + +#include "MsgPipe.h" // TODO: move; needed for ICAPInitXaction() +#include "ICAPServiceRep.h" // TODO: move; needed for ICAPInitXaction() + +// ICAP-related things needed by code unaware of ICAP internals. + +extern void ICAPInitModule(); +extern void ICAPCleanModule(); + +// let ICAP initialize ICAP-specific ends of message pipes + +class MsgPipe; +extern void ICAPInitXaction(ICAPServiceRep::Pointer, MsgPipe::Pointer virgin, MsgPipe::Pointer adapted); + +#endif /* SQUID_ICAPCLIENT_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClientReqmodPrecache.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,211 @@ +#include "squid.h" +#include "client_side_request.h" +#include "ClientRequestContext.h" +#include "MsgPipe.h" +#include "MsgPipeData.h" +#include "MsgPipeSource.h" +#include "MsgPipeSink.h" +#include "HttpRequest.h" +#include "ICAPClientReqmodPrecache.h" +#include "ICAPServiceRep.h" +#include "ICAPClient.h" + +#include "LeakFinder.h" + +extern LeakFinder *MsgPipeLeaker; + +CBDATA_CLASS_INIT(ICAPClientReqmodPrecache); + +ICAPClientReqmodPrecache::ICAPClientReqmodPrecache(ICAPServiceRep::Pointer aService): service(aService), http(NULL), virgin(NULL), adapted(NULL) +{ + debug(93,3)("ICAPClientReqmodPrecache constructed, this=%p\n", this); +} + +ICAPClientReqmodPrecache::~ICAPClientReqmodPrecache() +{ + stop(notifyNone); + cbdataReferenceDone(http); + debug(93,3)("ICAPClientReqmodPrecache destructed, this=%p\n", this); + + if (virgin != NULL) + freeVirgin(); + + if (adapted != NULL) + freeAdapted(); +} + +void ICAPClientReqmodPrecache::startReqMod(ClientHttpRequest *aHttp, HttpRequest *request) +{ + debug(93,3)("ICAPClientReqmodPrecache::startReqMod() called\n"); + http = cbdataReference(aHttp); + + virgin = new MsgPipe("virgin"); // this is the place to create a refcount ptr + leakTouch(virgin.getRaw(), MsgPipeLeaker); + virgin->source = this; + virgin->data = new MsgPipeData; + virgin->data->cause = NULL; + virgin->data->header = requestLink(request); + virgin->data->body = new MemBuf; + virgin->data->body->init(ICAP::MsgPipeBufSizeMin, ICAP::MsgPipeBufSizeMax); + + adapted = new MsgPipe("adapted"); + leakTouch(adapted.getRaw(), MsgPipeLeaker); + adapted->sink = this; + + ICAPInitXaction(service, virgin, adapted); + + virgin->sendSourceStart(); // we may have virgin data to provide + adapted->sendSinkNeed(); // we want adapted response, eventially +} + +void ICAPClientReqmodPrecache::sendMoreData(StoreIOBuffer buf) +{ + debug(93,3)("ICAPClientReqmodPrecache::sendMoreData() called\n"); + //buf.dump(); + /* + * The caller is responsible for not giving us more data + * than will fit in body MemBuf. Caller should use + * potentialSpaceSize() to find out how much we can hold. + */ + leakTouch(virgin.getRaw(), MsgPipeLeaker); + virgin->data->body->append(buf.data, buf.length); + virgin->sendSourceProgress(); +} + +int +ICAPClientReqmodPrecache::potentialSpaceSize() +{ + if (virgin == NULL) + return 0; + + leakTouch(virgin.getRaw(), MsgPipeLeaker); + + return (int) virgin->data->body->potentialSpaceSize(); +} + +// ClientHttpRequest says we have the entire HTTP message +void ICAPClientReqmodPrecache::doneSending() +{ + debug(93,3)("ICAPClientReqmodPrecache::doneSending() called\n"); + leakTouch(virgin.getRaw(), MsgPipeLeaker); + + virgin->sendSourceFinish(); +} + +// ClientHttpRequest tells us to abort +void ICAPClientReqmodPrecache::ownerAbort() +{ + debug(93,3)("ICAPClientReqmodPrecache::ownerAbort() called\n"); + stop(notifyIcap); +} + +// ICAP client needs more virgin response data +void ICAPClientReqmodPrecache::noteSinkNeed(MsgPipe *p) +{ + debug(93,3)("ICAPClientReqmodPrecache::noteSinkNeed() called\n"); + + leakTouch(virgin.getRaw(), MsgPipeLeaker); + + if (virgin->data->body->potentialSpaceSize()) + http->icapSpaceAvailable(); +} + +// ICAP client aborting +void ICAPClientReqmodPrecache::noteSinkAbort(MsgPipe *p) +{ + debug(93,3)("ICAPClientReqmodPrecache::noteSinkAbort() called\n"); + stop(notifyOwner); +} + +// ICAP client starts sending adapted response +// ICAP client has received new HTTP headers (if any) at this point +void ICAPClientReqmodPrecache::noteSourceStart(MsgPipe *p) +{ + debug(93,3)("ICAPClientReqmodPrecache::noteSourceStart() called\n"); + leakTouch(adapted.getRaw(), MsgPipeLeaker); + http->takeAdaptedHeaders(adapted->data->header); + noteSourceProgress(p); +} + +// ICAP client sends more data +void ICAPClientReqmodPrecache::noteSourceProgress(MsgPipe *p) +{ + debug(93,3)("ICAPClientReqmodPrecache::noteSourceProgress() called\n"); + //tell ClientHttpRequest to store a fresh portion of the adapted response + + leakTouch(p, MsgPipeLeaker); + + if (p->data->body->hasContent()) { + http->takeAdaptedBody(p->data->body); + } +} + +// ICAP client is done sending adapted response +void ICAPClientReqmodPrecache::noteSourceFinish(MsgPipe *p) +{ + debug(93,3)("ICAPClientReqmodPrecache::noteSourceFinish() called\n"); + //tell ClientHttpRequest that we expect no more response data + leakTouch(p, MsgPipeLeaker); + http->doneAdapting(); + stop(notifyNone); +} + +// ICAP client is aborting +void ICAPClientReqmodPrecache::noteSourceAbort(MsgPipe *p) +{ + debug(93,3)("ICAPClientReqmodPrecache::noteSourceAbort() called\n"); + leakTouch(p, MsgPipeLeaker); + stop(notifyOwner); +} + +// internal cleanup +void ICAPClientReqmodPrecache::stop(Notify notify) +{ + if (virgin != NULL) { + leakTouch(virgin.getRaw(), MsgPipeLeaker); + + if (notify == notifyIcap) + virgin->sendSourceAbort(); + else + virgin->source = NULL; + + freeVirgin(); + } + + if (adapted != NULL) { + leakTouch(adapted.getRaw(), MsgPipeLeaker); + + if (notify == notifyIcap) + adapted->sendSinkAbort(); + else + adapted->sink = NULL; + + freeAdapted(); + } + + if (http) { + if (notify == notifyOwner) + // tell ClientHttpRequest that we are aborting prematurely + http->abortAdapting(); + + cbdataReferenceDone(http); + + // http is now NULL, will not call it any more + } +} + +void ICAPClientReqmodPrecache::freeVirgin() +{ + // virgin->data->cause should be NULL; + requestUnlink(dynamic_cast(virgin->data->header)); + virgin->data->header = NULL; + leakTouch(virgin.getRaw(), MsgPipeLeaker); + virgin = NULL; // refcounted +} + +void ICAPClientReqmodPrecache::freeAdapted() +{ + adapted->data->header = NULL; // we don't own it + leakTouch(adapted.getRaw(), MsgPipeLeaker); + adapted = NULL; // refcounted +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClientReqmodPrecache.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,91 @@ + +/* + * $Id: ICAPClientReqmodPrecache.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPCLIENTSIDEHOOK_H +#define SQUID_ICAPCLIENTSIDEHOOK_H + +#include "MsgPipe.h" +#include "MsgPipeSource.h" +#include "MsgPipeSink.h" + +/* The ICAP ClientReqmodPrecache implements message pipe sink and source interfaces. It + * helps client-side to marshall the incoming/virgin HTTP message (being + * recieved from the HTTP client) to Squid's ICAP client module, using the + * MsgPipe interface. The same interface is used to get the adapted HTTP + * message back from the ICAP client. client-side is the "owner" of the + * ICAPClientReqmodPrecache. + */ + +class HttpRequest; + +class ClientRequestContext; + +class ICAPClientReqmodPrecache: public MsgPipeSource, public MsgPipeSink +{ + +public: + ICAPClientReqmodPrecache(ICAPServiceRep::Pointer); + virtual ~ICAPClientReqmodPrecache(); + + // synchronous calls called by ClientHttpRequest + void startReqMod(ClientHttpRequest *, HttpRequest *); + void sendMoreData(StoreIOBuffer buf); + void doneSending(); + void ownerAbort(); + int potentialSpaceSize(); /* how much data can we accept? */ + + // pipe source methods; called by ICAP while receiving the virgin message + virtual void noteSinkNeed(MsgPipe *p); + virtual void noteSinkAbort(MsgPipe *p); + + // pipe sink methods; called by ICAP while sending the adapted message + virtual void noteSourceStart(MsgPipe *p); + virtual void noteSourceProgress(MsgPipe *p); + virtual void noteSourceFinish(MsgPipe *p); + virtual void noteSourceAbort(MsgPipe *p); + +public: + ICAPServiceRep::Pointer service; + ClientHttpRequest *http; + MsgPipe::Pointer virgin; + MsgPipe::Pointer adapted; + +private: + typedef enum { notifyNone, notifyOwner, notifyIcap } Notify; + void stop(Notify notify); + void freeVirgin(); + void freeAdapted(); + CBDATA_CLASS2(ICAPClientReqmodPrecache); +}; + +#endif /* SQUID_ICAPCLIENTSIDEHOOK_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClientRespmodPrecache.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,246 @@ +#include "squid.h" +#include "http.h" +#include "MsgPipe.h" +#include "MsgPipeData.h" +#include "MsgPipeSource.h" +#include "MsgPipeSink.h" +#include "HttpRequest.h" +#include "HttpReply.h" +#include "ICAPClientRespmodPrecache.h" +#include "ICAPClient.h" +#include "ICAPServiceRep.h" + +#include "LeakFinder.h" + +CBDATA_CLASS_INIT(ICAPClientRespmodPrecache); + +extern LeakFinder *MsgPipeLeaker; + +ICAPClientRespmodPrecache::ICAPClientRespmodPrecache(ICAPServiceRep::Pointer aService): service(aService), httpState(NULL), virgin(NULL), adapted(NULL) +{ + debug(93,5)("ICAPClientRespmodPrecache constructed, this=%p\n", this); +} + +ICAPClientRespmodPrecache::~ICAPClientRespmodPrecache() +{ + stop(notifyNone); + cbdataReferenceDone(httpState); + debug(93,5)("ICAPClientRespmodPrecache destructed, this=%p\n", this); + + if (virgin != NULL) + freeVirgin(); + + if (adapted != NULL) + freeAdapted(); + + service = NULL; +} + +void ICAPClientRespmodPrecache::startRespMod(HttpStateData *anHttpState, HttpRequest *request, HttpReply *reply) +{ + httpState = cbdataReference(anHttpState); + + virgin = new MsgPipe("virgin"); // this is the place to create a refcount ptr + leakTouch(virgin.getRaw(), MsgPipeLeaker); + virgin->source = this; + virgin->data = new MsgPipeData; + virgin->data->cause = requestLink(request); + virgin->data->header = reply; + virgin->data->body = new MemBuf; + virgin->data->body->init(ICAP::MsgPipeBufSizeMin, ICAP::MsgPipeBufSizeMax); + + adapted = new MsgPipe("adapted"); + leakTouch(adapted.getRaw(), MsgPipeLeaker); + adapted->sink = this; +#if ICAP_ANCHOR_LOOPBACK + + adapted->data = new MsgPipeData; + adapted->data->cause = request; // should not hurt +#else + + ICAPInitXaction(service, virgin, adapted); +#endif + + virgin->sendSourceStart(); // we may have virgin data to provide + adapted->sendSinkNeed(); // we want adapted response, eventially +} + +void ICAPClientRespmodPrecache::sendMoreData(StoreIOBuffer buf) +{ + debug(93,5)("ICAPClientRespmodPrecache::sendMoreData() called\n"); + //buf.dump(); + /* + * The caller is responsible for not giving us more data + * than will fit in body MemBuf. Caller should use + * potentialSpaceSize() to find out how much we can hold. + */ + leakTouch(virgin.getRaw(), MsgPipeLeaker); + virgin->data->body->append(buf.data, buf.length); + virgin->sendSourceProgress(); +} + +int +ICAPClientRespmodPrecache::potentialSpaceSize() +{ + if (virgin == NULL) + return 0; + + leakTouch(virgin.getRaw(), MsgPipeLeaker); + + return (int) virgin->data->body->potentialSpaceSize(); +} + +// HttpStateData says we have the entire HTTP message +void ICAPClientRespmodPrecache::doneSending() +{ + debug(93,5)("ICAPClientRespmodPrecache::doneSending() called\n"); + +#if ICAP_ANCHOR_LOOPBACK + /* simple assignments are not the right way to do this */ + adapted->data->header = virgin->data->header; + adapted->data->body = virgin->data->body; + noteSourceFinish(adapted); + return; +#else + + leakTouch(virgin.getRaw(), MsgPipeLeaker); + virgin->sendSourceFinish(); +#endif +} + +// HttpStateData tells us to abort +void ICAPClientRespmodPrecache::ownerAbort() +{ + debug(93,5)("ICAPClientRespmodPrecache::ownerAbort() called\n"); + stop(notifyIcap); +} + +// ICAP client needs more virgin response data +void ICAPClientRespmodPrecache::noteSinkNeed(MsgPipe *p) +{ + debug(93,5)("ICAPClientRespmodPrecache::noteSinkNeed() called\n"); + + leakTouch(virgin.getRaw(), MsgPipeLeaker); + + if (virgin->data->body->potentialSpaceSize()) + httpState->icapSpaceAvailable(); +} + +// ICAP client aborting +void ICAPClientRespmodPrecache::noteSinkAbort(MsgPipe *p) +{ + debug(93,5)("ICAPClientRespmodPrecache::noteSinkAbort() called\n"); + stop(notifyOwner); +} + +// ICAP client starts sending adapted response +// ICAP client has received new HTTP headers (if any) at this point +void ICAPClientRespmodPrecache::noteSourceStart(MsgPipe *p) +{ + debug(93,5)("ICAPClientRespmodPrecache::noteSourceStart() called\n"); + leakTouch(adapted.getRaw(), MsgPipeLeaker); + + HttpReply *reply = dynamic_cast(adapted->data->header); + assert(reply); // check that ICAP xaction created the right object + httpState->takeAdaptedHeaders(reply); + + assert(reply == adapted->data->header); + adapted->data->header = NULL; + + noteSourceProgress(p); +} + +// ICAP client sends more data +void ICAPClientRespmodPrecache::noteSourceProgress(MsgPipe *p) +{ + debug(93,5)("ICAPClientRespmodPrecache::noteSourceProgress() called\n"); + //tell HttpStateData to store a fresh portion of the adapted response + + leakTouch(p, MsgPipeLeaker); + + if (p->data->body->hasContent()) { + httpState->takeAdaptedBody(p->data->body); + } +} + +// ICAP client is done sending adapted response +void ICAPClientRespmodPrecache::noteSourceFinish(MsgPipe *p) +{ + debug(93,5)("ICAPClientRespmodPrecache::noteSourceFinish() called\n"); + //tell HttpStateData that we expect no more response data + leakTouch(p, MsgPipeLeaker); + httpState->doneAdapting(); + stop(notifyNone); +} + +// ICAP client is aborting +void ICAPClientRespmodPrecache::noteSourceAbort(MsgPipe *p) +{ + debug(93,5)("ICAPClientRespmodPrecache::noteSourceAbort() called\n"); + leakTouch(p, MsgPipeLeaker); + stop(notifyOwner); +} + +// internal cleanup +void ICAPClientRespmodPrecache::stop(Notify notify) +{ + if (virgin != NULL) { + leakTouch(virgin.getRaw(), MsgPipeLeaker); + + if (notify == notifyIcap) + virgin->sendSourceAbort(); + else + virgin->source = NULL; + + freeVirgin(); + } + + if (adapted != NULL) { + leakTouch(adapted.getRaw(), MsgPipeLeaker); + + if (notify == notifyIcap) + adapted->sendSinkAbort(); + else + adapted->sink = NULL; + + freeAdapted(); + } + + if (httpState) { + if (notify == notifyOwner) + // tell HttpStateData that we are aborting prematurely + httpState->abortAdapting(); + + cbdataReferenceDone(httpState); + + // httpState is now NULL, will not call it any more + } +} + +void ICAPClientRespmodPrecache::freeVirgin() +{ + requestUnlink(virgin->data->cause); + virgin->data->cause = NULL; + virgin->data->header = NULL; + leakTouch(virgin.getRaw(), MsgPipeLeaker); + virgin = NULL; // refcounted +} + +void ICAPClientRespmodPrecache::freeAdapted() +{ + /* + * Note on adapted->data->header. ICAPXaction-side created it + * but gave control of it to us. Normally we give it to + * HttpStateData::takeAdaptedHeader. If not, we have to + * make sure it gets deleted; + */ + + if (adapted->data->header != NULL) { + debug(93,3)("hey, adapted->data->header is still set!\n"); + delete adapted->data->header; + adapted->data->header = NULL; + } + + leakTouch(adapted.getRaw(), MsgPipeLeaker); + adapted = NULL; // refcounted +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClientRespmodPrecache.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,92 @@ + +/* + * $Id: ICAPClientRespmodPrecache.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPANCHOR_H +#define SQUID_ICAPANCHOR_H + +#include "MsgPipe.h" +#include "MsgPipeSource.h" +#include "MsgPipeSink.h" +#include "ICAPServiceRep.h" + +/* The ICAP Anchor implements message pipe sink and source interfaces. It + * helps HttpStateData to marshall the incoming/virgin HTTP message (being + * recieved from the HTTP server) to Squid's ICAP client module, using the + * MsgPipe interface. The same interface is used to get the adapted HTTP + * message back from the ICAP client. HttpStateData is the "owner" of the + * ICAPClientRespmodPrecache. + */ + +class HttpRequest; + +class HttpReply; + +class ICAPClientRespmodPrecache: public MsgPipeSource, public MsgPipeSink +{ + +public: + ICAPClientRespmodPrecache(ICAPServiceRep::Pointer); + virtual ~ICAPClientRespmodPrecache(); + + // synchronous calls called by HttpStateData + void startRespMod(HttpStateData *anHttpState, HttpRequest *request, HttpReply *reply); + void sendMoreData(StoreIOBuffer buf); + void doneSending(); + void ownerAbort(); + int potentialSpaceSize(); /* how much data can we accept? */ + + // pipe source methods; called by ICAP while receiving the virgin message + virtual void noteSinkNeed(MsgPipe *p); + virtual void noteSinkAbort(MsgPipe *p); + + // pipe sink methods; called by ICAP while sending the adapted message + virtual void noteSourceStart(MsgPipe *p); + virtual void noteSourceProgress(MsgPipe *p); + virtual void noteSourceFinish(MsgPipe *p); + virtual void noteSourceAbort(MsgPipe *p); + +public: + ICAPServiceRep::Pointer service; + HttpStateData *httpState; + MsgPipe::Pointer virgin; + MsgPipe::Pointer adapted; + +private: + typedef enum { notifyNone, notifyOwner, notifyIcap } Notify; + void stop(Notify notify); + void freeVirgin(); + void freeAdapted(); + CBDATA_CLASS2(ICAPClientRespmodPrecache); +}; + +#endif /* SQUID_ICAPANCHOR_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClientStream.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,103 @@ + + + +#include "squid.h" +#include "ICAPClient.h" +#include "clientStream.h" +#include "client_side_reply.h" +#include "HttpHeader.h" +#include "HttpReply.h" + +struct _junk +{ + clientStreamNode *node; + clientHttpRequest *http; + HttpReply *rep; + StoreIOBuffer *receivedData; +}; + +static EVH someEvent; + + +/* + * This callback is called for each incoming data chunk. + * Note receivedData only gives us the message body, not + * the headers + */ +void +icapclientProcessStream(clientStreamNode *thisNode, clientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData) +{ + assert (thisNode != NULL); + assert (cbdataReferenceValid (thisNode)); + + debug(0,0)("This is icapclientProcessStream\n"); + debug(0,0)("\tthisNode=%p\n", thisNode); + debug(0,0)("\thttp=%p\n", http); + debug(0,0)("\trep=%p\n", rep); + //debug(0,0)("\trep->content_length=%d\n", rep->content_length); + char *foo; + foo = new char[receivedData.length+1]; + xstrncpy(foo, receivedData.data, receivedData.length+1); + *(foo+receivedData.length) = '\0'; + debug(0,0)("{%s}\n", foo); + + struct _junk *j = (struct _junk *) xcalloc(1, sizeof(*j)); + j->node = thisNode; + j->http = http; + j->rep = rep; + j->receivedData = &receivedData; + + eventAdd("someEvent", someEvent, j, 5.0, 0, 0); + +} + +void +icapclientStreamRead(clientStreamNode *thisNode, clientHttpRequest *http) +{ + debug(0,0)("This is icapclientStreamRead\n"); + + /* pass data through untouched */ + clientStreamNode *next = thisNode->next(); + clientStreamRead (thisNode, http, next->readBuffer); + return; +} + +void +icapclientStreamDetach(clientStreamNode *thisNode, clientHttpRequest *http) +{ + debug(0,0)("This is icapclientStreamDetach\n"); +} + +clientStream_status_t +icapclientStreamStatus(clientStreamNode *thisNode, clientHttpRequest *http) +{ + debug(0,0)("This is icapclientStreamStatus\n"); + + /* pass data through untouched */ + return clientStreamStatus (thisNode, http); + + return STREAM_NONE; +} + +static void +someEvent(void *foo) +{ + debug(0,0)("this is someEvent\n"); + + struct _junk *j = (struct _junk *) foo; + + + if (NULL != j->rep) { + httpHeaderPutExt(&j->rep->header, "X-foo", "bar-bar"); + } + + if (NULL == j->node->data.getRaw()) { + /* first call; setup our state */ + } + + /* pass data through untouched */ + clientStreamCallback (j->node, j->http, j->rep, *j->receivedData); + + free(j); + +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPClientStream.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,14 @@ + +#ifndef SQUID_ICAP_CLIENT_H +#define SQUID_ICAP_CLIENT_H + + +#include "clientStream.h" + +extern CSR icapclientStreamRead; +extern CSCB icapclientProcessStream; +extern CSD icapclientStreamDetach; +extern CSS icapclientStreamStatus; + + +#endif --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPConfig.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,380 @@ + +/* + * $Id: ICAPConfig.cc,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + * + * Copyright (c) 2003, Robert Collins + */ + +#include "squid.h" + +#include "ConfigParser.h" +#include "ACL.h" +#include "Store.h" +#include "Array.h" // really Vector +#include "ICAPConfig.h" +#include "ICAPServiceRep.h" +#include "HttpRequest.h" +#include "ACLChecklist.h" + +ICAPConfig TheICAPConfig; + +ICAPServiceRep::Pointer +ICAPConfig::findService(const String& key) +{ + Vector::iterator iter = services.begin(); + + while (iter != services.end()) { + if (iter->getRaw()->key == key) + return *iter; + + ++iter; + } + + return NULL; +} + +ICAPClass * +ICAPConfig::findClass(const String& key) +{ + if (!key.size()) + return NULL; + + Vector::iterator iter = classes.begin(); + + while (iter != classes.end()) { + if ((*iter)->key == key) + return *iter; + + ++iter; + } + + return NULL; +} + +int +ICAPClass::prepare() +{ + int found = 0; + wordlist *service_names = NULL; + wordlist *iter; + + ConfigParser::ParseString(&key); + ConfigParser::ParseWordList(&service_names); + + for (iter = service_names; iter; iter = iter->next) { + ICAPServiceRep::Pointer match = TheICAPConfig.findService(iter->key); + + if (match != NULL) { + found = 1; + services += match; + } + } + + return found; +}; + +// ================================================================================ // + +CBDATA_CLASS_INIT(ICAPAccessCheck); + +ICAPAccessCheck::ICAPAccessCheck(ICAP::Method aMethod, + ICAP::VectPoint aPoint, + HttpRequest *aReq, + HttpReply *aRep, + ICAPAccessCheckCallback *aCallback, + void *aCallbackData) +{ + method = aMethod; + point = aPoint; + req = requestLink(aReq); + rep = aRep; + callback = aCallback; + callback_data = aCallbackData; + candidateClasses.clean(); + matchedClass.clean(); + acl_checklist = NULL; + debug(93,5)("ICAPAccessCheck constructed for %s %s\n", + ICAP::methodStr(method), + ICAP::vectPointStr(point)); +} + +ICAPAccessCheck::~ICAPAccessCheck() +{ + requestUnlink(req); +} + +/* + * Walk the ICAPAccess list and find all classes that have at least + * one service with matching method and vectoring point. + */ +void +ICAPAccessCheck::check() +{ + debug(93,3)("ICAPAccessCheck::check\n"); + Vector::iterator ci; + + for (ci = TheICAPConfig.classes.begin(); ci != TheICAPConfig.classes.end(); ++ci) { + + ICAPClass *theClass = *ci; + + Vector::iterator si; + + for (si = theClass->services.begin(); si != theClass->services.end(); ++si) { + ICAPServiceRep *theService = si->getRaw(); + + if (method != theService->method) + continue; + + if (point != theService->point) + continue; + + debug(93,1)("ICAPAccessCheck::check: class '%s' has candidate service '%s'\n", theClass->key.buf(), theService->key.buf()); + + candidateClasses += theClass->key; + + break; + } + } + + checkCandidates(); +} + +void +ICAPAccessCheck::checkCandidates() +{ + while (!candidateClasses.empty()) { + // It didn't really match yet, but we use the name anyway. + matchedClass = candidateClasses.shift(); + ICAPClass *theClass = TheICAPConfig.findClass(matchedClass); + + if (theClass == NULL) + // class apparently went away (reconfigure) + continue; + + // XXX we don't have access to conn->rfc931 here. + acl_checklist = aclChecklistCreate(theClass->accessList, req, dash_str); + + acl_checklist->nonBlockingCheck(ICAPAccessCheckCallbackWrapper, this); + + return; + } + + /* + * when there are no canidates, set matchedClass to NULL string + * and call the wrapper with answer = 1 + */ + debug(93,1)("ICAPAccessCheck::check: NO candidates or matches found\n"); + + matchedClass.clean(); + + ICAPAccessCheckCallbackWrapper(1, this); + + return; +} + +void +ICAPAccessCheck::ICAPAccessCheckCallbackWrapper(int answer, void *data) +{ + debug(93,5)("ICAPAccessCheckCallbackWrapper: answer=%d\n", answer); + ICAPAccessCheck *ac = (ICAPAccessCheck*)data; + + if (ac->matchedClass.size()) { + debug(93,5)("ICAPAccessCheckCallbackWrapper matchedClass = %s\n", + ac->matchedClass.buf()); + } + + if (!answer) { + ac->checkCandidates(); + return; + } + + /* + * We use an event here to break deep function call sequences + */ + eventAdd("ICAPAccessCheckCallbackEvent", + ICAPAccessCheckCallbackEvent, + ac, + 0.0, + 0, + true); +} + +void +ICAPAccessCheck::ICAPAccessCheckCallbackEvent(void *data) +{ + debug(93,5)("ICAPAccessCheckCallbackEvent\n"); + ICAPAccessCheck *ac = (ICAPAccessCheck*)data; + ac->do_callback(); + delete ac; +} + +void +ICAPAccessCheck::do_callback() +{ + debug(93,3)("ICAPAccessCheck::do_callback\n"); + + if (matchedClass.size()) { + debug(93,3)("ICAPAccessCheck::do_callback matchedClass = %s\n", matchedClass.buf()); + } + + ICAPClass *theClass = TheICAPConfig.findClass(matchedClass); + + if (theClass == NULL) { + callback(NULL, callback_data); + return; + } + + matchedClass.clean(); + + Vector::iterator i; + + for (i = theClass->services.begin(); i != theClass->services.end(); ++i) { + ICAPServiceRep *theService = i->getRaw(); + + if (method != theService->method) + continue; + + if (point != theService->point) + continue; + + callback(*i, callback_data); + + return; + } + + callback(NULL, callback_data); +} + +// ================================================================================ // + +void +ICAPConfig::parseICAPService() +{ + ICAPServiceRep::Pointer S = new ICAPServiceRep(); + + if (S->configure(S)) + services += S; + else + S->invalidate(); +}; + +void +ICAPConfig::freeICAPService() +{ + services.clean(); +}; + +void +ICAPConfig::dumpICAPService(StoreEntry *entry, const char *name) +{ + typedef Vector::iterator VI; + + for (VI i = services.begin(); i != services.end(); ++i) { + const ICAPServiceRep::Pointer &r = *i; + storeAppendPrintf(entry, "%s %s_%s %s %d %s\n", name, r->key.buf(), + r->methodStr(), r->vectPointStr(), r->bypass, r->uri.buf()); + } +}; + +void +ICAPConfig::parseICAPClass() +{ + ICAPClass *C = new ICAPClass(); + + if (C->prepare()) { + classes.push_back(C); + } else { + delete C; + } +}; + +void +ICAPConfig::freeICAPClass() +{ + classes.clean(); +}; + +void +ICAPConfig::dumpICAPClass(StoreEntry *entry, const char *name) +{ + Vector::iterator i = classes.begin(); + + while (i != classes.end()) { + storeAppendPrintf(entry, "%s %s\n", name, (*i)->key.buf()); + ++i; + } +}; + +void +ICAPConfig::parseICAPAccess() +{ + String aKey; + ConfigParser::ParseString(&aKey); + ICAPClass *theClass = TheICAPConfig.findClass(aKey); + + if (theClass == NULL) + fatalf("Did not find ICAP class '%s' referenced on line %d\n", + aKey.buf(), config_lineno); + + aclParseAccessLine(&theClass->accessList); +}; + +void +ICAPConfig::freeICAPAccess() +{ + (void) 0; +}; + +void +ICAPConfig::dumpICAPAccess(StoreEntry *entry, const char *name) +{ + LOCAL_ARRAY(char, nom, 64); + + Vector::iterator i = classes.begin(); + + while (i != classes.end()) { + snprintf(nom, 64, "%s %s", name, (*i)->key.buf()); + dump_acl_access(entry, nom, (*i)->accessList); + ++i; + } +}; + +ICAPConfig::~ICAPConfig() +{ + + // invalidate each service so that it can be deleted when refcount=0 + Vector::iterator si; + + for (si = services.begin(); si != services.end(); ++si) + (*si)->invalidate(); + + classes.clean(); + +}; --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPConfig.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,125 @@ + +/* + * $Id: ICAPConfig.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + * + * Copyright (c) 2003, Robert Collins + */ + +#ifndef SQUID_ICAPCONFIG_H +#define SQUID_ICAPCONFIG_H + +#include "ICAPServiceRep.h" + +class acl_access; + +class ICAPConfig; + +class ICAPClass +{ + +public: + String key; + acl_access *accessList; + + Vector services; + + ICAPClass() : key(NULL) {}; + + int prepare(); +}; + +class ICAPAccessCheck +{ + +public: + typedef void ICAPAccessCheckCallback(ICAPServiceRep::Pointer match, void *data); + ICAPAccessCheck(ICAP::Method, ICAP::VectPoint, HttpRequest *, HttpReply *, ICAPAccessCheckCallback *, void *); + ~ICAPAccessCheck(); + +private: + ICAP::Method method; + ICAP::VectPoint point; + HttpRequest *req; + HttpReply *rep; + ICAPAccessCheckCallback *callback; + void *callback_data; + ACLChecklist *acl_checklist; + Vector candidateClasses; + String matchedClass; + void do_callback(); + +public: + void check(); + void checkCandidates(); + static void ICAPAccessCheckCallbackWrapper(int, void*); + static EVH ICAPAccessCheckCallbackEvent; + +private: + CBDATA_CLASS2(ICAPAccessCheck); +}; + +class ICAPConfig +{ + +public: + + int onoff; + int preview_enable; + int preview_size; + int check_interval; + int send_client_ip; + int auth_user; + char *auth_scheme; + + Vector services; + Vector classes; + + ICAPConfig() {}; + + ~ICAPConfig(); + + void parseICAPService(void); + void freeICAPService(void); + void dumpICAPService(StoreEntry *, const char *); + ICAPServiceRep::Pointer findService(const String&); + ICAPClass * ICAPConfig::findClass(const String& key); + + void parseICAPClass(void); + void freeICAPClass(void); + void dumpICAPClass(StoreEntry *, const char *); + + void parseICAPAccess(void); + void freeICAPAccess(void); + void dumpICAPAccess(StoreEntry *, const char *); + +}; + +#endif /* SQUID_ICAPCONFIG_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPElements.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,52 @@ +#include "squid.h" +#include "ICAPElements.h" + +const char *ICAP::crlf = "\r\n"; + + +const char * +ICAP::methodStr(ICAP::Method method) +{ + switch(method) { + + case ICAP::methodReqmod: + return "REQMOD"; + break; + + case ICAP::methodRespmod: + return "RESPMOD"; + break; + + case ICAP::methodOptions: + return "OPTIONS"; + break; + + default: + break; + } + + return "NONE"; +} + + +const char * +ICAP::vectPointStr(ICAP::VectPoint point) +{ + switch(point) { + + case ICAP::pointPreCache: + return "PRECACHE"; + break; + + case ICAP::pointPostCache: + return "POSTCACHE"; + break; + + default: + break; + } + + return "NONE"; +} + + --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPElements.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,55 @@ + +/* + * $Id: ICAPElements.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPELEMENTS_H +#define SQUID_ICAPELEMENTS_H + +// ICAP-related things shared by many ICAP classes + +// A "fake" class to encapsulate ICAP-related declarations without +// adding namespaces to Squid. Eventually, namespaces should be added. + +struct ICAP +{ + typedef enum { methodNone, methodReqmod, methodRespmod, methodOptions } Method; + typedef enum { pointNone, pointPreCache, pointPostCache } VectPoint; + + // recommended initial size and max capacity for MsgPipe buffer + enum { MsgPipeBufSizeMin = (4*1024), MsgPipeBufSizeMax = SQUID_TCP_SO_RCVBUF }; + + static const char *crlf; + static const char *methodStr(ICAP::Method); + static const char *vectPointStr(ICAP::VectPoint); +}; + +#endif /* SQUID_ICAPCLIENT_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPModXact.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,1201 @@ +/* + * DEBUG: section 93 ICAP (RFC 3507) Client + */ + +#include "squid.h" +#include "comm.h" +#include "MsgPipe.h" +#include "MsgPipeData.h" +#include "HttpRequest.h" +#include "HttpReply.h" +#include "ICAPServiceRep.h" +#include "ICAPModXact.h" +#include "ICAPClient.h" +#include "ChunkedCodingParser.h" +#include "TextException.h" + +// flow and terminology: +// HTTP| --> receive --> encode --> write --> |network +// end | <-- send <-- parse <-- read <-- |end + +// TODO: doneSending()/doneReceving() data members should probably be in sync +// with this->adapted/virgin pointers. Make adapted/virgin methods? + +// TODO: replace gotEncapsulated() with something faster; we call it often + +CBDATA_CLASS_INIT(ICAPModXact); + +static const size_t TheBackupLimit = ICAP::MsgPipeBufSizeMax; + + +ICAPModXact::State::State() +{ + memset(this, sizeof(*this), 0); +} + +ICAPModXact::ICAPModXact(): ICAPXaction("ICAPModXact"), + self(NULL), virgin(NULL), adapted(NULL), + icapReply(NULL), virginConsumed(0), + bodyParser(NULL) +{} + +void ICAPModXact::init(ICAPServiceRep::Pointer &aService, MsgPipe::Pointer &aVirgin, MsgPipe::Pointer &anAdapted, Pointer &aSelf) +{ + assert(!self.getRaw() && !virgin.getRaw() && !adapted.getRaw()); + assert(aSelf.getRaw() && aVirgin.getRaw() && anAdapted.getRaw()); + + self = aSelf; + service(aService); + + virgin = aVirgin; + adapted = anAdapted; + + // receiving end + virgin->sink = this; // should be 'self' and refcounted + // virgin pipe data is initiated by the source + + // sending end + adapted->source = this; // should be 'self' and refcounted + adapted->data = new MsgPipeData; + + adapted->data->body = new MemBuf; // XXX: make body a non-pointer? + adapted->data->body->init(ICAP::MsgPipeBufSizeMin, ICAP::MsgPipeBufSizeMax); + // headers are initialized when we parse them + + // writing and reading ends are handled by ICAPXaction + + // encoding + // nothing to do because we are using temporary buffers + + // parsing + icapReply = new HttpReply; + icapReply->protoPrefix = "ICAP/"; // TODO: make an IcapReply class? + + // XXX: make sure stop() cleans all buffers +} + +// HTTP side starts sending virgin data +void ICAPModXact::noteSourceStart(MsgPipe *p) +{ + ICAPXaction_Enter(noteSourceStart); + + // make sure TheBackupLimit is in-sync with the buffer size + Must(TheBackupLimit <= static_cast(virgin->data->body->max_capacity)); + + estimateVirginBody(); // before virgin disappears! + + // it is an ICAP violation to send request to a service w/o known OPTIONS + + if (service().up()) + startWriting(); + else + waitForService(); + + // XXX: but this has to be here to catch other errors. Thus, if + // commConnectStart in startWriting fails, we may get here + //_after_ the object got destroyed. Somebody please fix commConnectStart! + ICAPXaction_Exit(); +} + +static +void ICAPModXact_noteServiceReady(void *data, ICAPServiceRep::Pointer &) +{ + ICAPModXact *x = static_cast(data); + assert(x); + x->noteServiceReady(); +} + +void ICAPModXact::waitForService() +{ + Must(!state.serviceWaiting); + debugs(93, 7, "ICAPModXact will wait for the ICAP service " << status()); + state.serviceWaiting = true; + service().callWhenReady(&ICAPModXact_noteServiceReady, this); +} + +void ICAPModXact::noteServiceReady() +{ + ICAPXaction_Enter(noteServiceReady); + + Must(state.serviceWaiting); + state.serviceWaiting = false; + startWriting(); // will throw if service is not up + + ICAPXaction_Exit(); +} + +void ICAPModXact::startWriting() +{ + Must(service().up()); + + state.writing = State::writingConnect; + openConnection(); + // put nothing here as openConnection calls commConnectStart + // and that may call us back without waiting for the next select loop +} + +// connection with the ICAP service established +void ICAPModXact::handleCommConnected() +{ + Must(state.writing == State::writingConnect); + + startReading(); // wait for early errors from the ICAP server + + MemBuf requestBuf; + requestBuf.init(); + + makeRequestHeaders(requestBuf); + debugs(93, 9, "ICAPModXact ICAP request prefix " << status() << ":\n" << + (requestBuf.terminate(), requestBuf.content())); + + // write headers + state.writing = State::writingHeaders; + scheduleWrite(requestBuf); +} + +void ICAPModXact::handleCommWrote(size_t) +{ + if (state.writing == State::writingHeaders) + handleCommWroteHeaders(); + else + handleCommWroteBody(); +} + +void ICAPModXact::handleCommWroteHeaders() +{ + Must(state.writing == State::writingHeaders); + + if (virginBody.expected()) { + state.writing = preview.enabled() ? + State::writingPreview : State::writingPrime; + virginWriteClaim.protectAll(); + writeMore(); + } else { + stopWriting(); + } +} + +void ICAPModXact::writeMore() +{ + if (writer) // already writing something + return; + + switch (state.writing) { + + case State::writingInit: // waiting for service OPTIONS + Must(state.serviceWaiting); + + case State::writingConnect: // waiting for the connection to establish + + case State::writingHeaders: // waiting for the headers to be written + + case State::writingPaused: // waiting for the ICAP server response + + case State::writingDone: // nothing more to write + return; + + case State::writingPreview: + writePriviewBody(); + return; + + case State::writingPrime: + writePrimeBody(); + return; + + default: + throw TexcHere("ICAPModXact in bad writing state"); + } +} + +void ICAPModXact::writePriviewBody() +{ + debugs(93, 8, "ICAPModXact will write Preview body " << status()); + Must(state.writing == State::writingPreview); + + MsgPipeData::Body *body = virgin->data->body; + const size_t size = XMIN(preview.debt(), (size_t)body->contentSize()); + writeSomeBody("preview body", size); + + // change state once preview is written + + if (preview.done()) { + debugs(93, 7, "ICAPModXact wrote entire Preview body " << status()); + + if (preview.ieof()) + stopWriting(); + else + state.writing = State::writingPaused; + } +} + +void ICAPModXact::writePrimeBody() +{ + Must(state.writing == State::writingPrime); + Must(virginWriteClaim.active()); + + MsgPipeData::Body *body = virgin->data->body; + const size_t size = body->contentSize(); + writeSomeBody("prime virgin body", size); + + if (state.doneReceiving) + stopWriting(); +} + +void ICAPModXact::writeSomeBody(const char *label, size_t size) +{ + Must(!writer && !state.doneWriting()); + debugs(93, 8, "ICAPModXact will write up to " << size << " bytes of " << + label); + + MemBuf writeBuf; // TODO: suggest a min size based on size and lastChunk + + writeBuf.init(); // note: we assume that last-chunk will fit + + const size_t writeableSize = claimSize(virginWriteClaim); + const size_t chunkSize = XMIN(writeableSize, size); + + if (chunkSize) { + debugs(93, 7, "ICAPModXact will write " << chunkSize << + "-byte chunk of " << label); + } else { + debugs(93, 7, "ICAPModXact has no writeable " << label << " content"); + } + + moveRequestChunk(writeBuf, chunkSize); + + const bool lastChunk = + (state.writing == State::writingPreview && preview.done()) || + (state.doneReceiving && claimSize(virginWriteClaim) <= 0); + + if (lastChunk && virginBody.expected()) { + debugs(93, 8, "ICAPModXact will write last-chunk of " << label); + addLastRequestChunk(writeBuf); + } + + debugs(93, 7, "ICAPModXact will write " << writeBuf.contentSize() + << " raw bytes of " << label); + + if (writeBuf.hasContent()) { + scheduleWrite(writeBuf); // comm will free the chunk + } else { + writeBuf.clean(); + } +} + +void ICAPModXact::moveRequestChunk(MemBuf &buf, size_t chunkSize) +{ + if (chunkSize > 0) { + openChunk(buf, chunkSize); + buf.append(claimContent(virginWriteClaim), chunkSize); + closeChunk(buf, false); + + virginWriteClaim.release(chunkSize); + virginConsume(); + } + + if (state.writing == State::writingPreview) + preview.wrote(chunkSize, state.doneReceiving); // even if wrote nothing +} + +void ICAPModXact::addLastRequestChunk(MemBuf &buf) +{ + openChunk(buf, 0); + closeChunk(buf, state.writing == State::writingPreview && preview.ieof()); +} + +void ICAPModXact::openChunk(MemBuf &buf, size_t chunkSize) +{ + buf.Printf("%x\r\n", chunkSize); +} + +void ICAPModXact::closeChunk(MemBuf &buf, bool ieof) +{ + if (ieof) + buf.append("; ieof", 6); + + buf.append(ICAP::crlf, 2); // chunk-terminating CRLF +} + +size_t ICAPModXact::claimSize(const MemBufClaim &claim) const +{ + Must(claim.active()); + const size_t start = claim.offset(); + const size_t end = virginConsumed + virgin->data->body->contentSize(); + Must(virginConsumed <= start && start <= end); + return end - start; +} + +const char *ICAPModXact::claimContent(const MemBufClaim &claim) const +{ + Must(claim.active()); + const size_t start = claim.offset(); + Must(virginConsumed <= start); + return virgin->data->body->content() + (start - virginConsumed); +} + +void ICAPModXact::virginConsume() +{ + MemBuf &buf = *virgin->data->body; + const size_t have = static_cast(buf.contentSize()); + const size_t end = virginConsumed + have; + size_t offset = end; + + if (virginWriteClaim.active()) + offset = XMIN(virginWriteClaim.offset(), offset); + + if (virginSendClaim.active()) + offset = XMIN(virginSendClaim.offset(), offset); + + Must(virginConsumed <= offset && offset <= end); + + if (const size_t size = offset - virginConsumed) { + debugs(93, 8, "ICAPModXact consumes " << size << " out of " << have << + " virgin body bytes"); + buf.consume(size); + virginConsumed += size; + + if (!state.doneReceiving) + virgin->sendSinkNeed(); + } +} + +void ICAPModXact::handleCommWroteBody() +{ + writeMore(); +} + +void ICAPModXact::stopWriting() +{ + if (state.writing == State::writingDone) + return; + + debugs(93, 7, "ICAPModXact will no longer write " << status()); + + state.writing = State::writingDone; + + virginWriteClaim.disable(); + + virginConsume(); + + // Comm does not have an interface to clear the writer, but + // writeMore() will not write if our write callback is called + // when state.writing == State::writingDone; +} + +void ICAPModXact::stopBackup() +{ + if (!virginSendClaim.active()) + return; + + debugs(93, 7, "ICAPModXact will no longer backup " << status()); + + virginSendClaim.disable(); + + virginConsume(); +} + +bool ICAPModXact::doneAll() const +{ + return ICAPXaction::doneAll() && !state.serviceWaiting && + state.doneReceiving && doneSending() && + doneReading() && state.doneWriting(); +} + +void ICAPModXact::startReading() +{ + Must(connection >= 0); + Must(!reader); + Must(adapted.getRaw()); + Must(adapted->data); + Must(adapted->data->body); + + // we use the same buffer for headers and body and then consume headers + readMore(); +} + +void ICAPModXact::readMore() +{ + if (reader || doneReading()) + return; + + // do not fill readBuf if we have no space to store the result + if (!adapted->data->body->hasPotentialSpace()) + return; + + if (readBuf.hasSpace()) + scheduleRead(); +} + +// comm module read a portion of the ICAP response for us +void ICAPModXact::handleCommRead(size_t) +{ + Must(!state.doneParsing()); + parseMore(); + readMore(); +} + +void ICAPModXact::echoMore() +{ + Must(state.sending == State::sendingVirgin); + Must(virginSendClaim.active()); + + MemBuf &from = *virgin->data->body; + MemBuf &to = *adapted->data->body; + + const size_t sizeMax = claimSize(virginSendClaim); + const size_t size = XMIN(static_cast(to.potentialSpaceSize()), + sizeMax); + debugs(93, 5, "ICAPModXact echos " << size << " out of " << sizeMax << + " bytes"); + + if (size > 0) { + to.append(claimContent(virginSendClaim), size); + virginSendClaim.release(size); + virginConsume(); + adapted->sendSourceProgress(); + } + + if (!from.hasContent() && state.doneReceiving) { + debugs(93, 5, "ICAPModXact echoed all " << status()); + stopSending(true); + } else { + debugs(93, 5, "ICAPModXact has " << from.contentSize() << " bytes " << + "and expects more to echo " << status()); + virgin->sendSinkNeed(); // TODO: timeout if sink is broken + } +} + +bool ICAPModXact::doneSending() const +{ + Must((state.sending == State::sendingDone) == (!adapted)); + return state.sending == State::sendingDone; +} + +void ICAPModXact::stopSending(bool nicely) +{ + if (doneSending()) + return; + + if (state.sending != State::sendingUndecided) { + debugs(93, 7, "ICAPModXact will no longer send " << status()); + + if (nicely) + adapted->sendSourceFinish(); + else + adapted->sendSourceAbort(); + } else { + debugs(93, 7, "ICAPModXact will not start sending " << status()); + adapted->sendSourceAbort(); // or the sink may wait forever + } + + state.sending = State::sendingDone; + + /* + * Note on adapted->data->header: we created the header, but allow the + * other side (ICAPClientRespmodPrecache) to take control of it. We won't touch it here + * and instead rely on the Anchor-side to make sure it is properly freed. + */ + adapted = NULL; // refcounted +} + +void ICAPModXact::stopReceiving() +{ + // stopSending NULLifies adapted but we do not NULLify virgin. + // This is assymetric because we want to keep virgin->data even + // though we are not expecting any more virgin->data->body. + // TODO: can we cache just the needed headers info instead? + + // If they closed first, there is not point (or means) to notify them. + + if (state.doneReceiving) + return; + + // There is no sendSinkFinished() to notify the other side. + debugs(93, 7, "ICAPModXact will not receive " << status()); + + state.doneReceiving = true; +} + +void ICAPModXact::parseMore() +{ + debugs(93, 5, "have " << readBuf.contentSize() << " bytes to parse" << + status()); + + if (state.parsingHeaders()) + parseHeaders(); + + if (state.parsing == State::psBody) + parseBody(); +} + +// note that allocation for echoing is done in handle204NoContent() +void ICAPModXact::maybeAllocateHttpMsg() +{ + if (adapted->data->header) // already allocated + return; + + if (gotEncapsulated("res-hdr")) { + adapted->data->header = new HttpReply; + } else if (gotEncapsulated("req-hdr")) { + adapted->data->header = new HttpRequest; + } else + throw TexcHere("Neither res-hdr nor req-hdr in maybeAllocateHttpMsg()"); +} + +void ICAPModXact::parseHeaders() +{ + Must(state.parsingHeaders()); + + if (state.parsing == State::psIcapHeader) + parseIcapHead(); + + if (state.parsing == State::psHttpHeader) + parseHttpHead(); + + if (state.parsingHeaders()) { // need more data + Must(mayReadMore()); + return; + } + + adapted->sendSourceStart(); + + if (state.sending == State::sendingVirgin) + echoMore(); +} + +void ICAPModXact::parseIcapHead() +{ + Must(state.sending == State::sendingUndecided); + + if (!parseHead(icapReply)) + return; + + switch (icapReply->sline.status) { + + case 100: + handle100Continue(); + break; + + case 200: + handle200Ok(); + break; + + case 204: + handle204NoContent(); + break; + + default: + handleUnknownScode(); + break; + } + + // handle100Continue() manages state.writing on its own. + // Non-100 status means the server needs no postPreview data from us. + if (state.writing == State::writingPaused) + stopWriting(); + + // TODO: Consider applying a Squid 2.5 patch to recognize 201 responses +} + +void ICAPModXact::handle100Continue() +{ + Must(state.writing == State::writingPaused); + Must(preview.enabled() && preview.done() && !preview.ieof()); + Must(virginSendClaim.active()); + + if (virginSendClaim.limited()) // preview only + stopBackup(); + + state.parsing = State::psHttpHeader; // eventually + + state.writing = State::writingPrime; + + writeMore(); +} + +void ICAPModXact::handle200Ok() +{ + state.parsing = State::psHttpHeader; + state.sending = State::sendingAdapted; + stopBackup(); +} + +void ICAPModXact::handle204NoContent() +{ + stopParsing(); + Must(virginSendClaim.active()); + virginSendClaim.protectAll(); // extends protection if needed + state.sending = State::sendingVirgin; + + // We want to clone the HTTP message, but we do not want + // to copy non-HTTP state parts that HttpMsg kids carry in them. + // Thus, we cannot use a smart pointer, copy constructor, or equivalent. + // Instead, we simply write the HTTP message and "clone" it by parsing. + + HttpMsg *oldHead = virgin->data->header; + debugs(93, 7, "ICAPModXact cloning virgin message " << oldHead); + + MemBuf httpBuf; + + // write the virgin message into a memory buffer + httpBuf.init(); + packHead(httpBuf, oldHead); + + // allocate the adapted message + HttpMsg *&newHead = adapted->data->header; + Must(!newHead); + + if (dynamic_cast(oldHead)) + newHead = new HttpRequest; + else + if (dynamic_cast(oldHead)) + newHead = new HttpReply; + + Must(newHead); + + // parse the buffer back + http_status error = HTTP_STATUS_NONE; + + Must(newHead->parse(&httpBuf, true, &error)); + + Must(newHead->hdr_sz == httpBuf.contentSize()); // no leftovers + + httpBuf.clean(); + + debugs(93, 7, "ICAPModXact cloned virgin message " << oldHead << " to " << newHead); +} + +void ICAPModXact::handleUnknownScode() +{ + stopParsing(); + stopBackup(); + // TODO: mark connection as "bad" + + // Terminate the transaction; we do not know how to handle this response. + throw TexcHere("Unsupported ICAP status code"); +} + +void ICAPModXact::parseHttpHead() +{ + if (gotEncapsulated("res-hdr") || gotEncapsulated("req-hdr")) { + maybeAllocateHttpMsg(); + + if (!parseHead(adapted->data->header)) + return; + } + + state.parsing = State::psBody; +} + +bool ICAPModXact::parseHead(HttpMsg *head) +{ + assert(head); + debugs(93, 5, "have " << readBuf.contentSize() << " head bytes to parse" << + "; state: " << state.parsing); + + http_status error = HTTP_STATUS_NONE; + const bool parsed = head->parse(&readBuf, commEof, &error); + Must(parsed || !error); // success or need more data + + if (!parsed) { // need more data + head->reset(); + return false; + } + + readBuf.consume(head->hdr_sz); + return true; +} + +void ICAPModXact::parseBody() +{ + Must(state.parsing == State::psBody); + + debugs(93, 5, "have " << readBuf.contentSize() << " body bytes to parse"); + + if (gotEncapsulated("res-body")) { + if (!parsePresentBody()) // need more body data + return; + } else { + debugs(93, 5, "not expecting a body"); + } + + stopParsing(); + stopSending(true); +} + +// returns true iff complete body was parsed +bool ICAPModXact::parsePresentBody() +{ + if (!bodyParser) + bodyParser = new ChunkedCodingParser; + + // the parser will throw on errors + const bool parsed = bodyParser->parse(&readBuf, adapted->data->body); + + adapted->sendSourceProgress(); // TODO: do not send if parsed nothing + + debugs(93, 5, "have " << readBuf.contentSize() << " body bytes after " << + "parse; parsed all: " << parsed); + + if (parsed) + return true; + + if (bodyParser->needsMoreData()) + Must(mayReadMore()); + + if (bodyParser->needsMoreSpace()) { + Must(!doneSending()); // can hope for more space + Must(adapted->data->body->hasContent()); // paranoid + // TODO: there should be a timeout in case the sink is broken. + } + + return false; +} + +void ICAPModXact::stopParsing() +{ + if (state.parsing == State::psDone) + return; + + debugs(93, 7, "ICAPModXact will no longer parse " << status()); + + delete bodyParser; + + bodyParser = NULL; + + state.parsing = State::psDone; +} + +// HTTP side added virgin body data +void ICAPModXact::noteSourceProgress(MsgPipe *p) +{ + ICAPXaction_Enter(noteSourceProgress); + + Must(!state.doneReceiving); + writeMore(); + + if (state.sending == State::sendingVirgin) + echoMore(); + + ICAPXaction_Exit(); +} + +// HTTP side sent us all virgin info +void ICAPModXact::noteSourceFinish(MsgPipe *p) +{ + ICAPXaction_Enter(noteSourceFinish); + + Must(!state.doneReceiving); + stopReceiving(); + + // push writer and sender in case we were waiting for the last-chunk + writeMore(); + + if (state.sending == State::sendingVirgin) + echoMore(); + + ICAPXaction_Exit(); +} + +// HTTP side is aborting +void ICAPModXact::noteSourceAbort(MsgPipe *p) +{ + ICAPXaction_Enter(noteSourceAbort); + + Must(!state.doneReceiving); + stopReceiving(); + mustStop("HTTP source quit"); + + ICAPXaction_Exit(); +} + +// HTTP side wants more adapted data and possibly freed some buffer space +void ICAPModXact::noteSinkNeed(MsgPipe *p) +{ + ICAPXaction_Enter(noteSinkNeed); + + if (state.sending == State::sendingVirgin) + echoMore(); + else + if (state.sending == State::sendingAdapted) + parseMore(); + else + Must(state.sending == State::sendingUndecided); + + ICAPXaction_Exit(); +} + +// HTTP side aborted +void ICAPModXact::noteSinkAbort(MsgPipe *p) +{ + ICAPXaction_Enter(noteSinkAbort); + + mustStop("HTTP sink quit"); + + ICAPXaction_Exit(); +} + +// internal cleanup +void ICAPModXact::doStop() +{ + ICAPXaction::doStop(); + + stopWriting(); + stopBackup(); + + if (icapReply) { + delete icapReply; + icapReply = NULL; + } + + stopSending(false); + + // see stopReceiving() for reasons it cannot NULLify virgin there + + if (virgin != NULL) { + if (!state.doneReceiving) + virgin->sendSinkAbort(); + else + virgin->sink = NULL; + + virgin = NULL; // refcounted + } + + if (self != NULL) { + Pointer s = self; + self = NULL; + ICAPNoteXactionDone(s); + /* this object may be destroyed when 's' is cleared */ + } +} + +void ICAPModXact::makeRequestHeaders(MemBuf &buf) +{ + const ICAPServiceRep &s = service(); + buf.Printf("%s %s ICAP/1.0\r\n", s.methodStr(), s.uri.buf()); + buf.Printf("Host: %s:%d\r\n", s.host.buf(), s.port); + buf.Printf("Encapsulated: "); + + MemBuf httpBuf; + httpBuf.init(); + + // build HTTP request header, if any + ICAP::Method m = s.method; + + if (ICAP::methodRespmod == m && virgin->data->cause) + encapsulateHead(buf, "req-hdr", httpBuf, virgin->data->cause); + else if (ICAP::methodReqmod == m) + encapsulateHead(buf, "req-hdr", httpBuf, virgin->data->header); + + if (ICAP::methodRespmod == m) + if (const MsgPipeData::Header *prime = virgin->data->header) + encapsulateHead(buf, "res-hdr", httpBuf, prime); + + if (!virginBody.expected()) + buf.Printf("null-body=%d", httpBuf.contentSize()); + else if (ICAP::methodReqmod == m) + buf.Printf("req-body=%d", httpBuf.contentSize()); + else + buf.Printf("res-body=%d", httpBuf.contentSize()); + + buf.append(ICAP::crlf, 2); // terminate Encapsulated line + + if (shouldPreview()) { + buf.Printf("Preview: %d\r\n", (int)preview.ad()); + virginSendClaim.protectUpTo(preview.ad()); + } + + if (shouldAllow204()) { + buf.Printf("Allow: 204\r\n"); + // be robust: do not rely on the expected body size + virginSendClaim.protectAll(); + } + + buf.append(ICAP::crlf, 2); // terminate ICAP header + + // start ICAP request body with encapsulated HTTP headers + buf.append(httpBuf.content(), httpBuf.contentSize()); + + httpBuf.clean(); +} + +void ICAPModXact::encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const HttpMsg *head) +{ + // update ICAP header + icapBuf.Printf("%s=%d,", section, httpBuf.contentSize()); + + // pack HTTP head + packHead(httpBuf, head); +} + +void ICAPModXact::packHead(MemBuf &httpBuf, const HttpMsg *head) +{ + Packer p; + packerToMemInit(&p, &httpBuf); + head->packInto(&p, true); + packerClean(&p); +} + +// decides whether to offer a preview and calculates its size +bool ICAPModXact::shouldPreview() +{ + size_t wantedSize; + + if (!service().wantsPreview(wantedSize)) { + debugs(93, 5, "ICAPModXact should not offer preview"); + return false; + } + + Must(wantedSize >= 0); + + // cannot preview more than we can backup + size_t ad = XMIN(wantedSize, TheBackupLimit); + + if (virginBody.expected() && virginBody.knownSize()) + ad = XMIN(ad, virginBody.size()); // not more than we have + else + ad = 0; // questionable optimization? + + debugs(93, 5, "ICAPModXact should offer " << ad << "-byte preview " << + "(service wanted " << wantedSize << ")"); + + preview.enable(ad); + + return preview.enabled(); +} + +// decides whether to allow 204 responses +bool ICAPModXact::shouldAllow204() +{ + if (!service().allows204()) + return false; + + if (!virginBody.expected()) + return true; // no body means no problems with supporting 204s. + + // if there is a body, make sure we can backup it all + + if (!virginBody.knownSize()) + return false; + + // or should we have a different backup limit? + // note that '<' allows for 0-termination of the "full" backup buffer + return virginBody.size() < TheBackupLimit; +} + +// returns a temporary string depicting transaction status, for debugging +void ICAPModXact::fillPendingStatus(MemBuf &buf) const +{ + if (state.serviceWaiting) + buf.append("U", 1); + + if (!state.doneWriting() && state.writing != State::writingInit) + buf.Printf("w(%d)", state.writing); + + if (preview.enabled()) { + if (!preview.done()) + buf.Printf("P(%d)", preview.debt()); + } + + if (virginSendClaim.active()) + buf.append("B", 1); + + if (!state.doneParsing() && state.parsing != State::psIcapHeader) + buf.Printf("p(%d)", state.parsing); + + if (!doneSending() && state.sending != State::sendingUndecided) + buf.Printf("S(%d)", state.sending); +} + +void ICAPModXact::fillDoneStatus(MemBuf &buf) const +{ + if (state.doneReceiving) + buf.append("R", 1); + + if (state.doneWriting()) + buf.append("w", 1); + + if (preview.enabled()) { + if (preview.done()) + buf.Printf("P%s", preview.ieof() ? "(ieof)" : ""); + } + + if (doneReading()) + buf.append("r", 1); + + if (state.doneParsing()) + buf.append("p", 1); + + if (doneSending()) + buf.append("S", 1); +} + +bool ICAPModXact::gotEncapsulated(const char *section) const +{ + return httpHeaderGetByNameListMember(&icapReply->header, "Encapsulated", + section, ',').size() > 0; +} + +// calculate whether there is a virgin HTTP body and +// whether its expected size is known +void ICAPModXact::estimateVirginBody() +{ + // note: defaults should be fine but will disable previews and 204s + + Must(virgin != NULL && virgin->data->header); + + method_t method; + + if (virgin->data->cause) + method = virgin->data->cause->method; + else + if (HttpRequest *req= dynamic_cast(virgin->data-> + header)) + method = req->method; + else + return; + + ssize_t size; + if (virgin->data->header->expectingBody(method, size)) { + virginBody.expect(size) + ; + debugs(93, 6, "ICAPModXact expects virgin body; size: " << size); + } else { + debugs(93, 6, "ICAPModXact does not expect virgin body"); + } +} + + +// TODO: Move SizedEstimate, MemBufBackup, and ICAPPreview elsewhere + +SizedEstimate::SizedEstimate() + : theData(dtUnexpected) +{} + +void SizedEstimate::expect(ssize_t aSize) +{ + theData = (aSize >= 0) ? aSize : (ssize_t)dtUnknown; +} + +bool SizedEstimate::expected() const +{ + return theData != dtUnexpected; +} + +bool SizedEstimate::knownSize() const +{ + Must(expected()); + return theData != dtUnknown; +} + +size_t SizedEstimate::size() const +{ + Must(knownSize()); + return static_cast(theData); +} + + + +MemBufClaim::MemBufClaim(): theStart(-1), theGoal(-1) +{} + +void MemBufClaim::protectAll() +{ + if (theStart < 0) + theStart = 0; + + theGoal = -1; // no specific goal +} + +void MemBufClaim::protectUpTo(size_t aGoal) +{ + if (theStart < 0) + theStart = 0; + + Must(aGoal >= 0); + + theGoal = (theGoal < 0) ? static_cast(aGoal) : + XMIN(static_cast(aGoal), theGoal); +} + +void MemBufClaim::disable() +{ + theStart = -1; +} + +void MemBufClaim::release(size_t size) +{ + Must(active()); + Must(size >= 0); + theStart += static_cast(size); + + if (limited() && theStart >= theGoal) + disable(); +} + +size_t MemBufClaim::offset() const +{ + Must(active()); + return static_cast(theStart); +} + +bool MemBufClaim::limited() const +{ + Must(active()); + return theGoal >= 0; +} + + +ICAPPreview::ICAPPreview(): theWritten(0), theAd(0), theState(stDisabled) +{} + +void ICAPPreview::enable(size_t anAd) +{ + // TODO: check for anAd not exceeding preview size limit + Must(anAd >= 0); + Must(!enabled()); + theAd = anAd; + theState = stWriting; +} + +bool ICAPPreview::enabled() const +{ + return theState != stDisabled; +} + +size_t ICAPPreview::ad() const +{ + Must(enabled()); + return theAd; +} + +bool ICAPPreview::done() const +{ + Must(enabled()); + return theState >= stIeof; +} + +bool ICAPPreview::ieof() const +{ + Must(enabled()); + return theState == stIeof; +} + +size_t ICAPPreview::debt() const +{ + Must(enabled()); + return done() ? 0 : (theAd - theWritten); +} + +void ICAPPreview::wrote(size_t size, bool sawEof) +{ + Must(enabled()); + theWritten += size; + + if (theWritten >= theAd) + theState = stDone; // sawEof is irrelevant + else + if (sawEof) + theState = stIeof; +} + --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPModXact.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,279 @@ + +/* + * $Id: ICAPModXact.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPMODXACT_H +#define SQUID_ICAPMODXACT_H + +#include "ICAPXaction.h" +#include "MsgPipe.h" +#include "MsgPipeSource.h" +#include "MsgPipeSink.h" + +/* ICAPModXact implements ICAP REQMOD and RESPMOD transaction using ICAPXaction + * as the base. It implements message pipe sink and source interfaces for + * communication with various HTTP "anchors" and "hooks". ICAPModXact receives + * virgin HTTP messages, communicates with the ICAP server, and sends the + * adapted messages back. ICAPClient is the "owner" of the ICAPModXact. */ + +class ChunkedCodingParser; + +// estimated future presence and size of something (e.g., HTTP body) + +class SizedEstimate +{ + +public: + SizedEstimate(); // not expected by default + void expect(ssize_t aSize); // expect with any, even unknown size + bool expected() const; + + /* other members can be accessed iff expected() */ + + bool knownSize() const; + size_t size() const; // can be accessed iff knownSize() + +private: + enum { dtUnexpected = -2, dtUnknown = -1 }; + ssize_t theData; // combines expectation and size info to save RAM +}; + +// Protects buffer area. If area size is unknown, protects buffer suffix. +// Only "released" data can be consumed by the caller. Used to maintain +// write, preview, and 204 promises for ICAPModXact virgin->data-body buffer. + +class MemBufClaim +{ + +public: + MemBufClaim(); + + void protectAll(); + void protectUpTo(size_t aGoal); + void disable(); + bool active() const { return theStart >= 0; } + + // methods below require active() + + void release(size_t size); // stop protecting size more bytes + size_t offset() const; // protected area start + bool limited() const; // protects up to a known size goal + +private: + ssize_t theStart; // left area border + ssize_t theGoal; // "end" maximum, if any +}; + +// maintains preview-related sizes + +class ICAPPreview +{ + +public: + ICAPPreview(); // disabled + void enable(size_t anAd); // enabled with advertised size + bool enabled() const; + + /* other members can be accessed iff enabled() */ + + size_t ad() const; // advertised preview size + size_t debt() const; // remains to write + bool done() const; // wrote everything + bool ieof() const; // premature EOF + + void wrote(size_t size, bool sawEof); + +private: + size_t theWritten; + size_t theAd; + enum State { stDisabled, stWriting, stIeof, stDone } theState; +}; + +class ICAPModXact: public ICAPXaction, public MsgPipeSource, public MsgPipeSink +{ + +public: + typedef RefCount Pointer; + +public: + ICAPModXact(); + + // called by ICAPClient + void init(ICAPServiceRep::Pointer&, MsgPipe::Pointer &aVirgin, MsgPipe::Pointer &anAdapted, Pointer &aSelf); + + // pipe source methods; called by Anchor while receiving the adapted msg + virtual void noteSinkNeed(MsgPipe *p); + virtual void noteSinkAbort(MsgPipe *p); + + // pipe sink methods; called by ICAP while sending the virgin message + virtual void noteSourceStart(MsgPipe *p); + virtual void noteSourceProgress(MsgPipe *p); + virtual void noteSourceFinish(MsgPipe *p); + virtual void noteSourceAbort(MsgPipe *p); + + // comm handlers + virtual void handleCommConnected(); + virtual void handleCommWrote(size_t size); + virtual void handleCommRead(size_t size); + void handleCommWroteHeaders(); + void handleCommWroteBody(); + + // service waiting + void noteServiceReady(); + +private: + void estimateVirginBody(); + + void waitForService(); + + // will not send anything [else] on the adapted pipe + bool doneSending() const; + + void startWriting(); + void writeMore(); + void writePriviewBody(); + void writePrimeBody(); + void writeSomeBody(const char *label, size_t size); + + void startReading(); + void readMore(); + virtual bool doneReading() const { return commEof || state.doneParsing(); } + + size_t claimSize(const MemBufClaim &claim) const; + const char *claimContent(const MemBufClaim &claim) const; + void makeRequestHeaders(MemBuf &buf); + void moveRequestChunk(MemBuf &buf, size_t chunkSize); + void addLastRequestChunk(MemBuf &buf); + void openChunk(MemBuf &buf, size_t chunkSize); + void closeChunk(MemBuf &buf, bool ieof); + void virginConsume(); + + bool shouldPreview(); + bool shouldAllow204(); + void prepBackup(size_t expectedSize); + void backup(const MemBuf &buf); + + void parseMore(); + + void parseHeaders(); + void parseIcapHead(); + void parseHttpHead(); + bool parseHead(HttpMsg *head); + + void parseBody(); + bool parsePresentBody(); + void maybeAllocateHttpMsg(); + + void handle100Continue(); + void handle200Ok(); + void handle204NoContent(); + void handleUnknownScode(); + + void echoMore(); + + virtual bool doneAll() const; + + virtual void doStop(); + void stopReceiving(); + void stopSending(bool nicely); + void stopWriting(); + void stopParsing(); + void stopBackup(); + + virtual void fillPendingStatus(MemBuf &buf) const; + virtual void fillDoneStatus(MemBuf &buf) const; + +private: + void packHead(MemBuf &httpBuf, const HttpMsg *head); + void encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const HttpMsg *head); + bool gotEncapsulated(const char *section) const; + + Pointer self; + MsgPipe::Pointer virgin; + MsgPipe::Pointer adapted; + + HttpReply *icapReply; + + SizedEstimate virginBody; + MemBufClaim virginWriteClaim; // preserve virgin data buffer for writing + MemBufClaim virginSendClaim; // ... for sending (previe and 204s) + size_t virginConsumed; // virgin data consumed so far + ICAPPreview preview; // use for creating (writing) the preview + + ChunkedCodingParser *bodyParser; // ICAP response body parser + + class State + { + + public: + State(); + + public: + + unsigned serviceWaiting: + 1; // waiting for the ICAPServiceRep preparing the ICAP service + + unsigned doneReceiving: + 1; // expect no new virgin info (from the virgin pipe) + + // will not write anything [else] to the ICAP server connection + bool doneWriting() const { return writing == writingDone; } + + // parsed entire ICAP response from the ICAP server + bool doneParsing() const { return parsing == psDone; } + + // is parsing ICAP or HTTP headers read from the ICAP server + bool parsingHeaders() const + { + return parsing == psIcapHeader || + parsing == psHttpHeader; + } + + enum Parsing { psIcapHeader, psHttpHeader, psBody, psDone } parsing; + + // measures ICAP request writing progress + enum Writing { writingInit, writingConnect, writingHeaders, + writingPreview, writingPaused, writingPrime, writingDone } writing; + + enum Sending { sendingUndecided, sendingVirgin, sendingAdapted, + sendingDone } sending; + } + + state; + + CBDATA_CLASS2(ICAPModXact); +}; + +// destroys the transaction; implemented in ICAPClient.cc (ick?) +extern void ICAPNoteXactionDone(ICAPModXact::Pointer x); + +#endif /* SQUID_ICAPMOD_XACT_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPOptXact.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,111 @@ +/* + * DEBUG: section 93 ICAP (RFC 3507) Client + */ + +#include "squid.h" +#include "comm.h" +#include "HttpReply.h" + +#include "ICAPOptXact.h" +#include "ICAPOptions.h" +#include "TextException.h" + +CBDATA_CLASS_INIT(ICAPOptXact); + +ICAPOptXact::ICAPOptXact(): ICAPXaction("ICAPOptXact"), options(NULL), + cb(NULL), cbData(NULL) + +{ + debug(93,9)("ICAPOptXact constructed, this=%p\n", this); +} + +ICAPOptXact::~ICAPOptXact() +{ + Must(!options); // the caller must set to NULL + debug(93,9)("ICAPOptXact destructed, this=%p\n", this); +} + +void ICAPOptXact::start(ICAPServiceRep::Pointer &aService, Callback *aCb, void *aCbData) +{ + service(aService); + + Must(!cb && aCb && aCbData); + cb = aCb; + cbData = cbdataReference(aCbData); + + openConnection(); +} + +void ICAPOptXact::handleCommConnected() +{ + scheduleRead(); + + MemBuf requestBuf; + requestBuf.init(); + makeRequest(requestBuf); + debugs(93, 9, "ICAPOptXact request " << status() << ":\n" << + (requestBuf.terminate(), requestBuf.content())); + + scheduleWrite(requestBuf); +} + +void ICAPOptXact::doStop() +{ + ICAPXaction::doStop(); + + if (Callback *call = cb) { + cb = NULL; + void *data = NULL; + + if (cbdataReferenceValidDone(cbData, &data)) { + (*call)(this, data); // will delete us + return; + } + } + + // get rid of options if we did call the callback + delete options; + + options = NULL; +} + +void ICAPOptXact::makeRequest(MemBuf &buf) +{ + const ICAPServiceRep &s = service(); + buf.Printf("OPTIONS %s ICAP/1.0\r\n", s.uri.buf()); + buf.Printf("Host: %s:%d\r\n", s.host.buf(), s.port); + buf.append(ICAP::crlf, 2); +} + +void ICAPOptXact::handleCommWrote(size_t size) +{ + debugs(93, 9, "ICAPOptXact finished writing " << size << + "-byte request " << status()); +} + +// comm module read a portion of the ICAP response for us +void ICAPOptXact::handleCommRead(size_t) +{ + if (parseResponse()) + Must(done()); // there should be nothing else to do + else + scheduleRead(); +} + +bool ICAPOptXact::parseResponse() +{ + HttpReply *r = new HttpReply; + r->protoPrefix = "ICAP/"; // TODO: make an IcapReply class? + + if (!parseHttpMsg(r)) { + delete r; + return false; + } + + options = new ICAPOptions; + + options->configure(r); + + delete r; + return true; +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPOptXact.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,77 @@ +/* + * $Id: ICAPOptXact.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPOPTXACT_H +#define SQUID_ICAPOPTXACT_H + +#include "ICAPXaction.h" + +class ICAPOptions; + +/* ICAPOptXact sends an ICAP OPTIONS request to the ICAP service, + * converts the response into ICAPOptions object, and notifies + * the caller via the callback. NULL options objects means the + * ICAP service could not be contacted or did not return any response */ + +class ICAPOptXact: public ICAPXaction +{ + +public: + typedef void Callback(ICAPOptXact*, void *data); + + ICAPOptXact(); + virtual ~ICAPOptXact(); + + void start(ICAPServiceRep::Pointer &aService, Callback *aCb, void *aCbData); + + ICAPOptions *options; // result for the caller to take/handle + +protected: + virtual void handleCommConnected(); + virtual void handleCommWrote(size_t size); + virtual void handleCommRead(size_t size); + + void makeRequest(MemBuf &buf); + bool parseResponse(); + + void startReading(); + + virtual void doStop(); + +private: + Callback *cb; + void *cbData; + + CBDATA_CLASS2(ICAPOptXact); +}; + +#endif /* SQUID_ICAPOPTXACT_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPOptions.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,182 @@ +#include "squid.h" +#include "HttpReply.h" +#include "ICAPOptions.h" +#include "TextException.h" + +ICAPOptions::ICAPOptions(): error("unconfigured"), method(ICAP::methodNone), + max_connections(-1), allow204(false), + preview(-1), ttl(-1), transfer_ext(NULL) +{ + transfers.preview = transfers.ignore = transfers.complete = NULL; + transfers.other = TRANSFER_NONE; +}; + +ICAPOptions::~ICAPOptions() +{ + delete transfers.preview; + delete transfers.ignore; + delete transfers.complete; + delete transfer_ext; +}; + +ICAPOptions::transfer_type ICAPOptions::getTransferExt(const char *s) +{ + + if (transfer_ext) { + List *data = transfer_ext; + + while (data) { + if (*(data->element.ext) == *s) { + return data->element.type; + } + + data = data->next; + } + } + + return TRANSFER_NONE; +} + +#if UNUSED_CODE +void ICAPOptions::insertTransferExt(const char *t, transfer_type t_type) +{ + List **Tail; + TransferPair t_ext; + + if (t == "*") { + transfers.other = t_type; + return; + } + + for (Tail = &transfer_ext; *Tail; Tail = &((*Tail)->next)) { + if (*(*Tail)->element.ext == *t) { + (*Tail)->element.type = t_type; + return; + } + } + + t_ext.ext = xstrdup(t); + t_ext.type = t_type; + List *q = new List(t_ext); + *(Tail) = q; + +}; + +List *ICAPOptions::parseExtFileList(const char *start, const char *end, transfer_type t_type) +{ + const String s = xstrndup(start, end - start - 1); + const char *item; + const char *pos = NULL; + char *fext = NULL; + int ilen; + String t = NULL; + + List **Tail; + List *H; + + for (Tail = &H; *Tail; Tail = &((*Tail)->next)) + + ; + while (strListGetItem(&s, ',', &item, &ilen, &pos)) { + fext = xstrndup(item, ilen + 1); + t = fext; + List *q = new List (t); + *(Tail) = q; + Tail = &q->next; + insertTransferExt(fext, t_type); + } + + return H; +} + +#endif + +bool ICAPOptions::valid() const +{ + return !error; +} + +bool ICAPOptions::fresh() const +{ + return squid_curtime <= expire(); +} + +time_t ICAPOptions::expire() const +{ + Must(valid()); + return ttl >= 0 ? timestamp + ttl : -1; +} + +void ICAPOptions::configure(const HttpReply *reply) +{ + error = NULL; // reset initial "unconfigured" value (or an old error?) + + const HttpHeader *h = &reply->header; + + if (reply->sline.status != 200) + error = "unsupported status code of OPTIONS response"; + + // Methods + if (httpHeaderGetByNameListMember(h, "Methods", "REQMOD", ',').size()) + cfgMethod(ICAP::methodReqmod); + + if (httpHeaderGetByNameListMember(h, "Methods", "RESPMOD", ',').size()) + cfgMethod(ICAP::methodRespmod); + + service = httpHeaderGetByName(h, "Service"); + + serviceId = httpHeaderGetByName(h, "ServiceId"); + + istag = httpHeaderGetByName(h, "ISTag"); + + if (httpHeaderGetByName(h, "Opt-body-type").size()) + error = "ICAP service returns unsupported OPTIONS body"; + + cfgIntHeader(h, "Max-Connections", max_connections); + + cfgIntHeader(h, "Options-TTL", ttl); + + timestamp = httpHeaderGetTime(h, HDR_DATE); + + if (timestamp < 0) + timestamp = squid_curtime; + + if (httpHeaderGetByNameListMember(h, "Allow", "204", ',').size()) + allow204 = true; + + cfgIntHeader(h, "Preview", preview); + +#if 0 + + if (!strncasecmp("Transfer-Preview", start, 16)) + headers->transfer_preview = parseExtFileList(value_start, end, TRANSFER_PREVIEW); + + if (!strncasecmp("Transfer-Ignore", start, 15)) + headers->transfer_ignore = parseExtFileList(value_start, end, TRANSFER_IGNORE); + + if (!strncasecmp("Transfer-Complete", start, 17)) + headers->transfer_complete = parseExtFileList(value_start, end, TRANSFER_COMPLETE); + +#endif +} + +void ICAPOptions::cfgMethod(ICAP::Method m) +{ + Must(m != ICAP::methodNone); + + if (method == ICAP::methodNone) + method = m; + else + error = "the service claims to support several request methods"; +} + +// TODO: HttpHeader should provide a general method for this type of conversion +void ICAPOptions::cfgIntHeader(const HttpHeader *h, const char *fname, int &value) +{ + const String s = httpHeaderGetByName(h, fname); + + if (s.size() && xisdigit(*s.buf())) + value = atoi(s.buf()); + else + value = -1; +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPOptions.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,111 @@ + +/* + * $Id: ICAPOptions.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPOPTIONS_H +#define SQUID_ICAPOPTIONS_H + +#include "squid.h" +#include "List.h" +#include "ICAPClient.h" + +/* Maintains options supported by a given ICAP service. + * See RFC 3507, Section "4.10.2 OPTIONS Response". */ + +class ICAPOptions +{ + +public: + typedef void GetCallback(void *data, ICAPOptions *options); + static void Get(ICAPServiceRep::Pointer &service, GetCallback *cb, void *data); + +public: + ICAPOptions(); + ~ICAPOptions(); + + void configure(const HttpReply *reply); + + bool valid() const; + bool fresh() const; + time_t expire() const; + + typedef enum { TRANSFER_NONE, TRANSFER_PREVIEW, TRANSFER_IGNORE, TRANSFER_COMPLETE } transfer_type; + transfer_type getTransferExt(const char *); + +public: + const char *error; // human-readable information; set iff !valid() + + // ICAP server MUST supply this info + ICAP::Method method; + String istag; + + // ICAP server MAY supply this info. If not, Squid supplies defaults. + String service; + String serviceId; + int max_connections; + bool allow204; + int preview; + + // varios Transfer-* lists + + struct Transfers + { + List *preview; + List *ignore; + List *complete; + transfer_type other; // default X from Transfer-X: * + } + + transfers; + +protected: + int ttl; + time_t timestamp; + + // The list of pairs "file extension <-> transfer type" + + struct TransferPair + { + char *ext; + transfer_type type; + }; + + List *transfer_ext; + +private: + void cfgMethod(ICAP::Method m); + void cfgIntHeader(const HttpHeader *h, const char *fname, int &value); +}; + + + +#endif /* SQUID_ICAPOPTIONS_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPServiceRep.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,382 @@ +/* + * DEBUG: section 93 ICAP (RFC 3507) Client + */ + +#include "squid.h" +#include "TextException.h" +#include "ICAPServiceRep.h" +#include "ICAPOptions.h" +#include "ICAPOptXact.h" +#include "ConfigParser.h" + +CBDATA_CLASS_INIT(ICAPServiceRep); + +ICAPServiceRep::ICAPServiceRep(): method(ICAP::methodNone), + point(ICAP::pointNone), port(-1), bypass(false), unreachable(false), + theOptions(NULL), theState(stateInit), notifying(false), self(NULL) +{ +} + +ICAPServiceRep::~ICAPServiceRep() +{ + Must(!waiting()); + changeOptions(0); +} + +const char * +ICAPServiceRep::methodStr() const +{ + return ICAP::methodStr(method); +} + +ICAP::Method +ICAPServiceRep::parseMethod(const char *str) const +{ + if (!strncasecmp(str, "REQMOD", 6)) + return ICAP::methodReqmod; + + if (!strncasecmp(str, "RESPMOD", 7)) + return ICAP::methodRespmod; + + return ICAP::methodNone; +} + + +const char * +ICAPServiceRep::vectPointStr() const +{ + return ICAP::vectPointStr(point); +} + +ICAP::VectPoint +ICAPServiceRep::parseVectPoint(const char *service) const +{ + const char *t = service; + const char *q = strchr(t, '_'); + + if (q) + t = q + 1; + + if (!strcasecmp(t, "precache")) + return ICAP::pointPreCache; + + if (!strcasecmp(t, "postcache")) + return ICAP::pointPostCache; + + return ICAP::pointNone; +} + +bool +ICAPServiceRep::configure(Pointer &aSelf) +{ + assert(!self && aSelf != NULL); + self = aSelf; + + char *service_type = NULL; + + ConfigParser::ParseString(&key); + ConfigParser::ParseString(&service_type); + ConfigParser::ParseBool(&bypass); + ConfigParser::ParseString(&uri); + + debug(3, 5) ("ICAPService::parseConfigLine (line %d): %s %s %d\n", config_lineno, key.buf(), service_type, bypass); + + method = parseMethod(service_type); + point = parseVectPoint(service_type); + + debug(3, 5) ("ICAPService::parseConfigLine (line %d): service is %s_%s\n", config_lineno, methodStr(), vectPointStr()); + + if (uri.cmp("icap://", 7) != 0) { + debug(3, 0) ("ICAPService::parseConfigLine (line %d): wrong uri: %s\n", config_lineno, uri.buf()); + return false; + } + + const char *s = uri.buf() + 7; + + const char *e; + + bool have_port = false; + + if ((e = strchr(s, ':')) != NULL) { + have_port = true; + } else if ((e = strchr(s, '/')) != NULL) { + have_port = false; + } else { + return false; + } + + int len = e - s; + host.limitInit(s, len); + s = e; + + if (have_port) { + s++; + + if ((e = strchr(s, '/')) != NULL) { + char *t; + port = strtoul(s, &t, 0) % 65536; + + if (t != e) { + return false; + } + + s = e; + + if (s[0] != '/') { + return false; + } + } + } else { + + struct servent *serv = getservbyname("icap", "tcp"); + + if (serv) { + port = htons(serv->s_port); + } else { + port = 1344; + } + } + + s++; + e = strchr(s, '\0'); + len = e - s; + + if (len > 1024) { + debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); + } + + resource.limitInit(s, len + 1); + + if ((bypass != 0) && (bypass != 1)) { + return false; + } + + return true; + +}; + +void ICAPServiceRep::invalidate() +{ + assert(self != NULL); + self = NULL; // may destroy us and, hence, invalidate cbdata(this) + // TODO: it would be nice to invalidate cbdata(this) when not destroyed +} + +bool ICAPServiceRep::up() const +{ + return self != NULL && theState == stateUp; +} + +bool ICAPServiceRep::wantsPreview(size_t &wantedSize) const +{ + Must(up()); + + if (theOptions->preview < 0) + return false; + + wantedSize = theOptions->preview; + + return true; +} + +bool ICAPServiceRep::allows204() const +{ + Must(up()); + return true; // in the future, we may have ACLs to prevent 204s +} + + +static +void ICAPServiceRep_noteTimeToUpdate(void *data) +{ + ICAPServiceRep *service = static_cast(data); + Must(service); + service->noteTimeToUpdate(); +} + +void ICAPServiceRep::noteTimeToUpdate() +{ + if (!self || waiting()) { + debugs(93,5, "ICAPService ignores options update " << status()); + return; + } + + debugs(93,5, "ICAPService performs a regular options update " << status()); + startGettingOptions(); +} + +static +void ICAPServiceRep_noteTimeToNotify(void *data) +{ + ICAPServiceRep *service = static_cast(data); + Must(service); + service->noteTimeToNotify(); +} + +void ICAPServiceRep::noteTimeToNotify() +{ + Must(!notifying); + notifying = true; + debugs(93,7, "ICAPService notifies " << theClients.size() << " clients " << + status()); + + // note: we must notify even if we are invalidated + + Pointer us = NULL; + + while (!theClients.empty()) { + Client i = theClients.pop_back(); + us = i.service; // prevent callbacks from destroying us while we loop + + if (cbdataReferenceValid(i.data)) + (*i.callback)(i.data, us); + + cbdataReferenceDone(i.data); + } + + notifying = false; +} + +void ICAPServiceRep::callWhenReady(Callback *cb, void *data) +{ + Must(cb); + Must(self != NULL); + + Client i; + i.service = self; + i.callback = cb; + i.data = cbdataReference(data); + theClients.push_back(i); + + if (waiting() || notifying) + return; // do nothing, we will be picked up in noteTimeToNotify() + + if (needNewOptions()) + startGettingOptions(); + else + scheduleNotification(); +} + +void ICAPServiceRep::scheduleNotification() +{ + debugs(93,7, "ICAPService will notify " << theClients.size() << " clients"); + eventAdd("ICAPServiceRep::noteTimeToNotify", &ICAPServiceRep_noteTimeToNotify, this, 0, 0, true); +} + +bool ICAPServiceRep::waiting() const +{ + return theState == stateWait; +} + +bool ICAPServiceRep::needNewOptions() const +{ + return !theOptions || !theOptions->fresh(); +} + +void ICAPServiceRep::changeOptions(ICAPOptions *newOptions) +{ + debugs(93,9, "ICAPService changes options from " << theOptions << " to " << + newOptions); + delete theOptions; + theOptions = newOptions; +} + +static +void ICAPServiceRep_noteNewOptions(ICAPOptXact *x, void *data) +{ + ICAPServiceRep *service = static_cast(data); + Must(service); + service->noteNewOptions(x); +} + +void ICAPServiceRep::noteNewOptions(ICAPOptXact *x) +{ + Must(x); + Must(waiting()); + + theState = stateDown; // default in case we fail to set new options + + changeOptions(x->options); + x->options = NULL; + delete x; + + if (theOptions && theOptions->valid()) + theState = stateUp; + + debugs(93,6, "ICAPService got new options and is now " << + (up() ? "up" : "down")); + + scheduleUpdate(); + + scheduleNotification(); +} + +void ICAPServiceRep::startGettingOptions() +{ + debugs(93,6, "ICAPService will get new options " << status()); + theState = stateWait; + + ICAPOptXact *x = new ICAPOptXact; + x->start(self, &ICAPServiceRep_noteNewOptions, this); + // TODO: timeout incase ICAPOptXact never calls us back? +} + +void ICAPServiceRep::scheduleUpdate() +{ + int delay = -1; + + if (theOptions && theOptions->valid()) { + const time_t expire = theOptions->expire(); + + if (expire > squid_curtime) + delay = expire - squid_curtime; + else + if (expire >= 0) + delay = 1; // delay for expired or 'expiring now' options + else + delay = 60*60; // default for options w/o known expiration time + } else { + delay = 5*60; // delay for a down service + } + + if (delay <= 0) { + debugs(93,0, "internal error: ICAPServiceRep failed to compute options update schedule"); + delay = 5*60; // delay for an internal error + } + + // with zero delay, the state changes to stateWait before + // notifications are sent out to clients + assert(delay > 0); + + debugs(93,7, "ICAPService will update options in " << delay << " sec"); + + eventAdd("ICAPServiceRep::noteTimeToUpdate", + &ICAPServiceRep_noteTimeToUpdate, this, delay, 0, true); + + // XXX: prompt updates of valid options should not disable concurrent ICAP + // xactions. 'Wait' state should not mark the service 'down'! This will + // also remove 'delay == 0' as a special case above. +} + +const char *ICAPServiceRep::status() const +{ + if (!self) + return "[invalidated]"; + + switch (theState) { + + case stateInit: + return "[init]"; + + case stateWait: + return "[wait]"; + + case stateUp: + return "[up]"; + + case stateDown: + return "[down]"; + } + + return "[unknown]"; +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPServiceRep.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,138 @@ + +/* + * $Id: ICAPServiceRep.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPSERVICEREP_H +#define SQUID_ICAPSERVICEREP_H + +#include "ICAPElements.h" + +class ICAPOptions; + +class ICAPOptXact; + +/* The ICAP service representative maintains information about a single ICAP + service that Squid communicates with. The representative initiates OPTIONS + requests to the service to keep cached options fresh. One ICAP server may + host many ICAP services */ + +class ICAPServiceRep : public RefCountable +{ + +public: + typedef RefCount Pointer; + +public: + ICAPServiceRep(); + virtual ~ICAPServiceRep(); + + bool configure(Pointer &aSelf); // needs self pointer for ICAPOptXact + void invalidate(); // call when the service is no longer needed or valid + + const char *methodStr() const; + const char *vectPointStr() const; + + bool up() const; + + /* Service is "up" iff there is a fresh cached OPTIONS response. To + get an OPTIONS response, ICAPServiceRep does an OPTIONS + transaction. Failed transaction results in a "down" service. The + Callback is called if/once the service is in a steady ("up" or + "down") state. */ + typedef void Callback(void *data, Pointer &service); + void callWhenReady(Callback *cb, void *data); + + + // the methods below can only be called on an up() service + + bool wantsPreview(size_t &wantedSize) const; + bool allows204() const; + +public: + String key; + ICAP::Method method; + ICAP::VectPoint point; + String uri; // service URI + + // URI components + String host; + int port; + String resource; + + // non-options flags; TODO: check that both are used. + bool bypass; + bool unreachable; + +public: // treat these as private, they are for callbacks only + void noteTimeToUpdate(); + void noteTimeToNotify(); + void noteNewOptions(ICAPOptXact *x); + +private: + // stores Prepare() callback info + + struct Client + { + Pointer service; // one for each client to preserve service + Callback *callback; + void *data; + }; + + typedef Vector Clients; + Clients theClients; // all clients waiting for a call back + + ICAPOptions *theOptions; + + typedef enum { stateInit, stateWait, stateUp, stateDown } State; + State theState; + bool notifying; // may be true in any state except for the initial + +private: + ICAP::Method parseMethod(const char *) const; + ICAP::VectPoint parseVectPoint(const char *) const; + + bool waiting() const; + bool needNewOptions() const; + + void scheduleNotification(); + void changeOptions(ICAPOptions *newOptions); + void startGettingOptions(); + void scheduleUpdate(); + + const char *status() const; + + Pointer self; + CBDATA_CLASS2(ICAPServiceRep); +}; + + +#endif /* SQUID_ICAPSERVICEREP_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPXaction.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,400 @@ +/* + * DEBUG: section 93 ICAP (RFC 3507) Client + */ + +#include "squid.h" +#include "comm.h" +#include "HttpReply.h" +#include "ICAPXaction.h" +#include "ICAPClient.h" +#include "TextException.h" + +/* comm module handlers (wrappers around corresponding ICAPXaction methods */ + +// TODO: Teach comm module to call object methods directly + +//CBDATA_CLASS_INIT(ICAPXaction); + +static +ICAPXaction &ICAPXaction_fromData(void *data) +{ + ICAPXaction *x = static_cast(data); + assert(x); + return *x; +} + +static +void ICAPXaction_noteCommTimedout(int, void *data) +{ + ICAPXaction_fromData(data).noteCommTimedout(); +} + +static +void ICAPXaction_noteCommClosed(int, void *data) +{ + ICAPXaction_fromData(data).noteCommClosed(); +} + +static +void ICAPXaction_noteCommConnected(int, comm_err_t status, int xerrno, void *data) +{ + ICAPXaction_fromData(data).noteCommConnected(status); +} + +static +void ICAPXaction_noteCommWrote(int, char *, size_t size, comm_err_t status, void *data) +{ + ICAPXaction_fromData(data).noteCommWrote(status, size); +} + +static +void ICAPXaction_noteCommRead(int, char *, size_t size, comm_err_t status, int xerrno, void *data) +{ + ICAPXaction_fromData(data).noteCommRead(status, size); +} + +ICAPXaction::ICAPXaction(const char *aTypeName): + connection(-1), + commBuf(NULL), commBufSize(0), + commEof(false), + connector(NULL), reader(NULL), writer(NULL), closer(NULL), + typeName(aTypeName), + theService(NULL), + inCall(NULL) +{ + readBuf.init(SQUID_TCP_SO_RCVBUF, SQUID_TCP_SO_RCVBUF); + commBuf = (char*)memAllocBuf(SQUID_TCP_SO_RCVBUF, &commBufSize); + // make sure maximum readBuf space does not exceed commBuf size + Must(static_cast(readBuf.potentialSpaceSize()) <= commBufSize); +} + +ICAPXaction::~ICAPXaction() +{ + doStop(); + readBuf.clean(); + memFreeBuf(commBufSize, commBuf); +} + +// TODO: obey service-specific, OPTIONS-reported connection limit +void ICAPXaction::openConnection() +{ + const ICAPServiceRep &s = service(); + // TODO: check whether NULL domain is appropriate here + connection = pconnPop(s.host.buf(), s.port, NULL); + + if (connection < 0) { + connection = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, + COMM_NONBLOCKING, s.uri.buf()); + + if (connection < 0) + throw TexcHere("cannot connect to ICAP service " /* + uri */); + } + + debugs(93,3, typeName << " opens connection to " << s.host.buf() << ":" << s.port); + + commSetTimeout(connection, Config.Timeout.connect, + &ICAPXaction_noteCommTimedout, this); + + closer = &ICAPXaction_noteCommClosed; + comm_add_close_handler(connection, closer, this); + + connector = &ICAPXaction_noteCommConnected; + commConnectStart(connection, s.host.buf(), s.port, connector, this); +} + +void ICAPXaction::closeConnection() +{ + if (connection >= 0) { + commSetTimeout(connection, -1, NULL, NULL); + + if (closer) { + comm_remove_close_handler(connection, closer, this); + closer = NULL; + } + + cancelRead(); + + comm_close(connection); + + connector = NULL; + connection = -1; + } +} + +// connection with the ICAP service established +void ICAPXaction::noteCommConnected(comm_err_t commStatus) +{ + ICAPXaction_Enter(noteCommConnected); + + Must(connector); + connector = NULL; + Must(commStatus == COMM_OK); + + handleCommConnected(); + + ICAPXaction_Exit(); +} + +void ICAPXaction::scheduleWrite(MemBuf &buf) +{ + // comm module will free the buffer + writer = &ICAPXaction_noteCommWrote; + comm_old_write_mbuf(connection, &buf, writer, this); +} + +void ICAPXaction::noteCommWrote(comm_err_t commStatus, size_t size) +{ + ICAPXaction_Enter(noteCommWrote); + + Must(writer); + writer = NULL; + + Must(commStatus == COMM_OK); + + handleCommWrote(size); + + ICAPXaction_Exit(); +} + +// communication timeout with the ICAP service +void ICAPXaction::noteCommTimedout() +{ + ICAPXaction_Enter(noteCommTimedout); + + handleCommTimedout(); + + ICAPXaction_Exit(); +} + +void ICAPXaction::handleCommTimedout() +{ + mustStop("connection with ICAP service timed out"); +} + +// unexpected connection close while talking to the ICAP service +void ICAPXaction::noteCommClosed() +{ + closer = NULL; + ICAPXaction_Enter(noteCommClosed); + + handleCommClosed(); + + ICAPXaction_Exit(); +} + +void ICAPXaction::handleCommClosed() +{ + mustStop("ICAP service connection externally closed"); +} + +bool ICAPXaction::done() const +{ + if (stopReason != NULL) // mustStop() has been called + return true; + + return doneAll(); +} + +bool ICAPXaction::doneAll() const +{ + return !connector && !reader && !writer; +} + +void ICAPXaction::scheduleRead() +{ + Must(connection >= 0); + Must(!reader); + Must(readBuf.hasSpace()); + + reader = &ICAPXaction_noteCommRead; + /* + * See comments in ICAPXaction.h about why we use commBuf + * here instead of reading directly into readBuf.buf. + */ + + comm_read(connection, commBuf, readBuf.spaceSize(), reader, this); +} + +// comm module read a portion of the ICAP response for us +void ICAPXaction::noteCommRead(comm_err_t commStatus, size_t sz) +{ + ICAPXaction_Enter(noteCommRead); + + Must(reader); + reader = NULL; + + Must(commStatus == COMM_OK); + Must(sz >= 0); + + debugs(93, 5, "read " << sz << " bytes"); + + /* + * See comments in ICAPXaction.h about why we use commBuf + * here instead of reading directly into readBuf.buf. + */ + + if (sz > 0) + readBuf.append(commBuf, sz); + else + commEof = true; + + handleCommRead(sz); + + ICAPXaction_Exit(); +} + +void ICAPXaction::cancelRead() +{ + if (reader) { + // check callback presence because comm module removes + // fdc_table[].read.callback after the actual I/O but + // before we get the callback via a queued event. + // These checks try to mimic the comm_read_cancel() assertions. + + if (comm_has_pending_read(connection) && + !comm_has_pending_read_callback(connection)) + comm_read_cancel(connection, reader, this); + + reader = NULL; + } +} + +bool ICAPXaction::parseHttpMsg(HttpMsg *msg) +{ + debugs(93, 5, "have " << readBuf.contentSize() << " head bytes to parse"); + + http_status error = HTTP_STATUS_NONE; + const bool parsed = msg->parse(&readBuf, commEof, &error); + Must(parsed || !error); // success or need more data + + if (!parsed) { // need more data + Must(mayReadMore()); + msg->reset(); + return false; + } + + readBuf.consume(msg->hdr_sz); + return true; +} + +bool ICAPXaction::mayReadMore() const +{ + return !doneReading() && // will read more data + readBuf.hasSpace(); // have space for more data +} + +bool ICAPXaction::doneReading() const +{ + return commEof; +} + +void ICAPXaction::mustStop(const char *aReason) +{ + Must(inCall); // otherwise nobody will call doStop() + Must(!stopReason); + Must(aReason); + stopReason = aReason; + debugs(93, 5, typeName << " will stop, reason: " << stopReason); +} + +// internal cleanup +void ICAPXaction::doStop() +{ + debugs(93, 5, typeName << "::doStop " << status()); + + closeConnection(); // TODO: pconn support: close iff bad connection +} + +void ICAPXaction::service(ICAPServiceRep::Pointer &aService) +{ + Must(!theService); + Must(aService != NULL); + theService = aService; +} + +ICAPServiceRep &ICAPXaction::service() +{ + Must(theService != NULL); + return *theService; +} + +bool ICAPXaction::callStart(const char *method) +{ + debugs(93, 5, typeName << "::" << method << " called " << status()); + + if (inCall) { + // this may happen when we have bugs or when arguably buggy + // comm interface calls us while we are closing the connection + debugs(93, 5, typeName << "::" << inCall << " is in progress; " << + typeName << "::" << method << " cancels reentry."); + return false; + } + + inCall = method; + return true; +} + +void ICAPXaction::callException(const TextException &e) +{ + debugs(93, 4, typeName << "::" << inCall << " caught an exception: " << + e.message << ' ' << status()); + + if (!done()) + mustStop("exception"); +} + +void ICAPXaction::callEnd() +{ + if (done()) { + debugs(93, 5, "ICAPXaction::" << inCall << " ends xaction " << + status()); + doStop(); // may delete us + return; + } + + debugs(93, 6, typeName << "::" << inCall << " ended " << status()); + inCall = NULL; +} + +// returns a temporary string depicting transaction status, for debugging +const char *ICAPXaction::status() const +{ + static MemBuf buf; + buf.reset(); + + buf.append("[", 1); + + fillPendingStatus(buf); + buf.append("/", 1); + fillDoneStatus(buf); + + buf.append("]", 1); + + buf.terminate(); + + return buf.content(); +} + +void ICAPXaction::fillPendingStatus(MemBuf &buf) const +{ + if (connection >= 0) { + buf.Printf("Comm(%d", connection); + + if (writer) + buf.append("w", 1); + + if (reader) + buf.append("r", 1); + + buf.append(")", 1); + } +} + +void ICAPXaction::fillDoneStatus(MemBuf &buf) const +{ + if (connection >= 0 && commEof) + buf.Printf("Comm(%d)", connection); + + if (stopReason != NULL) + buf.Printf("Stopped"); +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/ICAPXaction.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,164 @@ + +/* + * $Id: ICAPXaction.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPXACTION_H +#define SQUID_ICAPXACTION_H + +#include "MemBuf.h" +#include "ICAPServiceRep.h" + +class HttpMsg; + +class TextException; + +/* The ICAP Xaction implements message pipe sink and source interfaces. It + * receives virgin HTTP messages, communicates with the ICAP server, and sends + * the adapted messages back. ICAPClient is the "owner" of the ICAPXaction. */ + +// Note: ICAPXaction must be the first parent for object-unaware cbdata to work + +class ICAPXaction: public RefCountable +{ + +public: + typedef RefCount Pointer; + +public: + ICAPXaction(const char *aTypeName); + virtual ~ICAPXaction(); + + // comm handler wrappers, treat as private + void noteCommConnected(comm_err_t status); + void noteCommWrote(comm_err_t status, size_t sz); + void noteCommRead(comm_err_t status, size_t sz); + void noteCommTimedout(); + void noteCommClosed(); + +protected: + // Set or get service pointer; ICAPXaction cbdata-locks it. + void service(ICAPServiceRep::Pointer &aService); + ICAPServiceRep &service(); + + // comm hanndlers; called by comm handler wrappers + virtual void handleCommConnected() = 0; + virtual void handleCommWrote(size_t sz) = 0; + virtual void handleCommRead(size_t sz) = 0; + virtual void handleCommTimedout(); + virtual void handleCommClosed(); + + void openConnection(); + void closeConnection(); + void scheduleRead(); + void scheduleWrite(MemBuf &buf); + + void cancelRead(); + + bool parseHttpMsg(HttpMsg *msg); // true=success; false=needMore; throw=err + bool mayReadMore() const; + virtual bool doneReading() const; + + bool done() const; + virtual bool doneAll() const; + virtual void doStop(); + void mustStop(const char *reason); + + // returns a temporary string depicting transaction status, for debugging + const char *status() const; + virtual void fillPendingStatus(MemBuf &buf) const; + virtual void fillDoneStatus(MemBuf &buf) const; + +protected: + int connection; // FD of the ICAP server connection + + /* + * We have two read buffers. We would prefer to read directly + * into the MemBuf, but since comm_read isn't MemBuf-aware, and + * uses event-delayed callbacks, it leaves the MemBuf in an + * inconsistent state. There would be data in the buffer, but + * MemBuf.size won't be updated until the (delayed) callback + * occurs. To avoid that situation we use a plain buffer + * (commBuf) and then copy (append) its contents to readBuf in + * the callback. If comm_read ever becomes MemBuf-aware, we + * can eliminate commBuf and this extra buffer copy. + */ + MemBuf readBuf; + char *commBuf; + size_t commBufSize; + bool commEof; + + const char *stopReason; + + // asynchronous call maintenance + bool callStart(const char *method); + void callException(const TextException &e); + void callEnd(); + + // active (pending) comm callbacks for the ICAP server connection + CNCB *connector; + IOCB *reader; + CWCB *writer; + PF *closer; + + const char *typeName; // the type of the final class (child), for debugging + +private: + ICAPServiceRep::Pointer theService; + + const char *inCall; // name of the asynchronous call being executed, if any + + //CBDATA_CLASS2(ICAPXaction); +}; + +// call guards for all "asynchronous" note*() methods + +// asynchronous call entry: +// - open the try clause; +// - call callStart(). +#define ICAPXaction_Enter(method) \ + try { \ + if (!callStart(#method)) \ + return; + +// asynchronous call exit: +// - close the try clause; +// - catch exceptions; +// - let callEnd() handle transaction termination conditions +#define ICAPXaction_Exit() \ + } \ + catch (const TextException &e) { \ + callException(e); \ + } \ + callEnd(); + + +#endif /* SQUID_ICAPXACTION_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/Makefile.am Wed Feb 14 13:35:39 2007 @@ -0,0 +1,38 @@ +# +# Makefile for the Squid Object Cache server +# +# $Id: Makefile.am,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ +# +# Uncomment and customize the following to suit your needs: +# + +install: all +install-strip: all + +AM_CFLAGS = @SQUID_CFLAGS@ +AM_CXXFLAGS = @SQUID_CXXFLAGS@ + +SUBDIRS = + +EXTRA_LIBRARIES = \ + libicap.a + +noinst_LIBRARIES = \ + libicap.a + +libicap_a_SOURCES = \ + ChunkedCodingParser.cc \ + ICAPClientRespmodPrecache.cc \ + ICAPClientReqmodPrecache.cc \ + ICAPClient.cc \ + ICAPElements.cc \ + ICAPXaction.cc \ + ICAPOptXact.cc \ + ICAPModXact.cc \ + ICAPServiceRep.cc \ + ICAPConfig.cc \ + ICAPOptions.cc \ + TextException.cc \ + MsgPipe.cc + +INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/MsgPipe.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,114 @@ +#include "squid.h" +#include "MsgPipe.h" +#include "MsgPipeSource.h" +#include "MsgPipeSink.h" +#include "MsgPipeData.h" + +#include "LeakFinder.h" +LeakFinder *MsgPipeLeaker = new LeakFinder; + +CBDATA_CLASS_INIT(MsgPipe); + +// static event callback template +// XXX: refcounting needed to make sure destination still exists +#define MsgPipe_MAKE_CALLBACK(callName, destination) \ +static \ +void MsgPipe_send ## callName(void *p) { \ + MsgPipe *pipe = static_cast(p); \ + if (pipe && pipe->canSend(pipe->destination, #callName, false)) \ + pipe->destination->note##callName(pipe); \ +} + +// static event callbacks +MsgPipe_MAKE_CALLBACK(SourceStart, sink) +MsgPipe_MAKE_CALLBACK(SourceProgress, sink) +MsgPipe_MAKE_CALLBACK(SourceFinish, sink) +MsgPipe_MAKE_CALLBACK(SourceAbort, sink) +MsgPipe_MAKE_CALLBACK(SinkNeed, source) +MsgPipe_MAKE_CALLBACK(SinkAbort, source) + + +MsgPipe::MsgPipe(const char *aName): name(aName), + data(NULL), source(NULL), sink(NULL) +{ + leakAdd(this, MsgPipeLeaker); +} + +MsgPipe::~MsgPipe() +{ + delete data; + delete source; + delete sink; + leakFree(this, MsgPipeLeaker); +}; + +void MsgPipe::sendSourceStart() +{ + leakTouch(this, MsgPipeLeaker); + debug(99,5)("MsgPipe::sendSourceStart() called\n"); + sendLater("SourceStart", &MsgPipe_sendSourceStart, sink); +} + + + +void MsgPipe::sendSourceProgress() +{ + leakTouch(this, MsgPipeLeaker); + debug(99,5)("MsgPipe::sendSourceProgress() called\n"); + sendLater("SourceProgress", &MsgPipe_sendSourceProgress, sink); +} + +void MsgPipe::sendSourceFinish() +{ + leakTouch(this, MsgPipeLeaker); + debug(99,5)("MsgPipe::sendSourceFinish() called\n"); + sendLater("sendSourceFinish", &MsgPipe_sendSourceFinish, sink); + source = NULL; +} + +void MsgPipe::sendSourceAbort() +{ + leakTouch(this, MsgPipeLeaker); + debug(99,5)("MsgPipe::sendSourceAbort() called\n"); + sendLater("SourceAbort", &MsgPipe_sendSourceAbort, sink); + source = NULL; +} + + +void MsgPipe::sendSinkNeed() +{ + leakTouch(this, MsgPipeLeaker); + debug(99,5)("MsgPipe::sendSinkNeed() called\n"); + sendLater("SinkNeed", &MsgPipe_sendSinkNeed, source); +} + +void MsgPipe::sendSinkAbort() +{ + leakTouch(this, MsgPipeLeaker); + debug(99,5)("MsgPipe::sendSinkAbort() called\n"); + sendLater("SinkAbort", &MsgPipe_sendSinkAbort, source); + sink = NULL; +} + +void MsgPipe::sendLater(const char *callName, EVH * handler, MsgPipeEnd *destination) +{ + leakTouch(this, MsgPipeLeaker); + + if (canSend(destination, callName, true)) + eventAdd(callName, handler, this, 0.0, 0, true); +} + +bool MsgPipe::canSend(MsgPipeEnd *destination, const char *callName, bool future) +{ + leakTouch(this, MsgPipeLeaker); + const bool res = destination != NULL; + const char *verb = future ? + (res ? "will send " : "wont send ") : + (res ? "sends " : "ignores "); + debugs(99,5, "MsgPipe " << name << "(" << this << ") " << + verb << callName << " to the " << + (destination ? destination->kind() : "destination") << "(" << + destination << "); " << + "data: " << data << "; source: " << source << "; sink " << sink); + return res; +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/MsgPipe.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,87 @@ + +/* + * $Id: MsgPipe.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_MSGPIPE_H +#define SQUID_MSGPIPE_H + + +// MsgPipe is a unidirectional communication channel for asynchronously +// transmitting potentially large messages. It aggregates the message +// being piped and pointers to the message sender and recepient. +// MsgPipe also provides convenience wrappers for asynchronous calls to +// recepient's and sender's note*() methods. + +class MsgPipeData; + +class MsgPipeEnd; + +class MsgPipeSource; + +class MsgPipeSink; + +class MsgPipe : public RefCountable +{ + +public: + typedef RefCount Pointer; + + MsgPipe(const char *aName = "anonym"); + ~MsgPipe(); + + // the pipe source calls these to notify the sink + void sendSourceStart(); + void sendSourceProgress(); + void sendSourceFinish(); + void sendSourceAbort(); + + // the pipe sink calls these to notify the source + void sendSinkNeed(); + void sendSinkAbort(); + + // private method exposed for the event handler only + bool canSend(MsgPipeEnd *destination, const char *callName, bool future); + +public: + const char *name; // unmanaged pointer used for debugging only + + MsgPipeData *data; + MsgPipeSource *source; + MsgPipeSink *sink; + +private: + void sendLater(const char *callName, EVH * handler, MsgPipeEnd *destination); + + CBDATA_CLASS2(MsgPipe); +}; + +#endif /* SQUID_MSGPIPE_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/MsgPipeData.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,76 @@ + +/* + * $Id: MsgPipeData.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_MSGPIPEDATA_H +#define SQUID_MSGPIPEDATA_H + +#include "HttpMsg.h" +#include "MemBuf.h" + +// MsgPipeData contains information about the HTTP message being sent +// from the pipe source to the sink. Since the entire message body may be +// large, only partial information about the body is kept. For HTTP +// responses, request header information is also available as metadata. + +class HttpRequest; + +class MsgPipeData +{ + +public: + MsgPipeData(): header(0), body(0), cause(0) {}; + + ~MsgPipeData() + { + assert(NULL == cause); + assert(NULL == header); + + if (body) { + body->clean(); + delete body; + } + }; + +public: + typedef HttpMsg Header; + typedef MemBuf Body; + + // message being piped + Header *header; // parsed HTTP status/request line and headers + Body *body; // a buffer for decoded HTTP body piping + + // HTTP request header for piped responses (the cause of the response) + HttpRequest *cause; +}; + +#endif /* SQUID_MSGPIPEDATA_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/MsgPipeEnd.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,50 @@ + +/* + * $Id: MsgPipeEnd.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_MSGPIPEEND_H +#define SQUID_MSGPIPEEND_H + +// MsgPipeEnd is a common part of the MsgPipeSource and MsgPipeSink interfaces. +// Mesage pipe ends must be refcounted so that the recepient does not disappear +// while a message is being [asynchoronously] delivered to it. + +class MsgPipeEnd: public RefCountable +{ + +public: + virtual ~MsgPipeEnd() {} + + virtual const char *kind() const = 0; // "sink" or "source", for debugging +}; + +#endif /* SQUID_MSGPIPEEND_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/MsgPipeSink.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,56 @@ + +/* + * $Id: MsgPipeSink.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_MSGPIPESINK_H +#define SQUID_MSGPIPESINK_H + +#include "MsgPipeEnd.h" + +// MsgPipeSink is an interface for the recepient of a given message +// over a given message pipe. Use MsgPipe to call sink methods. + +class MsgPipe; + +class MsgPipeSink: public MsgPipeEnd +{ + +public: + virtual void noteSourceStart(MsgPipe *p) = 0; + virtual void noteSourceProgress(MsgPipe *p) = 0; + virtual void noteSourceFinish(MsgPipe *p) = 0; + virtual void noteSourceAbort(MsgPipe *p) = 0; + + virtual const char *kind() const { return "sink"; } +}; + +#endif /* SQUID_MSGPIPESINK_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/MsgPipeSource.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,54 @@ + +/* + * $Id: MsgPipeSource.h,v 1.1.2.1 2005/11/21 21:05:51 dwsquid Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_MSGPIPESOURCE_H +#define SQUID_MSGPIPESOURCE_H + +#include "MsgPipeEnd.h" + +// MsgPipeSource is an interface for the sender of a given message +// over a given message pipe. Use MsgPipe to call source methods. + +class MsgPipe; + +class MsgPipeSource: public MsgPipeEnd +{ + +public: + virtual const char *kind() const { return "source"; } + + virtual void noteSinkNeed(MsgPipe *p) = 0; + virtual void noteSinkAbort(MsgPipe *p) = 0; +}; + +#endif /* SQUID_MSGPIPESOURCE_H */ --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/TextException.cc Wed Feb 14 13:35:39 2007 @@ -0,0 +1,27 @@ +#include "squid.h" +#include "TextException.h" + +TextException::TextException(const char *aMsg, const char *aFileName, int aLineNo): + message(xstrdup(aMsg)), theFileName(aFileName), theLineNo(aLineNo) +{} + +TextException::~TextException() +{ + xfree(message); +} + +void Throw(const char *message, const char *fileName, int lineNo) +{ + + // or should we let the exception recepient print the exception instead? + + if (fileName) { + debugs(0, 3, fileName << ':' << lineNo << ": exception" << + (message ? ": " : ".") << (message ? message : "")); + } else { + debugs(0, 3, "exception" << + (message ? ": " : ".") << (message ? message : "")); + } + + throw TextException(message, fileName, lineNo); +} --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAP/TextException.h Wed Feb 14 13:35:39 2007 @@ -0,0 +1,46 @@ +#ifndef SQUID__TEXTEXCEPTION_H +#define SQUID__TEXTEXCEPTION_H + +// Origin: xstd/TextException + + +// simple exception to report custom errors +// we may want to change the interface to be able to report system errors + +class TextException +{ + +public: + TextException(const char *aMessage, const char *aFileName = 0, int aLineNo = -1); + ~TextException(); + + // ostream &print(ostream &os) const; + +public: + char *message; // read-only + +protected: + // optional location information + const char *theFileName; + int theLineNo; +}; + +//inline +//ostream &operator <<(ostream &os, const TextException &exx) { +// return exx.print(os); +//} + +#if !defined(TexcHere) +# define TexcHere(msg) TextException((msg), __FILE__, __LINE__) +#endif + +extern void Throw(const char *message, const char *fileName, int lineNo); + +// Must(condition) is like assert(condition) but throws an exception instead +#if !defined(Must) +# define Must(cond) ((cond) ? \ + (void)0 : \ + (void)Throw(#cond, __FILE__, __LINE__)) +#endif + +#endif /* SQUID__TEXTEXCEPTION_H */