]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Added an adaptation service group API to support groups of services. Current
authorAlex Rousskov <rousskov@measurement-factory.com>
Thu, 3 Apr 2008 05:31:29 +0000 (23:31 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Thu, 3 Apr 2008 05:31:29 +0000 (23:31 -0600)
code supports service sets and single-service groups. The former provides a
way to group interchangeable services together so that one (the "best"
available) service is applied to the message. The latter is an internal
feature to allow user to mix service and group names in squid.conf ACLs.
TODO: support service chains (as a service group) and perhaps group of groups.

Moved adaptation access rule state from being shared between ICAPClass and
ICAPAccessCheck classes into a dedicated AccessRule class. This simplifies
both classes and allows for checking access rules in the correct order.

Use group names when linking adaptation access rules to groups. This is less
efficient (especially since we are still using Vector iteration to find a
matching group) but much simpler. TODO: Optimize.

All adaptation services, access rules, and service groups now have a
finalize() method that is called after configuration parsing has been
completed but before the main loop starts. This allows to verify or optimize
name-based links to other services, rules, and groups.

Moved adaptation access check and group classes into their own files.

Added adaptation_service_set squid.conf option, deprecating icap_class. The
new option has more accurate documentation and does not depend on the
adaptation protocol so one can group eCAP and ICAP services.

Added adaptation_service_set squid.conf option, deprecating icap_access. The
new option has more accurate documentation and does not depend on the
adaptation protocol so one can mix-and-match eCAP and ICAP ACL rules.

20 files changed:
src/ICAP/ICAPConfig.cc
src/ICAP/ICAPServiceRep.cc
src/ICAP/ICAPServiceRep.h
src/Server.cc
src/adaptation/AccessCheck.cc [new file with mode: 0644]
src/adaptation/AccessCheck.h [new file with mode: 0644]
src/adaptation/AccessRule.cc [new file with mode: 0644]
src/adaptation/AccessRule.h [new file with mode: 0644]
src/adaptation/Config.cc
src/adaptation/Config.h
src/adaptation/Makefile.am
src/adaptation/Service.cc
src/adaptation/Service.h
src/adaptation/ServiceGroups.cc [new file with mode: 0644]
src/adaptation/ServiceGroups.h [new file with mode: 0644]
src/adaptation/forward.h
src/cache_cf.cc
src/cf.data.depend
src/cf.data.pre
src/client_side_request.cc

index b4858776074eaeb75f939f408af89c683e9f3c9a..36118ff94fc63549180aface15f95ca2ccfd90ce 100644 (file)
@@ -62,7 +62,7 @@ Adaptation::ServicePointer
 ICAPConfig::createService(const Adaptation::ServiceConfig &cfg)
 {
     ICAPServiceRep::Pointer s = new ICAPServiceRep(cfg);
-    s->finalize(s);
+    s->setSelf(s);
     return s.getRaw();
 }
 
index b16e00f999c7f8724c69208cf587cdc23ecd55d0..c8de132f4340e2d8393a0916445af01fb01ef6b6 100644 (file)
@@ -29,14 +29,18 @@ ICAPServiceRep::~ICAPServiceRep()
     changeOptions(0);
 }
 
-bool
-ICAPServiceRep::finalize(Pointer &aSelf)
+void
+ICAPServiceRep::setSelf(Pointer &aSelf)
 {
     assert(!self && aSelf != NULL);
     self = aSelf;
+}
 
-       if (!Adaptation::Service::finalize())
-        return false;
+void
+ICAPServiceRep::finalize()
+{
+       Adaptation::Service::finalize();
+    assert(self != NULL);
 
     // use /etc/services or default port if needed
        const bool have_port = cfg().port >= 0;
@@ -49,8 +53,6 @@ ICAPServiceRep::finalize(Pointer &aSelf)
             writeableCfg().port = 1344;
         }
     }
-
-    return true;
 }
 
 void ICAPServiceRep::invalidate()
index 6f1d54f94d178a64c21a15eee12eece38ac5c1a3..ea9bd00ee2cbaa29518001cbacf175dcd9f133a9 100644 (file)
@@ -83,7 +83,9 @@ public:
     ICAPServiceRep(const Adaptation::ServiceConfig &config);
     virtual ~ICAPServiceRep();
 
-    bool finalize(Pointer &aSelf); // needs self pointer for ICAPOptXact
+    void setSelf(Pointer &aSelf); // needs self pointer for ICAPOptXact
+    virtual void finalize();
+
     void invalidate(); // call when the service is no longer needed or valid
 
     bool probed() const; // see comments above
index 489dc2006e015b93f8df664386d09af8f28459c8..e9bc416bd09eb8b8f4d696bac8cafb2598e9dc7a 100644 (file)
@@ -40,7 +40,7 @@
 #include "errorpage.h"
 
 #if USE_ADAPTATION
-#include "adaptation/Service.h"
+#include "adaptation/AccessCheck.h"
 #include "ICAP/ICAPConfig.h"
 extern ICAPConfig TheICAPConfig;
 #endif
