Support libecap v0.2.0; fixed eCAP body handling and logging bugs. Summary of changes follows. Please see merge log for many details. libecap v0.2.0 support: accept/update/log eCAP transaction meta-info. libecap v0.2.0 support: supply client IP and username to eCAP adapter. libecap v0.1.0 support: Support blockVirgin() API, serving ERR_ACCESS_DENIED. Use pkg-config's PKG_CHECK_MODULES to check for and link with libecap. Support adapter-specific parameters as a part of ecap_service configuration. Allow uri=value parameter when specifying adaptation service URIs. Fixed virgin body handling in our eCAP transaction wrapper (Ecap::XactionRep). Fixed BodyPipe.cc:144 "!theConsumer" assertion. Log "important" messages from eCAP adapters with DBG_IMPORTANT not DBG_DATA! Added XXXs to identify old unrelated problems to be fixed separately. === modified file 'configure.ac' --- configure.ac 2011-02-21 03:41:11 +0000 +++ configure.ac 2011-02-21 05:12:02 +0000 @@ -901,8 +901,10 @@ ] ) +dnl Necessary if the first PKG_CHECK_MODULES call is conditional +PKG_PROG_PKG_CONFIG + dnl Perform configuration consistency checks for eCAP -ECAPLIB="" if test "x$squid_opt_use_ecap" = "xyes"; then dnl eCAP support requires loadable modules, which are enabled by default @@ -912,17 +914,17 @@ fi dnl eCAP support requires libecap - AC_CHECK_LIB([ecap], [main], - [ECAPLIB="-lecap"], - [AC_MSG_FAILURE([eCAP support requires libecap library, but no usable library was found])] - ) + dnl This Squid supports libecap v0.2.x + dnl Use EXT prefix or libtool will get confused with two libecaps: + dnl one "external" (that we check for here) and one our own convenience lib + PKG_CHECK_MODULES(EXTLIBECAP, [libecap > 0.2 libecap < 0.3]) fi AM_CONDITIONAL(USE_ECAP, test "x$squid_opt_use_ecap" = "xyes") if test "x$squid_opt_use_ecap" = "xyes"; then AC_DEFINE(USE_ECAP,1,[Enable eCAP support]) - ECAP_LIBS="ecap/libecap.la $ECAP_LIBS" + ECAP_LIBS="ecap/libxecap.la" squid_opt_use_adaptation=yes else AC_DEFINE(USE_ECAP,0,[Disable eCAP support]) @@ -930,8 +932,6 @@ fi dnl convenience library AC_SUBST(ECAP_LIBS) -dnl -lecap if needed -AC_SUBST(ECAPLIB) dnl enable adaptation if requested by specific adaptation mechanisms === modified file 'src/AccessLogEntry.h' --- src/AccessLogEntry.h 2010-08-14 14:51:40 +0000 +++ src/AccessLogEntry.h 2011-02-21 05:12:02 +0000 @@ -155,8 +155,8 @@ Headers() : request(NULL), adapted_request(NULL), -#if ICAP_CLIENT - icap(NULL), +#if USE_ADAPTATION + adapt_last(NULL), #endif reply(NULL) {} @@ -165,8 +165,9 @@ char *adapted_request; //< HTTP request headers after adaptation and redirection -#if ICAP_CLIENT - char * icap; ///< last matching ICAP response header. +#if USE_ADAPTATION + + char *adapt_last; ///< last ICAP response header or eCAP meta received #endif char *reply; } headers; === modified file 'src/BodyPipe.cc' --- src/BodyPipe.cc 2010-09-13 02:06:48 +0000 +++ src/BodyPipe.cc 2011-02-21 05:12:02 +0000 @@ -10,24 +10,25 @@ // data from a BodyPipe class BodySink: public BodyConsumer { - bool done; public: - BodySink():AsyncJob("BodySink"), done(false) {} - virtual ~BodySink() {} + BodySink(const BodyPipe::Pointer &bp): AsyncJob("BodySink"), body_pipe(bp) {} + virtual ~BodySink() { assert(!body_pipe); } virtual void noteMoreBodyDataAvailable(BodyPipe::Pointer bp) { size_t contentSize = bp->buf().contentSize(); bp->consume(contentSize); } virtual void noteBodyProductionEnded(BodyPipe::Pointer bp) { - stopConsumingFrom(bp); - done = true; + stopConsumingFrom(body_pipe); } virtual void noteBodyProducerAborted(BodyPipe::Pointer bp) { - stopConsumingFrom(bp); - done = true; + stopConsumingFrom(body_pipe); } - bool doneAll() const {return done && AsyncJob::doneAll();} + bool doneAll() const {return !body_pipe && AsyncJob::doneAll();} + +private: + BodyPipe::Pointer body_pipe; ///< the pipe we are consuming from + CBDATA_CLASS2(BodySink); }; @@ -318,7 +319,7 @@ { Must(mustAutoConsume); Must(!theConsumer); - theConsumer = new BodySink; + theConsumer = new BodySink(this); debugs(91,7, HERE << "starting auto consumption" << status()); scheduleBodyDataNotification(); } === modified file 'src/Server.cc' --- src/Server.cc 2011-02-15 23:36:53 +0000 +++ src/Server.cc 2011-02-21 05:12:02 +0000 @@ -33,6 +33,7 @@ */ #include "squid.h" +#include "acl/Gadgets.h" #include "base/TextException.h" #include "comm/Write.h" #include "Server.h" @@ -660,10 +661,28 @@ // received adapted response headers (body may follow) void -ServerStateData::noteAdaptationAnswer(HttpMsg *msg) +ServerStateData::noteAdaptationAnswer(const Adaptation::Answer &answer) { clearAdaptation(adaptedHeadSource); // we do not expect more messages + switch (answer.kind) { + case Adaptation::Answer::akForward: + handleAdaptedHeader(answer.message); + break; + + case Adaptation::Answer::akBlock: + handleAdaptationBlocked(answer); + break; + + case Adaptation::Answer::akError: + handleAdaptationAborted(!answer.final); + break; + } +} + +void +ServerStateData::handleAdaptedHeader(HttpMsg *msg) +{ if (abortOnBadEntry("entry went bad while waiting for adapted headers")) return; @@ -685,14 +704,6 @@ } } -// will not receive adapted response headers (and, hence, body) -void -ServerStateData::noteAdaptationQueryAbort(bool final) -{ - clearAdaptation(adaptedHeadSource); - handleAdaptationAborted(!final); -} - // more adapted response body is available void ServerStateData::handleMoreAdaptedBodyAvailable() @@ -778,6 +789,37 @@ abortTransaction("ICAP failure"); } +// adaptation service wants us to deny HTTP client access to this response +void +ServerStateData::handleAdaptationBlocked(const Adaptation::Answer &answer) +{ + debugs(11,5, HERE << "handleAdaptationBlocked: " << answer.ruleId); + + if (abortOnBadEntry("entry went bad while ICAP aborted")) + return; + + if (!entry->isEmpty()) { // too late to block (should not really happen) + if (request) + request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_RESPMOD_BLOCK_LATE); + abortTransaction("late adaptation block"); + return; + } + + debugs(11,7, HERE << "creating adaptation block response"); + + err_type page_id = + aclGetDenyInfoPage(&Config.denyInfoList, answer.ruleId.termedBuf(), 1); + if (page_id == ERR_NONE) + page_id = ERR_ACCESS_DENIED; + + ErrorState *err = errorCon(page_id, HTTP_FORBIDDEN, request); + err->xerrno = ERR_DETAIL_RESPMOD_BLOCK_EARLY; + fwd->fail(err); + fwd->dontRetry(true); + + abortTransaction("timely adaptation block"); +} + void ServerStateData::adaptationAclCheckDone(Adaptation::ServiceGroupPointer group) { === modified file 'src/Server.h' --- src/Server.h 2010-11-28 15:28:09 +0000 +++ src/Server.h 2011-02-21 05:12:02 +0000 @@ -90,8 +90,7 @@ static void adaptationAclCheckDoneWrapper(Adaptation::ServiceGroupPointer group, void *data); // ICAPInitiator: start an ICAP transaction and receive adapted headers. - virtual void noteAdaptationAnswer(HttpMsg *message); - virtual void noteAdaptationQueryAbort(bool final); + virtual void noteAdaptationAnswer(const Adaptation::Answer &answer); // BodyProducer: provide virgin response body to ICAP. virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer ); @@ -154,7 +153,9 @@ void handleAdaptedBodyProductionEnded(); void handleAdaptedBodyProducerAborted(); + void handleAdaptedHeader(HttpMsg *msg); void handleAdaptationCompleted(); + void handleAdaptationBlocked(const Adaptation::Answer &answer); void handleAdaptationAborted(bool bypassable = false); #endif === modified file 'src/adaptation/Config.cc' --- src/adaptation/Config.cc 2010-07-05 23:57:04 +0000 +++ src/adaptation/Config.cc 2011-02-21 05:12:02 +0000 @@ -47,11 +47,21 @@ bool Adaptation::Config::Enabled = false; char *Adaptation::Config::masterx_shared_name = NULL; int Adaptation::Config::service_iteration_limit = 16; +int Adaptation::Config::send_client_ip = false; +int Adaptation::Config::send_username = false; +int Adaptation::Config::use_indirect_client = true; + + +Adaptation::ServiceConfig* +Adaptation::Config::newServiceConfig() const +{ + return new ServiceConfig(); +} void Adaptation::Config::parseService() { - ServiceConfig *cfg = new ServiceConfig; + ServiceConfigPointer cfg = newServiceConfig(); if (!cfg->parse()) { fatalf("%s:%d: malformed adaptation service configuration", cfg_filename, config_lineno); @@ -67,10 +77,7 @@ DetachServices(); - while (!serviceConfigs.empty()) { - delete serviceConfigs.back(); - serviceConfigs.pop_back(); - } + serviceConfigs.clean(); } void @@ -91,23 +98,28 @@ Adaptation::Config::finalize() { // create service reps from service configs - typedef Vector::const_iterator VISCI; - const Vector &configs = serviceConfigs; - debugs(93,3, HERE << "Found " << configs.size() << " service configs."); + int created = 0; + + typedef ServiceConfigs::const_iterator VISCI; + const ServiceConfigs &configs = serviceConfigs; for (VISCI i = configs.begin(); i != configs.end(); ++i) { - const ServiceConfig &cfg = **i; - if (FindService(cfg.key) != NULL) { + const ServiceConfigPointer cfg = *i; + if (FindService(cfg->key) != NULL) { debugs(93,0, "ERROR: Duplicate adaptation service name: " << - cfg.key); + cfg->key); continue; // TODO: make fatal } - ServicePointer s = createService(**i); - if (s != NULL) + ServicePointer s = createService(cfg); + if (s != NULL) { AllServices().push_back(s); + created++; + } } - debugs(93,3, HERE << "Created " << configs.size() << - " message adaptation services."); + debugs(93,3, HERE << "Created " << created << " adaptation services"); + + // services remember their configs; we do not have to + serviceConfigs.clean(); } // poor man for_each === modified file 'src/adaptation/Config.h' --- src/adaptation/Config.h 2010-07-05 23:57:05 +0000 +++ src/adaptation/Config.h 2011-02-21 05:12:02 +0000 @@ -32,17 +32,19 @@ // these are global squid.conf options, documented elsewhere static char *masterx_shared_name; // global TODO: do we need TheConfig? static int service_iteration_limit; + static int send_client_ip; + static int send_username; + static int use_indirect_client; + // Options below are accessed via Icap::TheConfig or Ecap::TheConfig // TODO: move ICAP-specific options to Icap::Config and add TheConfig int onoff; - int send_client_ip; - int send_client_username; int service_failure_limit; time_t oldest_service_failure; int service_revival_delay; - int icap_uses_indirect_client; - Vector serviceConfigs; + typedef Vector ServiceConfigs; + ServiceConfigs serviceConfigs; Config(); virtual ~Config(); @@ -54,11 +56,15 @@ virtual void finalize(); +protected: + /// creates service configuration object that will parse and keep cfg info + virtual ServiceConfig *newServiceConfig() const; + private: Config(const Config &); // unsupported Config &operator =(const Config &); // unsupported - virtual ServicePointer createService(const ServiceConfig &cfg) = 0; + virtual ServicePointer createService(ServiceConfigPointer cfg) = 0; static void ParseServiceGroup(ServiceGroupPointer group); static void FreeServiceGroups(void); === modified file 'src/adaptation/History.cc' --- src/adaptation/History.cc 2010-09-13 02:06:48 +0000 +++ src/adaptation/History.cc 2011-02-21 05:12:02 +0000 @@ -33,7 +33,10 @@ } -Adaptation::History::History(): theNextServices(TheNullServices) +Adaptation::History::History(): + lastMeta(hoReply), + allMeta(hoReply), + theNextServices(TheNullServices) { } @@ -136,3 +139,12 @@ theNextServices = TheNullServices; // prevents resetting the plan twice return true; } + +void Adaptation::History::recordMeta(const HttpHeader *lm) +{ + lastMeta.clean(); + lastMeta.update(lm, NULL); + + allMeta.update(lm, NULL); + allMeta.compact(); +} === modified file 'src/adaptation/History.h' --- src/adaptation/History.h 2010-07-05 23:57:05 +0000 +++ src/adaptation/History.h 2011-02-21 05:12:02 +0000 @@ -1,8 +1,9 @@ #ifndef SQUID_ADAPT_HISTORY_H #define SQUID_ADAPT_HISTORY_H +#include "Array.h" +#include "HttpHeader.h" #include "RefCount.h" -#include "Array.h" #include "SquidString.h" namespace Adaptation @@ -41,6 +42,15 @@ /// returns true, fills the value, and resets iff next services were set bool extractNextServices(String &value); + /// store the last meta header fields received from the adaptation service + void recordMeta(const HttpHeader *lm); + +public: + /// Last received meta header (REQMOD or RESPMOD, whichever comes last). + HttpHeader lastMeta; + /// All REQMOD and RESPMOD meta headers merged. Last field wins conflicts. + HttpHeader allMeta; + private: /// single Xaction stats (i.e., a historical record entry) class Entry === modified file 'src/adaptation/Initiate.cc' --- src/adaptation/Initiate.cc 2010-08-24 22:15:01 +0000 +++ src/adaptation/Initiate.cc 2011-02-21 05:12:02 +0000 @@ -8,29 +8,6 @@ #include "adaptation/Initiate.h" #include "base/AsyncJobCalls.h" -namespace Adaptation -{ - -// AdaptInitiator::noteAdaptionAnswer Dialer locks/unlocks the message in transit -// TODO: replace HTTPMSGLOCK with general RefCounting and delete this class -class AnswerDialer: public UnaryMemFunT -{ -public: - typedef UnaryMemFunT Parent; - - AnswerDialer(const Parent::JobPointer &job, Parent::Method meth, - HttpMsg *msg): Parent(job, meth, msg) { HTTPMSGLOCK(arg1); } - AnswerDialer(const AnswerDialer &d): Parent(d) { HTTPMSGLOCK(arg1); } - virtual ~AnswerDialer() { HTTPMSGUNLOCK(arg1); } - -private: - AnswerDialer &operator =(const AnswerDialer &); // not implemented -}; - -} // namespace Adaptation - - -/* Initiate */ Adaptation::Initiate::Initiate(const char *aTypeName): AsyncJob(aTypeName) { @@ -70,20 +47,18 @@ theInitiator.clear(); } -void Adaptation::Initiate::sendAnswer(HttpMsg *msg) +void Adaptation::Initiate::sendAnswer(const Answer &answer) { - assert(msg); + typedef UnaryMemFunT MyDialer; CallJob(93, 5, __FILE__, __LINE__, "Initiator::noteAdaptationAnswer", - AnswerDialer(theInitiator, &Initiator::noteAdaptationAnswer, msg)); + MyDialer(theInitiator, &Initiator::noteAdaptationAnswer, answer)); clearInitiator(); } void Adaptation::Initiate::tellQueryAborted(bool final) { - CallJobHere1(93, 5, theInitiator, - Initiator, noteAdaptationQueryAbort, final); - clearInitiator(); + sendAnswer(Answer::Error(final)); } const char *Adaptation::Initiate::status() const === modified file 'src/adaptation/Initiate.h' --- src/adaptation/Initiate.h 2010-08-24 00:00:44 +0000 +++ src/adaptation/Initiate.h 2011-02-21 05:12:02 +0000 @@ -5,8 +5,6 @@ #include "base/CbcPointer.h" #include "adaptation/forward.h" -class HttpMsg; - namespace Adaptation { @@ -34,7 +32,7 @@ virtual void noteInitiatorAborted() = 0; protected: - void sendAnswer(HttpMsg *msg); // send to the initiator + void sendAnswer(const Answer &answer); // send to the initiator void tellQueryAborted(bool final); // tell initiator void clearInitiator(); // used by noteInitiatorAborted; TODO: make private === modified file 'src/adaptation/Initiator.cc' --- src/adaptation/Initiator.cc 2010-08-24 00:00:44 +0000 +++ src/adaptation/Initiator.cc 2011-02-21 05:12:02 +0000 @@ -28,3 +28,46 @@ CallJobHere(93, 5, x, Initiate, noteInitiatorAborted); clearAdaptation(x); } + + +/* Adaptation::Answer */ + +// TODO: Move to src/adaptation/Answer.* + +Adaptation::Answer +Adaptation::Answer::Error(bool final) +{ + Answer answer(akError); + answer.final = final; + debugs(93, 4, HERE << "error: " << final); + return answer; +} + +Adaptation::Answer +Adaptation::Answer::Forward(HttpMsg *aMsg) +{ + Answer answer(akForward); + answer.message = aMsg; + debugs(93, 4, HERE << "forwarding: " << (void*)aMsg); + return answer; +} + + +Adaptation::Answer +Adaptation::Answer::Block(const String &aRule) +{ + Answer answer(akBlock); + answer.ruleId = aRule; + debugs(93, 4, HERE << "blocking rule: " << aRule); + return answer; +} + +std::ostream & +Adaptation::Answer::print(std::ostream &os) const +{ + return os << kind; // TODO: add more details +} + +Adaptation::Answer::Answer(Kind aKind): final(true), kind(aKind) +{ +} === modified file 'src/adaptation/Initiator.h' --- src/adaptation/Initiator.h 2010-08-24 00:00:44 +0000 +++ src/adaptation/Initiator.h 2011-02-21 05:12:02 +0000 @@ -4,6 +4,9 @@ #include "base/AsyncJob.h" #include "base/CbcPointer.h" #include "adaptation/forward.h" +#include "HttpMsg.h" + +#include /* * The ICAP Initiator is an ICAP vectoring point that initates ICAP @@ -14,23 +17,50 @@ * or aborting an ICAP transaction. */ -class HttpMsg; - namespace Adaptation { +/// summarizes adaptation service answer for the noteAdaptationAnswer() API +class Answer { +public: + /// helps interpret other members without a class hierarchy + typedef enum { + akForward, ///< forward the supplied adapted HTTP message + akBlock, ///< block or deny the master xaction; see authority + akError, ///< no adapted message will come; see bypassable + } Kind; + + static Answer Error(bool final); ///< create an akError answer + static Answer Forward(HttpMsg *aMsg); ///< create an akForward answer + static Answer Block(const String &aRule); ///< create an akBlock answer + + std::ostream &print(std::ostream &os) const; + +public: + HttpMsgPointerT message; ///< HTTP request or response to forward + String ruleId; ///< ACL (or similar rule) name that blocked forwarding + bool final; ///< whether the error, if any, cannot be bypassed + Kind kind; ///< the type of the answer + +private: + explicit Answer(Kind aKind); ///< use static creators instead +}; + +inline +std::ostream &operator <<(std::ostream &os, const Answer &answer) +{ + return answer.print(os); +} + class Initiator: virtual public AsyncJob { public: Initiator(): AsyncJob("Initiator") {} virtual ~Initiator() {} - // called when ICAP response headers are successfully interpreted - virtual void noteAdaptationAnswer(HttpMsg *message) = 0; - - // called when valid ICAP response headers are no longer expected - // the final parameter is set to disable bypass or retries - virtual void noteAdaptationQueryAbort(bool final) = 0; + /// called with the initial adaptation decision (adapt, block, error); + /// virgin and/or adapted body transmission may continue after this + virtual void noteAdaptationAnswer(const Answer &answer) = 0; protected: ///< starts freshly created initiate and returns a safe pointer to it === modified file 'src/adaptation/Iterator.cc' --- src/adaptation/Iterator.cc 2010-08-24 22:15:01 +0000 +++ src/adaptation/Iterator.cc 2011-02-21 05:12:02 +0000 @@ -51,7 +51,7 @@ Must(!theLauncher); if (thePlan.exhausted()) { // nothing more to do - sendAnswer(theMsg); + sendAnswer(Answer::Forward(theMsg)); Must(done()); return; } @@ -74,7 +74,26 @@ Must(!done()); } -void Adaptation::Iterator::noteAdaptationAnswer(HttpMsg *aMsg) +void +Adaptation::Iterator::noteAdaptationAnswer(const Answer &answer) +{ + switch (answer.kind) { + case Answer::akForward: + handleAdaptedHeader(answer.message); + break; + + case Answer::akBlock: + handleAdaptationBlock(answer); + break; + + case Answer::akError: + handleAdaptationError(answer.final); + break; + } +} + +void +Adaptation::Iterator::handleAdaptedHeader(HttpMsg *aMsg) { // set theCause if we switched to request satisfaction mode if (!theCause) { // probably sent a request message @@ -106,7 +125,16 @@ mustStop("initiator gone"); } -void Adaptation::Iterator::noteAdaptationQueryAbort(bool final) +void Adaptation::Iterator::handleAdaptationBlock(const Answer &answer) +{ + debugs(93,5, HERE << "blocked by " << answer); + clearAdaptation(theLauncher); + updatePlan(false); + sendAnswer(answer); + mustStop("blocked"); +} + +void Adaptation::Iterator::handleAdaptationError(bool final) { debugs(93,5, HERE << "final: " << final << " plan: " << thePlan); clearAdaptation(theLauncher); @@ -130,7 +158,7 @@ if (canIgnore && srcIntact && adapted) { debugs(85,3, HERE << "responding with older adapted msg"); - sendAnswer(theMsg); + sendAnswer(Answer::Forward(theMsg)); mustStop("sent older adapted msg"); return; } === modified file 'src/adaptation/Iterator.h' --- src/adaptation/Iterator.h 2010-08-24 00:00:44 +0000 +++ src/adaptation/Iterator.h 2011-02-21 05:12:02 +0000 @@ -29,8 +29,7 @@ void noteInitiatorAborted(); // Adaptation::Initiator: asynchronous communication with the current launcher - virtual void noteAdaptationAnswer(HttpMsg *message); - virtual void noteAdaptationQueryAbort(bool final); + virtual void noteAdaptationAnswer(const Answer &answer); protected: // Adaptation::Initiate API implementation @@ -47,6 +46,10 @@ /// creates service filter for the current step ServiceFilter filter() const; + void handleAdaptedHeader(HttpMsg *msg); + void handleAdaptationBlock(const Answer &answer); + void handleAdaptationError(bool final); + ServiceGroupPointer theGroup; ///< the service group we are iterating ServicePlan thePlan; ///< which services to use and in what order HttpMsg *theMsg; ///< the message being adapted (virgin for each step) === modified file 'src/adaptation/Makefile.am' --- src/adaptation/Makefile.am 2010-08-29 19:58:39 +0000 +++ src/adaptation/Makefile.am 2011-02-21 05:12:02 +0000 @@ -45,6 +45,7 @@ History.h # add libraries for specific adaptation schemes -libadaptation_la_LIBADD = $(ECAP_LIBS) $(ECAPLIB) $(ICAP_LIBS) +libadaptation_la_LDFLAGS = $(EXTLIBECAP_LIBS) +libadaptation_la_LIBADD = $(ECAP_LIBS) $(ICAP_LIBS) libadaptation_la_DEPENDENCIES = $(ECAP_LIBS) $(ICAP_LIBS) === modified file 'src/adaptation/Service.cc' --- src/adaptation/Service.cc 2010-07-05 23:57:06 +0000 +++ src/adaptation/Service.cc 2011-02-21 05:12:02 +0000 @@ -7,9 +7,10 @@ #include "adaptation/ServiceFilter.h" #include "adaptation/Service.h" -Adaptation::Service::Service(const ServiceConfig &aConfig): theConfig(aConfig) +Adaptation::Service::Service(ServiceConfigPointer aConfig): theConfig(aConfig) { - debugs(93,3, HERE << "creating adaptation service " << theConfig.key); + Must(theConfig != NULL); + debugs(93,3, HERE << "creating adaptation service " << cfg().key); } Adaptation::Service::~Service() === modified file 'src/adaptation/Service.h' --- src/adaptation/Service.h 2010-08-24 00:00:44 +0000 +++ src/adaptation/Service.h 2011-02-21 05:12:02 +0000 @@ -24,7 +24,7 @@ typedef String Id; public: - Service(const ServiceConfig &aConfig); + explicit Service(ServiceConfigPointer aConfig); virtual ~Service(); virtual bool probed() const = 0; // see comments above @@ -41,7 +41,7 @@ // called by transactions to report service failure virtual void noteFailure() = 0; - const ServiceConfig &cfg() const { return theConfig; } + const ServiceConfig &cfg() const { return *theConfig; } virtual void finalize(); // called after creation @@ -52,10 +52,10 @@ virtual bool detached() const = 0; protected: - ServiceConfig &writeableCfg() { return theConfig; } + ServiceConfig &writeableCfg() { return *theConfig; } private: - ServiceConfig theConfig; + ServiceConfigPointer theConfig; }; typedef Service::Pointer ServicePointer; === modified file 'src/adaptation/ServiceConfig.cc' --- src/adaptation/ServiceConfig.cc 2011-01-20 00:43:10 +0000 +++ src/adaptation/ServiceConfig.cc 2011-02-21 05:12:02 +0000 @@ -69,6 +69,7 @@ // handle optional service name=value parameters const char *lastOption = NULL; + bool grokkedUri = false; while (char *option = strtok(NULL, w_space)) { if (strcmp(option, "0") == 0) { // backward compatibility bypass = false; @@ -94,20 +95,21 @@ grokked = grokBool(bypass, name, value); else if (strcmp(name, "routing") == 0) grokked = grokBool(routing, name, value); + else if (strcmp(name, "uri") == 0) + grokked = grokkedUri = grokUri(value); else if (strcmp(name, "ipv6") == 0) { grokked = grokBool(ipv6, name, value); if (grokked && ipv6 && !Ip::EnableIpv6) debugs(3, DBG_IMPORTANT, "WARNING: IPv6 is disabled. ICAP service option ignored."); - } else { - debugs(3, 0, cfg_filename << ':' << config_lineno << ": " << - "unknown adaptation service option: " << name << '=' << value); - } + } else + grokked = grokExtension(name, value); + if (!grokked) return false; } // what is left must be the service URI - if (!grokUri(lastOption)) + if (!grokkedUri && !grokUri(lastOption)) return false; // there should be nothing else left @@ -243,3 +245,12 @@ return true; } + +bool +Adaptation::ServiceConfig::grokExtension(const char *name, const char *value) +{ + // we do not accept extensions by default + debugs(3, 0, cfg_filename << ':' << config_lineno << ": " << + "unknown adaptation service option: " << name << '=' << value); + return false; +} === modified file 'src/adaptation/ServiceConfig.h' --- src/adaptation/ServiceConfig.h 2010-08-14 14:51:42 +0000 +++ src/adaptation/ServiceConfig.h 2011-02-21 05:12:02 +0000 @@ -9,7 +9,7 @@ { // manages adaptation service configuration in squid.conf -class ServiceConfig +class ServiceConfig: public RefCountable { public: ServiceConfig(); @@ -42,6 +42,8 @@ /// interpret parsed values bool grokBool(bool &var, const char *name, const char *value); bool grokUri(const char *value); + /// handle name=value configuration option with name unknown to Squid + virtual bool grokExtension(const char *name, const char *value); }; } // namespace Adaptation === modified file 'src/adaptation/ecap/Config.cc' --- src/adaptation/ecap/Config.cc 2010-07-05 23:57:05 +0000 +++ src/adaptation/ecap/Config.cc 2011-02-21 05:12:02 +0000 @@ -26,10 +26,25 @@ CheckUnusedAdapterServices(AllServices()); } +Adaptation::ServiceConfig * +Adaptation::Ecap::Config::newServiceConfig() const +{ + return new ServiceConfig(); +} + Adaptation::ServicePointer -Adaptation::Ecap::Config::createService(const Adaptation::ServiceConfig &cfg) -{ - Adaptation::ServicePointer s = new Adaptation::Ecap::ServiceRep(cfg); - return s.getRaw(); +Adaptation::Ecap::Config::createService(ServiceConfigPointer cfg) +{ + return new Adaptation::Ecap::ServiceRep(cfg); +} + + +/* ServiceConfig */ + +bool +Adaptation::Ecap::ServiceConfig::grokExtension(const char *name, const char *value) +{ + extensions.push_back(std::make_pair(name, value)); + return true; } === modified file 'src/adaptation/ecap/Config.h' --- src/adaptation/ecap/Config.h 2010-07-05 23:57:05 +0000 +++ src/adaptation/ecap/Config.h 2011-02-21 05:12:02 +0000 @@ -7,12 +7,29 @@ #define SQUID_ECAP_CONFIG_H #include "adaptation/Config.h" +#include "adaptation/ServiceConfig.h" +#include +#include namespace Adaptation { namespace Ecap { +/// eCAP service configuration +class ServiceConfig: public Adaptation::ServiceConfig { +public: + // Adaptation::ServiceConfig API + virtual bool grokExtension(const char *name, const char *value); + +public: + typedef std::pair Extension; // name=value in cfg + typedef std::list Extensions; + Extensions extensions; +}; + + +/// General eCAP configuration class Config: public Adaptation::Config { @@ -22,11 +39,14 @@ virtual void finalize(); +protected: + virtual Adaptation::ServiceConfig *newServiceConfig() const; + private: Config(const Config &); // not implemented Config &operator =(const Config &); // not implemented - virtual Adaptation::ServicePointer createService(const Adaptation::ServiceConfig &cfg); + virtual Adaptation::ServicePointer createService(ServiceConfigPointer cfg); }; extern Config TheConfig; === modified file 'src/adaptation/ecap/Host.cc' --- src/adaptation/ecap/Host.cc 2011-02-15 20:23:40 +0000 +++ src/adaptation/ecap/Host.cc 2011-02-21 05:12:02 +0000 @@ -18,6 +18,7 @@ #if USE_HTCP const libecap::Name Adaptation::Ecap::protocolHtcp("Htcp", libecap::Name::NextId()); #endif +const libecap::Name Adaptation::Ecap::metaBypassable("bypassable", libecap::Name::NextId()); /// the host application (i.e., Squid) wrapper registered with libecap static libecap::shared_ptr TheHost; @@ -27,8 +28,12 @@ // assign our host-specific IDs to well-known names // this code can run only once + libecap::headerTransferEncoding.assignHostId(HDR_TRANSFER_ENCODING); libecap::headerReferer.assignHostId(HDR_REFERER); libecap::headerContentLength.assignHostId(HDR_CONTENT_LENGTH); + libecap::headerVia.assignHostId(HDR_VIA); + // TODO: libecap::headerXClientIp.assignHostId(HDR_X_CLIENT_IP); + // TODO: libecap::headerXServerIp.assignHostId(HDR_X_SERVER_IP); libecap::protocolHttp.assignHostId(PROTO_HTTP); libecap::protocolHttps.assignHostId(PROTO_HTTPS); @@ -43,6 +48,9 @@ #if USE_HTCP protocolHtcp.assignHostId(PROTO_HTCP); #endif + + // allows adapter to safely ignore this in adapter::Service::configure() + metaBypassable.assignHostId(1); } std::string @@ -74,7 +82,7 @@ return DBG_DATA; // is it a good idea to ignore other flags? if (lv.application()) - return DBG_DATA; // is it a good idea to ignore other flags? + return DBG_IMPORTANT; // is it a good idea to ignore other flags? return 2 + 2*lv.debugging() + 3*lv.operation() + 2*lv.xaction(); } === modified file 'src/adaptation/ecap/Host.h' --- src/adaptation/ecap/Host.h 2010-12-12 23:15:16 +0000 +++ src/adaptation/ecap/Host.h 2011-02-21 05:12:02 +0000 @@ -47,6 +47,7 @@ #if USE_HTCP extern const libecap::Name protocolHtcp; #endif +extern const libecap::Name metaBypassable; ///< an ecap_service parameter } // namespace Ecap } // namespace Adaptation === modified file 'src/adaptation/ecap/Makefile.am' --- src/adaptation/ecap/Makefile.am 2010-07-05 23:57:05 +0000 +++ src/adaptation/ecap/Makefile.am 2011-02-21 05:12:02 +0000 @@ -1,9 +1,9 @@ include $(top_srcdir)/src/Common.am include $(top_srcdir)/src/TestHeaders.am -noinst_LTLIBRARIES = libecap.la +noinst_LTLIBRARIES = libxecap.la -libecap_la_SOURCES = \ +libxecap_la_SOURCES = \ Config.h \ Config.cc \ Host.h \ @@ -16,3 +16,11 @@ XactionRep.cc \ \ Registry.h + +# add libecap using its pkg-config-produced configuration variables +libxecap_la_CPPFLAGS = $(EXTLIBECAP_CFLAGS) + +## It is tempting to put libxecap_la_LDFLAGS here, but it leads to weird +## linking errors. For example, "make clean all" works, but rebuilding after +## modifying a single source file leads to libtool's "file not found" errors. +## libxecap_la_LDFLAGS = $(EXTLIBECAP_LIBS) === modified file 'src/adaptation/ecap/MessageRep.cc' --- src/adaptation/ecap/MessageRep.cc 2011-02-15 20:23:40 +0000 +++ src/adaptation/ecap/MessageRep.cc 2011-02-21 05:12:02 +0000 @@ -8,6 +8,7 @@ #include #include #include +#include #include "adaptation/ecap/MessageRep.h" #include "adaptation/ecap/XactionRep.h" #include "adaptation/ecap/Host.h" /* for protocol constants */ @@ -66,6 +67,17 @@ theMessage.content_length = theHeader.getInt64(HDR_CONTENT_LENGTH); } +void +Adaptation::Ecap::HeaderRep::visitEach(libecap::NamedValueVisitor &visitor) const +{ + HttpHeaderPos pos = HttpHeaderInitPos; + while (HttpHeaderEntry *e = theHeader.getEntry(&pos)) { + const Name name(e->name.termedBuf()); // optimize: find std Names + name.assignHostId(e->id); + visitor.visit(name, Value(e->value.rawBuf(), e->value.size())); + } +} + libecap::Area Adaptation::Ecap::HeaderRep::image() const { === modified file 'src/adaptation/ecap/MessageRep.h' --- src/adaptation/ecap/MessageRep.h 2010-12-16 03:16:55 +0000 +++ src/adaptation/ecap/MessageRep.h 2011-02-21 05:12:02 +0000 @@ -41,6 +41,8 @@ virtual void add(const Name &name, const Value &value); virtual void removeAny(const Name &name); + virtual void visitEach(libecap::NamedValueVisitor &visitor) const; + virtual Area image() const; virtual void parse(const Area &buf); // throws on failures === modified file 'src/adaptation/ecap/ServiceRep.cc' --- src/adaptation/ecap/ServiceRep.cc 2010-12-16 03:17:13 +0000 +++ src/adaptation/ecap/ServiceRep.cc 2011-02-21 05:12:02 +0000 @@ -4,6 +4,11 @@ #include "squid.h" #include #include +#include +#include +#include +#include "adaptation/ecap/Config.h" +#include "adaptation/ecap/Host.h" #include "adaptation/ecap/ServiceRep.h" #include "adaptation/ecap/XactionRep.h" #include "base/TextException.h" @@ -11,7 +16,69 @@ // configured eCAP service wrappers static std::list TheServices; -Adaptation::Ecap::ServiceRep::ServiceRep(const Adaptation::ServiceConfig &cfg): +namespace Adaptation +{ +namespace Ecap +{ + +/// wraps Adaptation::Ecap::ServiceConfig to allow eCAP visitors +class ConfigRep: public libecap::Options +{ +public: + typedef Adaptation::Ecap::ServiceConfig Master; + typedef libecap::Name Name; + typedef libecap::Area Area; + + ConfigRep(const Master &aMaster); + + // libecap::Options API + virtual const libecap::Area option(const libecap::Name &name) const; + virtual void visitEachOption(libecap::NamedValueVisitor &visitor) const; + + const Master &master; ///< the configuration being wrapped +}; + +} // namespace Ecap +} // namespace Adaptation + + +Adaptation::Ecap::ConfigRep::ConfigRep(const Master &aMaster): master(aMaster) +{ +} + +const libecap::Area +Adaptation::Ecap::ConfigRep::option(const libecap::Name &name) const +{ + // we may supply the params we know about, but only when names have host ID + if (name == metaBypassable) + return Area(master.bypass ? "1" : "0", 1); + + // TODO: We could build a by-name index, but is it worth it? Good adapters + // should use visitEachOption() instead, to check for name typos/errors. + typedef Master::Extensions::const_iterator MECI; + for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i) { + if (name == i->first) + return Area(i->second.data(), i->second.size()); + } + + return Area(); +} + +void +Adaptation::Ecap::ConfigRep::visitEachOption(libecap::NamedValueVisitor &visitor) const +{ + // we may supply the params we know about too, but only if we set host ID + visitor.visit(metaBypassable, Area(master.bypass ? "1" : "0", 1)); + + // visit adapter-specific options (i.e., those not recognized by Squid) + typedef Master::Extensions::const_iterator MECI; + for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i) + visitor.visit(Name(i->first), Area::FromTempString(i->second)); +} + + + +Adaptation::Ecap::ServiceRep::ServiceRep(ServiceConfigPointer cfg): /*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg), isDetached(false) { @@ -32,6 +99,10 @@ Adaptation::Service::finalize(); theService = FindAdapterService(cfg().uri); if (theService) { + debugs(93,3, HERE << "configuring eCAP service: " << theService->uri()); + const ConfigRep cfgRep(dynamic_cast(cfg())); + theService->configure(cfgRep); + debugs(93,3, HERE << "starting eCAP service: " << theService->uri()); theService->start(); } else { === modified file 'src/adaptation/ecap/ServiceRep.h' --- src/adaptation/ecap/ServiceRep.h 2010-08-24 00:00:44 +0000 +++ src/adaptation/ecap/ServiceRep.h 2011-02-21 05:12:02 +0000 @@ -23,7 +23,7 @@ class ServiceRep : public Adaptation::Service { public: - ServiceRep(const Adaptation::ServiceConfig &config); + ServiceRep(ServiceConfigPointer aConfig); virtual ~ServiceRep(); typedef libecap::shared_ptr AdapterService; === modified file 'src/adaptation/ecap/XactionRep.cc' --- src/adaptation/ecap/XactionRep.cc 2010-12-12 22:28:46 +0000 +++ src/adaptation/ecap/XactionRep.cc 2011-02-21 05:12:02 +0000 @@ -4,16 +4,38 @@ #include "squid.h" #include #include +#include +#include #include #include "HttpRequest.h" #include "HttpReply.h" #include "SquidTime.h" #include "adaptation/ecap/XactionRep.h" +#include "adaptation/ecap/Config.h" +#include "adaptation/Initiator.h" #include "base/TextException.h" CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep); +/// a libecap Visitor for converting adapter transaction options to HttpHeader +class OptionsExtractor: public libecap::NamedValueVisitor +{ +public: + typedef libecap::Name Name; + typedef libecap::Area Area; + + OptionsExtractor(HttpHeader &aMeta): meta(aMeta) {} + + // libecap::NamedValueVisitor API + virtual void visit(const Name &name, const Area &value) + { + meta.putExt(name.image().c_str(), value.toString().c_str()); + } + + HttpHeader &meta; ///< where to put extracted options +}; + Adaptation::Ecap::XactionRep::XactionRep( HttpMsg *virginHeader, HttpRequest *virginCause, const Adaptation::ServicePointer &aService): @@ -21,9 +43,9 @@ Adaptation::Initiate("Adaptation::Ecap::XactionRep"), theService(aService), theVirginRep(virginHeader), theCauseRep(NULL), - proxyingVb(opUndecided), proxyingAb(opUndecided), + makingVb(opUndecided), proxyingAb(opUndecided), adaptHistoryId(-1), - canAccessVb(false), + vbProductionFinished(false), abProductionFinished(false), abProductionAtEnd(false) { if (virginCause) @@ -52,15 +74,99 @@ return *theService; } +const libecap::Area +Adaptation::Ecap::XactionRep::option(const libecap::Name &name) const +{ + if (name == libecap::metaClientIp) + return clientIpValue(); + if (name == libecap::metaUserName) + return usernameValue(); + if (name == Adaptation::Config::masterx_shared_name) + return masterxSharedValue(name); + + // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups + return libecap::Area(); +} + +void +Adaptation::Ecap::XactionRep::visitEachOption(libecap::NamedValueVisitor &visitor) const +{ + if (const libecap::Area value = clientIpValue()) + visitor.visit(libecap::metaClientIp, value); + if (const libecap::Area value = usernameValue()) + visitor.visit(libecap::metaUserName, value); + + if (Adaptation::Config::masterx_shared_name) { + const libecap::Name name(Adaptation::Config::masterx_shared_name); + if (const libecap::Area value = masterxSharedValue(name)) + visitor.visit(name, value); + } + + // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups +} + +const libecap::Area +Adaptation::Ecap::XactionRep::clientIpValue() const +{ + const HttpRequest *request = dynamic_cast(theCauseRep ? + theCauseRep->raw().header : theVirginRep.raw().header); + Must(request); + // TODO: move this logic into HttpRequest::clientIp(bool) and + // HttpRequest::clientIpString(bool) and reuse everywhere + if (TheConfig.send_client_ip && request) { + Ip::Address client_addr; +#if FOLLOW_X_FORWARDED_FOR + if (TheConfig.use_indirect_client) { + client_addr = request->indirect_client_addr; + } else +#endif + client_addr = request->client_addr; + if (!client_addr.IsAnyAddr() && !client_addr.IsNoAddr()) { + char ntoabuf[MAX_IPSTRLEN] = ""; + client_addr.NtoA(ntoabuf,MAX_IPSTRLEN); + return libecap::Area::FromTempBuffer(ntoabuf, strlen(ntoabuf)); + } + } + return libecap::Area(); +} + +const libecap::Area +Adaptation::Ecap::XactionRep::usernameValue() const +{ + const HttpRequest *request = dynamic_cast(theCauseRep ? + theCauseRep->raw().header : theVirginRep.raw().header); + Must(request); + if (request->auth_user_request != NULL) { + if (char const *name = request->auth_user_request->username()) + return libecap::Area::FromTempBuffer(name, strlen(name)); + } + return libecap::Area(); +} + +const libecap::Area +Adaptation::Ecap::XactionRep::masterxSharedValue(const libecap::Name &name) const +{ + const HttpRequest *request = dynamic_cast(theCauseRep ? + theCauseRep->raw().header : theVirginRep.raw().header); + Must(request); + if (name.known()) { // must check to avoid empty names matching unset cfg + Adaptation::History::Pointer ah = request->adaptHistory(false); + if (ah != NULL) { + String name, value; + if (ah->getXxRecord(name, value)) + return libecap::Area::FromTempBuffer(value.rawBuf(), value.size()); + } + } + return libecap::Area(); +} + void Adaptation::Ecap::XactionRep::start() { Must(theMaster); - if (theVirginRep.raw().body_pipe != NULL) - canAccessVb = true; /// assumes nobody is consuming; \todo check - else - proxyingVb = opNever; + if (!theVirginRep.raw().body_pipe) + makingVb = opNever; // there is nothing to deliver const HttpRequest *request = dynamic_cast (theCauseRep ? theCauseRep->raw().header : theVirginRep.raw().header); @@ -88,13 +194,9 @@ } } - if (proxyingVb == opOn) { - BodyPipe::Pointer body_pipe = theVirginRep.raw().body_pipe; - if (body_pipe != NULL) { - Must(body_pipe->stillConsuming(this)); - stopConsumingFrom(body_pipe); - } - } + BodyPipe::Pointer &body_pipe = theVirginRep.raw().body_pipe; + if (body_pipe != NULL && body_pipe->stillConsuming(this)) + stopConsumingFrom(body_pipe); terminateMaster(); @@ -149,26 +251,54 @@ bool Adaptation::Ecap::XactionRep::doneAll() const { - return proxyingVb >= opComplete && proxyingAb >= opComplete && + return makingVb >= opComplete && proxyingAb >= opComplete && Adaptation::Initiate::doneAll(); } -// stops receiving virgin and enables auto-consumption -void -Adaptation::Ecap::XactionRep::dropVirgin(const char *reason) -{ - debugs(93,4, HERE << "because " << reason << "; status:" << status()); - Must(proxyingVb = opOn); +// stops receiving virgin and enables auto-consumption, dropping any vb bytes +void +Adaptation::Ecap::XactionRep::sinkVb(const char *reason) +{ + debugs(93,4, HERE << "sink for " << reason << "; status:" << status()); + + // we reset raw().body_pipe when we are done, so use this one for checking + const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe; + if (permPipe != NULL) + permPipe->enableAutoConsumption(); + + forgetVb(reason); +} + +// stops receiving virgin but preserves it for others to use +void +Adaptation::Ecap::XactionRep::preserveVb(const char *reason) +{ + debugs(93,4, HERE << "preserve for " << reason << "; status:" << status()); + + // we reset raw().body_pipe when we are done, so use this one for checking + const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe; + if (permPipe != NULL) { + // if libecap consumed, we cannot preserve + Must(!permPipe->consumedSize()); + } + + forgetVb(reason); +} + +// disassociates us from vb; the last step of sinking or preserving vb +void +Adaptation::Ecap::XactionRep::forgetVb(const char *reason) +{ + debugs(93,9, HERE << "forget vb " << reason << "; status:" << status()); BodyPipePointer &p = theVirginRep.raw().body_pipe; - Must(p != NULL); - Must(p->stillConsuming(this)); - stopConsumingFrom(p); - p->enableAutoConsumption(); - proxyingVb = opComplete; - canAccessVb = false; + if (p != NULL && p->stillConsuming(this)) + stopConsumingFrom(p); - // called from adapter handler so does not inform adapter + if (makingVb == opUndecided) + makingVb = opNever; + else if (makingVb == opOn) + makingVb = opComplete; } void @@ -178,25 +308,14 @@ Must(proxyingAb == opUndecided); proxyingAb = opNever; - BodyPipePointer &vbody_pipe = theVirginRep.raw().body_pipe; + preserveVb("useVirgin"); HttpMsg *clone = theVirginRep.raw().header->clone(); // check that clone() copies the pipe so that we do not have to - Must(!vbody_pipe == !clone->body_pipe); - - if (proxyingVb == opOn) { - Must(vbody_pipe->stillConsuming(this)); - // if libecap consumed, we cannot shortcircuit - Must(!vbody_pipe->consumedSize()); - stopConsumingFrom(vbody_pipe); - canAccessVb = false; - proxyingVb = opComplete; - } else if (proxyingVb == opUndecided) { - vbody_pipe = NULL; // it is not our pipe anymore - proxyingVb = opNever; - } - - sendAnswer(clone); + Must(!theVirginRep.raw().header->body_pipe == !clone->body_pipe); + + updateHistory(); + sendAnswer(Answer::Forward(clone)); Must(done()); } @@ -211,7 +330,8 @@ HttpMsg *msg = answer().header; if (!theAnswerRep->body()) { // final, bodyless answer proxyingAb = opNever; - sendAnswer(msg); + updateHistory(); + sendAnswer(Answer::Forward(msg)); } else { // got answer headers but need to handle body proxyingAb = opOn; Must(!msg->body_pipe); // only host can set body pipes @@ -220,7 +340,8 @@ rep->tieBody(this); // sets us as a producer Must(msg->body_pipe != NULL); // check tieBody - sendAnswer(msg); + updateHistory(); + sendAnswer(Answer::Forward(msg)); debugs(93,4, HERE << "adapter will produce body" << status()); theMaster->abMake(); // libecap will produce @@ -228,46 +349,109 @@ } void +Adaptation::Ecap::XactionRep::blockVirgin() +{ + debugs(93,3, HERE << status()); + Must(proxyingAb == opUndecided); + proxyingAb = opNever; + + sinkVb("blockVirgin"); + + updateHistory(); + sendAnswer(Answer::Block(service().cfg().key)); + Must(done()); +} + +/// Called just before sendAnswer() to record adapter meta-information +/// which may affect answer processing and may be needed for logging. +void +Adaptation::Ecap::XactionRep::updateHistory() +{ + if (!theMaster) // all updates rely on being able to query the adapter + return; + + const HttpRequest *request = dynamic_cast(theCauseRep ? + theCauseRep->raw().header : theVirginRep.raw().header); + Must(request); + + // TODO: move common ICAP/eCAP logic to Adaptation::Xaction or similar + // TODO: optimize Area-to-String conversion + + // update the cross-transactional database if needed + if (const char *xxNameStr = Adaptation::Config::masterx_shared_name) { + Adaptation::History::Pointer ah = request->adaptHistory(true); + if (ah != NULL) { + libecap::Name xxName(xxNameStr); // TODO: optimize? + if (const libecap::Area val = theMaster->option(xxName)) + ah->updateXxRecord(xxNameStr, val.toString().c_str()); + } + } + + // update the adaptation plan if needed + if (service().cfg().routing) { + String services; + if (const libecap::Area services = theMaster->option(libecap::metaNextServices)) { + Adaptation::History::Pointer ah = request->adaptHistory(true); + if (ah != NULL) + ah->updateNextServices(services.toString().c_str()); + } + } // TODO: else warn (occasionally!) if we got libecap::metaNextServices + + // Store received meta headers for adapt::adaptLogHistory(); + if (ah != NULL) { + HttpHeader meta(hoReply); + OptionsExtractor extractor(meta); + theMaster->visitEachOption(extractor); + ah->recordMeta(&meta); + } +} + + +void Adaptation::Ecap::XactionRep::vbDiscard() { - Must(proxyingVb == opUndecided); + Must(makingVb == opUndecided); // if adapter does not need vb, we do not need to send it - dropVirgin("vbDiscard"); - Must(proxyingVb == opNever); + sinkVb("vbDiscard"); + Must(makingVb == opNever); } void Adaptation::Ecap::XactionRep::vbMake() { - Must(proxyingVb == opUndecided); + Must(makingVb == opUndecided); BodyPipePointer &p = theVirginRep.raw().body_pipe; Must(p != NULL); - Must(p->setConsumerIfNotLate(this)); // to make vb, we must receive vb - proxyingVb = opOn; + Must(p->setConsumerIfNotLate(this)); // to deliver vb, we must receive vb + makingVb = opOn; } void Adaptation::Ecap::XactionRep::vbStopMaking() { + Must(makingVb == opOn); // if adapter does not need vb, we do not need to receive it - if (proxyingVb == opOn) - dropVirgin("vbStopMaking"); - Must(proxyingVb == opComplete); + sinkVb("vbStopMaking"); + Must(makingVb == opComplete); } void Adaptation::Ecap::XactionRep::vbMakeMore() { - Must(proxyingVb == opOn); // cannot make more if done proxying + Must(makingVb == opOn); // cannot make more if done proxying // we cannot guarantee more vb, but we can check that there is a chance - Must(!theVirginRep.raw().body_pipe->exhausted()); + const BodyPipePointer &p = theVirginRep.raw().body_pipe; + Must(p != NULL && p->stillConsuming(this)); // we are plugged in + Must(!p->productionEnded() && p->mayNeedMoreData()); // and may get more } libecap::Area Adaptation::Ecap::XactionRep::vbContent(libecap::size_type o, libecap::size_type s) { - Must(canAccessVb); - // We may not be proxyingVb yet. It should be OK, but see vbContentShift(). + // We may not be makingVb yet. It should be OK, but see vbContentShift(). const BodyPipePointer &p = theVirginRep.raw().body_pipe; Must(p != NULL); @@ -291,8 +475,7 @@ void Adaptation::Ecap::XactionRep::vbContentShift(libecap::size_type n) { - Must(canAccessVb); - // We may not be proxyingVb yet. It should be OK now, but if BodyPipe + // We may not be makingVb yet. It should be OK now, but if BodyPipe // consume() requirements change, we would have to return empty vbContent // until the adapter registers as a consumer @@ -372,7 +555,7 @@ void Adaptation::Ecap::XactionRep::noteMoreBodyDataAvailable(RefCount bp) { - Must(proxyingVb == opOn); + Must(makingVb == opOn); // or we would not be registered as a consumer Must(theMaster); theMaster->noteVbContentAvailable(); } @@ -380,19 +563,19 @@ void Adaptation::Ecap::XactionRep::noteBodyProductionEnded(RefCount bp) { - Must(proxyingVb == opOn); + Must(makingVb == opOn); // or we would not be registered as a consumer Must(theMaster); theMaster->noteVbContentDone(true); - proxyingVb = opComplete; + vbProductionFinished = true; } void Adaptation::Ecap::XactionRep::noteBodyProducerAborted(RefCount bp) { - Must(proxyingVb == opOn); + Must(makingVb == opOn); // or we would not be registered as a consumer Must(theMaster); theMaster->noteVbContentDone(false); - proxyingVb = opComplete; + vbProductionFinished = true; } void @@ -426,24 +609,34 @@ buf.append(" [", 2); - if (proxyingVb == opOn) { - const BodyPipePointer &vp = theVirginRep.raw().body_pipe; - if (!canAccessVb) - buf.append("x", 1); - if (vp != NULL) { // XXX: but may not be stillConsuming() - buf.append("Vb", 2); - } else - buf.append("V.", 2); - } + if (makingVb) + buf.Printf("M%d", static_cast(makingVb)); + + const BodyPipePointer &vp = theVirginRep.raw().body_pipe; + if (!vp) + buf.append(" !V", 3); + else + if (vp->stillConsuming(const_cast(this))) + buf.append(" Vc", 3); + else + buf.append(" V?", 3); + + if (vbProductionFinished) + buf.append(".", 1); + + + buf.Printf(" A%d", static_cast(proxyingAb)); if (proxyingAb == opOn) { MessageRep *rep = dynamic_cast(theAnswerRep.get()); Must(rep); const BodyPipePointer &ap = rep->raw().body_pipe; - if (ap != NULL) { // XXX: but may not be stillProducing() - buf.append(" Ab", 3); - } else - buf.append(" A.", 3); + if (!ap) + buf.append(" !A", 3); + else if (ap->stillProducing(const_cast(this))) + buf.append(" Ap", 3); + else + buf.append(" A?", 3); } buf.Printf(" %s%u]", id.Prefix, id.value); === modified file 'src/adaptation/ecap/XactionRep.h' --- src/adaptation/ecap/XactionRep.h 2010-08-24 00:00:44 +0000 +++ src/adaptation/ecap/XactionRep.h 2011-02-21 05:12:02 +0000 @@ -35,11 +35,14 @@ void master(const AdapterXaction &aMaster); // establish a link // libecap::host::Xaction API + virtual const libecap::Area option(const libecap::Name &name) const; + virtual void visitEachOption(libecap::NamedValueVisitor &visitor) const; virtual libecap::Message &virgin(); virtual const libecap::Message &cause(); virtual libecap::Message &adapted(); virtual void useVirgin(); virtual void useAdapted(const libecap::shared_ptr &msg); + virtual void blockVirgin(); virtual void adaptationDelayed(const libecap::Delay &); virtual void adaptationAborted(); virtual void vbDiscard(); @@ -77,12 +80,20 @@ Adaptation::Message &answer(); - void dropVirgin(const char *reason); + void sinkVb(const char *reason); + void preserveVb(const char *reason); + void forgetVb(const char *reason); + void moveAbContent(); + void updateHistory(); void terminateMaster(); void scheduleStop(const char *reason); + const libecap::Area clientIpValue() const; + const libecap::Area usernameValue() const; + const libecap::Area masterxSharedValue(const libecap::Name &name) const; + private: AdapterXaction theMaster; // the actual adaptation xaction we represent Adaptation::ServicePointer theService; ///< xaction's adaptation service @@ -94,10 +105,10 @@ MessagePtr theAnswerRep; typedef enum { opUndecided, opOn, opComplete, opNever } OperationState; - OperationState proxyingVb; // delivering virgin body from core to adapter + OperationState makingVb; //< delivering virgin body from pipe to adapter OperationState proxyingAb; // delivering adapted body from adapter to core int adaptHistoryId; ///< adaptation history slot reservation - bool canAccessVb; // virgin BodyPipe content is accessible + bool vbProductionFinished; // whether there can be no more vb bytes bool abProductionFinished; // whether adapter has finished producing ab bool abProductionAtEnd; // whether adapter produced a complete ab === modified file 'src/adaptation/forward.h' --- src/adaptation/forward.h 2010-07-05 23:57:05 +0000 +++ src/adaptation/forward.h 2011-02-21 05:12:02 +0000 @@ -25,8 +25,10 @@ class ServicePlan; class ServiceFilter; class Message; +class Answer; typedef RefCount ServicePointer; +typedef RefCount ServiceConfigPointer; typedef RefCount ServiceGroupPointer; } // namespace Adaptation === modified file 'src/adaptation/icap/Config.cc' --- src/adaptation/icap/Config.cc 2010-07-05 23:57:05 +0000 +++ src/adaptation/icap/Config.cc 2011-02-21 05:12:02 +0000 @@ -58,7 +58,7 @@ } Adaptation::ServicePointer -Adaptation::Icap::Config::createService(const Adaptation::ServiceConfig &cfg) +Adaptation::Icap::Config::createService(ServiceConfigPointer cfg) { return new Adaptation::Icap::ServiceRep(cfg); } === modified file 'src/adaptation/icap/Config.h' --- src/adaptation/icap/Config.h 2010-07-05 23:57:05 +0000 +++ src/adaptation/icap/Config.h 2011-02-21 05:12:02 +0000 @@ -76,7 +76,7 @@ Config(const Config &); // not implemented Config &operator =(const Config &); // not implemented - virtual Adaptation::ServicePointer createService(const Adaptation::ServiceConfig &cfg); + virtual Adaptation::ServicePointer createService(ServiceConfigPointer cfg); }; extern Config TheConfig; === modified file 'src/adaptation/icap/History.cc' --- src/adaptation/icap/History.cc 2010-11-23 16:26:49 +0000 +++ src/adaptation/icap/History.cc 2011-02-21 05:12:02 +0000 @@ -3,68 +3,12 @@ #include "globals.h" #include "SquidTime.h" -Adaptation::Icap::History::History(): mergeOfIcapHeaders(hoRequest), - lastIcapHeader(hoRequest), logType(LOG_TAG_NONE), req_sz(0), +Adaptation::Icap::History::History(): + logType(LOG_TAG_NONE), req_sz(0), pastTime(0), concurrencyLevel(0) { } -Adaptation::Icap::History::History(const Adaptation::Icap::History& ih) -{ - assign(ih); -} - -Adaptation::Icap::History::~History() -{ - mergeOfIcapHeaders.clean(); - lastIcapHeader.clean(); - rfc931.clean(); -#if USE_SSL - ssluser.clean(); -#endif - log_uri.clean(); -} - -void Adaptation::Icap::History::assign(const Adaptation::Icap::History& ih) -{ - mergeOfIcapHeaders.clean(); - mergeOfIcapHeaders.update(&ih.mergeOfIcapHeaders, NULL); - lastIcapHeader.clean(); - lastIcapHeader.update(&ih.lastIcapHeader, NULL); - rfc931 = ih.rfc931; - -#if USE_SSL - ssluser = ih.ssluser; -#endif - - logType = ih.logType; - log_uri = ih.log_uri; - req_sz = ih.req_sz; - pastTime = ih.pastTime; - currentStart = ih.currentStart; - concurrencyLevel = ih.concurrencyLevel; - debugs(93,7, HERE << this << " = " << &ih); -} - -Adaptation::Icap::History& Adaptation::Icap::History::operator=(const History& ih) -{ - if (this != &ih) - assign(ih); - return *this; -} - -void Adaptation::Icap::History::setIcapLastHeader(const HttpHeader * lih) -{ - lastIcapHeader.clean(); - lastIcapHeader.update(lih, NULL); -} - -void Adaptation::Icap::History::mergeIcapHeaders(const HttpHeader * lih) -{ - mergeOfIcapHeaders.update(lih, NULL); - mergeOfIcapHeaders.compact(); -} - void Adaptation::Icap::History::start(const char *context) { if (!concurrencyLevel++) === modified file 'src/adaptation/icap/History.h' --- src/adaptation/icap/History.h 2010-07-05 23:57:05 +0000 +++ src/adaptation/icap/History.h 2011-02-21 05:12:02 +0000 @@ -2,7 +2,6 @@ #define SQUID_ICAPHISTORY_H #include "RefCount.h" -#include "HttpHeader.h" #include "enums.h" namespace Adaptation @@ -17,16 +16,6 @@ typedef RefCount Pointer; History(); - History(const History& ih); - - ~History(); - - History& operator=(const History& ih); - - ///store the last reply header from ICAP server - void setIcapLastHeader(const HttpHeader * lih); - ///merge new header and stored one - void mergeIcapHeaders(const HttpHeader * lih); /// record the start of an ICAP processing interval void start(const char *context); @@ -36,8 +25,6 @@ /// returns the total time of all ICAP processing intervals int processingTime() const; - HttpHeader mergeOfIcapHeaders; ///< Merge of REQMOD and RESPMOD responses. If both responses contain the header, the one from the last response will be used - HttpHeader lastIcapHeader; ///< Last received reply from ICAP server String rfc931; ///< the username from ident #if USE_SSL String ssluser; ///< the username from SSL @@ -48,8 +35,6 @@ size_t req_sz; ///< the request size private: - void assign(const History &); - int currentTime() const; ///< time since current start or zero timeval currentStart; ///< when the current processing interval started === modified file 'src/adaptation/icap/Launcher.cc' --- src/adaptation/icap/Launcher.cc 2010-08-24 22:15:01 +0000 +++ src/adaptation/icap/Launcher.cc 2011-02-21 05:12:02 +0000 @@ -50,12 +50,16 @@ Must(initiated(theXaction)); } -void Adaptation::Icap::Launcher::noteAdaptationAnswer(HttpMsg *message) +void Adaptation::Icap::Launcher::noteAdaptationAnswer(const Answer &answer) { - sendAnswer(message); + debugs(93,5, HERE << "launches: " << theLaunches << " answer: " << answer); + + // XXX: akError is unused by ICAPXaction in favor of noteXactAbort() + Must(answer.kind != Answer::akError); + + sendAnswer(answer); clearAdaptation(theXaction); Must(done()); - debugs(93,3, HERE << "Adaptation::Icap::Launcher::noteAdaptationAnswer exiting "); } void Adaptation::Icap::Launcher::noteInitiatorAborted() @@ -67,15 +71,6 @@ } -// XXX: this call is unused by ICAPXaction in favor of ICAPLauncher::noteXactAbort -void Adaptation::Icap::Launcher::noteAdaptationQueryAbort(bool final) -{ - debugs(93,5, HERE << "launches: " << theLaunches << "; final: " << final); - clearAdaptation(theXaction); - - Must(done()); // swanSong will notify the initiator -} - void Adaptation::Icap::Launcher::noteXactAbort(XactAbortInfo info) { debugs(93,5, HERE << "theXaction:" << theXaction << " launches: " << theLaunches); === modified file 'src/adaptation/icap/Launcher.h' --- src/adaptation/icap/Launcher.h 2010-08-24 22:15:01 +0000 +++ src/adaptation/icap/Launcher.h 2011-02-21 05:12:02 +0000 @@ -80,13 +80,12 @@ void noteInitiatorAborted(); // Adaptation::Initiator: asynchronous communication with the current transaction - virtual void noteAdaptationAnswer(HttpMsg *message); + virtual void noteAdaptationAnswer(const Answer &answer); virtual void noteXactAbort(XactAbortInfo info); private: bool canRetry(XactAbortInfo &info) const; //< true if can retry in the case of persistent connection failures bool canRepeat(XactAbortInfo &info) const; //< true if can repeat in the case of no or unsatisfactory response - virtual void noteAdaptationQueryAbort(bool final); protected: // Adaptation::Initiate API implementation === modified file 'src/adaptation/icap/ModXact.cc' --- src/adaptation/icap/ModXact.cc 2010-11-07 03:05:26 +0000 +++ src/adaptation/icap/ModXact.cc 2011-02-21 05:12:02 +0000 @@ -714,7 +714,7 @@ { disableRepeats("sent headers"); disableBypass("sent headers", true); - sendAnswer(adapted.header); + sendAnswer(Answer::Forward(adapted.header)); if (state.sending == State::sendingVirgin) echoMore(); @@ -770,7 +770,7 @@ // update the cross-transactional database if needed (all status codes!) if (const char *xxName = Adaptation::Config::masterx_shared_name) { Adaptation::History::Pointer ah = request->adaptHistory(true); - if (ah != NULL) { + if (ah != NULL) { // TODO: reorder checks to avoid creating history const String val = icapReply->header.getByName(xxName); if (val.size() > 0) // XXX: HttpHeader lacks empty value detection ah->updateXxRecord(xxName, val); @@ -791,11 +791,9 @@ // If we already have stored headers from previous ICAP transaction related to this // request, old headers will be replaced with the new one. - Adaptation::Icap::History::Pointer h = request->icapHistory(); - if (h != NULL) { - h->mergeIcapHeaders(&icapReply->header); - h->setIcapLastHeader(&icapReply->header); - } + Adaptation::History::Pointer ah = request->adaptLogHistory(); + if (ah != NULL) + ah->recordMeta(&icapReply->header); // handle100Continue() manages state.writing on its own. // Non-100 status means the server needs no postPreview data from us. @@ -1302,6 +1300,7 @@ // share the cross-transactional database records if needed if (Adaptation::Config::masterx_shared_name) { + // XXX: do not create history here: there can be no values in empty ah Adaptation::History::Pointer ah = request->adaptHistory(true); if (ah != NULL) { String name, value; @@ -1357,7 +1356,7 @@ if (TheConfig.send_client_ip && request) { Ip::Address client_addr; #if FOLLOW_X_FORWARDED_FOR - if (TheConfig.icap_uses_indirect_client) { + if (TheConfig.use_indirect_client) { client_addr = request->indirect_client_addr; } else #endif @@ -1366,7 +1365,7 @@ buf.Printf("X-Client-IP: %s\r\n", client_addr.NtoA(ntoabuf,MAX_IPSTRLEN)); } - if (TheConfig.send_client_username && request) + if (TheConfig.send_username && request) makeUsernameHeader(request, buf); // fprintf(stderr, "%s\n", buf.content()); === modified file 'src/adaptation/icap/OptXact.cc' --- src/adaptation/icap/OptXact.cc 2010-09-13 02:06:48 +0000 +++ src/adaptation/icap/OptXact.cc 2011-02-21 05:12:02 +0000 @@ -79,7 +79,7 @@ debugs(93, 7, HERE << "readAll=" << readAll); icap_tio_finish = current_time; setOutcome(xoOpt); - sendAnswer(icapReply); + sendAnswer(Answer::Forward(icapReply)); Must(done()); // there should be nothing else to do return; } === modified file 'src/adaptation/icap/ServiceRep.cc' --- src/adaptation/icap/ServiceRep.cc 2010-08-24 22:15:01 +0000 +++ src/adaptation/icap/ServiceRep.cc 2011-02-21 05:12:02 +0000 @@ -15,7 +15,7 @@ CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, ServiceRep); -Adaptation::Icap::ServiceRep::ServiceRep(const Adaptation::ServiceConfig &svcCfg): +Adaptation::Icap::ServiceRep::ServiceRep(ServiceConfigPointer svcCfg): AsyncJob("Adaptation::Icap::ServiceRep"), Adaptation::Service(svcCfg), theOptions(NULL), theOptionsFetcher(0), theLastUpdate(0), isSuspended(0), notifying(false), @@ -304,11 +304,19 @@ } // we are receiving ICAP OPTIONS response headers here or NULL on failures -void Adaptation::Icap::ServiceRep::noteAdaptationAnswer(HttpMsg *msg) +void Adaptation::Icap::ServiceRep::noteAdaptationAnswer(const Answer &answer) { Must(initiated(theOptionsFetcher)); clearAdaptation(theOptionsFetcher); + if (answer.kind == Answer::akError) { + debugs(93,3, HERE << "failed to fetch options " << status()); + handleNewOptions(0); + return; + } + + Must(answer.kind == Answer::akForward); // no akBlock for OPTIONS requests + HttpMsg *msg = answer.message; Must(msg); debugs(93,5, HERE << "is interpreting new options " << status()); @@ -324,15 +332,6 @@ handleNewOptions(newOptions); } -void Adaptation::Icap::ServiceRep::noteAdaptationQueryAbort(bool) -{ - Must(initiated(theOptionsFetcher)); - clearAdaptation(theOptionsFetcher); - - debugs(93,3, HERE << "failed to fetch options " << status()); - handleNewOptions(0); -} - // we (a) must keep trying to get OPTIONS and (b) are RefCounted so we // must keep our job alive (XXX: until nobody needs us) void Adaptation::Icap::ServiceRep::callException(const std::exception &e) === modified file 'src/adaptation/icap/ServiceRep.h' --- src/adaptation/icap/ServiceRep.h 2010-08-24 00:00:44 +0000 +++ src/adaptation/icap/ServiceRep.h 2011-02-21 05:12:02 +0000 @@ -87,7 +87,7 @@ typedef RefCount Pointer; public: - ServiceRep(const Adaptation::ServiceConfig &config); + ServiceRep(ServiceConfigPointer aConfig); virtual ~ServiceRep(); virtual void finalize(); @@ -119,8 +119,7 @@ void noteTimeToNotify(); // receive either an ICAP OPTIONS response header or an abort message - virtual void noteAdaptationAnswer(HttpMsg *msg); - virtual void noteAdaptationQueryAbort(bool); + virtual void noteAdaptationAnswer(const Answer &answer); private: // stores Prepare() callback info === modified file 'src/cf.data.pre' --- src/cf.data.pre 2011-02-15 20:23:40 +0000 +++ src/cf.data.pre 2011-02-21 05:12:02 +0000 @@ -2932,7 +2932,7 @@ sent to the first selected peer. The timer stops with the last I/O with the last peer. - If ICAP is enabled, the following two codes become available (as + If ICAP is enabled, the following code becomes available (as well as ICAP log codes documented with the icap_log option): icap::tt Total ICAP processing time for the HTTP @@ -2940,14 +2940,13 @@ ACLs are checked and when ICAP transaction is in progress. - icap::a %icap::to/%03icap::Hs %icap::icapHistory(); -#endif if (Config.onoff.log_mime_hdrs) { Packer p; MemBuf mb; @@ -575,14 +572,15 @@ aLogEntry->headers.request = xstrdup(mb.buf); } -#if ICAP_CLIENT - packerClean(&p); - mb.reset(); - packerToMemInit(&p, &mb); - - if (ih != NULL) - ih->lastIcapHeader.packInto(&p); - aLogEntry->headers.icap = xstrdup(mb.buf); +#if USE_ADAPTATION + const Adaptation::History::Pointer ah = request->adaptLogHistory(); + if (ah != NULL) { + packerClean(&p); + mb.reset(); + packerToMemInit(&p, &mb); + ah->lastMeta.packInto(&p); + aLogEntry->headers.adapt_last = xstrdup(mb.buf); + } #endif packerClean(&p); @@ -590,6 +588,7 @@ } #if ICAP_CLIENT + const Adaptation::Icap::History::Pointer ih = request->icapHistory(); if (ih != NULL) aLogEntry->icap.processingTime = ih->processingTime(); #endif === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2011-02-15 20:23:40 +0000 +++ src/client_side_request.cc 2011-02-21 05:12:02 +0000 @@ -1406,9 +1406,30 @@ } void -ClientHttpRequest::noteAdaptationAnswer(HttpMsg *msg) +ClientHttpRequest::noteAdaptationAnswer(const Adaptation::Answer &answer) { assert(cbdataReferenceValid(this)); // indicates bug + clearAdaptation(virginHeadSource); + assert(!adaptedBodySource); + + switch (answer.kind) { + case Adaptation::Answer::akForward: + handleAdaptedHeader(answer.message); + break; + + case Adaptation::Answer::akBlock: + handleAdaptationBlock(answer); + break; + + case Adaptation::Answer::akError: + handleAdaptationFailure(ERR_DETAIL_CLT_REQMOD_ABORT, !answer.final); + break; + } +} + +void +ClientHttpRequest::handleAdaptedHeader(HttpMsg *msg) +{ assert(msg); if (HttpRequest *new_req = dynamic_cast(msg)) { @@ -1457,11 +1478,13 @@ } void -ClientHttpRequest::noteAdaptationQueryAbort(bool final) +ClientHttpRequest::handleAdaptationBlock(const Adaptation::Answer &answer) { - clearAdaptation(virginHeadSource); - assert(!adaptedBodySource); - handleAdaptationFailure(ERR_DETAIL_CLT_REQMOD_ABORT, !final); + request->detailError(ERR_ACCESS_DENIED, ERR_DETAIL_REQMOD_BLOCK); + AclMatchedName = answer.ruleId.termedBuf(); + assert(calloutContext); + calloutContext->clientAccessCheckDone(ACCESS_DENIED); + AclMatchedName = NULL; } void === modified file 'src/client_side_request.h' --- src/client_side_request.h 2010-10-25 23:28:38 +0000 +++ src/client_side_request.h 2011-02-21 05:12:02 +0000 @@ -166,8 +166,9 @@ private: // Adaptation::Initiator API - virtual void noteAdaptationAnswer(HttpMsg *message); - virtual void noteAdaptationQueryAbort(bool final); + virtual void noteAdaptationAnswer(const Adaptation::Answer &answer); + void handleAdaptedHeader(HttpMsg *msg); + void handleAdaptationBlock(const Adaptation::Answer &answer); // BodyConsumer API, called by BodyPipe virtual void noteMoreBodyDataAvailable(BodyPipe::Pointer); === modified file 'src/err_detail_type.h' --- src/err_detail_type.h 2010-12-07 16:47:19 +0000 +++ src/err_detail_type.h 2011-02-21 05:12:02 +0000 @@ -9,6 +9,9 @@ ERR_DETAIL_CLT_REQMOD_RESP_BODY, // client-side detected REQMOD satisfaction reply body failure ERR_DETAIL_ICAP_RESPMOD_EARLY, // RESPMOD failed w/o store entry ERR_DETAIL_ICAP_RESPMOD_LATE, // RESPMOD failed with a store entry + ERR_DETAIL_REQMOD_BLOCK, // REQMOD denied client access + ERR_DETAIL_RESPMOD_BLOCK_EARLY, // RESPMOD denied client access to HTTP response, before any part of the response was sent + ERR_DETAIL_RESPMOD_BLOCK_LATE, // RESPMOD denied client access to HTTP response, after [a part of] the response was sent ERR_DETAIL_ICAP_XACT_START, // transaction start failure ERR_DETAIL_ICAP_XACT_BODY_CONSUMER_ABORT, // transaction body consumer gone ERR_DETAIL_ICAP_INIT_GONE, // initiator gone === modified file 'src/log/FormatSquidCustom.cc' --- src/log/FormatSquidCustom.cc 2011-01-20 00:43:10 +0000 +++ src/log/FormatSquidCustom.cc 2011-02-21 05:12:02 +0000 @@ -312,42 +312,43 @@ out = sb.termedBuf(); } break; + + case LFT_ADAPTATION_LAST_HEADER: + if (al->request) { + const Adaptation::History::Pointer ah = al->request->adaptHistory(); + if (ah != NULL) // XXX: add adapt::allMeta.getByName(fmt->data.header.header); + } + + // XXX: here and elsewhere: move such code inside the if guard + out = sb.termedBuf(); + + quote = 1; + + break; + + case LFT_ADAPTATION_LAST_HEADER_ELEM: + if (al->request) { + const Adaptation::History::Pointer ah = al->request->adaptHistory(); + if (ah != NULL) // XXX: add adapt::allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator); + } + + out = sb.termedBuf(); + + quote = 1; + + break; + + case LFT_ADAPTATION_LAST_ALL_HEADERS: + out = al->headers.adapt_last; + + quote = 1; + + break; #endif #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_HEADER: - if (al->request) { - Adaptation::Icap::History::Pointer ih = al->request->icapHistory(); - if (ih != NULL) - sb = ih->mergeOfIcapHeaders.getByName(fmt->data.header.header); - } - - out = sb.termedBuf(); - - quote = 1; - - break; - - case LFT_ICAP_LAST_MATCHED_HEADER_ELEM: - if (al->request) { - Adaptation::Icap::History::Pointer ih = al->request->icapHistory(); - if (ih != NULL) - sb = ih->mergeOfIcapHeaders.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator); - } - - out = sb.termedBuf(); - - quote = 1; - - break; - - case LFT_ICAP_LAST_MATCHED_ALL_HEADERS: - out = al->headers.icap; - - quote = 1; - - break; - case LFT_ICAP_ADDR: if (!out) out = al->icap.hostAddr.NtoA(tmp,1024); === modified file 'src/log/Tokens.cc' --- src/log/Tokens.cc 2010-12-12 23:17:33 +0000 +++ src/log/Tokens.cc 2011-02-21 05:12:02 +0000 @@ -161,11 +161,12 @@ #if USE_ADAPTATION {"adapt::all_trs", LTF_ADAPTATION_ALL_XACT_TIMES}, {"adapt::sum_trs", LTF_ADAPTATION_SUM_XACT_TIMES}, + {"adapt::type) { +#if USE_ADAPTATION + case LFT_ADAPTATION_LAST_HEADER: +#endif + #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_HEADER: - case LFT_ICAP_REQ_HEADER: case LFT_ICAP_REP_HEADER: @@ -376,10 +379,12 @@ case LFT_REPLY_HEADER: lt->type = LFT_REPLY_HEADER_ELEM; break; +#if USE_ADAPTATION + case LFT_ADAPTATION_LAST_HEADER: + lt->type = LFT_ADAPTATION_LAST_HEADER_ELEM; + break; +#endif #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_HEADER: - lt->type = LFT_ICAP_LAST_MATCHED_HEADER_ELEM; - break; case LFT_ICAP_REQ_HEADER: lt->type = LFT_ICAP_REQ_HEADER_ELEM; break; @@ -406,10 +411,12 @@ case LFT_REPLY_HEADER: lt->type = LFT_REPLY_ALL_HEADERS; break; +#if USE_ADAPTATION + case LFT_ADAPTATION_LAST_HEADER: + lt->type = LFT_ADAPTATION_LAST_ALL_HEADERS; + break; +#endif #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_HEADER: - lt->type = LFT_ICAP_LAST_MATCHED_ALL_HEADERS; - break; case LFT_ICAP_REQ_HEADER: lt->type = LFT_ICAP_REQ_ALL_HEADERS; break; @@ -510,8 +517,10 @@ case LFT_STRING: break; +#if USE_ADAPTATION + case LFT_ADAPTATION_LAST_HEADER_ELEM: +#endif #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_HEADER_ELEM: case LFT_ICAP_REQ_HEADER_ELEM: case LFT_ICAP_REP_HEADER_ELEM: #endif @@ -528,18 +537,20 @@ switch (type) { case LFT_REQUEST_HEADER_ELEM: - type = LFT_REQUEST_HEADER_ELEM; + type = LFT_REQUEST_HEADER_ELEM; // XXX: remove _ELEM? break; case LFT_ADAPTED_REQUEST_HEADER_ELEM: - type = LFT_ADAPTED_REQUEST_HEADER_ELEM; + type = LFT_ADAPTED_REQUEST_HEADER_ELEM; // XXX: remove _ELEM? break; case LFT_REPLY_HEADER_ELEM: - type = LFT_REPLY_HEADER_ELEM; - break; + type = LFT_REPLY_HEADER_ELEM; // XXX: remove _ELEM? + break; +#if USE_ADAPTATION + case LFT_ADAPTATION_LAST_HEADER_ELEM: + type = LFT_ADAPTATION_LAST_HEADER; + break; +#endif #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_HEADER_ELEM: - type = LFT_ICAP_LAST_MATCHED_HEADER; - break; case LFT_ICAP_REQ_HEADER_ELEM: type = LFT_ICAP_REQ_HEADER; break; @@ -557,8 +568,10 @@ case LFT_ADAPTED_REQUEST_ALL_HEADERS: case LFT_REPLY_ALL_HEADERS: +#if USE_ADAPTATION + case LFT_ADAPTATION_LAST_ALL_HEADERS: +#endif #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_ALL_HEADERS: case LFT_ICAP_REQ_ALL_HEADERS: case LFT_ICAP_REP_ALL_HEADERS: #endif @@ -573,10 +586,12 @@ case LFT_REPLY_ALL_HEADERS: type = LFT_REPLY_HEADER; break; +#if USE_ADAPTATION + case LFT_ADAPTATION_LAST_ALL_HEADERS: + type = LFT_ADAPTATION_LAST_HEADER; + break; +#endif #if ICAP_CLIENT - case LFT_ICAP_LAST_MATCHED_ALL_HEADERS: - type = LFT_ICAP_LAST_MATCHED_HEADER; - break; case LFT_ICAP_REQ_ALL_HEADERS: type = LFT_ICAP_REQ_HEADER; break; === modified file 'src/log/Tokens.h' --- src/log/Tokens.h 2010-12-12 23:17:33 +0000 +++ src/log/Tokens.h 2011-02-21 05:12:02 +0000 @@ -132,14 +132,14 @@ #if USE_ADAPTATION LTF_ADAPTATION_SUM_XACT_TIMES, LTF_ADAPTATION_ALL_XACT_TIMES, + LFT_ADAPTATION_LAST_HEADER, + LFT_ADAPTATION_LAST_HEADER_ELEM, + LFT_ADAPTATION_LAST_ALL_HEADERS, #endif #if ICAP_CLIENT LFT_ICAP_TOTAL_TIME, - LFT_ICAP_LAST_MATCHED_HEADER, - LFT_ICAP_LAST_MATCHED_HEADER_ELEM, - LFT_ICAP_LAST_MATCHED_ALL_HEADERS, LFT_ICAP_ADDR, LFT_ICAP_SERV_NAME, === modified file 'src/log/access_log.cc' --- src/log/access_log.cc 2010-12-12 23:17:33 +0000 +++ src/log/access_log.cc 2011-02-21 05:12:02 +0000 @@ -311,18 +311,17 @@ LogfileStatus = LOG_ENABLE; -#if USE_ADAPTATION || ICAP_CLIENT +#if USE_ADAPTATION for (logformat_token * curr_token = (log->logFormat?log->logFormat->format:NULL); curr_token; curr_token = curr_token->next) { -#if USE_ADAPTATION if (curr_token->type == LTF_ADAPTATION_SUM_XACT_TIMES || - curr_token->type == LTF_ADAPTATION_ALL_XACT_TIMES) { + curr_token->type == LTF_ADAPTATION_ALL_XACT_TIMES || + curr_token->type == LFT_ADAPTATION_LAST_HEADER || + curr_token->type == LFT_ADAPTATION_LAST_HEADER_ELEM || + curr_token->type == LFT_ADAPTATION_LAST_ALL_HEADERS) { alLogformatHasAdaptToken = true; } -#endif #if ICAP_CLIENT - if (curr_token->type == LFT_ICAP_LAST_MATCHED_HEADER || - curr_token->type == LFT_ICAP_LAST_MATCHED_HEADER_ELEM || - curr_token->type == LFT_ICAP_LAST_MATCHED_ALL_HEADERS) { + if (curr_token->type == LFT_ICAP_TOTAL_TIME) { alLogformatHasIcapToken = true; } #endif @@ -573,7 +572,7 @@ safe_free(aLogEntry->headers.request); #if ICAP_CLIENT - safe_free(aLogEntry->headers.icap); + safe_free(aLogEntry->headers.adapt_last); #endif safe_free(aLogEntry->headers.reply);