eCAP support requires loadable modules. Please do not use
--disable-loadable-modules with --enable-ecap.]);
fi
+
+ dnl eCAP support requires libecap
+ AC_CHECK_LIB([ecap], [main],
+ [ECAP_LIBS="-lecap"],
+ [AC_MSG_FAILURE([
+ eCAP support requires libecap library,
+ but no usable library was found])]
+ )
fi
AM_CONDITIONAL(USE_ECAP, test $use_ecap = yes)
if test $use_ecap = yes;
then
AC_DEFINE(USE_ECAP,1,[Enable eCAP support])
- ECAP_LIBS="eCAP/libeCAP.la"
+ ECAP_LIBS="eCAP/libeCAP.la $ECAP_LIBS"
use_adaptation=yes
else
AC_DEFINE(USE_ECAP,0,[Disable eCAP support])
bool mayNeedMoreData() const { return !bodySizeKnown() || needsMoreData(); }
bool needsMoreData() const { return bodySizeKnown() && unproducedSize() > 0; }
uint64_t unproducedSize() const; // size of still unproduced data
- bool stillProducing(Producer *producer) const { return theProducer == producer; }
+ bool stillProducing(const Producer *producer) const { return theProducer == producer; }
// called by consumers
bool setConsumerIfNotLate(Consumer *aConsumer);
void consume(size_t size);
bool expectMoreAfter(uint64_t offset) const;
bool exhausted() const; // saw eof/abort and all data consumed
- bool stillConsuming(Consumer *consumer) const { return theConsumer == consumer; }
+ bool stillConsuming(const Consumer *consumer) const { return theConsumer == consumer; }
// start or continue consuming when there is no consumer
void enableAutoConsumption();
virtual HttpMsg *_lock(); // please use HTTPMSGLOCK()
virtual void _unlock(); // please use HTTPMSGUNLOCK()
+ virtual HttpMsg *clone() const = 0;
+
public:
HttpVersion http_ver;
}
}
+// XXX: check that this is sufficient for eCAP cloning
HttpReply *
HttpReply::clone() const
{
rep->hdr_sz = hdr_sz;
rep->http_ver = http_ver;
rep->pstate = pstate;
+ rep->body_pipe = body_pipe;
+
rep->protocol = protocol;
rep->sline = sline;
return rep;
init();
}
+HttpRequest *
+HttpRequest::clone() const
+{
+ HttpRequest *copy = new HttpRequest(method, protocol, urlpath.buf());
+ // TODO: move common cloning clone to Msg::copyTo() or copy ctor
+ copy->header.append(&header);
+ copy->hdrCacheInit();
+ copy->hdr_sz = hdr_sz;
+ copy->http_ver = http_ver;
+ copy->pstate = pstate; // TODO: should we assert a specific state here?
+ copy->body_pipe = body_pipe;
+
+ strncpy(copy->login, login, sizeof(login)); // MAX_LOGIN_SZ
+ strncpy(copy->host, host, sizeof(host)); // SQUIDHOSTNAMELEN
+ copy->host_addr = host_addr;
+
+ if (auth_user_request) {
+ copy->auth_user_request = auth_user_request;
+ AUTHUSERREQUESTLOCK(copy->auth_user_request, "HttpRequest::clone");
+ }
+
+ copy->port = port;
+ // urlPath handled in ctor
+ copy->canonical = canonical ? xstrdup(canonical) : NULL;
+
+ // This may be too conservative for the 204 No Content case
+ // may eventually need cloneNullAdaptationImmune() for that.
+ copy->flags = flags.cloneAdaptationImmune();
+
+ copy->range = range ? new HttpHdrRange(*range) : NULL;
+ copy->ims = ims;
+ copy->imslen = imslen;
+ copy->max_forwards = max_forwards;
+ copy->client_addr = client_addr;
+ copy->my_addr = my_addr;
+ copy->hier = hier; // Is it safe to copy? Should we?
+
+ copy->errType = errType;
+
+ // XXX: what to do with copy->peer_login?
+
+ copy->lastmod = lastmod;
+ copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
+ // XXX: what to do with copy->peer_domain?
+
+ copy->tag = tag;
+ copy->extacl_user = extacl_user;
+ copy->extacl_passwd = extacl_passwd;
+ copy->extacl_log = extacl_log;
+
+ return copy;
+}
+
bool
HttpRequest::sanityCheckStartLine(MemBuf *buf, http_status *error)
{
void initHTTP(const HttpRequestMethod& aMethod, protocol_t aProtocol, const char *aUrlpath);
+ virtual HttpRequest *clone() const;
+
/* are responses to this request potentially cachable */
bool cacheable() const;
return self != NULL && !isSuspended && hasOptions();
}
-bool ICAPServiceRep::broken() const
-{
- return probed() && !up();
-}
-
bool ICAPServiceRep::wantsUrl(const String &urlPath) const
{
Must(hasOptions());
void invalidate(); // call when the service is no longer needed or valid
bool probed() const; // see comments above
- bool broken() const; // see comments above
bool up() const; // see comments above
virtual Adaptation::Initiate *makeXactLauncher(Adaptation::Initiator *, HttpMsg *virginHeader, HttpRequest *virginCause);
int count = 0;
for (const wordlist *i = names; i; i = i->next, ++count)
LoadModule(i->key);
- debugs(1, 1, "loaded " << count << " Squid modules");
+ debugs(1, 1, "Squid modules loaded: " << count);
}
@DISK_LINKOBJS@ \
@REPL_OBJS@ \
@AUTH_LINKOBJS@ \
- @AUTH_OBJS@ \
- ${ADAPTATION_LIBS}
+ @AUTH_OBJS@
if USE_LOADABLE_MODULES
squid_SOURCES += $(LOADABLE_MODULES_SOURCES)
@DISK_LINKOBJS@ \
@REPL_OBJS@ \
@AUTH_LINKOBJS@ \
- @AUTH_OBJS@ \
- ${ADAPTATION_LIBS}
+ @AUTH_OBJS@
nodist_ufsdump_SOURCES = \
repl_modules.cc \
tests_testCacheManager_LDFLAGS = $(LIBADD_DL)
tests_testCacheManager_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \
@REPL_OBJS@ \
- @SQUID_CPPUNIT_LA@ \
- ${ADAPTATION_LIBS}
+ @SQUID_CPPUNIT_LA@
tests_testDiskIO_SOURCES= \
$(SWAP_TEST_SOURCES) \
tests_testEvent_LDFLAGS = $(LIBADD_DL)
tests_testEvent_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \
@REPL_OBJS@ \
- @SQUID_CPPUNIT_LA@ \
- ${ADAPTATION_LIBS}
+ @SQUID_CPPUNIT_LA@
## Tests of the EventLoop module.
tests_testEventLoop_SOURCES = \
tests_testEventLoop_LDFLAGS = $(LIBADD_DL)
tests_testEventLoop_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \
@REPL_OBJS@ \
- @SQUID_CPPUNIT_LA@ \
- ${ADAPTATION_LIBS}
+ @SQUID_CPPUNIT_LA@
tests_test_http_range_SOURCES = \
tests/test_http_range.cc \
tests_testHttpRequest_LDFLAGS = $(LIBADD_DL)
tests_testHttpRequest_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \
@REPL_OBJS@ \
- @SQUID_CPPUNIT_LA@ \
- ${ADAPTATION_LIBS}
+ @SQUID_CPPUNIT_LA@
## Tests of the ICMP base module.
# Its used by pinger so SHOULD NOT require more dependancies! :-(
tests_testURL_LDFLAGS = $(LIBADD_DL)
tests_testURL_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \
@REPL_OBJS@ \
- @SQUID_CPPUNIT_LA@ \
- ${ADAPTATION_LIBS}
+ @SQUID_CPPUNIT_LA@
ServerStateData::~ServerStateData()
{
+ // paranoid: check that swanSong has been called
+ assert(!requestBodySource);
+#if USE_ADAPTATION
+ assert(!virginBodyDestination);
+ assert(!adaptedBodySource);
+#endif
+
entry->unlock();
HTTPMSGUNLOCK(request);
fwd = NULL; // refcounted
+ if (responseBodyBuffer != NULL) {
+ delete responseBodyBuffer;
+ responseBodyBuffer = NULL;
+ }
+}
+
+void
+ServerStateData::swanSong()
+{
+ // get rid of our piping obligations
if (requestBodySource != NULL)
requestBodySource->clearConsumer();
cleanAdaptation();
#endif
- if (responseBodyBuffer != NULL) {
- delete responseBodyBuffer;
- responseBodyBuffer = NULL;
- }
+ BodyConsumer::swanSong();
+#if USE_ADAPTATION
+ Initiator::swanSong();
+ BodyProducer::swanSong();
+#endif
}
+
HttpReply *
ServerStateData::virginReply() {
assert(theVirginReply);
virtual void processReplyBody() = 0;
//AsyncJob virtual methods
+ virtual void swanSong();
virtual bool doneAll() const { return
#if USE_ADAPTATION
Adaptation::Initiator::doneAll() &&
const Vector<ServiceConfig*> &configs = serviceConfigs;
debugs(93,3, "Found " << configs.size() << " service configs.");
for (VISCI i = configs.begin(); i != configs.end(); ++i) {
+ const ServiceConfig &cfg = **i;
+ if (FindService(cfg.key) != NULL) {
+ debugs(93,0, "ERROR: Duplicate adaptation service name: " <<
+ cfg.key);
+ continue; // TODO: make fatal
+ }
ServicePointer s = createService(**i);
if (s != NULL)
AllServices().push_back(s);
ServicePointer findService(const String&);
Class * findClass(const String& key);
- void finalize();
+ virtual void finalize();
private:
Config(const Config &); // unsupported
Adaptation::Initiate::~Initiate()
{
- assert(!theInitiator);
+ // TODO: we cannot assert(!theInitiator) because that fails if a child
+ // constructor fails. AsyncJob should have wasStarted flag so that we
+ // can assert(!(wasStarted && theInitiator)).
}
// internal cleanup
}
const char *Adaptation::Initiate::status() const {
- return ""; // for now
+ return AsyncJob::status(); // for now
}
Initiate.h \
Initiator.cc \
Initiator.h \
+ Message.cc \
+ Message.h \
Service.cc \
Service.h \
ServiceConfig.cc \
--- /dev/null
+/*
+ * DEBUG: section XXX
+ */
+
+#include "squid.h"
+#include "HttpMsg.h"
+#include "TextException.h"
+#include "adaptation/Message.h"
+
+Adaptation::Message::Message(): header(NULL)
+{
+}
+
+Adaptation::Message::Message(Header *aHeader): header(NULL)
+{
+ set(aHeader);
+}
+
+Adaptation::Message::~Message()
+{
+ clear();
+}
+
+void
+Adaptation::Message::clear()
+{
+ HTTPMSGUNLOCK(header);
+ body_pipe = NULL;
+}
+
+void
+Adaptation::Message::set(Header *aHeader)
+{
+ clear();
+ if (aHeader) {
+ header = HTTPMSGLOCK(aHeader);
+ body_pipe = header->body_pipe;
+ }
+}
+
+void
+Adaptation::Message::ShortCircuit(Message &src, Message &dest)
+{
+ Must(!dest.header); // the message is not "used"
+ Must(!dest.body_pipe); // can relax if needed, but need !body_pipe->used()
+ Must(src.header); // or there is nothing to shortcircuit
+
+ if (src.header->body_pipe != NULL) {
+ // check that it would not be too late to shortcircuit the pipe
+ Must(!src.header->body_pipe->consumedSize());
+ src.header->body_pipe->clearConsumer(); // if any
+ // note: current header->body_pipe producer may later become
+ // dest.body_pipe consumer and consume its own data
+ // TODO: consumer should detect and bypass short-circuit adaptation
+ }
+ dest.set(src.header->clone());
+}
--- /dev/null
+
+/*
+ * $Id$
+ */
+
+#ifndef SQUID__ADAPTATION__MESSAGE_H
+#define SQUID__ADAPTATION__MESSAGE_H
+
+class HttpMsg;
+class BodyPipe;
+template <class C>
+class RefCount;
+typedef RefCount<BodyPipe> BodyPipePointer;
+
+namespace Adaptation {
+
+// Manages the header and the body of an HTTP message being worked on.
+// Adaptation transactions use this class for virgin and adapted HTTP messages.
+// TODO: remove this class after adding refcounted message pointers and
+// after making sure nobody abruptly clears the HttpMsg::body_pipe pointer.
+class Message
+{
+
+public:
+ typedef HttpMsg Header;
+
+ Message();
+ Message(Header *aHeader);
+ ~Message();
+
+ void clear();
+ void set(Header *aHeader);
+
+ static void ShortCircuit(Message &src, Message &dest);
+
+public:
+ // virgin or adapted message being worked on
+ Header *header; // parsed HTTP status/request line and headers
+
+ // Copy of header->body_pipe, in case somebody moves the original.
+ BodyPipePointer body_pipe;
+
+private:
+ Message(const Message &); // not implemented
+ Message &operator =(const Message &); // not implemented
+};
+
+} // namespace Adaptation;
+
+// TODO: replace ICAPInOut with Adaptation::Message (adding one for "cause")
+
+#endif /* SQUID__ADAPTATION__MESSAGE_H */
{
}
+bool Adaptation::Service::broken() const
+{
+ return probed() && !up();
+}
+
Adaptation::Services &
Adaptation::AllServices()
{
virtual void invalidate() = 0;
virtual bool probed() const = 0; // see comments above
- virtual bool broken() const = 0; // see comments above
+ virtual bool broken() const;
virtual bool up() const = 0; // see comments above
virtual Initiate *makeXactLauncher(Initiator *, HttpMsg *virginHeader, HttpRequest *virginCause) = 0;
class AccessCheck;
class AccessRule;
class ServiceGroup;
+class Message;
typedef RefCount<Service> ServicePointer;
#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"
+static void free_adaptation_access_type(const char *);
+
static void parse_icap_service_type(ICAPConfig *);
static void dump_icap_service_type(StoreEntry *, const char *, const ICAPConfig &);
static void free_icap_service_type(ICAPConfig *);
#endif
+#if USE_ECAP
+#include "eCAP/Config.h"
+static void parse_ecap_service_type(Ecap::Config *);
+static void dump_ecap_service_type(StoreEntry *, const char *, const Ecap::Config &);
+static void free_ecap_service_type(Ecap::Config *);
+#endif
+
CBDATA_TYPE(peer);
static const char *const T_SECOND_STR = "second";
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 */
{
debugs(93, 0, "WARNING: 'icap_access' is depricated. " <<
"Use 'adaptation_access' instead");
- parse_adaptation_access_type();
+ Adaptation::Config::ParseAccess(LegacyParser);
}
+
static void
free_icap_access_type(ICAPConfig *)
{
- free_adaptation_access_type();
+ Adaptation::Config::FreeAccess();
}
static void
dump_icap_access_type(StoreEntry * entry, const char *name, const ICAPConfig &)
{
- dump_adaptation_access_type(entry, name);
+ Adaptation::Config::DumpAccess(entry, name);
}
#endif
+
+
+#if USE_ECAP
+
+static void
+parse_ecap_service_type(Ecap::Config * cfg)
+{
+ cfg->parseService();
+}
+
+static void
+free_ecap_service_type(Ecap::Config * cfg)
+{
+ cfg->freeService();
+}
+
+static void
+dump_ecap_service_type(StoreEntry * entry, const char *name, const Ecap::Config &cfg)
+{
+ cfg.dumpService(entry, name);
+}
+
+#endif /* USE_ECAP */
icap_access_type icap_class acl
icap_class_type icap_service
icap_service_type
+ecap_service_type
int
kb_int64_t
kb_size_t
NAME: icap_class
TYPE: icap_class_type
IFDEF: ICAP_CLIENT
-LOC: TheICAPConfig
+LOC: none
DEFAULT: none
DOC_START
This depricated option was documented to define an ICAP service
NAME: icap_access
TYPE: icap_access_type
IFDEF: ICAP_CLIENT
-LOC: TheICAPConfig
+LOC: none
DEFAULT: none
DOC_START
This option is depricated. Please use adaptation_access, which
-----------------------------------------------------------------------------
COMMENT_END
+NAME: ecap_enable
+TYPE: onoff
+IFDEF: USE_ECAP
+COMMENT: on|off
+LOC: Ecap::TheConfig.onoff
+DEFAULT: off
+DOC_START
+ Controls whether eCAP support is enabled.
+DOC_END
+
+NAME: ecap_service
+TYPE: ecap_service_type
+IFDEF: USE_ECAP
+LOC: Ecap::TheConfig
+DEFAULT: none
+DOC_START
+ Defines a single eCAP service
+
+ ecap_service servicename vectoring_point bypass service_url
+
+ vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache
+ This specifies at which point of transaction processing the
+ eCAP service should be activated. *_postcache vectoring points
+ are not yet supported.
+ bypass = 1|0
+ If set to 1, the eCAP service is treated as optional. If the
+ service cannot be reached or malfunctions, Squid will try to
+ ignore any errors and process the message as if the service
+ was not enabled. No all eCAP errors can be bypassed.
+ If set to 0, the eCAP service is treated as essential and all
+ eCAP errors will result in an error page returned to the
+ HTTP client.
+ service_url = ecap://vendor/service_name?custom&cgi=style¶meters=optional
+
+Example:
+ecap_service service_1 reqmod_precache 0 ecap://filters-R-us/leakDetector?on_error=block
+ecap_service service_2 respmod_precache 1 icap://filters-R-us/virusFilter?config=/etc/vf.cfg
+DOC_END
+
NAME: loadable_modules
TYPE: wordlist
IFDEF: USE_LOADABLE_MODULES
--- /dev/null
+
+/*
+ * $Id$
+ */
+
+#include "squid.h"
+
+#include <libecap/common/registry.h>
+#include "eCAP/Host.h"
+#include "eCAP/ServiceRep.h"
+#include "eCAP/Config.h"
+
+Ecap::Config Ecap::TheConfig;
+
+Ecap::Config::Config()
+{
+}
+
+Ecap::Config::~Config()
+{
+}
+
+void
+Ecap::Config::finalize() {
+ Adaptation::Config::finalize();
+ static Ecap::Host *TheHost = new Ecap::Host;
+ libecap::RegisterHost(TheHost);
+}
+
+Adaptation::ServicePointer
+Ecap::Config::createService(const Adaptation::ServiceConfig &cfg)
+{
+ Adaptation::ServicePointer s = new Ecap::ServiceRep(cfg);
+ return s.getRaw();
+}
+
--- /dev/null
+
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_ECAP_CONFIG_H
+#define SQUID_ECAP_CONFIG_H
+
+#include "adaptation/Config.h"
+
+namespace Ecap {
+
+class Config: public Adaptation::Config
+{
+
+public:
+ Config();
+ ~Config();
+
+ virtual void finalize();
+
+private:
+ Config(const Config &); // not implemented
+ Config &operator =(const Config &); // not implemented
+
+ virtual Adaptation::ServicePointer createService(const Adaptation::ServiceConfig &cfg);
+};
+
+extern Config TheConfig;
+
+} // namespace Ecap
+
+#endif /* SQUID_ECAP_CONFIG_H */
--- /dev/null
+#include "squid.h"
+#include <libecap/adapter/service.h>
+#include <libecap/common/names.h>
+#include "TextException.h"
+#include "eCAP/ServiceRep.h"
+#include "eCAP/Host.h"
+
+const libecap::Name Ecap::protocolInternal("internal", libecap::Name::NextId());
+const libecap::Name Ecap::protocolCacheObj("cache_object", libecap::Name::NextId());
+const libecap::Name Ecap::protocolIcp("ICP", libecap::Name::NextId());
+#if USE_HTCP
+const libecap::Name Ecap::protocolHtcp("Htcp", libecap::Name::NextId());
+#endif
+
+Ecap::Host::Host()
+{
+ // assign our host-specific IDs to well-known names
+ libecap::headerReferer.assignHostId(HDR_REFERER);
+
+ libecap::protocolHttp.assignHostId(PROTO_HTTP);
+ libecap::protocolHttps.assignHostId(PROTO_HTTPS);
+ libecap::protocolFtp.assignHostId(PROTO_FTP);
+ libecap::protocolGopher.assignHostId(PROTO_GOPHER);
+ libecap::protocolWais.assignHostId(PROTO_WAIS);
+ libecap::protocolUrn.assignHostId(PROTO_URN);
+ libecap::protocolWhois.assignHostId(PROTO_WHOIS);
+ protocolInternal.assignHostId(PROTO_INTERNAL);
+ protocolCacheObj.assignHostId(PROTO_CACHEOBJ);
+ protocolIcp.assignHostId(PROTO_ICP);
+#if USE_HTCP
+ protocolHtcp.assignHostId(PROTO_HTCP);
+#endif
+}
+
+std::string
+Ecap::Host::uri() const
+{
+ return "ecap://squid-cache.org/ecap/hosts/squid";
+}
+
+void
+Ecap::Host::describe(std::ostream &os) const
+{
+ os << PACKAGE_NAME << " v" << PACKAGE_VERSION;
+}
+
+void
+Ecap::Host::noteService(const libecap::weak_ptr<libecap::adapter::Service> &weak)
+{
+ // Many ecap_service lines may use the same service URI. Find each
+ // matching service rep, make sure it is an eCAP rep,
+ // and update it with the actual eCAP service.
+ int found = 0;
+
+ libecap::shared_ptr<libecap::adapter::Service> shared(weak);
+ typedef Adaptation::Services::iterator SI;
+ for (SI i = Adaptation::AllServices().begin(); i != Adaptation::AllServices().end(); ++i) {
+ if ((*i)->cfg().uri == shared->uri().c_str()) {
+ ServiceRep *rep = dynamic_cast<ServiceRep*>(i->getRaw());
+ Must(rep);
+ rep->noteService(shared);
+ ++found;
+ }
+ }
+
+ debugs(93,5, "Found " << found << " ecap_service configs for " <<
+ shared->uri());
+ if (!found) {
+ debugs(93,1, "Warning: ignoring loaded eCAP module service without " <<
+ "a matching ecap_service configuration: " << shared->uri());
+ }
+}
+
+static int
+SquidLogLevel(libecap::LogVerbosity lv)
+{
+ if (lv.critical())
+ return DBG_CRITICAL; // is it a good idea to ignore other flags?
+
+ if (lv.large())
+ return DBG_DATA; // is it a good idea to ignore other flags?
+
+ if (lv.application())
+ return DBG_DATA; // is it a good idea to ignore other flags?
+
+ return 2 + 2*lv.debugging() + 3*lv.operation() + 2*lv.xaction();
+}
+
+std::ostream *
+Ecap::Host::openDebug(libecap::LogVerbosity lv)
+{
+ const int squidLevel = SquidLogLevel(lv);
+ const int squidSection = 93; // XXX: this should be a global constant
+ // XXX: Debug.h should provide this to us
+ if ((Debug::level = squidLevel) <= Debug::Levels[squidSection])
+ return &Debug::getDebugOut();
+ else
+ return NULL;
+}
+
+void
+Ecap::Host::closeDebug(std::ostream *debug)
+{
+ if (debug)
+ Debug::finishDebug();
+}
--- /dev/null
+
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_ECAP_HOST_H
+#define SQUID_ECAP_HOST_H
+
+#include <libecap/host/host.h>
+
+namespace Ecap {
+
+// Squid wrapper, providing host application functionality to eCAP services.
+class Host : public libecap::host::Host
+{
+public:
+ Host();
+
+ // About
+ virtual std::string uri() const; // unique across all vendors
+ virtual void describe(std::ostream &os) const; // free-format info
+
+ // Service management
+ virtual void noteService(const libecap::weak_ptr<libecap::adapter::Service> &s);
+
+ // Logging
+ virtual std::ostream *openDebug(libecap::LogVerbosity lv);
+ virtual void closeDebug(std::ostream *debug);
+};
+
+extern const libecap::Name protocolInternal;
+extern const libecap::Name protocolCacheObj;
+extern const libecap::Name protocolIcp;
+#if USE_HTCP
+extern const libecap::Name protocolHtcp;
+#endif
+
+} // namespace Ecap
+
+#endif /* SQUID_ECAP_HOST_H */
noinst_LTLIBRARIES = libeCAP.la
libeCAP_la_SOURCES = \
+ Config.h \
+ Config.cc \
+ Host.h \
+ Host.cc \
+ MessageRep.h \
+ MessageRep.cc \
+ ServiceRep.h \
+ ServiceRep.cc \
+ XactionRep.h \
+ XactionRep.cc \
+ \
Registry.h
INCLUDES = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \
-I$(top_srcdir)/src
-
-
-# Sample adapter section.
-
-EXTRA_DIST = \
- MinimalAdapter.cc
-
-lib_LTLIBRARIES = MinimalAdapter.la
-MinimalAdapter_la_SOURCES = MinimalAdapter.cc
-MinimalAdapter_la_LDFLAGS = -module -avoid-version
-MinimalAdapter_la_LIBADD = ./libeCAP.la
--- /dev/null
+/*
+ * DEBUG: section XXX
+ */
+
+#include "squid.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "BodyPipe.h"
+#include "TextException.h"
+#include "adaptation/Message.h"
+#include <libecap/common/names.h>
+#include <libecap/common/area.h>
+#include <libecap/common/version.h>
+#include "eCAP/MessageRep.h"
+#include "eCAP/XactionRep.h"
+#include "eCAP/Host.h" /* for protocol constants */
+
+/* HeaderRep */
+
+Ecap::HeaderRep::HeaderRep(HttpMsg &aMessage): theHeader(aMessage.header),
+ theMessage(aMessage)
+{
+}
+
+bool
+Ecap::HeaderRep::hasAny(const Name &name) const
+{
+ const http_hdr_type squidId = TranslateHeaderId(name);
+ // XXX: optimize to remove getByName: we do not need the value here
+ return squidId == HDR_OTHER ?
+ theHeader.getByName(name.image().c_str()).size() > 0:
+ (bool)theHeader.has(squidId);
+}
+
+Ecap::HeaderRep::Value
+Ecap::HeaderRep::value(const Name &name) const
+{
+ const http_hdr_type squidId = TranslateHeaderId(name);
+ const String value = squidId == HDR_OTHER ?
+ theHeader.getByName(name.image().c_str()) :
+ theHeader.getStrOrList(squidId);
+ return Value::FromTempString(value.buf());
+}
+
+void
+Ecap::HeaderRep::add(const Name &name, const Value &value)
+{
+ const http_hdr_type squidId = TranslateHeaderId(name); // HDR_OTHER OK
+ HttpHeaderEntry *e = new HttpHeaderEntry(squidId, name.image().c_str(),
+ value.toString().c_str());
+ theHeader.addEntry(e);
+}
+
+void
+Ecap::HeaderRep::removeAny(const Name &name)
+{
+ const http_hdr_type squidId = TranslateHeaderId(name);
+ if (squidId == HDR_OTHER)
+ theHeader.delByName(name.image().c_str());
+ else
+ theHeader.delById(squidId);
+}
+
+libecap::Area
+Ecap::HeaderRep::image() const
+{
+ MemBuf mb;
+ mb.init();
+
+ Packer p;
+ packerToMemInit(&p, &mb);
+ theMessage.packInto(&p, true);
+ packerClean(&p);
+ return Area::FromTempBuffer(mb.content(), mb.contentSize());
+}
+
+// throws on failures
+void
+Ecap::HeaderRep::parse(const Area &buf)
+{
+ MemBuf mb;
+ mb.init();
+ mb.append(buf.start, buf.size);
+ http_status error;
+ Must(theMessage.parse(&mb, true, &error));
+}
+
+http_hdr_type
+Ecap::HeaderRep::TranslateHeaderId(const Name &name)
+{
+ if (name.assignedHostId())
+ return static_cast<http_hdr_type>(name.hostId());
+ return HDR_OTHER;
+}
+
+
+/* FirstLineRep */
+
+Ecap::FirstLineRep::FirstLineRep(HttpMsg &aMessage): theMessage(aMessage)
+{
+}
+
+libecap::Version
+Ecap::FirstLineRep::version() const
+{
+ return libecap::Version(theMessage.http_ver.major,
+ theMessage.http_ver.minor);
+}
+
+void
+Ecap::FirstLineRep::version(const libecap::Version &aVersion)
+{
+ theMessage.http_ver.major = aVersion.majr;
+ theMessage.http_ver.minor = aVersion.minr;
+}
+
+libecap::Name
+Ecap::FirstLineRep::protocol() const
+{
+ // TODO: optimize?
+ switch (theMessage.protocol) {
+ case PROTO_HTTP: return libecap::protocolHttp;
+ case PROTO_HTTPS: return libecap::protocolHttps;
+ case PROTO_FTP: return libecap::protocolFtp;
+ case PROTO_GOPHER: return libecap::protocolGopher;
+ case PROTO_WAIS: return libecap::protocolWais;
+ case PROTO_WHOIS: return libecap::protocolWhois;
+ case PROTO_URN: return libecap::protocolUrn;
+ case PROTO_ICP: return protocolIcp;
+#if USE_HTCP
+ case PROTO_HTCP: return protocolHtcp;
+#endif
+ case PROTO_CACHEOBJ: return protocolCacheObj;
+ case PROTO_INTERNAL: return protocolInternal;
+ case PROTO_NONE: return Name();
+
+ case PROTO_MAX: break; // should not happen
+ // no default to catch PROTO_ additions
+ }
+ Must(false); // not reached
+ return Name();
+}
+
+void
+Ecap::FirstLineRep::protocol(const Name &p)
+{
+ // TODO: what happens if we fail to translate some protocol?
+ theMessage.protocol = TranslateProtocolId(p);
+}
+
+protocol_t
+Ecap::FirstLineRep::TranslateProtocolId(const Name &name)
+{
+ if (name.assignedHostId())
+ return static_cast<protocol_t>(name.hostId());
+ return PROTO_NONE; // no PROTO_OTHER
+}
+
+
+/* RequestHeaderRep */
+
+Ecap::RequestLineRep::RequestLineRep(HttpRequest &aMessage):
+ FirstLineRep(aMessage), theMessage(aMessage)
+{
+}
+
+void
+Ecap::RequestLineRep::uri(const Area &aUri)
+{
+ // TODO: if method is not set, urlPath will assume it is not connect;
+ // Can we change urlParse API to remove the method parameter?
+ // TODO: optimize: urlPath should take constant URL buffer
+ char *buf = xstrdup(aUri.toString().c_str());
+ const bool ok = urlParse(theMessage.method, buf, &theMessage);
+ xfree(buf);
+ Must(ok);
+}
+
+Ecap::RequestLineRep::Area
+Ecap::RequestLineRep::uri() const
+{
+ return Area::FromTempBuffer(theMessage.urlpath.buf(),
+ theMessage.urlpath.size());
+}
+
+void
+Ecap::RequestLineRep::method(const Name &aMethod)
+{
+ if (aMethod.assignedHostId()) {
+ const int id = aMethod.hostId();
+ Must(METHOD_NONE < id && id < METHOD_ENUM_END);
+ Must(id != METHOD_OTHER);
+ theMessage.method = HttpRequestMethod(static_cast<_method_t>(id));
+ } else {
+ const std::string &image = aMethod.image();
+ theMessage.method = HttpRequestMethod(image.data(),
+ image.data() + image.size());
+ }
+}
+
+Ecap::RequestLineRep::Name
+Ecap::RequestLineRep::method() const
+{
+ switch (theMessage.method.id()) {
+ case METHOD_GET: return libecap::methodGet;
+ case METHOD_POST: return libecap::methodPost;
+ case METHOD_PUT: return libecap::methodPut;
+ case METHOD_HEAD: return libecap::methodHead;
+ case METHOD_CONNECT: return libecap::methodConnect;
+ case METHOD_DELETE: return libecap::methodDelete;
+ case METHOD_TRACE: return libecap::methodTrace;
+ default: return Name(theMessage.method.image());
+ }
+}
+
+libecap::Version
+Ecap::RequestLineRep::version() const
+{
+ return FirstLineRep::version();
+}
+
+void
+Ecap::RequestLineRep::version(const libecap::Version &aVersion)
+{
+ FirstLineRep::version(aVersion);
+}
+
+libecap::Name
+Ecap::RequestLineRep::protocol() const
+{
+ return FirstLineRep::protocol();
+}
+
+void
+Ecap::RequestLineRep::protocol(const Name &p)
+{
+ FirstLineRep::protocol(p);
+}
+
+
+/* ReplyHeaderRep */
+
+Ecap::StatusLineRep::StatusLineRep(HttpReply &aMessage):
+ FirstLineRep(aMessage), theMessage(aMessage)
+{
+}
+
+void
+Ecap::StatusLineRep::statusCode(int code)
+{
+ // TODO: why is .status a enum? Do we not support unknown statuses?
+ theMessage.sline.status = static_cast<http_status>(code);
+}
+
+int
+Ecap::StatusLineRep::statusCode() const
+{
+ // TODO: see statusCode(code) TODO above
+ return static_cast<int>(theMessage.sline.status);
+}
+
+void
+Ecap::StatusLineRep::reasonPhrase(const Area &)
+{
+ // Squid does not support custom reason phrases
+ theMessage.sline.reason = NULL;
+}
+
+Ecap::StatusLineRep::Area
+Ecap::StatusLineRep::reasonPhrase() const
+{
+ return theMessage.sline.reason ?
+ Area::FromTempString(std::string(theMessage.sline.reason)) : Area();
+}
+
+libecap::Version
+Ecap::StatusLineRep::version() const
+{
+ return FirstLineRep::version();
+}
+
+void
+Ecap::StatusLineRep::version(const libecap::Version &aVersion)
+{
+ FirstLineRep::version(aVersion);
+}
+
+libecap::Name
+Ecap::StatusLineRep::protocol() const
+{
+ return FirstLineRep::protocol();
+}
+
+void
+Ecap::StatusLineRep::protocol(const Name &p)
+{
+ FirstLineRep::protocol(p);
+}
+
+/* BodyRep */
+
+Ecap::BodyRep::BodyRep(const BodyPipe::Pointer &aBody): theBody(aBody)
+{
+}
+
+void
+Ecap::BodyRep::tie(const BodyPipe::Pointer &aBody)
+{
+ Must(!theBody);
+ Must(aBody != NULL);
+ theBody = aBody;
+}
+
+Ecap::BodyRep::BodySize
+Ecap::BodyRep::bodySize() const
+{
+ return !theBody ? BodySize() : BodySize(theBody->bodySize());
+}
+
+Ecap::BodyRep::size_type
+Ecap::BodyRep::consumedSize() const
+{
+ return theBody->consumedSize();
+}
+
+bool
+Ecap::BodyRep::productionEnded() const
+{
+ return theBody->productionEnded();
+}
+
+Ecap::BodyRep::Area
+Ecap::BodyRep::prefix(Ecap::BodyRep::size_type size) const
+{
+ Must(size <= static_cast<size_type>(theBody->buf().contentSize()));
+ // XXX: optimize by making theBody a shared_ptr (see FromTemp*() src)
+ return Area::FromTempBuffer(theBody->buf().content(), size);
+}
+
+
+/* MessageRep */
+
+Ecap::MessageRep::MessageRep(HttpMsg *rawHeader):
+ theMessage(rawHeader), theFirstLineRep(NULL),
+ theHeaderRep(NULL), theBodyRep(NULL)
+{
+ Must(theMessage.header); // we do not want to represent a missing message
+
+ if (HttpRequest *req = dynamic_cast<HttpRequest*>(theMessage.header))
+ theFirstLineRep = new RequestLineRep(*req);
+ else
+ if (HttpReply *rep = dynamic_cast<HttpReply*>(theMessage.header))
+ theFirstLineRep = new StatusLineRep(*rep);
+ else
+ Must(false); // unknown message header type
+
+ theHeaderRep = new HeaderRep(*theMessage.header);
+
+ if (theMessage.body_pipe != NULL)
+ theBodyRep = new BodyRep(theMessage.body_pipe);
+}
+
+Ecap::MessageRep::~MessageRep()
+{
+ delete theHeaderRep;
+ delete theBodyRep;
+}
+
+libecap::shared_ptr<libecap::Message>
+Ecap::MessageRep::clone() const
+{
+ HttpMsg *hdr = theMessage.header->clone();
+ hdr->body_pipe = NULL; // if any; TODO: remove pipe cloning from ::clone?
+ libecap::shared_ptr<libecap::Message> res(new MessageRep(hdr));
+
+ // restore indication of a body if needed, but not the pipe
+ if (theMessage.header->body_pipe != NULL)
+ res->addBody();
+
+ return res;
+}
+
+libecap::FirstLine &
+Ecap::MessageRep::firstLine()
+{
+ return *theFirstLineRep;
+}
+
+const libecap::FirstLine &
+Ecap::MessageRep::firstLine() const
+{
+ return *theFirstLineRep;
+}
+
+libecap::Header &
+Ecap::MessageRep::header()
+{
+ return *theHeaderRep;
+}
+
+const libecap::Header &
+Ecap::MessageRep::header() const
+{
+ return *theHeaderRep;
+}
+
+libecap::Body *
+Ecap::MessageRep::body()
+{
+ return theBodyRep;
+}
+
+void
+Ecap::MessageRep::addBody()
+{
+ Must(!theBodyRep);
+ Must(!theMessage.body_pipe); // set in tieBody()
+ theBodyRep = new BodyRep(NULL);
+}
+
+void
+Ecap::MessageRep::tieBody(Ecap::XactionRep *x)
+{
+ Must(theBodyRep != NULL); // addBody must be called first
+ Must(!theMessage.body_pipe);
+ theMessage.body_pipe = new BodyPipe(x);
+ theBodyRep->tie(theMessage.body_pipe);
+}
+
+const libecap::Body *Ecap::MessageRep::body() const
+{
+ return theBodyRep;
+}
--- /dev/null
+
+/*
+ * $Id$
+ */
+
+#ifndef SQUID__ECAP__MESSAGE_REP_H
+#define SQUID__ECAP__MESSAGE_REP_H
+
+#include "adaptation/forward.h"
+#include <libecap/common/message.h>
+#include <libecap/common/header.h>
+#include <libecap/common/body.h>
+
+namespace Ecap {
+
+class XactionRep;
+
+// Translates Squid HttpMsg into libecap::Header.
+class HeaderRep: public libecap::Header
+{
+public:
+ typedef libecap::Name Name;
+ typedef libecap::Area Area;
+
+public:
+ HeaderRep(HttpMsg &aMessage);
+
+ virtual bool hasAny(const Name &name) const;
+ virtual Value value(const Name &name) const;
+
+ virtual void add(const Name &name, const Value &value);
+ virtual void removeAny(const Name &name);
+
+ virtual Area image() const;
+ virtual void parse(const Area &buf); // throws on failures
+
+protected:
+ static http_hdr_type TranslateHeaderId(const Name &name);
+
+private:
+ HttpHeader &theHeader; // the header being translated to libecap
+ HttpMsg &theMessage; // the message being translated to libecap
+};
+
+
+// Helps translate Squid HttpMsg into libecap::FirstLine (see children).
+class FirstLineRep
+{
+public:
+ typedef libecap::Name Name;
+
+public:
+ FirstLineRep(HttpMsg &aMessage);
+
+ libecap::Version version() const;
+ void version(const libecap::Version &aVersion);
+ Name protocol() const;
+ void protocol(const Name &aProtocol);
+
+protected:
+ static protocol_t TranslateProtocolId(const Name &name);
+
+private:
+ HttpMsg &theMessage; // the message which first line is being translated
+};
+
+// Translates Squid HttpRequest into libecap::RequestLine.
+class RequestLineRep: public libecap::RequestLine, public FirstLineRep
+{
+public:
+// typedef libecap::Name Name;
+ typedef libecap::Area Area;
+
+public:
+ RequestLineRep(HttpRequest &aMessage);
+
+ virtual void uri(const Area &aUri);
+ virtual Area uri() const;
+
+ virtual void method(const Name &aMethod);
+ virtual Name method() const;
+
+ virtual libecap::Version version() const;
+ virtual void version(const libecap::Version &aVersion);
+ virtual Name protocol() const;
+ virtual void protocol(const Name &aProtocol);
+
+private:
+ HttpRequest &theMessage; // the request header being translated to libecap
+};
+
+// Translates Squid HttpReply into libecap::StatusLine.
+class StatusLineRep: public libecap::StatusLine, public FirstLineRep
+{
+public:
+ typedef libecap::Name Name;
+ typedef libecap::Area Area;
+
+public:
+ StatusLineRep(HttpReply &aMessage);
+
+ virtual void statusCode(int code);
+ virtual int statusCode() const;
+
+ virtual void reasonPhrase(const Area &phrase);
+ virtual Area reasonPhrase() const;
+
+ virtual libecap::Version version() const;
+ virtual void version(const libecap::Version &aVersion);
+ virtual Name protocol() const;
+ virtual void protocol(const Name &aProtocol);
+
+private:
+ HttpReply &theMessage; // the request header being translated to libecap
+};
+
+
+// Translates Squid BodyPipe into libecap::Body.
+class BodyRep: public libecap::Body
+{
+public:
+ typedef libecap::Area Area;
+ typedef libecap::BodySize BodySize;
+ using libecap::Body::size_type;
+
+public:
+ BodyRep(const BodyPipe::Pointer &aBody); // using NULL pointer? see tie()
+
+ void tie(const BodyPipe::Pointer &aBody); // late binding if !theBody;
+
+ // libecap::Body API
+ virtual BodySize bodySize() const;
+ virtual size_type consumedSize() const;
+ virtual bool productionEnded() const;
+ virtual Area prefix(size_type size) const;
+
+private:
+ BodyPipe::Pointer theBody; // the body being translated to libecap
+};
+
+// Translates Squid Adaptation::Message into libecap::Message.
+class MessageRep: public libecap::Message
+{
+public:
+ explicit MessageRep(HttpMsg *rawHeader);
+ virtual ~MessageRep();
+
+ virtual libecap::shared_ptr<libecap::Message> clone() const;
+
+ virtual libecap::FirstLine &firstLine();
+ virtual const libecap::FirstLine &firstLine() const;
+
+ virtual libecap::Header &header();
+ virtual const libecap::Header &header() const;
+
+ virtual void addBody();
+ virtual libecap::Body *body();
+ virtual const libecap::Body *body() const;
+ void tieBody(Ecap::XactionRep *x); // to a specific transaction
+
+ Adaptation::Message &raw() { return theMessage; } // for host access
+ const Adaptation::Message &raw() const { return theMessage; } // for host
+
+private:
+ Adaptation::Message theMessage; // the message being translated to libecap
+ libecap::FirstLine *theFirstLineRep; // request or status line wrapper
+ HeaderRep *theHeaderRep; // header wrapper
+ BodyRep *theBodyRep; // body wrapper
+};
+
+} // namespace Ecap;
+
+#endif /* SQUID__E_CAP__MESSAGE_REP_H */
--- /dev/null
+#include "squid.h"
+#include <libecap/adapter/service.h>
+#include "TextException.h"
+#include "assert.h"
+#include "eCAP/ServiceRep.h"
+#include "eCAP/XactionRep.h"
+
+Ecap::ServiceRep::ServiceRep(const Adaptation::ServiceConfig &cfg):
+ /*AsyncJob("Ecap::ServiceRep"),*/ Adaptation::Service(cfg)
+{
+}
+
+Ecap::ServiceRep::~ServiceRep()
+{
+}
+
+void Ecap::ServiceRep::noteService(const AdapterService &s) {
+ Must(s != NULL);
+ theService = s;
+ debugs(93,7, "Matched loaded and configured eCAP services: " <<
+ s->uri() << ' ' << cfg().key << "\n");
+}
+
+void Ecap::ServiceRep::invalidate() {
+ theService->retire();
+ theService.reset();
+}
+
+void Ecap::ServiceRep::noteFailure() {
+ assert(false); // XXX: should this be ICAP-specific?
+}
+
+void
+Ecap::ServiceRep::finalize()
+{
+ Adaptation::Service::finalize();
+ if (!theService) {
+ debugs(93,1, "Warning: configured ecap_service was not loaded: " <<
+ cfg().uri);
+ }
+}
+
+bool Ecap::ServiceRep::probed() const
+{
+ return true; // we "probe" the adapter in finalize().
+}
+
+bool Ecap::ServiceRep::up() const
+{
+ return theService != NULL;
+}
+
+bool Ecap::ServiceRep::wantsUrl(const String &urlPath) const
+{
+ Must(up());
+ return theService->wantsUrl(urlPath.buf());
+}
+
+Adaptation::Initiate *
+Ecap::ServiceRep::makeXactLauncher(Adaptation::Initiator *initiator,
+ HttpMsg *virgin, HttpRequest *cause)
+{
+ Must(up());
+ XactionRep *rep = new XactionRep(initiator, virgin, cause, Pointer(this));
+ XactionRep::AdapterXaction x(theService->makeXaction(rep));
+ rep->master(x);
+ return rep;
+}
+
+// returns a temporary string depicting service status, for debugging
+const char *Ecap::ServiceRep::status() const
+{
+ assert(false); // move generic stuff from ICAP to Adaptation
+ // add theService->status()?
+ return NULL;
+}
--- /dev/null
+
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_ECAP_SERVICE_REP_H
+#define SQUID_ECAP_SERVICE_REP_H
+
+#include "adaptation/Service.h"
+#include "adaptation/forward.h"
+#include <libecap/common/forward.h>
+#include <libecap/common/memory.h>
+
+namespace Ecap {
+
+/* The eCAP service representative maintains information about a single eCAP
+ service that Squid communicates with. One eCAP module may register many
+ eCAP services. */
+
+class ServiceRep : public Adaptation::Service
+{
+public:
+ ServiceRep(const Adaptation::ServiceConfig &config);
+ virtual ~ServiceRep();
+
+ typedef libecap::shared_ptr<libecap::adapter::Service> AdapterService;
+ void noteService(const AdapterService &s);
+
+ virtual void finalize();
+
+ // call when the service is no longer needed or valid
+ virtual void invalidate();
+
+ virtual bool probed() const;
+ virtual bool up() const;
+
+ Adaptation::Initiate *makeXactLauncher(Adaptation::Initiator *, HttpMsg *virginHeader, HttpRequest *virginCause);
+
+ // the methods below can only be called on an up() service
+ virtual bool wantsUrl(const String &urlPath) const;
+
+ // called by transactions to report service failure
+ virtual void noteFailure();
+
+ virtual const char *status() const;
+
+private:
+ AdapterService theService; // the actual adaptation service we represent
+};
+
+} // namespace Ecap
+
+#endif /* SQUID_ECAP_SERVICE_REP_H */
--- /dev/null
+#include "squid.h"
+#include <libecap/common/area.h>
+#include <libecap/common/delay.h>
+#include <libecap/adapter/xaction.h>
+#include "TextException.h"
+#include "assert.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "eCAP/XactionRep.h"
+
+// CBDATA_CLASS_INIT(Ecap::XactionRep);
+// TODO: add CBDATA_NAMESPACED_CLASS_INIT(namespace, classname)
+cbdata_type Ecap::XactionRep::CBDATA_XactionRep = CBDATA_UNKNOWN;
+
+
+Ecap::XactionRep::XactionRep(Adaptation::Initiator *anInitiator,
+ HttpMsg *virginHeader, HttpRequest *virginCause,
+ const Adaptation::ServicePointer &aService):
+ AsyncJob("Ecap::XactionRep"),
+ Adaptation::Initiate("Ecap::XactionRep", anInitiator, aService),
+ theVirginRep(virginHeader), theCauseRep(NULL)
+{
+ if (virginCause)
+ theCauseRep = new MessageRep(virginCause);
+}
+
+Ecap::XactionRep::~XactionRep()
+{
+ assert(!theMaster);
+ delete theCauseRep;
+ theAnswerRep.reset();
+}
+
+void
+Ecap::XactionRep::master(const AdapterXaction &x)
+{
+ Must(!theMaster);
+ Must(x != NULL);
+ theMaster = x;
+}
+
+void
+Ecap::XactionRep::start()
+{
+ Must(theMaster);
+
+ // register as a consumer if there is a body
+ // we do not actually consume unless the adapter tells us to
+ BodyPipePointer &p = theVirginRep.raw().body_pipe;
+ Must(!p || p->setConsumerIfNotLate(this));
+
+ theMaster->start();
+}
+
+void
+Ecap::XactionRep::swanSong()
+{
+ terminateMaster();
+ Adaptation::Initiate::swanSong();
+}
+
+void
+Ecap::XactionRep::terminateMaster()
+{
+ if (theMaster) {
+ AdapterXaction x = theMaster;
+ theMaster.reset();
+ x->stop();
+ }
+}
+
+libecap::Message &
+Ecap::XactionRep::virgin()
+{
+ return theVirginRep;
+}
+
+const libecap::Message &
+Ecap::XactionRep::cause()
+{
+ Must(theCauseRep != NULL);
+ return *theCauseRep;
+}
+
+libecap::Message &
+Ecap::XactionRep::adapted()
+{
+ Must(theAnswerRep != NULL);
+ return *theAnswerRep;
+}
+
+Adaptation::Message &
+Ecap::XactionRep::answer()
+{
+ MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get());
+ Must(rep);
+ return rep->raw();
+}
+
+bool
+Ecap::XactionRep::doneAll() const
+{
+ if (theMaster) {
+ if (!doneWithAdapted() || sendingVirgin())
+ return false;
+ }
+
+ return Adaptation::Initiate::doneAll();
+}
+
+// are we still sending virgin body to theMaster?
+bool
+Ecap::XactionRep::doneWithAdapted() const
+{
+ if (!theAnswerRep)
+ return false;
+
+ // we are not done if we are producing
+ MessageRep *answer = dynamic_cast<MessageRep*>(theAnswerRep.get());
+ Must(answer);
+ const BodyPipePointer &ap = answer->raw().body_pipe;
+ return !ap || !ap->stillProducing(this);
+}
+
+// are we still sending virgin body to theMaster?
+bool
+Ecap::XactionRep::sendingVirgin() const
+{
+ // we are sending if we are consuming
+ const BodyPipePointer &vp = theVirginRep.raw().body_pipe;
+ return vp != NULL && vp->stillConsuming(this);
+}
+
+// stops sending virgin to theMaster and enables auto-consumption
+void
+Ecap::XactionRep::dropVirgin(const char *reason)
+{
+ debugs(93,4, HERE << "because " << reason);
+
+ BodyPipePointer &p = theVirginRep.raw().body_pipe;
+ Must(p != NULL);
+ Must(p->stillConsuming(this));
+ stopConsumingFrom(p);
+ p->enableAutoConsumption();
+ if (doneWithAdapted())
+ theMaster.reset();
+}
+
+void
+Ecap::XactionRep::useVirgin()
+{
+ debugs(93,3, HERE << status());
+
+ // XXX: check state everywhere
+ Must(!theAnswerRep);
+ theMaster.reset();
+
+ HttpMsg *answer = theVirginRep.raw().header->clone();
+ Must(!theVirginRep.raw().body_pipe == !answer->body_pipe); // check clone()
+
+ if (answer->body_pipe != NULL) {
+ // if libecap consumed, we cannot shortcircuit
+ Must(!answer->body_pipe->consumedSize());
+ Must(answer->body_pipe->stillConsuming(this));
+ stopConsumingFrom(answer->body_pipe);
+ }
+
+ sendAnswer(answer);
+ Must(done());
+}
+
+void
+Ecap::XactionRep::useAdapted(const libecap::shared_ptr<libecap::Message> &m)
+{
+ debugs(93,3, HERE << status());
+ theAnswerRep = m;
+ MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get());
+ Must(rep);
+ HttpMsg *answer = rep->raw().header;
+ if (!theAnswerRep->body()) {
+ if (!sendingVirgin())
+ theMaster.reset();
+ sendAnswer(answer);
+ } else {
+ Must(!answer->body_pipe); // only host can set body pipes
+ rep->tieBody(this);
+ debugs(93,4, HERE << "adapter will produce body" << status());
+ // libecap will produce
+ sendAnswer(answer);
+ }
+}
+
+// if adapter does not want to consume, we should not either
+void
+Ecap::XactionRep::adapterWontConsume()
+{
+ if (sendingVirgin())
+ dropVirgin("adapterWontConsume");
+}
+
+void
+Ecap::XactionRep::adapterWillConsume()
+{
+ Must(sendingVirgin());
+ theMaster->noteVirginDataAvailable(); // XXX: async
+}
+
+void
+Ecap::XactionRep::adapterDoneConsuming()
+{
+ if (sendingVirgin())
+ dropVirgin("adapterDoneConsuming");
+}
+
+void
+Ecap::XactionRep::consumeVirgin(size_type n)
+{
+ BodyPipePointer &p = theVirginRep.raw().body_pipe;
+ Must(p != NULL);
+ const size_t size = static_cast<size_t>(n); // XXX: check for overflow
+ const size_t sizeMax = static_cast<size_t>(p->buf().contentSize()); // TODO: make MemBuf use size_t?
+ p->consume(min(size, sizeMax));
+}
+
+void
+Ecap::XactionRep::pauseVirginProduction()
+{
+ // TODO: support production pauses
+}
+
+void
+Ecap::XactionRep::resumeVirginProduction()
+{
+ // TODO: support production pauses
+}
+
+void
+Ecap::XactionRep::setAdaptedBodySize(const libecap::BodySize &size)
+{
+ Must(answer().body_pipe != NULL);
+ if (size.known())
+ answer().body_pipe->setBodySize(size.value());
+ // else the piped body size is unknown by default
+}
+
+void
+Ecap::XactionRep::appendAdapted(const libecap::Area &area)
+{
+ BodyPipe *p = answer().body_pipe.getRaw();
+ Must(p);
+ Must(p->putMoreData(area.start, area.size) == area.size);
+}
+
+bool
+Ecap::XactionRep::callable() const
+{
+ return !done();
+}
+
+void
+Ecap::XactionRep::noteAdaptedBodyEnd()
+{
+ Must(answer().body_pipe != NULL);
+ answer().body_pipe->clearProducer(true);
+ if (!sendingVirgin())
+ theMaster.reset();
+}
+
+void
+Ecap::XactionRep::adaptationDelayed(const libecap::Delay &d)
+{
+ debugs(93,3, HERE << "adapter needs time: " <<
+ d.state << '/' << d.progress);
+ // XXX: set timeout?
+}
+
+void
+Ecap::XactionRep::adaptationAborted()
+{
+ theMaster.reset();
+ tellQueryAborted(true); // should eCAP support retries?
+}
+
+void
+Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp)
+{
+ Must(theMaster);
+ theMaster->noteAdaptedSpaceAvailable();
+}
+
+void
+Ecap::XactionRep::noteBodyConsumerAborted(RefCount<BodyPipe> bp)
+{
+ terminateMaster();
+}
+
+void
+Ecap::XactionRep::noteMoreBodyDataAvailable(RefCount<BodyPipe> bp)
+{
+ Must(theMaster);
+ theMaster->noteVirginDataAvailable();
+}
+
+void
+Ecap::XactionRep::noteBodyProductionEnded(RefCount<BodyPipe> bp)
+{
+ Must(theMaster);
+ theMaster->noteVirginDataEnded();
+}
+
+void
+Ecap::XactionRep::noteBodyProducerAborted(RefCount<BodyPipe> bp)
+{
+ terminateMaster();
+}
+
+void
+Ecap::XactionRep::noteInitiatorAborted()
+{
+ mustStop("initiator aborted");
+}
+
+const char *Ecap::XactionRep::status() const
+{
+ static MemBuf buf;
+ buf.reset();
+
+ buf.append(" [", 2);
+
+ if (theAnswerRep != NULL) {
+ MessageRep *answer = dynamic_cast<MessageRep*>(theAnswerRep.get());
+ Must(answer);
+ const BodyPipePointer &ap = answer->raw().body_pipe;
+ if (ap != NULL && ap->stillProducing(this))
+ buf.append("Ab ", 3);
+ else
+ buf.append("A. ", 3);
+ }
+
+ const BodyPipePointer &vp = theVirginRep.raw().body_pipe;
+ if (vp != NULL && vp->stillConsuming(this))
+ buf.append("Vb ", 3);
+ else
+ buf.append("V. ", 3);
+
+ buf.Printf(" ecapx%d]", id);
+
+ buf.terminate();
+
+ return buf.content();
+}
--- /dev/null
+
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_ECAP_XACTION_REP_H
+#define SQUID_ECAP_XACTION_REP_H
+
+#include "BodyPipe.h"
+#include "adaptation/Initiate.h"
+#include "adaptation/Service.h"
+#include "adaptation/Message.h"
+#include "eCAP/MessageRep.h"
+#include <libecap/common/forward.h>
+#include <libecap/common/memory.h>
+#include <libecap/host/xaction.h>
+#include <libecap/adapter/xaction.h>
+
+namespace Ecap {
+
+/* The eCAP xaction representative maintains information about a single eCAP
+ xaction that Squid communicates with. One eCAP module may register many
+ eCAP xactions. */
+class XactionRep : public Adaptation::Initiate, public libecap::host::Xaction,
+ public BodyConsumer, public BodyProducer
+{
+public:
+ XactionRep(Adaptation::Initiator *anInitiator, HttpMsg *virginHeader, HttpRequest *virginCause, const Adaptation::ServicePointer &service);
+ virtual ~XactionRep();
+
+ typedef libecap::shared_ptr<libecap::adapter::Xaction> AdapterXaction;
+ void master(const AdapterXaction &aMaster); // establish a link
+
+ // libecap::host::Xaction API
+ virtual libecap::Message &virgin();
+ virtual const libecap::Message &cause();
+ virtual libecap::Message &adapted();
+ virtual void useVirgin();
+ virtual void useAdapted(const libecap::shared_ptr<libecap::Message> &msg);
+ virtual void adaptationDelayed(const libecap::Delay &);
+ virtual void adaptationAborted();
+ virtual void adapterWontConsume();
+ virtual void adapterWillConsume();
+ virtual void adapterDoneConsuming();
+ virtual void consumeVirgin(size_type n);
+ virtual void pauseVirginProduction();
+ virtual void resumeVirginProduction();
+ virtual void setAdaptedBodySize(const libecap::BodySize &size);
+ virtual void appendAdapted(const libecap::Area &area);
+ virtual void noteAdaptedBodyEnd();
+
+ // libecap::Callable API, via libecap::host::Xaction
+ virtual bool callable() const;
+
+ // BodyProducer API
+ virtual void noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp);
+ virtual void noteBodyConsumerAborted(RefCount<BodyPipe> bp);
+
+ // BodyConsumer API
+ virtual void noteMoreBodyDataAvailable(RefCount<BodyPipe> bp);
+ virtual void noteBodyProductionEnded(RefCount<BodyPipe> bp);
+ virtual void noteBodyProducerAborted(RefCount<BodyPipe> bp);
+
+ // Initiate API
+ virtual void noteInitiatorAborted();
+
+ // AsyncJob API (via Initiate)
+ virtual void start();
+ virtual bool doneAll() const;
+ virtual void swanSong();
+ virtual const char *status() const;
+
+protected:
+ Adaptation::Message &answer();
+
+ bool sendingVirgin() const;
+ void dropVirgin(const char *reason);
+ bool doneWithAdapted() const;
+
+ void terminateMaster();
+ void scheduleStop(const char *reason);
+
+private:
+ AdapterXaction theMaster; // the actual adaptation xaction we represent
+
+ MessageRep theVirginRep;
+ MessageRep *theCauseRep;
+
+ typedef libecap::shared_ptr<libecap::Message> MessagePtr;
+ MessagePtr theAnswerRep;
+
+ CBDATA_CLASS2(XactionRep);
+};
+
+} // namespace Ecap
+
+#endif /* SQUID_ECAP_XACTION_REP_H */
#include "forward.h"
#include "MemPool.h"
#include "ICMPSquid.h"
+#include "TextException.h"
#if USE_LOADABLE_MODULES
#include "LoadableModules.h"
#if ICAP_CLIENT
#include "ICAP/ICAPConfig.h"
#endif
+#if USE_ECAP
+#include "eCAP/Config.h"
+#endif
#if USE_ADAPTATION
#include "adaptation/Config.h"
#endif
// We can remove this dependency on specific adaptation mechanisms
// if we create a generic Registry of such mechanisms. Should we?
#if ICAP_CLIENT
- TheICAPConfig.finalize(); // must be after we load modules
- enableAdaptation = TheICAPConfig.onoff;
+ TheICAPConfig.finalize();
+ enableAdaptation = TheICAPConfig.onoff || enableAdaptation;
+#endif
+#if USE_ECAP
+ Ecap::TheConfig.finalize(); // must be after we load modules
+ enableAdaptation = Ecap::TheConfig.onoff || enableAdaptation;
#endif
- // same for eCAP
-
// must be the last adaptation-related finalize
Adaptation::Config::Finalize(enableAdaptation);
#endif
configured_once = 1;
}
+static int SquidMain(int argc, char **argv);
+static int SquidMainSafe(int argc, char **argv);
+
#if USE_WIN32_SERVICE
/* When USE_WIN32_SERVICE is defined, the main function is placed in win32.cc */
extern "C" void WINAPI
SquidWinSvcMain(int argc, char **argv)
-{
- SquidMain(argc, argv);
-}
-
-int
-SquidMain(int argc, char **argv)
#else
int
main(int argc, char **argv)
#endif
+{
+ SquidMainSafe(argc, argv);
+}
+
+static int
+SquidMainSafe(int argc, char **argv)
+{
+ try {
+ return SquidMain(argc, argv);
+ }
+ catch (const TextException &e) {
+ // XXX: add TextException::print
+ std::cerr << "dying from unhandled exception: " << e.message << std::endl;
+ }
+ catch (...) {
+ std::cerr << "dying from unhandled exception." << std::endl;
+ }
+ return -1;
+}
+
+static int
+SquidMain(int argc, char **argv)
{
#ifdef _SQUID_WIN32_