diff --git a/src/adaptation/AccessCheck.cc b/src/adaptation/AccessCheck.cc
new file mode 100644 (file)
index 0000000..1cc5733
--- /dev/null
@@ -0,0 +1,234 @@
+#include "squid.h"
+#include "structs.h"
+
+#include "ConfigParser.h"
+#include "ACL.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "ACLChecklist.h"
+#include "adaptation/Service.h"
+#include "adaptation/ServiceGroups.h"
+#include "adaptation/AccessRule.h"
+#include "adaptation/AccessCheck.h"
+
+
+cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
+
+Adaptation::AccessCheck::AccessCheck(Method aMethod,
+                                 VectPoint aPoint,
+                                 HttpRequest *aReq,
+                                 HttpReply *aRep,
+                                 AccessCheckCallback *aCallback,
+                                 void *aCallbackData): AsyncJob("AccessCheck"), done(FALSE)
+{
+    // TODO: assign these at creation time
+
+    method = aMethod;
+    point = aPoint;
+
+    req = HTTPMSGLOCK(aReq);
+    rep = aRep ? HTTPMSGLOCK(aRep) : NULL;
+
+    callback = aCallback;
+
+    callback_data = cbdataReference(aCallbackData);
+
+    acl_checklist = NULL;
+
+    debugs(93, 5, "AccessCheck constructed for " << methodStr(method) << " " << vectPointStr(point));
+}
+
+Adaptation::AccessCheck::~AccessCheck()
+{
+    HTTPMSGUNLOCK(req);
+    HTTPMSGUNLOCK(rep);
+}
+
+/*
+ * Walk the access rules list and find all classes that have at least
+ * one service with matching method and vectoring point.
+ */
+void
+Adaptation::AccessCheck::check()
+{
+    debugs(93, 4, "Adaptation::AccessCheck::check");
+
+    typedef AccessRules::iterator ARI;
+    for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
+
+        /*
+         * We only find the first matching service because we only need
+         * one matching service to justify ACL-checking a class.  We might
+         * use other services belonging to the class if the first service
+         * turns out to be unusable for some reason.
+         */
+        AccessRule *r = *i;
+        ServicePointer service = findBestService(*r, false);
+        if (service != NULL) {
+            debugs(93, 5, "Adaptation::AccessCheck::check: rule '" << r->id << "' has candidate service '" << service->cfg().key << "'");
+            candidates += r->id;
+        }
+    }
+
+    checkCandidates();
+}
+
+// XXX: Here and everywhere we call FindRule(topCandidate()):
+// Once we identified the candidate, we should not just ignore it
+// if reconfigure changes rules. We should either lock the rule to
+// prevent reconfigure from stealing it or restart the check with
+// new rules. Throwing an exception may also be appropriate.
+void
+Adaptation::AccessCheck::checkCandidates()
+{
+    debugs(93, 3, "Adaptation::AccessCheck checks " << candidates.size());
+
+    while (!candidates.empty()) {
+        if (AccessRule *r = FindRule(topCandidate())) {
+            // XXX: we do not have access to conn->rfc931 here.
+            acl_checklist = aclChecklistCreate(r->acl, req, dash_str);
+            acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
+            return;
+        }
+
+        candidates.shift(); // the rule apparently went away (reconfigure)
+    }
+
+    // when there are no canidates, fake answer 1
+    debugs(93, 3, "Adaptation::AccessCheck::check: NO candidates left");
+    noteAnswer(1);
+}
+
+void
+Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer, void *data)
+{
+    debugs(93, 8, "AccessCheckCallbackWrapper: answer=" << answer);
+    AccessCheck *ac = (AccessCheck*)data;
+    ac->noteAnswer(answer);
+}
+
+void
+Adaptation::AccessCheck::noteAnswer(int answer)
+{
+    debugs(93, 5, HERE << "AccessCheck::noteAnswer " << answer);
+    if (candidates.size())
+        debugs(93, 5, HERE << "was checking " << topCandidate());
+
+    if (!answer) {
+        checkCandidates();
+        return;
+    }
+
+    /*
+     * We use an event here to break deep function call sequences
+     */
+    // XXX: use AsyncCall for callback and remove
+    CallJobHere(93, 5, this, Adaptation::AccessCheck::do_callback);
+}
+
+void
+Adaptation::AccessCheck::do_callback()
+{
+    debugs(93, 3, "Adaptation::AccessCheck::do_callback");
+
+    if (candidates.size())
+        debugs(93, 3, HERE << "was checking rule" << topCandidate());
+
+    void *validated_cbdata;
+    if (!cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
+        debugs(93,3,HERE << "do_callback: callback_data became invalid, skipping");
+        return;
+    }
+
+    ServicePointer service = NULL;
+    if (candidates.size()) {
+        if (AccessRule *r = FindRule(topCandidate())) {
+            service = findBestService(*r, true);
+            if (service != NULL)
+                debugs(93,3,HERE << "do_callback: with service " << service->cfg().uri);
+            else
+                debugs(93,3,HERE << "do_callback: no service for rule" << r->id);
+        } else {
+            debugs(93,3,HERE << "do_callback: no rule" << topCandidate());
+        }
+        candidates.shift(); // done with topCandidate()
+       } else {
+        debugs(93,3,HERE << "do_callback: no candidate rules");
+       }
+
+    callback(service, validated_cbdata);
+    done = TRUE;
+}
+
+Adaptation::ServicePointer
+Adaptation::AccessCheck::findBestService(AccessRule &r, bool preferUp) {
+
+    const char *what = preferUp ? "up " : "";
+    debugs(93,7,HERE << "looking for the first matching " << 
+        what << "service in group " << r.groupId);
+
+    ServicePointer secondBest;
+
+    ServiceGroup *g = FindGroup(r.groupId);
+
+    if (!g) {
+        debugs(93,5,HERE << "lost " << r.groupId << " group in rule" << r.id);
+        return ServicePointer();
+       }
+
+    ServiceGroup::Loop loop(g->initialServices());
+    typedef ServiceGroup::iterator SGI;
+    for (SGI i = loop.begin; i != loop.end; ++i) {
+
+        ServicePointer service = FindService(*i);
+
+        if (!service)
+            continue;
+
+        if (method != service->cfg().method)
+            continue;
+
+        if (point != service->cfg().point)
+            continue;
+
+        // sending a message to a broken service is likely to cause errors
+        if (service->cfg().bypass && service->broken())
+            continue;
+
+        if (service->up()) {
+            // sending a message to a service that does not want it is useless
+            // note that we cannot check wantsUrl for service that is not "up"
+            // note that even essential services are skipped on unwanted URLs!
+            if (!service->wantsUrl(req->urlpath))
+                continue;
+        } else {
+            if (!secondBest)
+                secondBest = service;
+            if (preferUp) {
+                // the caller asked for an "up" service and we can bypass this one
+                if (service->cfg().bypass)
+                    continue;
+                debugs(93,5,HERE << "cannot skip an essential down service");
+                what = "down-but-essential ";
+            }
+        }
+
+        debugs(93,5,HERE << "found first matching " <<
+            what << "service for " << r.groupId << " group in rule" << r.id <<
+            ": " << service->cfg().key);
+
+        return service;
+    }
+
+    if (secondBest != NULL) {
+        what = "down ";
+        debugs(93,5,HERE << "found first matching " <<
+            what << "service for " << r.groupId << " group in rule" << r.id <<
+            ": " << secondBest->cfg().key);
+        return secondBest;
+    }
+
+    debugs(93,5,HERE << "found no matching " << 
+        what << "services for " << r.groupId << " group in rule" << r.id);
+    return ServicePointer();
+}
diff --git a/src/adaptation/AccessCheck.h b/src/adaptation/AccessCheck.h
new file mode 100644 (file)
index 0000000..418c85b
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef SQUID_ADAPTATION__ACCESS_H
+#define SQUID_ADAPTATION__ACCESS_H
+
+#include "ICAP/AsyncJob.h"
+#include "adaptation/Elements.h"
+#include "adaptation/forward.h"
+
+class HttpRequest;
+class HttpReply;
+
+namespace Adaptation {
+
+class AccessRule;
+
+// checks adaptation_access rules to find a matching adaptation service
+class AccessCheck: public virtual AsyncJob
+{
+
+public:
+    typedef void AccessCheckCallback(ServicePointer match, void *data);
+    AccessCheck(Method, VectPoint, HttpRequest *, HttpReply *, AccessCheckCallback *, void *);
+    ~AccessCheck();
+
+private:
+    Method method;
+    VectPoint point;
+    HttpRequest *req;
+    HttpReply *rep;
+    AccessCheckCallback *callback;
+    void *callback_data;
+    ACLChecklist *acl_checklist;
+
+    typedef int Candidate;
+    typedef Vector<Candidate> Candidates;
+    Candidates candidates;
+    Candidate topCandidate() { return *candidates.begin(); }
+
+    void do_callback();
+    ServicePointer findBestService(AccessRule &r, bool preferUp);
+    bool done;
+
+public:
+    void check();
+    void checkCandidates();
+    static void AccessCheckCallbackWrapper(int, void*);
+#if 0
+    static EVH AccessCheckCallbackEvent;
+#endif
+    void noteAnswer(int answer);
+
+//AsyncJob virtual methods
+    virtual bool doneAll() const { return AsyncJob::doneAll() && done;}
+
+private:
+    CBDATA_CLASS2(AccessCheck);
+};
+
+} // namespace Adaptation
+
+#endif /* SQUID_ADAPTATION__ACCESS_H */
diff --git a/src/adaptation/AccessRule.cc b/src/adaptation/AccessRule.cc
new file mode 100644 (file)
index 0000000..c62d819
--- /dev/null
@@ -0,0 +1,63 @@
+#include "squid.h"
+#include "structs.h"
+
+#include "ConfigParser.h"
+#include "ACL.h"
+#include "adaptation/AccessRule.h"
+#include "adaptation/Service.h"
+#include "adaptation/ServiceGroups.h"
+
+
+int Adaptation::AccessRule::LastId = 0;
+
+Adaptation::AccessRule::AccessRule(): id(++LastId), acl(NULL)
+{
+}
+
+Adaptation::AccessRule::~AccessRule()
+{
+    // XXX: leaking acls here?
+}
+
+void
+Adaptation::AccessRule::parse(ConfigParser &parser)
+{
+    ConfigParser::ParseString(&groupId);
+    aclParseAccessLine(parser, &acl);
+}
+
+void
+Adaptation::AccessRule::finalize()
+{
+    if (!group()) {
+        debugs(93,0, "ERROR: Unknown adaptation service or group name: '" <<
+            groupId << "'"); // TODO: fail on failures
+       }
+}
+
+Adaptation::ServiceGroup *
+Adaptation::AccessRule::group()
+{
+    return FindGroup(groupId);
+}
+
+
+Adaptation::AccessRules &
+Adaptation::AllRules()
+{
+    static AccessRules TheRules;
+    return TheRules;
+}
+
+// TODO: make AccessRules::find work
+Adaptation::AccessRule *
+Adaptation::FindRule(const AccessRule::Id &id)
+{
+    typedef AccessRules::iterator ARI;
+    for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
+        if ((*i)->id == id)
+            return *i;
+    }
+
+    return NULL;
+}
diff --git a/src/adaptation/AccessRule.h b/src/adaptation/AccessRule.h
new file mode 100644 (file)
index 0000000..6d4b84e
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef SQUID_ADAPTATION__ACCESS_RULE_H
+#define SQUID_ADAPTATION__ACCESS_RULE_H
+
+#include "SquidString.h"
+#include "adaptation/forward.h"
+
+class acl_access;
+
+namespace Adaptation {
+
+// manages adaptation_access configuration by associating an acl with
+// an adaptation service group
+class AccessRule {
+public:
+    AccessRule();
+    ~AccessRule();
+
+    void parse(ConfigParser &parser);
+    void finalize();   
+
+    // service group consisting of one or more services
+    ServiceGroup *group();
+
+public:
+    typedef int Id;
+    const Id id;
+    String groupId;
+    acl_access *acl;
+
+private:
+    static Id LastId;
+};
+
+typedef Vector<Adaptation::AccessRule*> AccessRules;
+extern AccessRules &AllRules();
+extern AccessRule *FindRule(const AccessRule::Id &id);
+
+} // namespace Adaptation
+
+#endif /* SQUID_ADAPTATION__ACCESS_RULE_H */
index 68ba53e2555ac019736dfc3aef423732d7a81220..b6d1e7df878eafa88b09f9e1582d62cdfa2e3b98 100644 (file)
 #include "ACL.h"
 #include "Store.h"
 #include "Array.h"     // really Vector
