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
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;
}
#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;
--- /dev/null
+
+/*
+ * $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();
+ }
+}
--- /dev/null
+#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 */