]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Support libecap::host::xaction::blockVirgin() API, serving ERR_ACCESS_DENIED.
authorAlex Rousskov <rousskov@measurement-factory.com>
Wed, 15 Dec 2010 17:52:35 +0000 (10:52 -0700)
committerAlex Rousskov <rousskov@measurement-factory.com>
Wed, 15 Dec 2010 17:52:35 +0000 (10:52 -0700)
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.

21 files changed:
src/Server.cc
src/Server.h
src/adaptation/Initiate.cc
src/adaptation/Initiate.h
src/adaptation/Initiator.cc
src/adaptation/Initiator.h
src/adaptation/Iterator.cc
src/adaptation/Iterator.h
src/adaptation/ecap/XactionRep.cc
src/adaptation/ecap/XactionRep.h
src/adaptation/forward.h
src/adaptation/icap/Launcher.cc
src/adaptation/icap/Launcher.h
src/adaptation/icap/ModXact.cc
src/adaptation/icap/OptXact.cc
src/adaptation/icap/ServiceRep.cc
src/adaptation/icap/ServiceRep.h
src/cf.data.pre
src/client_side_request.cc
src/client_side_request.h
src/err_detail_type.h

index 8256a1f77a2f9c6a45a8e154102d37a7d6a18d7d..c1e47de2927ce1f42ccbaffa1ae0c04ce712a12a 100644 (file)
@@ -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)
 {
index 31db3c98dac6d71d58c941d8a835c2273da90988..1ec7acfd2dd021653920af86a02e278310993c83 100644 (file)
@@ -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
 
index ffba5bc923b7b64d4f5d5c8eef141c6db373269f..7a0c3594ae337e60b7091bd832bb8e3b2e1bc8a5 100644 (file)
@@ -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<Initiator, HttpMsg*>
-{
-public:
-    typedef UnaryMemFunT<Initiator, HttpMsg*> 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<Initiator, Answer, const Answer &> 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
index 1baa959c274a693c38875606d90096f4e8ac2fea..39f6a28b9240b01f5a1aa2026e3679e25d871f38 100644 (file)
@@ -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
 
index f72e69f8ea6f50cfe14540f46f6bb5042c3c6346..72805aee69cbf1c8fe10f8e5bc61fed7e9408bea 100644 (file)
@@ -28,3 +28,46 @@ Adaptation::Initiator::announceInitiatorAbort(CbcPointer<Initiate> &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)
+{
+}
index 32c6a249d742700c914c0240729b62d919fe0c5d..a17c5cdf42f1dea8a8d7514744fdfeab6d09f228 100644 (file)
@@ -4,6 +4,9 @@
 #include "base/AsyncJob.h"
 #include "base/CbcPointer.h"
 #include "adaptation/forward.h"
+#include "HttpMsg.h"
+
+#include <iosfwd>
 
 /*
  * The ICAP Initiator is an ICAP vectoring point that initates ICAP
  * 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<HttpMsg> 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
index 6f46121c3894ae96190e69223d9ad3bffd66a0b9..712a34f140030d11f0dda057704d7010ba205964 100644 (file)
@@ -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;
     }
index b1313bf190b7daf2ff9253a5abca0ec93c8730aa..586532ac6eb82472b8b091cd290e2a180db485c8 100644 (file)
@@ -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)
index a638049cc9575e209e45480f1ba48a2469f1236f..39786d813aef9acd88320c8d65c2e56cedb85bc8 100644 (file)
@@ -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_ptr<libecap::Mess
     HttpMsg *msg = answer().header;
     if (!theAnswerRep->body()) { // 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_ptr<libecap::Mess
         rep->tieBody(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);
 }
 
index e07cb981bcc3285201d0cef04a02e88b15728c40..a55f9e1df53d649929ead42cdbe551cd4788fa6a 100644 (file)
@@ -40,6 +40,7 @@ public:
     virtual libecap::Message &adapted();
     virtual void useVirgin();
     virtual void useAdapted(const libecap::shared_ptr<libecap::Message> &msg);
+    virtual void blockVirgin();
     virtual void adaptationDelayed(const libecap::Delay &);
     virtual void adaptationAborted();
     virtual void vbDiscard();
index 9e693a3e4b5139c58218d4ab2d0cd2d2bdfee3b7..c9ddf65ad3ef259c8e341fd295a66f45dfdefffd 100644 (file)
@@ -25,6 +25,7 @@ class ServiceGroup;
 class ServicePlan;
 class ServiceFilter;
 class Message;
+class Answer;
 
 typedef RefCount<Service> ServicePointer;
 typedef RefCount<ServiceGroup> ServiceGroupPointer;
index f6a417cf50c1fbf6212f8c9b95311df183324cc4..9ee142a08e0fd62cecb83663dbd64d79ec9f3052 100644 (file)
@@ -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);
index 054b96bf981ce60bee3385db48638127e9cb2a07..166f5705d1294101d70281284fe093ce88852b13 100644 (file)
@@ -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
index 9fed32bb962c4ad3a09f0a6320bf56499997fd4e..d093c09221bd54e8418967a84f48017de85af2aa 100644 (file)
@@ -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();
index eaf633c4965bab22073395cb9b7ada627470bfec..22971dc934a2f336a99e675c07412770c6c3e138 100644 (file)
@@ -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;
     }
index 7f06646764113dda8a6bc83c7b6a6d9437512338..cb6131cbdcd6bfc505ec982772264cce9b4ab681 100644 (file)
@@ -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)
index fa67bde23aec2b1c516f1858b1c1a3348b681509..67c746f78c7c9c8ba2e76315dd2b98d8d77cbdf9 100644 (file)
@@ -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
index 3aaa0b68ae2aa10a7f5464f8693dcb371f1323f8..e485a9adfdc3e6e64d3b98ff31dcbb75569b1876 100644 (file)
@@ -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:
index 76e3d3aadc463a44d38395b3d08f5fe8cb626624..d54dbbcf27e39629a3f12a866b72811ea3b5d794 100644 (file)
@@ -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<HttpRequest*>(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
index f39d8b54a5b76bf076a113794665668ed0bd596b..7e60938f83e6cb2a354e2178674e5862459fb1df 100644 (file)
@@ -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);
index 3246a2fb5d02d961a68432dc1bc097f5da1d6df9..826617d25c0fdfd10d90536b3b12e664c6f82005 100644 (file)
@@ -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