]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Moved configuration options that do not depend on the adaptation method
authorAlex Rousskov <rousskov@measurement-factory.com>
Sun, 30 Mar 2008 19:13:51 +0000 (13:13 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Sun, 30 Mar 2008 19:13:51 +0000 (13:13 -0600)
from ICAP/ICAPConfig to adaptation/Config. ICAP and eCAP will not share
the same base configuration but will share the same base configuration code
(i.e., there will be two independent ICAPConfig and ecap::Config objects,
but they will have a common parent).

Implemented delayed creation of adaptation services. We used to create
ICAPServiceRep objects when parsing the configuration file.
Create-as-you-parse is imperfect for several reasons, especially if the
services are dynamically loaded as is the case with eCAP. We now remember the
service configuration and then create the actual service object _after_ the
configuration has been parsed and loadable modules, if any, have been loaded.

No functional changes are expected from this change.

src/ICAP/ICAPConfig.cc
src/ICAP/ICAPConfig.h
src/adaptation/Config.cc [new file with mode: 0644]
src/adaptation/Config.h [new file with mode: 0644]

index 5aa2d6e2b260679a0c8871dda072ed4c8aad0fe4..b4858776074eaeb75f939f408af89c683e9f3c9a 100644 (file)
 
 ICAPConfig TheICAPConfig;
 
-ICAPServiceRep::Pointer
-ICAPConfig::findService(const String& key)
+ICAPConfig::ICAPConfig(): preview_enable(0), preview_size(0),
+    connect_timeout_raw(0), io_timeout_raw(0), reuse_connections(0),
+    client_username_header(NULL), client_username_encode(0)
 {
-    Vector<ICAPServiceRep::Pointer>::iterator iter = services.begin();
-
-    while (iter != services.end()) {
-        if ((*iter)->key == key)
-            return *iter;
-
-        ++iter;
-    }
-
-    return NULL;
-}
-
-ICAPClass *
-ICAPConfig::findClass(const String& key)
-{
-    if (!key.size())
-        return NULL;
-
-    Vector<ICAPClass*>::iterator iter = classes.begin();
-
-    while (iter != classes.end()) {
-        if ((*iter)->key == key)
-            return *iter;
-
-        ++iter;
-    }
-
-    return NULL;
-}
-
-int
-ICAPClass::prepare()
-{
-    int found = 0;
-    wordlist *service_names = NULL;
-    wordlist *iter;
-
-    ConfigParser::ParseString(&key);
-    ConfigParser::ParseWordList(&service_names);
-
-    if (service_names && service_names->next) {
-        debugs(3,0, "WARNING: Multiple ICAP services per icap_class are " <<
-            "not yet supported. See Squid bug #2087.");
-    }
-
-    for (iter = service_names; iter; iter = iter->next) {
-        ICAPServiceRep::Pointer match = TheICAPConfig.findService(iter->key);
-
-        if (match != NULL) {
-            found = 1;
-            services += match;
-        }
-    }
-
-    return found;
-};
-
-// ================================================================================ //
-
-CBDATA_CLASS_INIT(ICAPAccessCheck);
-
-ICAPAccessCheck::ICAPAccessCheck(ICAP::Method aMethod,
-                                 ICAP::VectPoint aPoint,
-                                 HttpRequest *aReq,
-                                 HttpReply *aRep,
-                                 ICAPAccessCheckCallback *aCallback,
-                                 void *aCallbackData): AsyncJob("ICAPAccessCheck"), 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, "ICAPAccessCheck constructed for " << ICAP::methodStr(method) << " " << ICAP::vectPointStr(point));
 }
 
