]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merged from trunk.
authorAlex Rousskov <rousskov@measurement-factory.com>
Sat, 27 Sep 2008 18:33:49 +0000 (12:33 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Sat, 27 Sep 2008 18:33:49 +0000 (12:33 -0600)
36 files changed:
configure.in
src/BodyPipe.h
src/HttpMsg.h
src/HttpReply.cc
src/HttpRequest.cc
src/HttpRequest.h
src/ICAP/ICAPServiceRep.cc
src/ICAP/ICAPServiceRep.h
src/LoadableModules.cc
src/Makefile.am
src/Server.cc
src/Server.h
src/adaptation/Config.cc
src/adaptation/Config.h
src/adaptation/Initiate.cc
src/adaptation/Makefile.am
src/adaptation/Message.cc [new file with mode: 0644]
src/adaptation/Message.h [new file with mode: 0644]
src/adaptation/Service.cc
src/adaptation/Service.h
src/adaptation/forward.h
src/cache_cf.cc
src/cf.data.depend
src/cf.data.pre
src/eCAP/Config.cc [new file with mode: 0644]
src/eCAP/Config.h [new file with mode: 0644]
src/eCAP/Host.cc [new file with mode: 0644]
src/eCAP/Host.h [new file with mode: 0644]
src/eCAP/Makefile.am
src/eCAP/MessageRep.cc [new file with mode: 0644]
src/eCAP/MessageRep.h [new file with mode: 0644]
src/eCAP/ServiceRep.cc [new file with mode: 0644]
src/eCAP/ServiceRep.h [new file with mode: 0644]
src/eCAP/XactionRep.cc [new file with mode: 0644]
src/eCAP/XactionRep.h [new file with mode: 0644]
src/main.cc

index da81a91c75823645a1d7f8de44bbbb48f1643f1e..3e8e08315a8760bf53eb4b9e989afad92db143a3 100755 (executable)
@@ -784,13 +784,21 @@ then
             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])
index c56a6cdb112bda0bebc8e76ad578b79d433728b5..0db4cc4baa1e598ad5fe979a34d305e031cc9c0f 100644 (file)
@@ -96,7 +96,7 @@ class BodyPipe: public RefCountable {
                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);
@@ -105,7 +105,7 @@ class BodyPipe: public RefCountable {
                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();
index 842333bc08e9ebfcfd9dad1eaacc761b542abf4f..cf8e696899d6958e54796443713efca7c209387a 100644 (file)
@@ -55,6 +55,8 @@ public:
     virtual HttpMsg *_lock();  // please use HTTPMSGLOCK()
     virtual void _unlock();    // please use HTTPMSGUNLOCK()
 
+       virtual HttpMsg *clone() const = 0;
+
 public:
     HttpVersion http_ver;
 
index 97e327313b1da870ffbab552a9d1362c4285e6a1..bfbd0b06fe709602d2cb7e6b7efdd5fdd851e3a5 100644 (file)
@@ -552,6 +552,7 @@ HttpReply::calcMaxBodySize(HttpRequest& request)
     }
 }
 
+// XXX: check that this is sufficient for eCAP cloning
 HttpReply *
 HttpReply::clone() const
 {
@@ -561,6 +562,8 @@ 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;
index f099d6919f7c52f8851fe08949b473bc7d3e82d4..3f17df12ec53dbb26f31fb993458ee045aa59cf8 100644 (file)
@@ -143,6 +143,59 @@ HttpRequest::reset()
     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)
 {
index 39a0694dc56b03f63dbde846eda227774441afc3..2dcec900a3fd9ea86fe30a96839113a5818f07a3 100644 (file)
@@ -65,6 +65,8 @@ public:
 
     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;
 
index e63c81a5ea2747adc586923871fb7d1448943df5..d9b0833f09c5a1cc2a5141e7d10e563178e4885d 100644 (file)
@@ -110,11 +110,6 @@ bool ICAPServiceRep::up() const
     return self != NULL && !isSuspended && hasOptions();
 }
 