-#include "HttpRequest.h"
-#include "HttpReply.h"
-#include "ACLChecklist.h"
-#include "wordlist.h"
 #include "adaptation/Config.h"
 #include "adaptation/Service.h"
+#include "adaptation/AccessRule.h"
+#include "adaptation/ServiceGroups.h"
 
 
-Adaptation::Config::Classes &
-Adaptation::Config::AllClasses()
-{
-    static Classes TheClasses;
-    return TheClasses;
-}
-
-Adaptation::Class *
-Adaptation::Config::FindClass(const String& key)
-{
-       if (!key.size())
-               return NULL;
-
-    typedef Classes::iterator SI;
-    for (SI i = AllClasses().begin(); i != AllClasses().end(); ++i) {
-        if ((*i)->key == key)
-            return *i;
-    }
-
-    return NULL;
-}
-
-Adaptation::Config::Services &
-Adaptation::Config::AllServices()
-{
-    static Services TheServices;
-    return TheServices;
-}
-
-Adaptation::ServicePointer
-Adaptation::Config::FindService(const String& key)
-{
-debugs(1,1, HERE << "looking for " << key << " among " << AllServices().size() << " services");
-    typedef Services::iterator SI;
-    for (SI i = AllServices().begin(); i != AllServices().end(); ++i) {
-debugs(1,1, HERE << "\tcompare: " << key << " ? " << (*i)->cfg().key);
-        if ((*i)->cfg().key == key)
-            return *i;
-    }
-debugs(1,1, HERE << "not found " << key << " among " << AllServices().size() << " services");
-
-    return NULL;
-}
-
-void
-Adaptation::Config::AddService(ServicePointer s)
-{
-    AllServices().push_back(s);
-}
-
-void
-Adaptation::Config::AddClass(Class *c)
-{
-    AllClasses().push_back(c);
-}
-
-
-Adaptation::Class::Class(): key(NULL), accessList(NULL), service_names(NULL)
-{
-    wordlistDestroy(&service_names);
-}
-
-Adaptation::Class::~Class()
-{
-    wordlistDestroy(&service_names);
-}
-
-int
-Adaptation::Class::prepare()
-{
-    ConfigParser::ParseString(&key);
-    ConfigParser::ParseWordList(&service_names);
-
-    if (service_names && service_names->next) {
-        debugs(3,0, "WARNING: Multiple  services per icap_class are " <<
-            "not yet supported. See Squid bug #2087.");
-        // TODO: fail on failures
-    }
-
-    return 1;
-}
-
-void
-Adaptation::Class::finalize()
-{
-   for (wordlist *iter = service_names; iter; iter = iter->next) {
-       ServicePointer match = Config::FindService(iter->key);
-       if (match != NULL)
-           services += match;
-   }
-}
-
-// ================================================================================ //
-
-cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
-
-Adaptation::AccessCheck::AccessCheck(Method aMethod,
-                                 VectPoint aPoint,
-                                 HttpRequest *aReq,
-                                 HttpReply *aRep,
-                                 AccessCheckCallback *aCallback,
-                                 void *aCallbackData): AsyncJob("AccessCheck"), done(FALSE)
-{
-    method = aMethod;
-    point = aPoint;
-
-    req = HTTPMSGLOCK(aReq);
-    rep = aRep ? HTTPMSGLOCK(aRep) : NULL;
-
-    callback = aCallback;
-
-    callback_data = cbdataReference(aCallbackData);
-
-    candidateClasses.clean();
-
-    matchedClass.clean();
-
-    acl_checklist = NULL;
-
-    debugs(93, 5, "AccessCheck constructed for " << methodStr(method) << " " << vectPointStr(point));
-}
-
-Adaptation::AccessCheck::~AccessCheck()
-{
-    HTTPMSGUNLOCK(req);
-    HTTPMSGUNLOCK(rep);
-}
-
-/*
- * Walk the Access list and find all classes that have at least
- * one service with matching method and vectoring point.
- */
-void
-Adaptation::AccessCheck::check()
-{
-    debugs(93, 3, "Adaptation::AccessCheck::check");
-
-    typedef Config::Classes::iterator CI;
-    for (CI ci = Config::AllClasses().begin(); ci != Config::AllClasses().end(); ++ci) {
-
-        /*
-         * We only find the first matching service because we only need
-         * one matching service to justify ACL-checking a class.  We might
-         * use other services belonging to the class if the first service
-         * turns out to be unusable for some reason.
-         */
-        Class *c = *ci;
-        ServicePointer service = findBestService(c, false);
-        if (service != NULL) {
-            debugs(93, 3, "Adaptation::AccessCheck::check: class '" << c->key.buf() << "' has candidate service '" << service->cfg().key.buf() << "'");
-            candidateClasses += c->key;
-        }
-    }
-
-    checkCandidates();
-}
-
-void
-Adaptation::AccessCheck::checkCandidates()
-{
-    while (!candidateClasses.empty()) {
-        // It didn't really match yet, but we use the name anyway.
-        matchedClass = candidateClasses.shift();
-        Class *c = Config::FindClass(matchedClass);
-
-        if (!c) // class apparently went away (reconfigure)
-            continue;
-
-        // XXX we don't have access to conn->rfc931 here.
-        acl_checklist = aclChecklistCreate(c->accessList, req, dash_str);
-
-        acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
-
-        return;
-    }
-
-    /*
-     * when there are no canidates, set matchedClass to NULL string
-     * and call the wrapper with answer = 1
-     */
-    debugs(93, 3, "Adaptation::AccessCheck::check: NO candidates or matches found");
-
-    matchedClass.clean();
-
-    AccessCheckCallbackWrapper(1, this);
-
-    return;
-}
-
-void
-Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer, void *data)
-{
-    debugs(93, 5, "AccessCheckCallbackWrapper: answer=" << answer);
-    AccessCheck *ac = (AccessCheck*)data;
-
-    if (ac->matchedClass.size()) {
-        debugs(93, 5, "AccessCheckCallbackWrapper matchedClass = " << ac->matchedClass.buf());
-    }
-
-    if (!answer) {
-        ac->checkCandidates();
-        return;
-    }
-
-    /*
-     * We use an event here to break deep function call sequences
-     */
-    CallJobHere(93, 5, ac, Adaptation::AccessCheck::do_callback);
-}
-
-#if 0
-void
-Adaptation::AccessCheck::AccessCheckCallbackEvent(void *data)
-{
-    debugs(93, 5, "AccessCheckCallbackEvent");
-    AccessCheck *ac = (AccessCheck*)data;
-    ac->do_callback();
-    delete ac;
-}
-#endif
-
-void
-Adaptation::AccessCheck::do_callback()
-{
-    debugs(93, 3, "Adaptation::AccessCheck::do_callback");
-
-    if (matchedClass.size()) {
-        debugs(93, 3, "Adaptation::AccessCheck::do_callback matchedClass = " << matchedClass.buf());
-    }
-
-    void *validated_cbdata;
-    if (!cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
-        debugs(93,3,HERE << "do_callback: callback_data became invalid, skipping");
-        return;
-    }
-
-    ServicePointer service = NULL;
-    if (Class *c = Config::FindClass(matchedClass)) {
-        service = findBestService(c, true);
-        if (service != NULL)
-            debugs(93,3,HERE << "do_callback: with service " << service->cfg().uri);
-        else
-            debugs(93,3,HERE << "do_callback: no " << matchedClass << " service");
-    } else {
-        debugs(93,3,HERE << "do_callback: no " << matchedClass << " class");
-    }
-
-    callback(service, validated_cbdata);
-    done = TRUE;
-}
-
-Adaptation::ServicePointer
-Adaptation::AccessCheck::findBestService(Class *c, bool preferUp) {
-
-    const char *what = preferUp ? "up " : "";
-    debugs(93,7,HERE << "looking for the first matching " << 
-        what << "service among " << c->services.size() <<
-        " services in class " << c->key);
-
-    ServicePointer secondBest;
-
-    Vector<ServicePointer>::iterator si;
-    for (si = c->services.begin(); si != c->services.end(); ++si) {
-        ServicePointer service = *si;
-
-        if (method != service->cfg().method)
-            continue;
-
-        if (point != service->cfg().point)
-            continue;
-
-        // sending a message to a broken service is likely to cause errors
-        if (service->cfg().bypass && service->broken())
-            continue;
-
-        if (service->up()) {
-            // sending a message to a service that does not want it is useless
-            // note that we cannot check wantsUrl for service that is not "up"
-            // note that even essential services are skipped on unwanted URLs!
-            if (!service->wantsUrl(req->urlpath))
-                continue;
-        } else {
-            if (!secondBest)
-                secondBest = service;
-            if (preferUp) {
-                // the caller asked for an "up" service and we can bypass this one
-                if (service->cfg().bypass)
-                    continue;
-                debugs(93,5,HERE << "cannot skip an essential down service");
-                what = "down-but-essential ";
-            }
-        }
-
-        debugs(93,5,HERE << "found first matching " <<
-            what << "service in class " << c->key <<
-            ": " << service->cfg().key);
-
-        return service;
-    }
-
-    if (secondBest != NULL) {
-        what = "down ";
-        debugs(93,5,HERE << "found first matching " <<
-            what << "service in class " << c->key <<
-            ": " << secondBest->cfg().key);
-        return secondBest;
-    }
-
-    debugs(93,5,HERE << "found no matching " << 
-        what << "services in class " << c->key);
-    return ServicePointer();
-}
-
-// ================================================================================ //
-
 void
 Adaptation::Config::parseService()
 {
@@ -371,7 +54,10 @@ Adaptation::Config::parseService()
 void
 Adaptation::Config::freeService()
 {
-    // XXX: leaking Services and ServiceConfigs?
+    while (!serviceConfigs.empty()) {
+        delete serviceConfigs.back();
+        serviceConfigs.pop_back();
+       }
 }
 
 void
@@ -392,85 +78,87 @@ Adaptation::Config::finalize()
     typedef Vector<ServiceConfig*>::const_iterator VISCI;
     const Vector<ServiceConfig*> &configs = serviceConfigs;
     debugs(93,3, "Found " << configs.size() << " service configs.");
-    for (VISCI ci = configs.begin(); ci != configs.end(); ++ci) {
-        ServicePointer s = createService(**ci);
+    for (VISCI i = configs.begin(); i != configs.end(); ++i) {
+        ServicePointer s = createService(**i);
         if (s != NULL)
-            AddService(s);
+            AllServices().push_back(s);
     }
 
-    debugs(93,1, "Initialized " << configs.size() <<
+    debugs(93,3, "Created " << configs.size() <<
         " message adaptation services.");
 }
 
-void
-Adaptation::Config::Finalize()
+// poor man for_each
+template <class Collection>
+static void
+FinalizeEach(Collection &collection, const char *label)
 {
-    // link classes with the service reps they use
-    typedef Classes::iterator CI;
-    for (CI ci = AllClasses().begin(); ci != AllClasses().end(); ++ci) {
-        Class *c = *ci;
-        c->finalize(); // TODO: fail on failures
-    }
+    typedef typename Collection::iterator CI;
+    for (CI i = collection.begin(); i != collection.end(); ++i)
+        (*i)->finalize();
 
-    debugs(93,2, "Initialized " << AllClasses().size() <<
-        " message adaptation service classes.");
+    debugs(93,2, "Initialized " << collection.size() << ' ' << label);
 }
 
 void
-Adaptation::Config::parseClass()
+Adaptation::Config::Finalize()
 {
-    Class *C = new Class();
-
-    if (C->prepare()) {
-        AddClass(C);
-    } else {
-        delete C;
-    }
-};
+    FinalizeEach(AllServices(), "message adaptation services");
+    FinalizeEach(AllGroups(), "message adaptation service groups");
+    FinalizeEach(AllRules(), "message adaptation access rules");
+}
 
 void
-Adaptation::Config::freeClass()
+Adaptation::Config::ParseServiceSet()
 {
-    // XXX: leaking Classes here?
+    ServiceSet *g = new ServiceSet();
+    g->parse();
+    AllGroups().push_back(g);
 }
 
 void
-Adaptation::Config::dumpClass(StoreEntry *entry, const char *name) const
+Adaptation::Config::FreeServiceSet()
 {
-    typedef Classes::iterator CI;
-    for (CI i = AllClasses().begin(); i != AllClasses().end(); ++i)
-        storeAppendPrintf(entry, "%s %s\n", name, (*i)->key.buf());
+    while (!AllGroups().empty()) {
+               delete AllGroups().back();
+               AllGroups().pop_back();
+       }
 }
 
 void
-Adaptation::Config::parseAccess(ConfigParser &parser)
+Adaptation::Config::DumpServiceSet(StoreEntry *entry, const char *name)
 {
-    String aKey;
-    ConfigParser::ParseString(&aKey);
-    Class *c = FindClass(aKey);
-
-    if (!c)
-        fatalf("Did not find  class '%s' referenced on line %d\n",
-               aKey.buf(), config_lineno);
+    typedef Groups::iterator GI;
+    for (GI i = AllGroups().begin(); i != AllGroups().end(); ++i)
+        storeAppendPrintf(entry, "%s %s\n", name, (*i)->id.buf());
+}
 
-    aclParseAccessLine(parser, &c->accessList);
-};
+void
+Adaptation::Config::ParseAccess(ConfigParser &parser)
+{
+    AccessRule *r = new AccessRule;
+    r->parse(parser);
+    AllRules().push_back(r);
+}
 
 void
