char *Adaptation::Config::masterx_shared_name = NULL;
int Adaptation::Config::service_iteration_limit = 16;
+
+Adaptation::ServiceConfig*
+Adaptation::Config::newServiceConfig() const
+{
+ return new ServiceConfig();
+}
+
void
Adaptation::Config::parseService()
{
- ServiceConfig *cfg = new ServiceConfig;
+ ServiceConfigPointer cfg = newServiceConfig();
if (!cfg->parse()) {
fatalf("%s:%d: malformed adaptation service configuration",
cfg_filename, config_lineno);
DetachServices();
- while (!serviceConfigs.empty()) {
- delete serviceConfigs.back();
- serviceConfigs.pop_back();
- }
+ serviceConfigs.clean();
}
void
Adaptation::Config::finalize()
{
// create service reps from service configs
- typedef Vector<ServiceConfig*>::const_iterator VISCI;
- const Vector<ServiceConfig*> &configs = serviceConfigs;
- debugs(93,3, HERE << "Found " << configs.size() << " service configs.");
+ int created = 0;
+
+ typedef ServiceConfigs::const_iterator VISCI;
+ const ServiceConfigs &configs = serviceConfigs;
for (VISCI i = configs.begin(); i != configs.end(); ++i) {
- const ServiceConfig &cfg = **i;
- if (FindService(cfg.key) != NULL) {
+ const ServiceConfigPointer cfg = *i;
+ if (FindService(cfg->key) != NULL) {
debugs(93,0, "ERROR: Duplicate adaptation service name: " <<
- cfg.key);
+ cfg->key);
continue; // TODO: make fatal
}
- ServicePointer s = createService(**i);
- if (s != NULL)
+ ServicePointer s = createService(cfg);
+ if (s != NULL) {
AllServices().push_back(s);
+ created++;
+ }
}
- debugs(93,3, HERE << "Created " << configs.size() <<
- " message adaptation services.");
+ debugs(93,3, HERE << "Created " << created << " adaptation services");
+
+ // services remember their configs; we do not have to
+ serviceConfigs.clean();
}
// poor man for_each
int service_revival_delay;
int icap_uses_indirect_client;
- Vector<ServiceConfig*> serviceConfigs;
+ typedef Vector<ServiceConfigPointer> ServiceConfigs;
+ ServiceConfigs serviceConfigs;
Config();
virtual ~Config();
virtual void finalize();
+protected:
+ /// creates service configuration object that will parse and keep cfg info
+ virtual ServiceConfig *newServiceConfig() const;
+
private:
Config(const Config &); // unsupported
Config &operator =(const Config &); // unsupported
- virtual ServicePointer createService(const ServiceConfig &cfg) = 0;
+ virtual ServicePointer createService(ServiceConfigPointer cfg) = 0;
static void ParseServiceGroup(ServiceGroupPointer group);
static void FreeServiceGroups(void);
#include "adaptation/ServiceFilter.h"
#include "adaptation/Service.h"
-Adaptation::Service::Service(const ServiceConfig &aConfig): theConfig(aConfig)
+Adaptation::Service::Service(ServiceConfigPointer aConfig): theConfig(aConfig)
{
- debugs(93,3, HERE << "creating adaptation service " << theConfig.key);
+ Must(theConfig != NULL);
+ debugs(93,3, HERE << "creating adaptation service " << cfg().key);
}
Adaptation::Service::~Service()
typedef String Id;
public:
- Service(const ServiceConfig &aConfig);
+ explicit Service(ServiceConfigPointer aConfig);
virtual ~Service();
virtual bool probed() const = 0; // see comments above
// called by transactions to report service failure
virtual void noteFailure() = 0;
- const ServiceConfig &cfg() const { return theConfig; }
+ const ServiceConfig &cfg() const { return *theConfig; }
virtual void finalize(); // called after creation
virtual bool detached() const = 0;
protected:
- ServiceConfig &writeableCfg() { return theConfig; }
+ ServiceConfig &writeableCfg() { return *theConfig; }
private:
- ServiceConfig theConfig;
+ ServiceConfigPointer theConfig;
};
typedef Service::Pointer ServicePointer;
grokked = grokBool(ipv6, name, value);
if (grokked && ipv6 && !Ip::EnableIpv6)
debugs(3, DBG_IMPORTANT, "WARNING: IPv6 is disabled. ICAP service option ignored.");
- } else {
- debugs(3, 0, cfg_filename << ':' << config_lineno << ": " <<
- "unknown adaptation service option: " << name << '=' << value);
- }
+ } else
+ grokked = grokExtension(name, value);
+
if (!grokked)
return false;
}
return true;
}
+
+bool
+Adaptation::ServiceConfig::grokExtension(const char *name, const char *value)
+{
+ // we do not accept extensions by default
+ debugs(3, 0, cfg_filename << ':' << config_lineno << ": " <<
+ "unknown adaptation service option: " << name << '=' << value);
+ return false;
+}
{
// manages adaptation service configuration in squid.conf
-class ServiceConfig
+class ServiceConfig: public RefCountable
{
public:
ServiceConfig();
/// interpret parsed values
bool grokBool(bool &var, const char *name, const char *value);
bool grokUri(const char *value);
+ /// handle name=value configuration option with name unknown to Squid
+ virtual bool grokExtension(const char *name, const char *value);
};
} // namespace Adaptation
CheckUnusedAdapterServices(AllServices());
}
+Adaptation::ServiceConfig *
+Adaptation::Ecap::Config::newServiceConfig() const
+{
+ return new ServiceConfig();
+}
+
Adaptation::ServicePointer
-Adaptation::Ecap::Config::createService(const Adaptation::ServiceConfig &cfg)
+Adaptation::Ecap::Config::createService(ServiceConfigPointer cfg)
+{
+ return new Adaptation::Ecap::ServiceRep(cfg);
+}
+
+
+/* ServiceConfig */
+
+bool
+Adaptation::Ecap::ServiceConfig::grokExtension(const char *name, const char *value)
{
- Adaptation::ServicePointer s = new Adaptation::Ecap::ServiceRep(cfg);
- return s.getRaw();
+ extensions.push_back(std::make_pair(name, value));
+ return true;
}
#define SQUID_ECAP_CONFIG_H
#include "adaptation/Config.h"
+#include "adaptation/ServiceConfig.h"
+#include <list>
+#include <utility>
namespace Adaptation
{
namespace Ecap
{
+class ServiceConfig: public Adaptation::ServiceConfig {
+public:
+ // Adaptation::ServiceConfig API
+ virtual bool grokExtension(const char *name, const char *value);
+
+public:
+ typedef std::pair<std::string, std::string> Extension; // name=value in cfg
+ typedef std::list<Extension> Extensions;
+ Extensions extensions;
+};
+
+
class Config: public Adaptation::Config
{
virtual void finalize();
+protected:
+ virtual Adaptation::ServiceConfig *newServiceConfig() const;
+
private:
Config(const Config &); // not implemented
Config &operator =(const Config &); // not implemented
- virtual Adaptation::ServicePointer createService(const Adaptation::ServiceConfig &cfg);
+ virtual Adaptation::ServicePointer createService(ServiceConfigPointer cfg);
};
extern Config TheConfig;
#include "squid.h"
#include <list>
#include <libecap/adapter/service.h>
+#include <libecap/common/config.h>
+#include <libecap/common/name.h>
+#include <libecap/common/named_values.h>
+#include "adaptation/ecap/Config.h"
#include "adaptation/ecap/ServiceRep.h"
#include "adaptation/ecap/XactionRep.h"
#include "base/TextException.h"
// configured eCAP service wrappers
static std::list<Adaptation::Ecap::ServiceRep::AdapterService> TheServices;
-Adaptation::Ecap::ServiceRep::ServiceRep(const Adaptation::ServiceConfig &cfg):
+/// wraps Adaptation::Ecap::ServiceConfig to allow eCAP visitors
+class ConfigRep: public libecap::Config
+{
+public:
+ typedef Adaptation::Ecap::ServiceConfig Master;
+ typedef libecap::Name Name;
+ typedef libecap::Area Area;
+
+ ConfigRep(const Master &aMaster): master(aMaster) {}
+
+ // libecap::Config API
+ virtual void visitEach(libecap::NamedValueVisitor &visitor) const;
+
+ const Master &master; ///< the configuration being wrapped
+};
+
+void
+ConfigRep::visitEach(libecap::NamedValueVisitor &visitor) const
+{
+ // we may supply the params we know about too, but only if we set host ID
+ static const Name optBypass("bypassable");
+ if (!optBypass.assignedHostId())
+ optBypass.assignHostId(1); // allows adapter to safely ignore this
+ visitor.visit(optBypass, Area(master.bypass ? "1" : "0", 1));
+
+ // visit adapter-specific options (i.e., those not recognized by Squid)
+ typedef Master::Extensions::const_iterator MECI;
+ for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i)
+ visitor.visit(Name(i->first), Area::FromTempString(i->second));
+}
+
+
+
+Adaptation::Ecap::ServiceRep::ServiceRep(ServiceConfigPointer cfg):
/*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg),
isDetached(false)
{
Adaptation::Service::finalize();
theService = FindAdapterService(cfg().uri);
if (theService) {
+ debugs(93,3, HERE << "configuring eCAP service: " << theService->uri());
+ ConfigRep cfgRep(dynamic_cast<const ServiceConfig&>(cfg()));
+ theService->configure(cfgRep);
+
debugs(93,3, HERE << "starting eCAP service: " << theService->uri());
theService->start();
} else {
class ServiceRep : public Adaptation::Service
{
public:
- ServiceRep(const Adaptation::ServiceConfig &config);
+ ServiceRep(ServiceConfigPointer aConfig);
virtual ~ServiceRep();
typedef libecap::shared_ptr<libecap::adapter::Service> AdapterService;
Adaptation::Initiate("Adaptation::Ecap::XactionRep"),
theService(aService),
theVirginRep(virginHeader), theCauseRep(NULL),
- proxyingVb(opUndecided), proxyingAb(opUndecided),
+ makingVb(opUndecided), proxyingAb(opUndecided),
adaptHistoryId(-1),
- canAccessVb(false),
+ vbProductionFinished(false),
abProductionFinished(false), abProductionAtEnd(false)
{
if (virginCause)
{
Must(theMaster);
- if (theVirginRep.raw().body_pipe != NULL)
- canAccessVb = true; /// assumes nobody is consuming; \todo check
- else
- proxyingVb = opNever;
+ if (!theVirginRep.raw().body_pipe)
+ makingVb = opNever; // there is nothing to deliver
const HttpRequest *request = dynamic_cast<const HttpRequest*> (theCauseRep ?
theCauseRep->raw().header : theVirginRep.raw().header);
}
}
- if (proxyingVb == opOn) {
- BodyPipe::Pointer body_pipe = theVirginRep.raw().body_pipe;
- if (body_pipe != NULL) {
- Must(body_pipe->stillConsuming(this));
- stopConsumingFrom(body_pipe);
- }
- }
+ BodyPipe::Pointer &body_pipe = theVirginRep.raw().body_pipe;
+ if (body_pipe != NULL && body_pipe->stillConsuming(this))
+ stopConsumingFrom(body_pipe);
terminateMaster();
bool
Adaptation::Ecap::XactionRep::doneAll() const
{
- return proxyingVb >= opComplete && proxyingAb >= opComplete &&
+ return makingVb >= opComplete && proxyingAb >= opComplete &&
Adaptation::Initiate::doneAll();
}
-// stops receiving virgin and enables auto-consumption
+// stops receiving virgin and enables auto-consumption, dropping any vb bytes
void
-Adaptation::Ecap::XactionRep::dropVirgin(const char *reason)
+Adaptation::Ecap::XactionRep::sinkVb(const char *reason)
{
- debugs(93,4, HERE << "because " << reason << "; status:" << status());
+ debugs(93,4, HERE << "sink for " << reason << "; status:" << status());
- BodyPipePointer &p = theVirginRep.raw().body_pipe;
- Must(p != NULL);
- p->enableAutoConsumption();
+ // we reset raw().body_pipe when we are done, so use this one for checking
+ const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe;
+ if (permPipe != NULL)
+ permPipe->enableAutoConsumption();
- if (proxyingVb == opOn) {
- Must(p->stillConsuming(this));
- stopConsumingFrom(p);
- proxyingVb = opComplete;
- } else {
- Must(!p->stillConsuming(this));
- if (proxyingVb == opUndecided)
- proxyingVb = opNever;
+ forgetVb(reason);
+}
+
+// stops receiving virgin but preserves it for others to use
+void
+Adaptation::Ecap::XactionRep::preserveVb(const char *reason)
+{
+ debugs(93,4, HERE << "preserve for " << reason << "; status:" << status());
+
+ // we reset raw().body_pipe when we are done, so use this one for checking
+ const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe;
+ if (permPipe != NULL) {
+ // if libecap consumed, we cannot preserve
+ Must(!permPipe->consumedSize());
}
- canAccessVb = false;
+ forgetVb(reason);
+}
+
+// disassociates us from vb; the last step of sinking or preserving vb
+void
+Adaptation::Ecap::XactionRep::forgetVb(const char *reason)
+{
+ debugs(93,9, HERE << "forget vb " << reason << "; status:" << status());
- // called from adapter handler so does not inform adapter
+ BodyPipePointer &p = theVirginRep.raw().body_pipe;
+ if (p != NULL && p->stillConsuming(this))
+ stopConsumingFrom(p);
+
+ if (makingVb == opUndecided)
+ makingVb = opNever;
+ else if (makingVb == opOn)
+ makingVb = opComplete;
}
void
Must(proxyingAb == opUndecided);
proxyingAb = opNever;
- BodyPipePointer &vbody_pipe = theVirginRep.raw().body_pipe;
+ preserveVb("useVirgin");
HttpMsg *clone = theVirginRep.raw().header->clone();
// check that clone() copies the pipe so that we do not have to
- Must(!vbody_pipe == !clone->body_pipe);
-
- if (proxyingVb == opOn) {
- Must(vbody_pipe->stillConsuming(this));
- // if libecap consumed, we cannot shortcircuit
- Must(!vbody_pipe->consumedSize());
- stopConsumingFrom(vbody_pipe);
- canAccessVb = false;
- proxyingVb = opComplete;
- } else if (proxyingVb == opUndecided) {
- vbody_pipe = NULL; // it is not our pipe anymore
- proxyingVb = opNever;
- }
+ Must(!theVirginRep.raw().header->body_pipe == !clone->body_pipe);
sendAnswer(Answer::Forward(clone));
Must(done());
Must(proxyingAb == opUndecided);
proxyingAb = opNever;
- dropVirgin("blockVirgin");
+ sinkVb("blockVirgin");
sendAnswer(Answer::Block(service().cfg().key));
Must(done());
void
Adaptation::Ecap::XactionRep::vbDiscard()
{
- Must(proxyingVb == opUndecided);
+ Must(makingVb == opUndecided);
// if adapter does not need vb, we do not need to send it
- dropVirgin("vbDiscard");
- Must(proxyingVb == opNever);
+ sinkVb("vbDiscard");
+ Must(makingVb == opNever);
}
void
Adaptation::Ecap::XactionRep::vbMake()
{
- Must(proxyingVb == opUndecided);
+ Must(makingVb == opUndecided);
BodyPipePointer &p = theVirginRep.raw().body_pipe;
Must(p != NULL);
- Must(p->setConsumerIfNotLate(this)); // to make vb, we must receive vb
- proxyingVb = opOn;
+ Must(p->setConsumerIfNotLate(this)); // to deliver vb, we must receive vb
+ makingVb = opOn;
}
void
Adaptation::Ecap::XactionRep::vbStopMaking()
{
+ Must(makingVb == opOn);
// if adapter does not need vb, we do not need to receive it
- dropVirgin("vbStopMaking");
- Must(proxyingVb == opComplete);
+ sinkVb("vbStopMaking");
+ Must(makingVb == opComplete);
}
void
Adaptation::Ecap::XactionRep::vbMakeMore()
{
- Must(proxyingVb == opOn); // cannot make more if done proxying
+ Must(makingVb == opOn); // cannot make more if done proxying
// we cannot guarantee more vb, but we can check that there is a chance
- Must(!theVirginRep.raw().body_pipe->exhausted());
+ const BodyPipePointer &p = theVirginRep.raw().body_pipe;
+ Must(p != NULL && p->stillConsuming(this)); // we are plugged in
+ Must(!p->productionEnded() && p->mayNeedMoreData()); // and may get more
}
libecap::Area
Adaptation::Ecap::XactionRep::vbContent(libecap::size_type o, libecap::size_type s)
{
- Must(canAccessVb);
- // We may not be proxyingVb yet. It should be OK, but see vbContentShift().
+ // We may not be makingVb yet. It should be OK, but see vbContentShift().
const BodyPipePointer &p = theVirginRep.raw().body_pipe;
Must(p != NULL);
void
Adaptation::Ecap::XactionRep::vbContentShift(libecap::size_type n)
{
- Must(canAccessVb);
- // We may not be proxyingVb yet. It should be OK now, but if BodyPipe
+ // We may not be makingVb yet. It should be OK now, but if BodyPipe
// consume() requirements change, we would have to return empty vbContent
// until the adapter registers as a consumer
void
Adaptation::Ecap::XactionRep::noteMoreBodyDataAvailable(RefCount<BodyPipe> bp)
{
- Must(proxyingVb == opOn);
+ Must(makingVb == opOn); // or we would not be registered as a consumer
Must(theMaster);
theMaster->noteVbContentAvailable();
}
void
Adaptation::Ecap::XactionRep::noteBodyProductionEnded(RefCount<BodyPipe> bp)
{
- Must(proxyingVb == opOn);
+ Must(makingVb == opOn); // or we would not be registered as a consumer
Must(theMaster);
theMaster->noteVbContentDone(true);
- proxyingVb = opComplete;
+ vbProductionFinished = true;
}
void
Adaptation::Ecap::XactionRep::noteBodyProducerAborted(RefCount<BodyPipe> bp)
{
- Must(proxyingVb == opOn);
+ Must(makingVb == opOn); // or we would not be registered as a consumer
Must(theMaster);
theMaster->noteVbContentDone(false);
- proxyingVb = opComplete;
+ vbProductionFinished = true;
}
void
buf.append(" [", 2);
- if (proxyingVb == opOn) {
- const BodyPipePointer &vp = theVirginRep.raw().body_pipe;
- if (!canAccessVb)
- buf.append("x", 1);
- if (vp != NULL) { // XXX: but may not be stillConsuming()
- buf.append("Vb", 2);
- } else
- buf.append("V.", 2);
- }
+ if (makingVb)
+ buf.Printf("M%d", static_cast<int>(makingVb));
+
+ const BodyPipePointer &vp = theVirginRep.raw().body_pipe;
+ if (!vp)
+ buf.append(" !V", 3);
+ else
+ if (vp->stillConsuming(const_cast<XactionRep*>(this)))
+ buf.append(" Vc", 3);
+ else
+ buf.append(" V?", 3);
+
+ if (vbProductionFinished)
+ buf.append(".", 1);
+
+
+ buf.Printf(" A%d", static_cast<int>(proxyingAb));
if (proxyingAb == opOn) {
MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get());
Must(rep);
const BodyPipePointer &ap = rep->raw().body_pipe;
- if (ap != NULL) { // XXX: but may not be stillProducing()
- buf.append(" Ab", 3);
- } else
- buf.append(" A.", 3);
+ if (!ap)
+ buf.append(" !A", 3);
+ else if (ap->stillProducing(const_cast<XactionRep*>(this)))
+ buf.append(" Ap", 3);
+ else
+ buf.append(" A?", 3);
}
buf.Printf(" %s%u]", id.Prefix, id.value);
Adaptation::Message &answer();
- void dropVirgin(const char *reason);
+ void sinkVb(const char *reason);
+ void preserveVb(const char *reason);
+ void forgetVb(const char *reason);
+
void moveAbContent();
void terminateMaster();
MessagePtr theAnswerRep;
typedef enum { opUndecided, opOn, opComplete, opNever } OperationState;
- OperationState proxyingVb; // delivering virgin body from core to adapter
+ OperationState makingVb; //< delivering virgin body from pipe to adapter
OperationState proxyingAb; // delivering adapted body from adapter to core
int adaptHistoryId; ///< adaptation history slot reservation
- bool canAccessVb; // virgin BodyPipe content is accessible
+ bool vbProductionFinished; // whether there can be no more vb bytes
bool abProductionFinished; // whether adapter has finished producing ab
bool abProductionAtEnd; // whether adapter produced a complete ab
class Answer;
typedef RefCount<Service> ServicePointer;
+typedef RefCount<ServiceConfig> ServiceConfigPointer;
typedef RefCount<ServiceGroup> ServiceGroupPointer;
} // namespace Adaptation
}
Adaptation::ServicePointer
-Adaptation::Icap::Config::createService(const Adaptation::ServiceConfig &cfg)
+Adaptation::Icap::Config::createService(ServiceConfigPointer cfg)
{
return new Adaptation::Icap::ServiceRep(cfg);
}
Config(const Config &); // not implemented
Config &operator =(const Config &); // not implemented
- virtual Adaptation::ServicePointer createService(const Adaptation::ServiceConfig &cfg);
+ virtual Adaptation::ServicePointer createService(ServiceConfigPointer cfg);
};
extern Config TheConfig;
CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, ServiceRep);
-Adaptation::Icap::ServiceRep::ServiceRep(const Adaptation::ServiceConfig &svcCfg):
+Adaptation::Icap::ServiceRep::ServiceRep(ServiceConfigPointer svcCfg):
AsyncJob("Adaptation::Icap::ServiceRep"), Adaptation::Service(svcCfg),
theOptions(NULL), theOptionsFetcher(0), theLastUpdate(0),
isSuspended(0), notifying(false),
typedef RefCount<ServiceRep> Pointer;
public:
- ServiceRep(const Adaptation::ServiceConfig &config);
+ ServiceRep(ServiceConfigPointer aConfig);
virtual ~ServiceRep();
virtual void finalize();