-ICAPAccessCheck::~ICAPAccessCheck()
-{
-    HTTPMSGUNLOCK(req);
-    HTTPMSGUNLOCK(rep);
-}
-
-/*
- * Walk the ICAPAccess list and find all classes that have at least
- * one service with matching method and vectoring point.
- */
-void
-ICAPAccessCheck::check()
-{
-    debugs(93, 3, "ICAPAccessCheck::check");
-    Vector<ICAPClass*>::iterator ci;
-
-    for (ci = TheICAPConfig.classes.begin(); ci != TheICAPConfig.classes.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.
-         */
-        ICAPClass *c = *ci;
-        ICAPServiceRep::Pointer service = findBestService(c, false);
-        if (service != NULL) {
-            debugs(93, 3, "ICAPAccessCheck::check: class '" << c->key.buf() << "' has candidate service '" << service->key.buf() << "'");
-            candidateClasses += c->key;
-        }
-    }
-
-    checkCandidates();
-}
-
-void
-ICAPAccessCheck::checkCandidates()
-{
-    while (!candidateClasses.empty()) {
-        // It didn't really match yet, but we use the name anyway.
-        matchedClass = candidateClasses.shift();
-        ICAPClass *theClass = TheICAPConfig.findClass(matchedClass);
-
-        if (theClass == NULL)
-            // class apparently went away (reconfigure)
-            continue;
-
-        // XXX we don't have access to conn->rfc931 here.
-        acl_checklist = aclChecklistCreate(theClass->accessList, req, dash_str);
-
-        acl_checklist->nonBlockingCheck(ICAPAccessCheckCallbackWrapper, this);
-
-        return;
-    }
-
-    /*
-     * when there are no canidates, set matchedClass to NULL string
-     * and call the wrapper with answer = 1
-     */
-    debugs(93, 3, "ICAPAccessCheck::check: NO candidates or matches found");
-
-    matchedClass.clean();
-
-    ICAPAccessCheckCallbackWrapper(1, this);
-
-    return;
-}
-
-void
-ICAPAccessCheck::ICAPAccessCheckCallbackWrapper(int answer, void *data)
+ICAPConfig::~ICAPConfig()
 {
-    debugs(93, 5, "ICAPAccessCheckCallbackWrapper: answer=" << answer);
-    ICAPAccessCheck *ac = (ICAPAccessCheck*)data;
-
-    if (ac->matchedClass.size()) {
-        debugs(93, 5, "ICAPAccessCheckCallbackWrapper matchedClass = " << ac->matchedClass.buf());
-    }
-
-    if (!answer) {
-        ac->checkCandidates();
-        return;
-    }
-
-    /*
-     * We use an event here to break deep function call sequences
-     */
-    CallJobHere(93, 5, ac, ICAPAccessCheck::do_callback);
+    // TODO: delete client_username_header?
 }
 
-#if 0
-void
-ICAPAccessCheck::ICAPAccessCheckCallbackEvent(void *data)
+Adaptation::ServicePointer
+ICAPConfig::createService(const Adaptation::ServiceConfig &cfg)
 {
-    debugs(93, 5, "ICAPAccessCheckCallbackEvent");
-    ICAPAccessCheck *ac = (ICAPAccessCheck*)data;
-    ac->do_callback();
-    delete ac;
+    ICAPServiceRep::Pointer s = new ICAPServiceRep(cfg);
+    s->finalize(s);
+    return s.getRaw();
 }