-Adaptation::Config::freeAccess()
+Adaptation::Config::FreeAccess()
 {
-    (void) 0;
+    while (!AllRules().empty()) {
+        delete AllRules().back();
+        AllRules().pop_back();
+       }
 }
 
 void
-Adaptation::Config::dumpAccess(StoreEntry *entry, const char *name) const
+Adaptation::Config::DumpAccess(StoreEntry *entry, const char *name)
 {
     LOCAL_ARRAY(char, nom, 64);
 
-    typedef Classes::iterator CI;
-    for (CI i = AllClasses().begin(); i != AllClasses().end(); ++i) {
-        snprintf(nom, 64, "%s %s", name, (*i)->key.buf());
-        dump_acl_access(entry, nom, (*i)->accessList);
+    typedef AccessRules::iterator CI;
+    for (CI i = AllRules().begin(); i != AllRules().end(); ++i) {
+        snprintf(nom, 64, "%s %s", name, (*i)->groupId.buf());
+        dump_acl_access(entry, nom, (*i)->acl);
        }
 }
 
@@ -479,18 +167,18 @@ Adaptation::Config::Config()
     // XXX: should we init members?
 }
 
+// XXX: this is called for ICAP and eCAP configs, but deals mostly 
+// with global arrays shared by those individual configs
 Adaptation::Config::~Config()
 {
+    FreeAccess();
+    FreeServiceSet();
 
     // invalidate each service so that it can be deleted when refcount=0
-    typedef Services::iterator SCI;
-    for (SCI i = AllServices().begin(); i != AllServices().end(); ++i)
-        (*i)->invalidate();
-
-    AllServices().clean();
-
-    while (!AllClasses().empty()) {
-               delete AllClasses().back();
-               AllClasses().pop_back();
+    while (!AllServices().empty()) {
+        AllServices().back()->invalidate();
+        AllServices().pop_back();
        }
+
+    freeService();
 }
index 8f5f49b0105490c2bd7d586d71e4eca22a057856..065ea0f4dcc4a76b195a33c41d9714605583d4b1 100644 (file)
@@ -19,70 +19,22 @@ class Class;
 
 typedef RefCount<Service> ServicePointer;
 
-class Class
-{
-
-public:
-    String key;
-    acl_access *accessList;
-
-    Vector<ServicePointer> services;
-
-    Class();
-    ~Class();
-
-    int prepare();
-    void finalize();
-
-private:
-    wordlist *service_names;
-};
-
-class AccessCheck: public virtual AsyncJob
-{
-
-public:
-    typedef void AccessCheckCallback(ServicePointer match, void *data);
-    AccessCheck(Method, VectPoint, HttpRequest *, HttpReply *, AccessCheckCallback *, void *);
-    ~AccessCheck();
-
-private:
-    Method method;
-    VectPoint point;
-    HttpRequest *req;
-    HttpReply *rep;
-    AccessCheckCallback *callback;
-    void *callback_data;
-    ACLChecklist *acl_checklist;
-    Vector<String> candidateClasses;
-    String matchedClass;
-    void do_callback();
-    ServicePointer findBestService(Class *c, bool preferUp);
-    bool done;
-
-public:
-    void check();
-    void checkCandidates();
-    static void AccessCheckCallbackWrapper(int, void*);
-#if 0
-    static EVH AccessCheckCallbackEvent;
-#endif
-//AsyncJob virtual methods
-    virtual bool doneAll() const { return AsyncJob::doneAll() && done;}
-
-private:
-    CBDATA_CLASS2(AccessCheck);
-};
+class ServiceGroup;
+class AccessRule;
 
 class Config
 {
 public:
-       static ServicePointer FindService(const String &key);
-       static Class *FindClass(const String &key);
-    static void AddService(ServicePointer s);
-       static void AddClass(Class *c);
        static void Finalize();
 
+    static void ParseServiceSet(void);
+    static void FreeServiceSet(void);
+    static void DumpServiceSet(StoreEntry *, const char *);
+
+    static void ParseAccess(ConfigParser &parser);
+    static void FreeAccess(void);
+    static void DumpAccess(StoreEntry *, const char *);
+
        friend class AccessCheck;
 
 public:
@@ -104,23 +56,8 @@ public:
     ServicePointer findService(const String&);
     Class * findClass(const String& key);
 
-    void parseClass(void);
-    void freeClass(void);
-    void dumpClass(StoreEntry *, const char *) const;
-
-    void parseAccess(ConfigParser &parser);
-    void freeAccess(void);
-    void dumpAccess(StoreEntry *, const char *) const;
-
     void finalize();
 
-protected:
-    // TODO: use std::hash_map<string, ...> instead
-    typedef Vector<Adaptation::ServicePointer> Services;
-    typedef Vector<Adaptation::Class*> Classes;
-    static Services &AllServices();
-    static Classes &AllClasses();
-
 private:
     Config(const Config &); // unsupported
     Config &operator =(const Config &); // unsupported
index cc7692f2482b2fb6dcb09af7e9cc4c259844e98b..05bddb5a838a0bbdf20c979571cc9406be044de7 100644 (file)
@@ -9,6 +9,10 @@ INCLUDES = \
 noinst_LTLIBRARIES = libadaptation.la
 
 libadaptation_la_SOURCES = \
+       AccessCheck.cc \
+       AccessCheck.h \
+       AccessRule.cc \
+       AccessRule.h \
        Config.cc \
        Config.h \
        Elements.cc \
@@ -22,8 +26,8 @@ libadaptation_la_SOURCES = \
        Service.h \
        ServiceConfig.cc \
        ServiceConfig.h \
-       Xaction.cc \
-       Xaction.h
+       ServiceGroups.cc \
+       ServiceGroups.h
 
 check_PROGRAMS = testHeaders
 
index 3d44f171f700d828994966b822f616969bac8fdf..a299bc24c065cbb01c77be62e2c1fa23ba0d1d22 100644 (file)
@@ -11,8 +11,25 @@ Adaptation::Service::Service(const ServiceConfig &aConfig): theConfig(aConfig)
 Adaptation::Service::~Service()
 {}
 
-bool
+void
 Adaptation::Service::finalize()
 {
-    return true;
+}
+
+Adaptation::Services &
+Adaptation::AllServices()
+{
+    static Services TheServices;
+    return TheServices;
+}
+
+Adaptation::ServicePointer
+Adaptation::FindService(const Service::Id& key)
+{
+    typedef Services::iterator SI;
+    for (SI i = AllServices().begin(); i != AllServices().end(); ++i) {
+        if ((*i)->cfg().key == key)
+            return *i;
+       }
+    return NULL;
 }
index 2e1d3af53d6a8305d43d1a9396da3dd95fd6c485..9d6303ed384ec39468ccacfa057aab6c8d783057 100644 (file)
@@ -20,6 +20,7 @@ class Service: public RefCountable
 {
 public:
     typedef RefCount<Service> Pointer;
+    typedef String Id;
 
 public:
     Service(const ServiceConfig &aConfig);
@@ -45,8 +46,9 @@ public:
 
     const ServiceConfig &cfg() const { return theConfig; }
 
+    virtual void finalize(); // called after creation
+
 protected:
-    bool finalize(); // called after creation
     ServiceConfig &writeableCfg() { return theConfig; }
 
 private:
@@ -55,6 +57,10 @@ private:
 
 typedef Service::Pointer ServicePointer;
 
+typedef Vector<Adaptation::ServicePointer> Services;
+extern Services &AllServices();
+extern ServicePointer FindService(const Service::Id &key);
+
 } // namespace Adaptation
 
 #endif /* SQUID_ADAPTATION__SERVICE_H */
diff --git a/src/adaptation/ServiceGroups.cc b/src/adaptation/ServiceGroups.cc
new file mode 100644 (file)
index 0000000..bbe11e7
--- /dev/null
@@ -0,0 +1,84 @@
+#include "squid.h"
+
+#include "ConfigParser.h"
+#include "Array.h"      // really Vector
+#include "adaptation/Config.h"
+#include "adaptation/AccessRule.h"
+#include "adaptation/Service.h"
+#include "adaptation/ServiceGroups.h"
+
+
+Adaptation::ServiceGroup::ServiceGroup(const String &aKind): kind(aKind)
+{
+}
+
+Adaptation::ServiceGroup::~ServiceGroup()
+{
+}
+
+void
+Adaptation::ServiceGroup::parse()
+{
+    ConfigParser::ParseString(&id);
+
+    wordlist *names = NULL;
+    ConfigParser::ParseWordList(&names);
+    for (wordlist *i = names; i; i = i->next)
+        services.push_back(i->key);
+    wordlistDestroy(&names);
+}
+
+void
+Adaptation::ServiceGroup::finalize()
+{
+    for (iterator i = services.begin(); i != services.end(); ++i) {
+        const String &id = *i;
+        // TODO: fail on failures
+        if (!FindService(id))
+            debugs(93,0, "ERROR: Unknown adaptation name: " << id);
+       }
+}
+
+/* ServiceSet */
+
+Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set")
+{
+}
+
+Adaptation::ServiceGroup::Loop Adaptation::ServiceSet::initialServices()
+{
+    return Loop(services.begin(), services.end());
+}
+
+#if FUTURE_OPTIMIZATION
+void
+Adaptation::ServiceSet::finalize()
+{
+    ServiceGroup::finalize();
+
+    for (wordlist *iter = service_names; iter; iter = iter->next) {
+        ServicePointer match = Config::FindService(iter->id);
+        if (match != NULL)
+            services += match;
+    }
+}
+#endif
+
+Adaptation::Groups &
+Adaptation::AllGroups()
+{
+    static Groups TheGroups;
+    return TheGroups;
+}
+
+Adaptation::ServiceGroup *
+Adaptation::FindGroup(const ServiceGroup::Id &id)
+{
+    typedef Groups::iterator GI;
+    for (GI i = AllGroups().begin(); i != AllGroups().end(); ++i) {
+        if ((*i)->id == id)
+            return *i;
+    }
+
+    return NULL;
+}
diff --git a/src/adaptation/ServiceGroups.h b/src/adaptation/ServiceGroups.h
new file mode 100644 (file)
index 0000000..e76f147
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef SQUID_ADAPTATION__SERVICE_GROUPS_H
+#define SQUID_ADAPTATION__SERVICE_GROUPS_H
+
+#include "adaptation/forward.h"
+
+namespace Adaptation {
+
+// Interface for grouping adaptation services together.
+// Specific groups differ in how the first and the next services are selected
+class ServiceGroup
+{
+public:
+    typedef Vector<String> Store;
+    typedef Store::iterator iterator;
+    typedef String Id;
+
+    // Information sufficient to iterate services stored in the group,
+    // grouped together to simplify initial/sequentialServices interfaces.
+    // The iterators point back to 
+    struct Loop {
+        Loop(const iterator &b, const iterator &e): begin(b), end(e) {}
+               iterator begin;
+               iterator end;
+       };
+
+public:
+    ServiceGroup(const String &aKind);
+    virtual ~ServiceGroup();
+
+    virtual void parse();
+    virtual void finalize(); // called after all are parsed
+
+    virtual Loop initialServices() = 0;
+    // TODO: virtual Loop sequentialServices() = 0;
+
+public:
+    String kind;
+    Id id;
+    Store services;
+};
+
+// a group of equivalent services; one service per set is usually used
+class ServiceSet: public ServiceGroup
+{
+public:
+    ServiceSet();
+    virtual Loop initialServices();
+};
+
+// corner case: a group consisting of one service
+class SingleService: public ServiceGroup
+{
+public:
+    SingleService(const String &aServiceKey);
+    virtual Loop initialServices();
+};
+
+// TODO: a group of services that must be used one after another
+// class ServiceChain: public ServiceGroup
+
+
+typedef Vector<Adaptation::ServiceGroup*> Groups;
+extern Groups &AllGroups();
+extern ServiceGroup *FindGroup(const ServiceGroup::Id &id);
+
+
+} // namespace Adaptation
+
+#endif /* SQUID_ADAPTATION__SERVICE_GROUPS_H */
+
index 09446f4b68f9d62c28807229cbe71518e6f3913f..3c7ecb1b90ba76d30f215b53a964b4c905a6ba98 100644 (file)
@@ -6,6 +6,11 @@
 template <class C>
 class RefCount;
 
+// For various collections such as AllServices
+// TODO: use std::hash_map<> instead
+template <class Item>
+class Vector;
+
 namespace Adaptation {
 
 class Service;
@@ -14,6 +19,8 @@ class Class;
 class Initiate;
 class Initiator;
 class AccessCheck;
+class AccessRule;
+class ServiceGroup;
 
 typedef RefCount<Service> ServicePointer;
 
index a5ea59007f4692dc40af12648953f909b95bd074..6e7eef95df7133216fb718849a1ecd8eb14b9d4a 100644 (file)
 #include "ESIParser.h"
 #endif
 
+#if USE_ADAPTATION
+#include "adaptation/Config.h"
+
+static void parse_adaptation_service_set_type();
+
+static void parse_adaptation_access_type();
+static void dump_adaptation_access_type(StoreEntry *, const char *);
+static void free_adaptation_access_type();
+
+#endif
+
 #if ICAP_CLIENT
 #include "ICAP/ICAPConfig.h"
 
@@ -3421,6 +3432,35 @@ free_access_log(customlog ** definitions)
     }
 }
 
+#if USE_ADAPTATION
+
+static void
+parse_adaptation_service_set_type()
+{
+    Adaptation::Config::ParseServiceSet();
+}
+
+static void
+parse_adaptation_access_type()
+{
+    Adaptation::Config::ParseAccess(LegacyParser);
+}
+
+static void
+free_adaptation_access_type()
+{
+    Adaptation::Config::FreeAccess();
+}
+
+static void
+dump_adaptation_access_type(StoreEntry * entry, const char *name)
+{
+    Adaptation::Config::DumpAccess(entry, name);
+}
+
+#endif /* USE_ADAPTATION */
+
+
 #if ICAP_CLIENT
 
 static void
@@ -3442,39 +3482,43 @@ dump_icap_service_type(StoreEntry * entry, const char *name, const ICAPConfig &c
 }
 
 static void
-parse_icap_class_type(ICAPConfig * cfg)
+parse_icap_class_type(ICAPConfig *)
 {
-    cfg->parseClass();
+    debugs(93, 0, "WARNING: 'icap_class' is depricated. " <<
+        "Use 'adaptation_service_set' instead");
+    Adaptation::Config::ParseServiceSet();
 }
 
 static void
-free_icap_class_type(ICAPConfig * cfg)
+free_icap_class_type(ICAPConfig *)
 {
-    cfg->freeClass();
+    Adaptation::Config::FreeServiceSet();
 }
 
 static void
-dump_icap_class_type(StoreEntry * entry, const char *name, const ICAPConfig &cfg)
+dump_icap_class_type(StoreEntry * entry, const char *name, const ICAPConfig &)
 {
-    cfg.dumpClass(entry, name);
+    Adaptation::Config::DumpServiceSet(entry, name);
 }
 
 static void
-parse_icap_access_type(ICAPConfig * cfg)
+parse_icap_access_type(ICAPConfig *)
 {
-    cfg->parseAccess(LegacyParser);
+    debugs(93, 0, "WARNING: 'icap_access' is depricated. " <<
+        "Use 'adaptation_access' instead");
+    parse_adaptation_access_type();
 }
 
 static void
-free_icap_access_type(ICAPConfig * cfg)
+free_icap_access_type(ICAPConfig *)
 {
-    cfg->freeAccess();
+    free_adaptation_access_type();
 }
 
 static void
-dump_icap_access_type(StoreEntry * entry, const char *name, const ICAPConfig &cfg)
+dump_icap_access_type(StoreEntry * entry, const char *name, const ICAPConfig &)
 {
-    cfg.dumpAccess(entry, name);
+    dump_adaptation_access_type(entry, name);
 }
 
 #endif
index 389f5107f3e84dfda910d7f4fb24a048725adf69..5c48bc19dc43b8fe8b7dfd11bf2be65b4f0cb5e7 100644 (file)
@@ -25,6 +25,8 @@ http_header_access
 http_header_replace
 http_port_list
 https_port_list
+adaptation_access_type adaptation_service_set acl icap_service icap_class
+adaptation_service_set_type    icap_service
 icap_access_type       icap_class acl
 icap_class_type                icap_service
 icap_service_type
index 86d01b6b0fd32dca20e4442544ce4e8a049f7480..e5daa5a7e7577755acd85ebb0b0d6a1d305431b0 100644 (file)
@@ -5007,16 +5007,15 @@ IFDEF: ICAP_CLIENT
 LOC: TheICAPConfig
 DEFAULT: none
 DOC_START
-       Defines an ICAP service chain. Eventually, multiple services per
-       vectoring point will be supported. For now, please specify a single
-       service per class:
+       This depricated option was documented to define an ICAP service
+       chain, even though it actually defined a set of similar, redundant
+       services, and the chains were not supported. 
 
-       icap_class classname servicename
+       To define a set of redundant services, please use the
+       adaptation_service_set directive.
 
-Example:
-icap_class class_1 service_1
-icap class class_2 service_1
-icap class class_3 service_3
+       If you need adaptation service chains, patches or sponsorship
+       is welcome.
 DOC_END
 
 NAME: icap_access
@@ -5025,21 +5024,9 @@ IFDEF: ICAP_CLIENT
 LOC: TheICAPConfig
 DEFAULT: none
 DOC_START
-       Redirects a request through an ICAP service class, depending
-       on given acls
-
-       icap_access classname allow|deny [!]aclname...
-
-       The icap_access statements are processed in the order they appear in
-       this configuration file. If an access list matches, the processing stops.
-       For an "allow" rule, the specified class is used for the request. A "deny"
-       rule simply stops processing without using the class. You can also use the
-       special classname "None".
-
-       For backward compatibility, it is also possible to use services
-       directly here.
-Example:
-icap_access class_1 allow all
+        This option is depricated. Please use adaptation_access, which
+       has the same ICAP functionality, but comes with better
+       documentation, and eCAP support.
 DOC_END
 
 COMMENT_START
@@ -5059,6 +5046,75 @@ Example:
 loadable_modules @DEFAULT_PREFIX@/lib/MinimalAdapter.so
 DOC_END
 
+COMMENT_START
+ MESSAGE ADAPTATION OPTIONS
+ -----------------------------------------------------------------------------
+COMMENT_END
+
+NAME: adaptation_service_set
+TYPE: adaptation_service_set_type
+IFDEF: USE_ADAPTATION
+LOC: none
+DEFAULT: none
+DOC_START
+
+       Defines a named adaptation service set. The set is populated in
+       the order of adaptation_service_set directives in this file.
+       When adaptation ACLs are processed, the first and only the first
+       applicable adaptation service from the set will be used. Thus,
+       the set should group similar, redundant services, rather than a
+       chain of complementary services.
+
+       If you have a single adaptation service, you do not need to
+       define a set containing it because adaptation_access accepts
+       service names.
+
+       See also: adaptation_access
+
+Example:
+adaptation_service_set svcBlocker urlFilterPrimary urlFilterBackup
+adaptation service_set svcLogger loggerLocal loggerRemote
+DOC_END
+
+NAME: adaptation_access
+TYPE: adaptation_access_type
+IFDEF: USE_ADAPTATION
+LOC: none
+DEFAULT: none
+DOC_START
+       Sends an HTTP transaction to an ICAP or eCAP adaptation service.
+
+       adaptation_access service_name allow|deny [!]aclname...
+       adaptation_access set_name     allow|deny [!]aclname...
+
+       At each supported vectoring point, the adaptation_access
+       statements are processed in the order they appear in this
+       configuration file. Statements pointing to the following services
+       are ignored (i.e., skipped without checking their ACL):
+
+           - services serving different vectoring points
+           - "broken-but-bypassable" services
+           - "up" services configured to ignore such transactions
+              (e.g., based on the ICAP Transfer-Ignore header).
+
+        When a set_name is used, all services in the set are checked
+       using the same rules, to find the first applicable one. See
+       adaptation_service_set for details.
+
+       If an access list is checked and there is a match, the
+       processing stops: For an "allow" rule, the corresponding
+       adaptation service is used for the transaction. For a "deny"
+       rule, no adaptation service is activated.
+
+       It is currently not possible to apply more than one adaptation
+       service at the same vectoring point to the same HTTP transaction.
+
+        See also: icap_service and ecap_service
+
+Example:
+adaptation_access service_1 allow all
+DOC_END
+
 COMMENT_START
  DNS OPTIONS
  -----------------------------------------------------------------------------
index e083285df63e5897d0967cf4bf06055cb4e9877c..1beec35392f6f11372d6233efe2708d1eee77d44 100644 (file)
@@ -60,6 +60,7 @@
 #include "wordlist.h"
 
 #if USE_ADAPTATION
+#include "adaptation/AccessCheck.h"
 #include "ICAP/ICAPConfig.h" /* XXX: replace with generic adaptation config */
 static void adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data);
 #endif