ICAPConfig::createService(const Adaptation::ServiceConfig &cfg)
{
ICAPServiceRep::Pointer s = new ICAPServiceRep(cfg);
- s->finalize(s);
+ s->setSelf(s);
return s.getRaw();
}
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;
writeableCfg().port = 1344;
}
}
-
- return true;
}
void ICAPServiceRep::invalidate()
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
#include "errorpage.h"
#if USE_ADAPTATION
-#include "adaptation/Service.h"
+#include "adaptation/AccessCheck.h"
#include "ICAP/ICAPConfig.h"
extern ICAPConfig TheICAPConfig;
#endif
--- /dev/null
+#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();
+}
--- /dev/null
+#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 */
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
#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()
{
void
Adaptation::Config::freeService()
{
- // XXX: leaking Services and ServiceConfigs?
+ while (!serviceConfigs.empty()) {
+ delete serviceConfigs.back();
+ serviceConfigs.pop_back();
+ }
}
void
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);
}
}
// 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();
}
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:
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
noinst_LTLIBRARIES = libadaptation.la
libadaptation_la_SOURCES = \
+ AccessCheck.cc \
+ AccessCheck.h \
+ AccessRule.cc \
+ AccessRule.h \
Config.cc \
Config.h \
Elements.cc \
Service.h \
ServiceConfig.cc \
ServiceConfig.h \
- Xaction.cc \
- Xaction.h
+ ServiceGroups.cc \
+ ServiceGroups.h
check_PROGRAMS = testHeaders
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;
}
{
public:
typedef RefCount<Service> Pointer;
+ typedef String Id;
public:
Service(const ServiceConfig &aConfig);
const ServiceConfig &cfg() const { return theConfig; }
+ virtual void finalize(); // called after creation
+
protected:
- bool finalize(); // called after creation
ServiceConfig &writeableCfg() { return theConfig; }
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 */
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
+
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;
class Initiate;
class Initiator;
class AccessCheck;
+class AccessRule;
+class ServiceGroup;
typedef RefCount<Service> ServicePointer;
#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"
}
}
+#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
}
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
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
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
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
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
-----------------------------------------------------------------------------
#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