-#endif
-
-void
-ICAPAccessCheck::do_callback()
-{
-    debugs(93, 3, "ICAPAccessCheck::do_callback");
-
-    if (matchedClass.size()) {
-        debugs(93, 3, "ICAPAccessCheck::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;
-    }
-
-    ICAPServiceRep::Pointer service = NULL;
-    if (ICAPClass *c = TheICAPConfig.findClass(matchedClass)) {
-        service = findBestService(c, true);
-        if (service != NULL)
-            debugs(93,3,HERE << "do_callback: with service " << service->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;
-}
-
-ICAPServiceRep::Pointer
-ICAPAccessCheck::findBestService(ICAPClass *c, bool preferUp) {
-
-    const char *what = preferUp ? "up " : "";
-    debugs(93,7,HERE << "looking for the first matching " << 
-        what << "service in class " << c->key);
-
-    ICAPServiceRep::Pointer secondBest;
-
-    Vector<ICAPServiceRep::Pointer>::iterator si;
-    for (si = c->services.begin(); si != c->services.end(); ++si) {
-        ICAPServiceRep::Pointer service = *si;
-
-        if (method != service->method)
-            continue;
-
-        if (point != service->point)
-            continue;
-
-        // sending a message to a broken service is likely to cause errors
-        if (service->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->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->key);
-
-        return service;
-    }
-
-    if (secondBest != NULL) {
-        what = "down ";
-        debugs(93,5,HERE << "found first matching " <<
-            what << "service in class " << c->key <<
-            ": " << secondBest->key);
-        return secondBest;
-    }
-
-    debugs(93,5,HERE << "found no matching " << 
-        what << "services in class " << c->key);
-    return ICAPServiceRep::Pointer();
-}
-
-// ================================================================================ //
-
-void
-ICAPConfig::parseICAPService()
-{
-    ICAPServiceRep::Pointer S = new ICAPServiceRep();
-
-    if (S->configure(S))
-        services += S;
-    else
-        S->invalidate();
-};
-
-void
-ICAPConfig::freeICAPService()
-{
-    services.clean();
-};
-
-void
-ICAPConfig::dumpICAPService(StoreEntry *entry, const char *name) const
-{
-    typedef Vector<ICAPServiceRep::Pointer>::const_iterator VI;
-
-    for (VI i = services.begin(); i != services.end(); ++i) {
-        const ICAPServiceRep::Pointer &r = *i;
-        storeAppendPrintf(entry, "%s %s_%s %s %d %s\n", name, r->key.buf(),
-                          r->methodStr(), r->vectPointStr(), r->bypass, r->uri.buf());
-    }
-};
-
-void
-ICAPConfig::parseICAPClass()
-{
-    ICAPClass *C = new ICAPClass();
-
-    if (C->prepare()) {
-        classes.push_back(C);
-    } else {
-        delete C;
-    }
-};
-
-void
-ICAPConfig::freeICAPClass()
-{
-    classes.clean();
-};
-
-void
-ICAPConfig::dumpICAPClass(StoreEntry *entry, const char *name) const
-{
-    Vector<ICAPClass*>::const_iterator i = classes.begin();
-
-    while (i != classes.end()) {
-        storeAppendPrintf(entry, "%s %s\n", name, (*i)->key.buf());
-        ++i;
-    }
-};
-
-void
-ICAPConfig::parseICAPAccess(ConfigParser &parser)
-{
-    String aKey;
-    ConfigParser::ParseString(&aKey);
-    ICAPClass *theClass = TheICAPConfig.findClass(aKey);
-
-    if (theClass == NULL)
-        fatalf("Did not find ICAP class '%s' referenced on line %d\n",
-               aKey.buf(), config_lineno);
-
-    aclParseAccessLine(parser, &theClass->accessList);
-};
-
-void
-ICAPConfig::freeICAPAccess()
-{
-    (void) 0;
-};
-
-void
-ICAPConfig::dumpICAPAccess(StoreEntry *entry, const char *name) const
-{
-    LOCAL_ARRAY(char, nom, 64);
-
-    Vector<ICAPClass*>::const_iterator i = classes.begin();
-
-    while (i != classes.end()) {
-        snprintf(nom, 64, "%s %s", name, (*i)->key.buf());
-        dump_acl_access(entry, nom, (*i)->accessList);
-        ++i;
-    }
-};
-
-ICAPConfig::~ICAPConfig()
-{
-
-    // invalidate each service so that it can be deleted when refcount=0
-    Vector<ICAPServiceRep::Pointer>::iterator si;
-
-    for (si = services.begin(); si != services.end(); ++si)
-        (*si)->invalidate();
-
-    classes.clean();
-
-};
 
 time_t ICAPConfig::connect_timeout(bool bypassable) const
 {
     if (connect_timeout_raw > 0)
         return connect_timeout_raw; // explicitly configured
 
-    return bypassable ? Config.Timeout.peer_connect : Config.Timeout.connect;
+    return bypassable ? ::Config.Timeout.peer_connect : ::Config.Timeout.connect;
 }
 
 time_t ICAPConfig::io_timeout(bool) const
@@ -449,16 +80,5 @@ time_t ICAPConfig::io_timeout(bool) const
         return io_timeout_raw; // explicitly configured
     // TODO: provide a different default for an ICAP transaction that 
     // can still be bypassed
-    return Config.Timeout.read; 
-}
-
-ICAPConfig::ICAPConfig(const ICAPConfig &)
-{
-    assert(false); // unsupported
-}
-
-ICAPConfig &ICAPConfig::operator =(const ICAPConfig &)
-{
-    assert(false); // unsupported
-    return *this;
+    return ::Config.Timeout.read; 
 }
index 518f227c37884e1b4039a898493a200d0dbac3e4..1bdd9953640a9b85718dbdeab4ec422b148b2d72 100644 (file)
 
 #include "event.h"
 #include "AsyncCall.h"
+#include "adaptation/Config.h"
 #include "ICAPServiceRep.h"
 
 class acl_access;
 
 class ConfigParser;
 
-class ICAPClass
+class ICAPConfig: public Adaptation::Config
 {
 
 public:
-    String key;
-    acl_access *accessList;
-
-    Vector<ICAPServiceRep::Pointer> services;
-
-    ICAPClass() : key(NULL), accessList(NULL) {};
-
-    int prepare();
-};
-
-class ICAPAccessCheck: public virtual AsyncJob
-{
-
-public:
-    typedef void ICAPAccessCheckCallback(ICAPServiceRep::Pointer match, void *data);
-    ICAPAccessCheck(ICAP::Method, ICAP::VectPoint, HttpRequest *, HttpReply *, ICAPAccessCheckCallback *, void *);
-    ~ICAPAccessCheck();
-
-private:
-    ICAP::Method method;
-    ICAP::VectPoint point;
-    HttpRequest *req;
-    HttpReply *rep;
-    ICAPAccessCheckCallback *callback;
-    void *callback_data;
-    ACLChecklist *acl_checklist;
-    Vector<String> candidateClasses;
-    String matchedClass;
-    void do_callback();
-    ICAPServiceRep::Pointer findBestService(ICAPClass *c, bool preferUp);
-    bool done;
-
-public:
-    void check();
-    void checkCandidates();
-    static void ICAPAccessCheckCallbackWrapper(int, void*);
-#if 0
-    static EVH ICAPAccessCheckCallbackEvent;
-#endif
-//AsyncJob virtual methods
-    virtual bool doneAll() const { return AsyncJob::doneAll() && done;}
-
-private:
-    CBDATA_CLASS2(ICAPAccessCheck);
-};
-
-class ICAPConfig
-{
-
-public:
-
-    int onoff;
+    int default_options_ttl;
     int preview_enable;
     int preview_size;
     time_t connect_timeout_raw;
     time_t io_timeout_raw;
-    int default_options_ttl;
-    int send_client_ip;
-    int send_client_username;
     int reuse_connections;
-    int service_failure_limit;
-    int service_revival_delay;
     char* client_username_header;
     int client_username_encode;
 
-    Vector<ICAPServiceRep::Pointer> services;
-    Vector<ICAPClass*> classes;
-
-    ICAPConfig() {};
-
+    ICAPConfig();
     ~ICAPConfig();
 
     time_t connect_timeout(bool bypassable) const;
     time_t io_timeout(bool bypassable) const;
 
-    void parseICAPService(void);
-    void freeICAPService(void);
-    void dumpICAPService(StoreEntry *, const char *) const;
-    ICAPServiceRep::Pointer findService(const String&);
-    ICAPClass * findClass(const String& key);
-
-    void parseICAPClass(void);
-    void freeICAPClass(void);
-    void dumpICAPClass(StoreEntry *, const char *) const;
-
-    void parseICAPAccess(ConfigParser &parser);
-    void freeICAPAccess(void);
-    void dumpICAPAccess(StoreEntry *, const char *) const;
-
 private:
-    ICAPConfig(const ICAPConfig &); // unsupported
-    ICAPConfig &operator =(const ICAPConfig &); // unsupported
+    ICAPConfig(const ICAPConfig &); // not implemented
+    ICAPConfig &operator =(const ICAPConfig &); // not implemented
+
+    virtual Adaptation::ServicePointer createService(const Adaptation::ServiceConfig &cfg);
 };
 
 extern ICAPConfig TheICAPConfig;
diff --git a/src/adaptation/Config.cc b/src/adaptation/Config.cc
new file mode 100644 (file)
index 0000000..68ba53e
--- /dev/null
@@ -0,0 +1,496 @@
+
+/*
+ * $Id: ICAPConfig.cc,v 1.21 2008/02/12 23:12:45 rousskov Exp $
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "structs.h"
+
+#include "ConfigParser.h"
+#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"
+
+
+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()
+{
+    ServiceConfig *cfg = new ServiceConfig;
+    cfg->parse();
+    serviceConfigs.push_back(cfg);
+}
+
+void
+Adaptation::Config::freeService()
+{
+    // XXX: leaking Services and ServiceConfigs?
+}
+
+void
+Adaptation::Config::dumpService(StoreEntry *entry, const char *name) const
+{
+    typedef Services::iterator SCI;
+    for (SCI i = AllServices().begin(); i != AllServices().end(); ++i) {
+        const ServiceConfig &cfg = (*i)->cfg();
+        storeAppendPrintf(entry, "%s %s_%s %s %d %s\n", name, cfg.key.buf(),
+            cfg.methodStr(), cfg.vectPointStr(), cfg.bypass, cfg.uri.buf());
+    }
+}
+
+void
+Adaptation::Config::finalize()
+{
+    // create service reps from service configs
+    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);
+        if (s != NULL)
+            AddService(s);
+    }
+
+    debugs(93,1, "Initialized " << configs.size() <<
+        " message adaptation services.");
+}
+
+void
+Adaptation::Config::Finalize()
+{
+    // 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
+    }
+
+    debugs(93,2, "Initialized " << AllClasses().size() <<
+        " message adaptation service classes.");
+}
+
+void
+Adaptation::Config::parseClass()
+{
+    Class *C = new Class();
+
+    if (C->prepare()) {
+        AddClass(C);
+    } else {
+        delete C;
+    }
+};
+
+void
+Adaptation::Config::freeClass()
+{
+    // XXX: leaking Classes here?
+}
+
+void
+Adaptation::Config::dumpClass(StoreEntry *entry, const char *name) const
+{
+    typedef Classes::iterator CI;
+    for (CI i = AllClasses().begin(); i != AllClasses().end(); ++i)
+        storeAppendPrintf(entry, "%s %s\n", name, (*i)->key.buf());
+}
+
+void
+Adaptation::Config::parseAccess(ConfigParser &parser)
+{
+    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);
+
+    aclParseAccessLine(parser, &c->accessList);
+};
+
+void
+Adaptation::Config::freeAccess()
+{
+    (void) 0;
+}
+
+void
+Adaptation::Config::dumpAccess(StoreEntry *entry, const char *name) const
+{
+    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);
+       }
+}
+
+Adaptation::Config::Config()
+{
+    // XXX: should we init members?
+}
+
+Adaptation::Config::~Config()
+{
+
+    // 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();
+       }
+}
diff --git a/src/adaptation/Config.h b/src/adaptation/Config.h
new file mode 100644 (file)
index 0000000..8f5f49b
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef SQUID_ADAPTATION__CONFIG_H
+#define SQUID_ADAPTATION__CONFIG_H
+
+#include "event.h"
+#include "AsyncCall.h"
+#include "adaptation/Elements.h"
+
+class acl_access;
+class ConfigParser;
+
+template <class C>
+class RefCount;
+
+namespace Adaptation {
+
+class Service;
+class ServiceConfig;
+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 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();
+
+       friend class AccessCheck;
+
+public:
+
+    int onoff;
+    int send_client_ip;
+    int send_client_username;
+    int service_failure_limit;
+    int service_revival_delay;
+
+    Vector<ServiceConfig*> serviceConfigs;
+
+    Config();
+    virtual ~Config();
+
+    void parseService(void);
+    void freeService(void);
+    void dumpService(StoreEntry *, const char *) const;
+    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
+
+    virtual ServicePointer createService(const ServiceConfig &cfg) = 0;
+};
+
+} // namespace Adaptation
+
+#endif /* SQUID_ADAPTATION__CONFIG_H */