From: Alex Rousskov Date: Wed, 15 Dec 2010 17:52:35 +0000 (-0700) Subject: Support libecap::host::xaction::blockVirgin() API, serving ERR_ACCESS_DENIED. X-Git-Tag: take1~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3af10ac07bfc86881f6e7437faca4ff5fbb550ec;p=thirdparty%2Fsquid.git Support libecap::host::xaction::blockVirgin() API, serving ERR_ACCESS_DENIED. deny_info logic is supported for these blocked responses, with the ACL name replaced by the adaptation service name. This allows eCAP adapters to focus on adaptation and blocking logic while letting Squid to serve a configurable block message, with language negotiation and such. Merged noteAdaptationAnswer(msg) and noteAdaptationQueryAbort(bool) into noteAdaptationAnswer(answer). The Adaptation::Answer class manages all currently supported adaptation decisions: forward the adapted message, block user access to the virgin response, and bypassable or fatal error. This "single answer hook" design allows us to add more information to adaption answers without rewriting all the code that forwards those answers to the adaptation initiator. We still often use multiple methods to handle multiple answer categories, but that "forking" is optional and the decision to fork is made locally, inside each answer recepient, reducing the overall code complexity. Also fixed a few virgin body handling corner cases that led to unnecessary exceptions in Adaptation::Ecap::XactionRep despite correct adapter behavior. --- diff --git a/src/Server.cc b/src/Server.cc index 8256a1f77a..c1e47de292 100644 --- a/src/Server.cc +++ b/src/Server.cc @@ -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 @@ ServerStateData::noteBodyConsumerAborted(BodyPipe::Pointer) // 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 @@ ServerStateData::noteAdaptationAnswer(HttpMsg *msg) } } -// 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 @@ ServerStateData::handleAdaptationAborted(bool bypassable) 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) { diff --git a/src/Server.h b/src/Server.h index 31db3c98da..1ec7acfd2d 100644 --- a/src/Server.h +++ b/src/Server.h @@ -90,8 +90,7 @@ public: 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 @@ protected: void handleAdaptedBodyProductionEnded(); void handleAdaptedBodyProducerAborted(); + void handleAdaptedHeader(HttpMsg *msg); void handleAdaptationCompleted(); + void handleAdaptationBlocked(const Adaptation::Answer &answer); void handleAdaptationAborted(bool bypassable = false); #endif diff --git a/src/adaptation/Initiate.cc b/src/adaptation/Initiate.cc index ffba5bc923..7a0c3594ae 100644 --- a/src/adaptation/Initiate.cc +++ b/src/adaptation/Initiate.cc @@ -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 @@ void Adaptation::Initiate::clearInitiator() 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 diff --git a/src/adaptation/Initiate.h b/src/adaptation/Initiate.h index 1baa959c27..39f6a28b92 100644 --- a/src/adaptation/Initiate.h +++ b/src/adaptation/Initiate.h @@ -5,8 +5,6 @@ #include "base/CbcPointer.h" #include "adaptation/forward.h" -class HttpMsg; - namespace Adaptation { @@ -34,7 +32,7 @@ public: 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 diff --git a/src/adaptation/Initiator.cc b/src/adaptation/Initiator.cc index f72e69f8ea..72805aee69 100644 --- a/src/adaptation/Initiator.cc +++ b/src/adaptation/Initiator.cc @@ -28,3 +28,46 @@ Adaptation::Initiator::announceInitiatorAbort(CbcPointer &x) 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) +{ +} diff --git a/src/adaptation/Initiator.h b/src/adaptation/Initiator.h index 32c6a249d7..a17c5cdf42 100644 --- a/src/adaptation/Initiator.h +++ b/src/adaptation/Initiator.h @@ -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 diff --git a/src/adaptation/Iterator.cc b/src/adaptation/Iterator.cc index 6f46121c38..712a34f140 100644 --- a/src/adaptation/Iterator.cc +++ b/src/adaptation/Iterator.cc @@ -51,7 +51,7 @@ void Adaptation::Iterator::step() Must(!theLauncher); if (thePlan.exhausted()) { // nothing more to do - sendAnswer(theMsg); + sendAnswer(Answer::Forward(theMsg)); Must(done()); return; } @@ -74,7 +74,26 @@ void Adaptation::Iterator::step() 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 @@ void Adaptation::Iterator::noteInitiatorAborted() 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 @@ void Adaptation::Iterator::noteAdaptationQueryAbort(bool final) 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; } diff --git a/src/adaptation/Iterator.h b/src/adaptation/Iterator.h index b1313bf190..586532ac6e 100644 --- a/src/adaptation/Iterator.h +++ b/src/adaptation/Iterator.h @@ -29,8 +29,7 @@ public: 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 @@ protected: /// 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) diff --git a/src/adaptation/ecap/XactionRep.cc b/src/adaptation/ecap/XactionRep.cc index a638049cc9..39786d813a 100644 --- a/src/adaptation/ecap/XactionRep.cc +++ b/src/adaptation/ecap/XactionRep.cc @@ -9,6 +9,7 @@ #include "HttpReply.h" #include "SquidTime.h" #include "adaptation/ecap/XactionRep.h" +#include "adaptation/Initiator.h" #include "base/TextException.h" CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep); @@ -158,14 +159,21 @@ void Adaptation::Ecap::XactionRep::dropVirgin(const char *reason) { debugs(93,4, HERE << "because " << reason << "; status:" << status()); - Must(proxyingVb = opOn); BodyPipePointer &p = theVirginRep.raw().body_pipe; Must(p != NULL); - Must(p->stillConsuming(this)); - stopConsumingFrom(p); p->enableAutoConsumption(); - proxyingVb = opComplete; + + if (proxyingVb == opOn) { + Must(p->stillConsuming(this)); + stopConsumingFrom(p); + proxyingVb = opComplete; + } else { + Must(!p->stillConsuming(this)); + if (proxyingVb == opUndecided) + proxyingVb = opNever; + } + canAccessVb = false; // called from adapter handler so does not inform adapter @@ -196,7 +204,7 @@ Adaptation::Ecap::XactionRep::useVirgin() proxyingVb = opNever; } - sendAnswer(clone); + sendAnswer(Answer::Forward(clone)); Must(done()); } @@ -211,7 +219,7 @@ Adaptation::Ecap::XactionRep::useAdapted(const libecap::shared_ptrbody()) { // final, bodyless answer proxyingAb = opNever; - sendAnswer(msg); + 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,13 +228,26 @@ Adaptation::Ecap::XactionRep::useAdapted(const libecap::shared_ptrtieBody(this); // sets us as a producer Must(msg->body_pipe != NULL); // check tieBody - sendAnswer(msg); + sendAnswer(Answer::Forward(msg)); debugs(93,4, HERE << "adapter will produce body" << status()); theMaster->abMake(); // libecap will produce } } +void +Adaptation::Ecap::XactionRep::blockVirgin() +{ + debugs(93,3, HERE << status()); + Must(proxyingAb == opUndecided); + proxyingAb = opNever; + + dropVirgin("blockVirgin"); + + sendAnswer(Answer::Block(service().cfg().key)); + Must(done()); +} + void Adaptation::Ecap::XactionRep::vbDiscard() { @@ -250,8 +271,7 @@ void Adaptation::Ecap::XactionRep::vbStopMaking() { // if adapter does not need vb, we do not need to receive it - if (proxyingVb == opOn) - dropVirgin("vbStopMaking"); + dropVirgin("vbStopMaking"); Must(proxyingVb == opComplete); } diff --git a/src/adaptation/ecap/XactionRep.h b/src/adaptation/ecap/XactionRep.h index e07cb981bc..a55f9e1df5 100644 --- a/src/adaptation/ecap/XactionRep.h +++ b/src/adaptation/ecap/XactionRep.h @@ -40,6 +40,7 @@ public: 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(); diff --git a/src/adaptation/forward.h b/src/adaptation/forward.h index 9e693a3e4b..c9ddf65ad3 100644 --- a/src/adaptation/forward.h +++ b/src/adaptation/forward.h @@ -25,6 +25,7 @@ class ServiceGroup; class ServicePlan; class ServiceFilter; class Message; +class Answer; typedef RefCount ServicePointer; typedef RefCount ServiceGroupPointer; diff --git a/src/adaptation/icap/Launcher.cc b/src/adaptation/icap/Launcher.cc index f6a417cf50..9ee142a08e 100644 --- a/src/adaptation/icap/Launcher.cc +++ b/src/adaptation/icap/Launcher.cc @@ -50,12 +50,16 @@ void Adaptation::Icap::Launcher::launchXaction(const char *xkind) 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 @@ void Adaptation::Icap::Launcher::noteInitiatorAborted() } -// 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); diff --git a/src/adaptation/icap/Launcher.h b/src/adaptation/icap/Launcher.h index 054b96bf98..166f5705d1 100644 --- a/src/adaptation/icap/Launcher.h +++ b/src/adaptation/icap/Launcher.h @@ -80,13 +80,12 @@ public: 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 diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc index 9fed32bb96..d093c09221 100644 --- a/src/adaptation/icap/ModXact.cc +++ b/src/adaptation/icap/ModXact.cc @@ -714,7 +714,7 @@ void Adaptation::Icap::ModXact::startSending() { disableRepeats("sent headers"); disableBypass("sent headers", true); - sendAnswer(adapted.header); + sendAnswer(Answer::Forward(adapted.header)); if (state.sending == State::sendingVirgin) echoMore(); diff --git a/src/adaptation/icap/OptXact.cc b/src/adaptation/icap/OptXact.cc index eaf633c496..22971dc934 100644 --- a/src/adaptation/icap/OptXact.cc +++ b/src/adaptation/icap/OptXact.cc @@ -79,7 +79,7 @@ void Adaptation::Icap::OptXact::handleCommRead(size_t) 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; } diff --git a/src/adaptation/icap/ServiceRep.cc b/src/adaptation/icap/ServiceRep.cc index 7f06646764..cb6131cbdc 100644 --- a/src/adaptation/icap/ServiceRep.cc +++ b/src/adaptation/icap/ServiceRep.cc @@ -304,11 +304,19 @@ void Adaptation::Icap::ServiceRep::announceStatusChange(const char *downPhrase, } // 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 @@ void Adaptation::Icap::ServiceRep::noteAdaptationAnswer(HttpMsg *msg) 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) diff --git a/src/adaptation/icap/ServiceRep.h b/src/adaptation/icap/ServiceRep.h index fa67bde23a..67c746f78c 100644 --- a/src/adaptation/icap/ServiceRep.h +++ b/src/adaptation/icap/ServiceRep.h @@ -119,8 +119,7 @@ public: // treat these as private, they are for callbacks only 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 diff --git a/src/cf.data.pre b/src/cf.data.pre index 3aaa0b68ae..e485a9adfd 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -5917,6 +5917,8 @@ DOC_START the first authentication related acl encountered - When none of the http_access lines matches. It's then the last acl processed on the last http_access line. + - When the decision to deny access was made by an adaptation service, + the acl name is the corresponding eCAP or ICAP service_name. NP: If providing your own custom error pages with error_directory you may also specify them by your custom file name: diff --git a/src/client_side_request.cc b/src/client_side_request.cc index 76e3d3aadc..d54dbbcf27 100644 --- a/src/client_side_request.cc +++ b/src/client_side_request.cc @@ -1407,9 +1407,30 @@ ClientHttpRequest::startAdaptation(const Adaptation::ServiceGroupPointer &g) } 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)) { @@ -1458,11 +1479,13 @@ ClientHttpRequest::noteAdaptationAnswer(HttpMsg *msg) } 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 diff --git a/src/client_side_request.h b/src/client_side_request.h index f39d8b54a5..7e60938f83 100644 --- a/src/client_side_request.h +++ b/src/client_side_request.h @@ -166,8 +166,9 @@ public: 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); diff --git a/src/err_detail_type.h b/src/err_detail_type.h index 3246a2fb5d..826617d25c 100644 --- a/src/err_detail_type.h +++ b/src/err_detail_type.h @@ -9,6 +9,9 @@ typedef enum { 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