-bool ICAPServiceRep::broken() const
-{
-    return probed() && !up();
-}
-
 bool ICAPServiceRep::wantsUrl(const String &urlPath) const
 {
     Must(hasOptions());
index ea9bd00ee2cbaa29518001cbacf175dcd9f133a9..7c8011f77cfe8dbbdacbb43baefb4e738b90c4cf 100644 (file)
@@ -89,7 +89,6 @@ public:
     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);
index c9b2725da72b2227286bf367f1724997ec3c2a9a..8a40c11f912fbf76544cb0f724ad088e6caf4eb1 100644 (file)
@@ -21,5 +21,5 @@ LoadableModulesConfigure(const wordlist *names)
     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);
 }
index d16a8c4f37dc9a412cfc03e74e2a50e6448ff707..1b20279161dd178cc0a6ebc3df0e1136cd2c1f29 100644 (file)
@@ -723,8 +723,7 @@ squid_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \
        @DISK_LINKOBJS@ \
        @REPL_OBJS@ \
        @AUTH_LINKOBJS@ \
-       @AUTH_OBJS@ \
-       ${ADAPTATION_LIBS}
+       @AUTH_OBJS@
 
 if USE_LOADABLE_MODULES
 squid_SOURCES += $(LOADABLE_MODULES_SOURCES)
@@ -960,8 +959,7 @@ ufsdump_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \
        @DISK_LINKOBJS@ \
        @REPL_OBJS@ \
        @AUTH_LINKOBJS@ \
-       @AUTH_OBJS@ \
-       ${ADAPTATION_LIBS}
+       @AUTH_OBJS@
 
 nodist_ufsdump_SOURCES = \
        repl_modules.cc \
@@ -1513,8 +1511,7 @@ tests_testCacheManager_LDADD = \
 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) \
@@ -1687,8 +1684,7 @@ tests_testEvent_LDADD = \
 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 = \
@@ -1845,8 +1841,7 @@ tests_testEventLoop_LDADD = \
 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 \
