1 Support libecap v1.0, allowing asynchronous adapters and eCAP version checks.
3 After these changes, Squid can support eCAP adapters built with libecap v1.0,
4 but stops supporting adapters built with earlier libecap versions (due to API
5 changes). The new libecap version allows Squid to better check the version of
6 the eCAP adapter being loaded as well as the version of the eCAP library being
7 used. This should help with migration to libecap v1.0.
10 Expose [running] main event loop as a global so that modules can add engines.
12 === modified file 'configure.ac'
13 --- configure.ac 2013-07-09 11:15:51 +0000
14 +++ configure.ac 2013-09-12 23:08:18 +0000
15 @@ -1035,10 +1035,10 @@
17 if test -n "$PKG_CONFIG"; then
18 dnl eCAP support requires libecap.
19 - dnl This Squid supports libecap v0.2.x.
20 + dnl This Squid supports libecap v1.0.x.
21 dnl Use EXT_ prefix to distinguish external libecap (that we check for
22 dnl here) from our own convenience ecap library in Makefiles.
23 - PKG_CHECK_MODULES([EXT_LIBECAP],[libecap >= 0.2.0 libecap < 0.3])
24 + PKG_CHECK_MODULES([EXT_LIBECAP],[libecap >= 1.0 libecap < 1.1])
26 AC_MSG_NOTICE([eCAP support requires pkg-config to verify the correct library version. Trouble may follow.])
29 === modified file 'src/EventLoop.cc'
30 --- src/EventLoop.cc 2013-02-17 02:09:16 +0000
31 +++ src/EventLoop.cc 2013-09-16 00:07:51 +0000
33 #include "base/AsyncCallQueue.h"
34 #include "SquidTime.h"
36 +EventLoop *EventLoop::Running = NULL;
38 EventLoop::EventLoop() : errcount(0), last_loop(false), timeService(NULL),
40 loop_delay(EVENT_LOOP_TIMEOUT),
55 === modified file 'src/EventLoop.h'
56 --- src/EventLoop.h 2013-05-04 11:50:26 +0000
57 +++ src/EventLoop.h 2013-09-16 00:07:51 +0000
62 + /// the [main program] loop running now; may be nil
63 + /// for simplicity, we assume there are no concurrent loops
64 + static EventLoop *Running;
67 /** setup state variables prior to running */
70 === modified file 'src/adaptation/ecap/Host.cc'
71 --- src/adaptation/ecap/Host.cc 2012-08-28 13:00:30 +0000
72 +++ src/adaptation/ecap/Host.cc 2013-09-16 18:04:34 +0000
74 os << PACKAGE_NAME << " v" << PACKAGE_VERSION;
77 +/// Strips libecap version components not affecting compatibility decisions.
79 +EssentialVersion(const std::string &raw)
81 + // all libecap x.y.* releases are supposed to be compatible so we strip
82 + // everything after the second period
83 + const std::string::size_type minorPos = raw.find('.');
84 + const std::string::size_type microPos = minorPos == std::string::npos ?
85 + std::string::npos : raw.find('.', minorPos+1);
86 + return raw.substr(0, microPos); // becomes raw if microPos is npos
89 +/// If "their" libecap version is not compatible with what Squid has been built
90 +/// with, then complain and return false.
92 +SupportedVersion(const char *vTheir, const std::string &them)
94 + if (!vTheir || !*vTheir) {
95 + debugs(93, DBG_CRITICAL, "ERROR: Cannot use " << them <<
96 + " with libecap prior to v1.0.");
100 + // we support what we are built with
101 + const std::string vSupported(LIBECAP_VERSION);
102 + debugs(93, 2, them << " with libecap v" << vTheir << "; us: v" << vSupported);
104 + if (EssentialVersion(vTheir) == EssentialVersion(vSupported))
105 + return true; // their version is supported
107 + debugs(93, DBG_CRITICAL, "ERROR: Cannot use " << them <<
108 + " with libecap v" << vTheir <<
109 + ": incompatible with supported libecap v" << vSupported);
114 -Adaptation::Ecap::Host::noteService(const libecap::weak_ptr<libecap::adapter::Service> &weak)
115 +Adaptation::Ecap::Host::noteVersionedService(const char *vGiven, const libecap::weak_ptr<libecap::adapter::Service> &weak)
117 - Must(!weak.expired());
118 - RegisterAdapterService(weak.lock());
120 + * Check that libecap used to build the service is compatible with ours.
121 + * This has to be done using vGiven string and not Service object itself
122 + * because dereferencing a Service pointer coming from an unsupported
123 + * version is unsafe.
125 + if (SupportedVersion(vGiven, "eCAP service built")) {
126 + Must(!weak.expired());
127 + RegisterAdapterService(weak.lock());
134 Adaptation::Ecap::Host::Register()
137 + if (!TheHost && SupportedVersion(libecap::VersionString(),
138 + "Squid executable dynamically linked")) {
139 TheHost.reset(new Adaptation::Ecap::Host);
140 libecap::RegisterHost(TheHost);
143 === modified file 'src/adaptation/ecap/Host.h'
144 --- src/adaptation/ecap/Host.h 2012-10-04 11:10:17 +0000
145 +++ src/adaptation/ecap/Host.h 2013-09-16 18:04:34 +0000
147 /* libecap::host::Host API */
148 virtual std::string uri() const; // unique across all vendors
149 virtual void describe(std::ostream &os) const; // free-format info
150 - virtual void noteService(const libecap::weak_ptr<libecap::adapter::Service> &s);
151 + virtual void noteVersionedService(const char *libEcapVersion, const libecap::weak_ptr<libecap::adapter::Service> &s);
152 virtual std::ostream *openDebug(libecap::LogVerbosity lv);
153 virtual void closeDebug(std::ostream *debug);
154 typedef libecap::shared_ptr<libecap::Message> MessagePtr;
156 === modified file 'src/adaptation/ecap/ServiceRep.cc'
157 --- src/adaptation/ecap/ServiceRep.cc 2012-08-28 13:00:30 +0000
158 +++ src/adaptation/ecap/ServiceRep.cc 2013-09-16 18:04:34 +0000
160 * DEBUG: section 93 eCAP Interface
163 +#include "adaptation/ecap/Config.h"
164 +#include "adaptation/ecap/Host.h"
165 +#include "adaptation/ecap/ServiceRep.h"
166 +#include "adaptation/ecap/XactionRep.h"
167 +#include "AsyncEngine.h"
168 +#include "base/TextException.h"
171 +#include "EventLoop.h"
172 #include <libecap/adapter/service.h>
173 #include <libecap/common/options.h>
174 #include <libecap/common/name.h>
175 #include <libecap/common/named_values.h>
176 -#include "adaptation/ecap/Config.h"
177 -#include "adaptation/ecap/Host.h"
178 -#include "adaptation/ecap/ServiceRep.h"
179 -#include "adaptation/ecap/XactionRep.h"
180 -#include "base/TextException.h"
186 -// configured eCAP service wrappers
187 -static std::list<Adaptation::Ecap::ServiceRep::AdapterService> TheServices;
188 +/// libecap::adapter::services indexed by their URI
189 +typedef std::map<std::string, Adaptation::Ecap::ServiceRep::AdapterService> AdapterServices;
190 +/// all loaded services
191 +static AdapterServices TheServices;
192 +/// configured services producing async transactions
193 +static AdapterServices AsyncServices;
198 const Master &master; ///< the configuration being wrapped
201 +/// manages async eCAP transactions
202 +class Engine: public AsyncEngine
205 + /* AsyncEngine API */
206 + virtual int checkEvents(int timeout);
209 + void kickAsyncServices(timeval &timeout);
213 } // namespace Adaptation
216 visitor.visit(Name(i->first), Area::FromTempString(i->second));
219 +/* Adaptation::Ecap::Engine */
222 +Adaptation::Ecap::Engine::checkEvents(int)
224 + // Start with the default I/O loop timeout, convert from milliseconds.
225 + static const struct timeval maxTimeout {
226 + EVENT_LOOP_TIMEOUT/1000, // seconds
227 + (EVENT_LOOP_TIMEOUT % 1000)*1000
229 + struct timeval timeout = maxTimeout;
231 + kickAsyncServices(timeout);
232 + if (timeout.tv_sec == maxTimeout.tv_sec && timeout.tv_usec == maxTimeout.tv_usec)
235 + debugs(93, 7, "timeout: " << timeout.tv_sec << "s+" << timeout.tv_usec << "us");
237 + // convert back to milliseconds, avoiding int overflows
238 + if (timeout.tv_sec >= std::numeric_limits<int>::max()/1000 - 1000)
239 + return std::numeric_limits<int>::max();
241 + return timeout.tv_sec*1000 + timeout.tv_usec/1000;
244 +/// resumes async transactions (if any) and returns true if they set a timeout
246 +Adaptation::Ecap::Engine::kickAsyncServices(timeval &timeout)
248 + if (AsyncServices.empty())
251 + debugs(93, 3, "async services: " << AsyncServices.size());
253 + // Activate waiting async transactions, if any.
254 + typedef AdapterServices::iterator ASI;
255 + for (ASI s = AsyncServices.begin(); s != AsyncServices.end(); ++s) {
257 + s->second->resume(); // may call Ecap::Xaction::resume()
260 + // Give services a chance to decrease the default timeout.
261 + for (ASI s = AsyncServices.begin(); s != AsyncServices.end(); ++s) {
262 + s->second->suspend(timeout);
266 +/* Adaptation::Ecap::ServiceRep */
268 Adaptation::Ecap::ServiceRep::ServiceRep(const ServiceConfigPointer &cfg):
269 /*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg),
273 debugs(93,DBG_IMPORTANT, "Starting eCAP service: " << theService->uri());
276 + if (theService->makesAsyncXactions()) {
277 + AsyncServices[theService->uri()] = theService;
278 + debugs(93, 5, "asyncs: " << AsyncServices.size());
282 /// handles failures while configuring or starting an eCAP service;
288 + // register now because (a) we need EventLoop::Running and (b) we do not
289 + // want to add more main loop overheads unless an async service is used.
290 + static AsyncEngine *TheEngine = NULL;
291 + if (AsyncServices.size() && !TheEngine && EventLoop::Running) {
292 + TheEngine = new Engine;
293 + EventLoop::Running->registerEngine(TheEngine);
294 + debugs(93, 3, "asyncs: " << AsyncServices.size() << ' ' << TheEngine);
297 XactionRep *rep = new XactionRep(virgin, cause, Pointer(this));
298 XactionRep::AdapterXaction x(theService->makeXaction(rep));
300 @@ -210,11 +294,10 @@
301 Adaptation::Ecap::ServiceRep::AdapterService
302 Adaptation::Ecap::FindAdapterService(const String& serviceUri)
304 - typedef std::list<ServiceRep::AdapterService>::const_iterator ASCI;
305 - for (ASCI s = TheServices.begin(); s != TheServices.end(); ++s) {
307 - if (serviceUri == (*s)->uri().c_str())
309 + AdapterServices::const_iterator pos = TheServices.find(serviceUri.termedBuf());
310 + if (pos != TheServices.end()) {
312 + return pos->second;
314 return ServiceRep::AdapterService();
316 @@ -222,30 +305,18 @@
318 Adaptation::Ecap::RegisterAdapterService(const Adaptation::Ecap::ServiceRep::AdapterService& adapterService)
320 - typedef std::list<ServiceRep::AdapterService>::iterator ASI;
321 - for (ASI s = TheServices.begin(); s != TheServices.end(); ++s) {
323 - if (adapterService->uri() == (*s)->uri()) {
324 - *s = adapterService;
325 - debugs(93, 3, "updated eCAP module service: " <<
326 - adapterService->uri());
330 - TheServices.push_back(adapterService);
331 - debugs(93, 3, "registered eCAP module service: " << adapterService->uri());
332 + TheServices[adapterService->uri()] = adapterService; // may update old one
333 + debugs(93, 3, "stored eCAP module service: " << adapterService->uri());
334 + // We do not update AsyncServices here in case they are not configured.
338 Adaptation::Ecap::UnregisterAdapterService(const String& serviceUri)
340 - typedef std::list<ServiceRep::AdapterService>::iterator ASI;
341 - for (ASI s = TheServices.begin(); s != TheServices.end(); ++s) {
342 - if (serviceUri == (*s)->uri().c_str()) {
343 - TheServices.erase(s);
344 - debugs(93, 3, "unregistered eCAP module service: " << serviceUri);
347 + if (TheServices.erase(serviceUri.termedBuf())) {
348 + debugs(93, 3, "unregistered eCAP module service: " << serviceUri);
349 + AsyncServices.erase(serviceUri.termedBuf()); // no-op for non-async
352 debugs(93, 3, "failed to unregister eCAP module service: " << serviceUri);
354 @@ -253,16 +324,16 @@
356 Adaptation::Ecap::CheckUnusedAdapterServices(const Adaptation::Services& cfgs)
358 - typedef std::list<ServiceRep::AdapterService>::const_iterator ASCI;
359 + typedef AdapterServices::const_iterator ASCI;
360 for (ASCI loaded = TheServices.begin(); loaded != TheServices.end();
363 for (Services::const_iterator cfged = cfgs.begin();
364 cfged != cfgs.end() && !found; ++cfged) {
365 - found = (*cfged)->cfg().uri == (*loaded)->uri().c_str();
366 + found = (*cfged)->cfg().uri == loaded->second->uri().c_str();
369 debugs(93, DBG_IMPORTANT, "Warning: loaded eCAP service has no matching " <<
370 - "ecap_service config option: " << (*loaded)->uri());
371 + "ecap_service config option: " << loaded->second->uri());
375 === modified file 'src/adaptation/ecap/XactionRep.cc'
376 --- src/adaptation/ecap/XactionRep.cc 2013-06-03 14:05:16 +0000
377 +++ src/adaptation/ecap/XactionRep.cc 2013-09-23 16:22:57 +0000
379 * DEBUG: section 93 eCAP Interface
382 +#include "adaptation/Answer.h"
383 +#include "adaptation/ecap/XactionRep.h"
384 +#include "adaptation/ecap/Config.h"
385 +#include "adaptation/Initiator.h"
386 +#include "base/AsyncJobCalls.h"
387 +#include "base/TextException.h"
388 +#include "HttpRequest.h"
389 +#include "HttpReply.h"
390 +#include "SquidTime.h"
391 #include <libecap/common/area.h>
392 #include <libecap/common/delay.h>
393 #include <libecap/common/named_values.h>
394 #include <libecap/common/names.h>
395 #include <libecap/adapter/xaction.h>
396 -#include "HttpRequest.h"
397 -#include "HttpReply.h"
398 -#include "SquidTime.h"
399 -#include "adaptation/Answer.h"
400 -#include "adaptation/ecap/XactionRep.h"
401 -#include "adaptation/ecap/Config.h"
402 -#include "adaptation/Initiator.h"
403 -#include "base/TextException.h"
405 CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep);
408 Adaptation::Initiate::swanSong();
412 +Adaptation::Ecap::XactionRep::resume()
414 + // go async to gain exception protection and done()-based job destruction
415 + typedef NullaryMemFunT<Adaptation::Ecap::XactionRep> Dialer;
416 + AsyncCall::Pointer call = asyncCall(93, 5, "Adaptation::Ecap::XactionRep::doResume",
417 + Dialer(this, &Adaptation::Ecap::XactionRep::doResume));
418 + ScheduleCallHere(call);
421 +/// the guts of libecap::host::Xaction::resume() API implementation
422 +/// which just goes async in Adaptation::Ecap::XactionRep::resume().
424 +Adaptation::Ecap::XactionRep::doResume()
427 + theMaster->resume();
431 Adaptation::Ecap::XactionRep::virgin()
434 mustStop("adaptationAborted");
438 -Adaptation::Ecap::XactionRep::callable() const
444 Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp)
447 === modified file 'src/adaptation/ecap/XactionRep.h'
448 --- src/adaptation/ecap/XactionRep.h 2012-10-04 11:10:17 +0000
449 +++ src/adaptation/ecap/XactionRep.h 2013-09-23 16:22:57 +0000
451 virtual void blockVirgin();
452 virtual void adaptationDelayed(const libecap::Delay &);
453 virtual void adaptationAborted();
454 + virtual void resume();
455 virtual void vbDiscard();
456 virtual void vbMake();
457 virtual void vbStopMaking();
459 virtual void noteAbContentDone(bool atEnd);
460 virtual void noteAbContentAvailable();
462 - // libecap::Callable API, via libecap::host::Xaction
463 - virtual bool callable() const;
466 virtual void noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp);
467 virtual void noteBodyConsumerAborted(RefCount<BodyPipe> bp);
469 /// Return the adaptation meta headers and their values
470 void visitEachMetaHeader(libecap::NamedValueVisitor &visitor) const;
475 AdapterXaction theMaster; // the actual adaptation xaction we represent
476 Adaptation::ServicePointer theService; ///< xaction's adaptation service
478 === modified file 'src/main.cc'
479 --- src/main.cc 2013-06-07 04:35:25 +0000
480 +++ src/main.cc 2013-09-16 00:12:40 +0000
481 @@ -225,17 +225,15 @@
485 - SignalEngine(EventLoop &evtLoop) : loop(evtLoop) {}
486 virtual int checkEvents(int timeout);
489 - static void StopEventLoop(void * data) {
490 - static_cast<SignalEngine *>(data)->loop.stop();
491 + static void StopEventLoop(void *) {
492 + if (EventLoop::Running)
493 + EventLoop::Running->stop();
496 void doShutdown(time_t wait);
502 @@ -1506,7 +1504,7 @@
506 - SignalEngine signalEngine(mainLoop);
507 + SignalEngine signalEngine;
509 mainLoop.registerEngine(&signalEngine);