@@ -2156,8 +2151,7 @@ tests_testHttpRequest_LDADD = \
 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! :-(
@@ -2521,5 +2515,4 @@ tests_testURL_LDADD = \
 tests_testURL_LDFLAGS = $(LIBADD_DL)
 tests_testURL_DEPENDENCIES =  $(top_builddir)/lib/libmiscutil.a \
        @REPL_OBJS@ \
-       @SQUID_CPPUNIT_LA@ \
-       ${ADAPTATION_LIBS}
+       @SQUID_CPPUNIT_LA@
index deae0318439de18937f304446ab9206cc12b3bfd..03443fb74c45935e60e584cfa0df13ac6ca86ed4 100644 (file)
@@ -66,6 +66,13 @@ ServerStateData::ServerStateData(FwdState *theFwdState): AsyncJob("ServerStateDa
 
 ServerStateData::~ServerStateData()
 {
+    // paranoid: check that swanSong has been called
+    assert(!requestBodySource);
+#if USE_ADAPTATION
+    assert(!virginBodyDestination);
+    assert(!adaptedBodySource);
+#endif
+
     entry->unlock();
 
     HTTPMSGUNLOCK(request);
@@ -74,6 +81,16 @@ ServerStateData::~ServerStateData()
 
     fwd = NULL; // refcounted
 
+    if (responseBodyBuffer != NULL) {
+       delete responseBodyBuffer;
+       responseBodyBuffer = NULL;
+    }
+}
+
+void
+ServerStateData::swanSong()
+{
+    // get rid of our piping obligations
     if (requestBodySource != NULL)
         requestBodySource->clearConsumer();
 
@@ -81,12 +98,14 @@ ServerStateData::~ServerStateData()
     cleanAdaptation();
 #endif
 
-    if (responseBodyBuffer != NULL) {
-       delete responseBodyBuffer;
-       responseBodyBuffer = NULL;
-    }
+    BodyConsumer::swanSong();
+#if USE_ADAPTATION
+    Initiator::swanSong();
+    BodyProducer::swanSong();
+#endif
 }
 
+
 HttpReply *
 ServerStateData::virginReply() {
     assert(theVirginReply);
index 28d4b5f0826111b289a28fad9c9629186404cf24..c7d102ecacf09f39a9ae73460878cde5592a7abe 100644 (file)
@@ -100,6 +100,7 @@ public:
     virtual void processReplyBody() = 0;
 
 //AsyncJob virtual methods
+    virtual void swanSong();
     virtual bool doneAll() const { return
 #if USE_ADAPTATION
                        Adaptation::Initiator::doneAll() &&
index aa3ca03691f3e4668c8d6c2c042de82f8c2df9b9..61bbc4c8cca9da6f9d0e4e485d41f3bd2e52b69e 100644 (file)
@@ -81,6 +81,12 @@ Adaptation::Config::finalize()
     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);
index 5de8be95e09a5ae7d0f0a199ec15ab28d8e64cfe..22c9bc2a8918da0df3a944b0f3fca197f0d371a5 100644 (file)
@@ -57,7 +57,7 @@ public:
     ServicePointer findService(const String&);
     Class * findClass(const String& key);
 
-    void finalize();
+    virtual void finalize();
 
 private:
     Config(const Config &); // unsupported
index 57a4ca9ed2cd1c8aac8b2b109476469f74669677..72a6f24644cd8d66462060c5e18492f36c146a35 100644 (file)
@@ -39,7 +39,9 @@ Adaptation::Initiate::Initiate(const char *aTypeName,
 
 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
@@ -89,7 +91,7 @@ Adaptation::Initiate::service()
 }
 
 const char *Adaptation::Initiate::status() const {
-    return ""; // for now
+    return AsyncJob::status(); // for now
 }
 
 
index ec77f05ce598ce8a6e2188c204e0e97e99c451c5..960603efdafdb1ab510a45389e4689eaff729b17 100644 (file)
@@ -22,6 +22,8 @@ libadaptation_la_SOURCES = \
        Initiate.h \
        Initiator.cc \
        Initiator.h \
+       Message.cc \
+       Message.h \
        Service.cc \
        Service.h \
        ServiceConfig.cc \
diff --git a/src/adaptation/Message.cc b/src/adaptation/Message.cc
new file mode 100644 (file)
index 0000000..ee4bac6
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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());
+}
diff --git a/src/adaptation/Message.h b/src/adaptation/Message.h
new file mode 100644 (file)
index 0000000..2273c00
--- /dev/null
@@ -0,0 +1,52 @@
+
+/*
+ * $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 */
index 8d3f6ebef014d268b99a0e513e9a9140790ccd81..6f35d33e2512caf2106c1db73b2a5f3664abe7f5 100644 (file)
@@ -18,6 +18,11 @@ Adaptation::Service::finalize()
 {
 }
 
+bool Adaptation::Service::broken() const
+{
+    return probed() && !up();
+}
+
 Adaptation::Services &
 Adaptation::AllServices()
 {
index 9d6303ed384ec39468ccacfa057aab6c8d783057..15806dd3eaf91deb64c2a4511ef682167aece493 100644 (file)
@@ -30,7 +30,7 @@ public:
     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;
index 3c7ecb1b90ba76d30f215b53a964b4c905a6ba98..b26fcc9e0be551534d3f1cf72dc559b084ad99e8 100644 (file)
@@ -21,6 +21,7 @@ class Initiator;
 class AccessCheck;
 class AccessRule;
 class ServiceGroup;
+class Message;
 
 typedef RefCount<Service> ServicePointer;
 
index 6d356dd164266cc9fe3921084b674b323107e2b3..216568dcc81983879b80e7e789fd5160639b9955 100644 (file)
 #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 *);
@@ -86,6 +85,13 @@ static void free_icap_access_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";
@@ -3496,18 +3502,6 @@ 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 */
 
 
@@ -3556,19 +3550,43 @@ parse_icap_access_type(ICAPConfig *)
 {
     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 */
index 05d70deedb2fa42633a13593e8f94d4dab36aeec..54b5c33cefe548877b4eff46080a851153add4a9 100644 (file)
@@ -30,6 +30,7 @@ adaptation_service_set_type   icap_service
 icap_access_type       icap_class acl
 icap_class_type                icap_service
 icap_service_type
+ecap_service_type
 int
 kb_int64_t
 kb_size_t
index 4b206a3e5eac9b4a6d99b26f8b090c427f334c65..86a1120189e87a2ba09870c186a13f95a4adc4d7 100644 (file)
@@ -5261,7 +5261,7 @@ DOC_END
 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
@@ -5278,7 +5278,7 @@ DOC_END
 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
@@ -5291,6 +5291,45 @@ COMMENT_START
  -----------------------------------------------------------------------------
 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&parameters=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
diff --git a/src/eCAP/Config.cc b/src/eCAP/Config.cc
new file mode 100644 (file)
index 0000000..4ba2e76
--- /dev/null
@@ -0,0 +1,36 @@
+
+/*
+ * $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();
+}
+
diff --git a/src/eCAP/Config.h b/src/eCAP/Config.h
new file mode 100644 (file)
index 0000000..84e7bff
--- /dev/null
@@ -0,0 +1,34 @@
+
+/*
+ * $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 */
diff --git a/src/eCAP/Host.cc b/src/eCAP/Host.cc
new file mode 100644 (file)
index 0000000..813f9fa
--- /dev/null
@@ -0,0 +1,106 @@
+#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();
+}
diff --git a/src/eCAP/Host.h b/src/eCAP/Host.h
new file mode 100644 (file)
index 0000000..569f619
--- /dev/null
@@ -0,0 +1,40 @@
+
+/*
+ * $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 */
index 9bddacf0a63b03f071f32fe4febe115c59d6f415..d52aac9b905c09e3042e5328dc66fa8bfabcfac3 100644 (file)
@@ -9,18 +9,18 @@ AM_CXXFLAGS = @SQUID_CXXFLAGS@
 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
diff --git a/src/eCAP/MessageRep.cc b/src/eCAP/MessageRep.cc
new file mode 100644 (file)
index 0000000..2995fda
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * 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;
+}
diff --git a/src/eCAP/MessageRep.h b/src/eCAP/MessageRep.h
new file mode 100644 (file)
index 0000000..9abef7d
--- /dev/null
@@ -0,0 +1,173 @@
+
+/*
+ * $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 */
diff --git a/src/eCAP/ServiceRep.cc b/src/eCAP/ServiceRep.cc
new file mode 100644 (file)
index 0000000..5d1cba5
--- /dev/null
@@ -0,0 +1,76 @@
+#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;
+}
diff --git a/src/eCAP/ServiceRep.h b/src/eCAP/ServiceRep.h
new file mode 100644 (file)
index 0000000..ae045a1
--- /dev/null
@@ -0,0 +1,53 @@
+
+/*
+ * $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 */
diff --git a/src/eCAP/XactionRep.cc b/src/eCAP/XactionRep.cc
new file mode 100644 (file)
index 0000000..c497d70
--- /dev/null
@@ -0,0 +1,351 @@
+#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();
+}
diff --git a/src/eCAP/XactionRep.h b/src/eCAP/XactionRep.h
new file mode 100644 (file)
index 0000000..ce193af
--- /dev/null
@@ -0,0 +1,97 @@
+
+/*
+ * $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 */
index 6eaea0acded0a3300a63323fd7d65e022ea3e480..38e08fe333eea262e8f0cd2bd91ba036e30b2cab 100644 (file)
@@ -69,6 +69,7 @@
 #include "forward.h"
 #include "MemPool.h"
 #include "ICMPSquid.h"
+#include "TextException.h"
 
 #if USE_LOADABLE_MODULES
 #include "LoadableModules.h"
@@ -77,6 +78,9 @@
 #if ICAP_CLIENT
 #include "ICAP/ICAPConfig.h"
 #endif
+#if USE_ECAP
+#include "eCAP/Config.h"
+#endif
 #if USE_ADAPTATION
 #include "adaptation/Config.h"
 #endif
@@ -1048,11 +1052,13 @@ mainInitialize(void)
     // 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
@@ -1082,20 +1088,39 @@ mainInitialize(void)
     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_