ServiceRep.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 93 eCAP Interface */
10 
11 #include "squid.h"
12 #include "adaptation/ecap/Config.h"
13 #include "adaptation/ecap/Host.h"
16 #include "AsyncEngine.h"
17 #include "base/TextException.h"
18 #include "debug/Stream.h"
19 #include "EventLoop.h"
20 
21 #if HAVE_LIBECAP_ADAPTER_SERVICE_H
22 #include <libecap/adapter/service.h>
23 #endif
24 #if HAVE_LIBECAP_COMMON_OPTIONS_H
25 #include <libecap/common/options.h>
26 #endif
27 #if HAVE_LIBECAP_COMMON_NAME_H
28 #include <libecap/common/name.h>
29 #endif
30 #if HAVE_LIBECAP_COMMON_NAMED_VALUES_H
31 #include <libecap/common/named_values.h>
32 #endif
33 
34 #include <limits>
35 #include <map>
36 
38 typedef std::map<std::string, Adaptation::Ecap::ServiceRep::AdapterService> AdapterServices;
43 
44 namespace Adaptation
45 {
46 namespace Ecap
47 {
48 
51 {
52 public:
54  typedef libecap::Name Name;
55  typedef libecap::Area Area;
56 
57  ConfigRep(const Master &aMaster);
58 
59  // libecap::Options API
60  const libecap::Area option(const libecap::Name &name) const override;
61  void visitEachOption(libecap::NamedValueVisitor &visitor) const override;
62 
63  const Master &master;
64 };
65 
67 class Engine: public AsyncEngine
68 {
69 public:
70  /* AsyncEngine API */
71  int checkEvents(int timeout) override;
72 
73 private:
74  void kickAsyncServices(timeval &timeout);
75 };
76 
77 } // namespace Ecap
78 } // namespace Adaptation
79 
80 Adaptation::Ecap::ConfigRep::ConfigRep(const Master &aMaster): master(aMaster)
81 {
82 }
83 
84 const libecap::Area
85 Adaptation::Ecap::ConfigRep::option(const libecap::Name &name) const
86 {
87  // we may supply the params we know about, but only when names have host ID
88  if (name == metaBypassable)
89  return Area(master.bypass ? "1" : "0", 1);
90 
91  // TODO: We could build a by-name index, but is it worth it? Good adapters
92  // should use visitEachOption() instead, to check for name typos/errors.
93  typedef Master::Extensions::const_iterator MECI;
94  for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i) {
95  if (name == i->first)
96  return Area(i->second.data(), i->second.size());
97  }
98 
99  return Area();
100 }
101 
102 void
103 Adaptation::Ecap::ConfigRep::visitEachOption(libecap::NamedValueVisitor &visitor) const
104 {
105  // we may supply the params we know about too, but only if we set host ID
106  visitor.visit(metaBypassable, Area(master.bypass ? "1" : "0", 1));
107 
108  // visit adapter-specific options (i.e., those not recognized by Squid)
109  typedef Master::Extensions::const_iterator MECI;
110  for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i)
111  visitor.visit(Name(i->first), Area::FromTempString(i->second));
112 }
113 
114 /* Adaptation::Ecap::Engine */
115 
116 int
118 {
119  // Start with the default I/O loop timeout, convert from milliseconds.
120  static const struct timeval maxTimeout = {
121  EVENT_LOOP_TIMEOUT/1000, // seconds
122  (EVENT_LOOP_TIMEOUT % 1000)*1000
123  }; // microseconds
124  struct timeval timeout = maxTimeout;
125 
126  kickAsyncServices(timeout);
127  if (timeout.tv_sec == maxTimeout.tv_sec && timeout.tv_usec == maxTimeout.tv_usec)
128  return EVENT_IDLE;
129 
130  debugs(93, 7, "timeout: " << timeout.tv_sec << "s+" << timeout.tv_usec << "us");
131 
132  // convert back to milliseconds, avoiding int overflows
133  if (timeout.tv_sec >= std::numeric_limits<int>::max()/1000 - 1000)
135  else
136  return timeout.tv_sec*1000 + timeout.tv_usec/1000;
137 }
138 
140 void
142 {
143  if (AsyncServices.empty())
144  return;
145 
146  debugs(93, 3, "async services: " << AsyncServices.size());
147 
148  // Activate waiting async transactions, if any.
149  typedef AdapterServices::iterator ASI;
150  for (ASI s = AsyncServices.begin(); s != AsyncServices.end(); ++s) {
151  assert(s->second);
152  s->second->resume(); // may call Ecap::Xaction::resume()
153  }
154 
155  // Give services a chance to decrease the default timeout.
156  for (ASI s = AsyncServices.begin(); s != AsyncServices.end(); ++s) {
157  s->second->suspend(timeout);
158  }
159 }
160 
161 /* Adaptation::Ecap::ServiceRep */
162 
164  /*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg),
165  isDetached(false)
166 {
167 }
168 
170 {
171 }
172 
174 {
175  assert(false); // XXX: should this be ICAP-specific?
176 }
177 
178 void
180 {
182  if (!cfg().connectionEncryption.configured())
183  writeableCfg().connectionEncryption.defaultTo(true);
184  theService = FindAdapterService(cfg().uri);
185  if (theService) {
186  try {
187  tryConfigureAndStart();
188  Must(up());
189  } catch (const std::exception &e) { // standardized exceptions
190  if (!handleFinalizeFailure(e.what()))
191  throw; // rethrow for upper layers to handle
192  } catch (...) { // all other exceptions
193  if (!handleFinalizeFailure("unrecognized exception"))
194  throw; // rethrow for upper layers to handle
195  }
196  return; // success or handled exception
197  } else {
198  debugs(93,DBG_IMPORTANT, "WARNING: configured ecap_service was not loaded: " << cfg().uri);
199  }
200 }
201 
203 void
205 {
206  debugs(93,2, "configuring eCAP service: " << theService->uri());
207  const ConfigRep cfgRep(dynamic_cast<const ServiceConfig&>(cfg()));
208  theService->configure(cfgRep);
209 
210  debugs(93,DBG_IMPORTANT, "Starting eCAP service: " << theService->uri());
211  theService->start();
212 
213  if (theService->makesAsyncXactions()) {
214  AsyncServices[theService->uri()] = theService;
215  debugs(93, 5, "asyncs: " << AsyncServices.size());
216  }
217 }
218 
221 bool
223 {
224  const bool salvage = cfg().bypass;
225  const int level = salvage ? DBG_IMPORTANT :DBG_CRITICAL;
226  const char *kind = salvage ? "optional" : "essential";
227  debugs(93, level, "ERROR: failed to start " << kind << " eCAP service: " <<
228  cfg().uri << ":\n" << error);
229 
230  if (!salvage)
231  return false; // we cannot handle the problem; the caller may escalate
232 
233  // make up() false, preventing new adaptation requests and enabling bypass
234  theService.reset();
235  debugs(93, level, "WARNING: " << kind << " eCAP service is " <<
236  "down after initialization failure: " << cfg().uri);
237 
238  return true; // tell the caller to ignore the problem because we handled it
239 }
240 
242 {
243  return true; // we "probe" the adapter in finalize().
244 }
245 
247 {
248  return bool(theService);
249 }
250 
252 {
253  Must(up());
254  SBuf nonConstUrlPath = urlPath;
255  // c_str() reallocates and terminates for libecap API
256  return theService->wantsUrl(nonConstUrlPath.c_str());
257 }
258 
262 {
263  Must(up());
264 
265  // register now because (a) we need EventLoop::Running and (b) we do not
266  // want to add more main loop overheads unless an async service is used.
267  static AsyncEngine *TheEngine = nullptr;
268  if (AsyncServices.size() && !TheEngine && EventLoop::Running) {
269  TheEngine = new Engine;
271  debugs(93, 3, "asyncs: " << AsyncServices.size() << ' ' << TheEngine);
272  }
273 
274  XactionRep *rep = new XactionRep(virgin, cause, alp, Pointer(this));
275  XactionRep::AdapterXaction x(theService->makeXaction(rep));
276  rep->master(x);
277  return rep;
278 }
279 
280 // returns a temporary string depicting service status, for debugging
282 {
283  // TODO: move generic stuff from eCAP and ICAP to Adaptation
284  static MemBuf buf;
285 
286  buf.reset();
287  buf.append("[", 1);
288 
289  if (up())
290  buf.append("up", 2);
291  else
292  buf.append("down", 4);
293 
294  if (detached())
295  buf.append(",detached", 9);
296 
297  buf.append("]", 1);
298  buf.terminate();
299 
300  return buf.content();
301 }
302 
304 {
305  isDetached = true;
306 }
307 
309 {
310  return isDetached;
311 }
312 
315 {
316  AdapterServices::const_iterator pos = TheServices.find(serviceUri.termedBuf());
317  if (pos != TheServices.end()) {
318  Must(pos->second);
319  return pos->second;
320  }
322 }
323 
324 void
326 {
327  TheServices[adapterService->uri()] = adapterService; // may update old one
328  debugs(93, 3, "stored eCAP module service: " << adapterService->uri());
329  // We do not update AsyncServices here in case they are not configured.
330 }
331 
332 void
334 {
335  if (TheServices.erase(serviceUri.termedBuf())) {
336  debugs(93, 3, "unregistered eCAP module service: " << serviceUri);
337  AsyncServices.erase(serviceUri.termedBuf()); // no-op for non-async
338  return;
339  }
340  debugs(93, 3, "failed to unregister eCAP module service: " << serviceUri);
341 }
342 
343 void
345 {
346  typedef AdapterServices::const_iterator ASCI;
347  for (ASCI loaded = TheServices.begin(); loaded != TheServices.end();
348  ++loaded) {
349  bool found = false;
350  for (Services::const_iterator cfged = cfgs.begin();
351  cfged != cfgs.end() && !found; ++cfged) {
352  found = (*cfged)->cfg().uri == loaded->second->uri().c_str();
353  }
354  if (!found)
355  debugs(93, DBG_IMPORTANT, "WARNING: loaded eCAP service has no matching " <<
356  "ecap_service config option: " << loaded->second->uri());
357  }
358 }
359 
void visitEachOption(libecap::NamedValueVisitor &visitor) const override
Definition: ServiceRep.cc:103
static AdapterServices AsyncServices
configured services producing async transactions
Definition: ServiceRep.cc:42
void terminate()
Definition: MemBuf.cc:241
#define DBG_CRITICAL
Definition: Stream.h:37
common parts of HttpRequest and HttpReply
Definition: Message.h:25
ServiceRep(const ServiceConfigPointer &aConfig)
Definition: ServiceRep.cc:163
const libecap::Name metaBypassable
an ecap_service parameter
std::vector< const Option * > Options
Definition: Options.h:217
static EventLoop * Running
Definition: EventLoop.h:73
const libecap::Area option(const libecap::Name &name) const override
Definition: ServiceRep.cc:85
bool wantsUrl(const SBuf &urlPath) const override
Definition: ServiceRep.cc:251
bool probed() const override
Definition: ServiceRep.cc:241
virtual void finalize()
Definition: Service.cc:26
void error(char *format,...)
void UnregisterAdapterService(const String &serviceUri)
unregister loaded eCAP module service by service uri
Definition: ServiceRep.cc:333
Definition: SBuf.h:93
ServiceRep::AdapterService FindAdapterService(const String &serviceUri)
returns loaded eCAP module service by service uri
Definition: ServiceRep.cc:314
void tryConfigureAndStart()
attempts to configure and start eCAP service; the caller handles exceptions
Definition: ServiceRep.cc:204
const A & max(A const &lhs, A const &rhs)
std::map< std::string, Adaptation::Ecap::ServiceRep::AdapterService > AdapterServices
libecap::adapter::services indexed by their URI
Definition: ServiceRep.cc:38
bool handleFinalizeFailure(const char *error)
Definition: ServiceRep.cc:222
void noteFailure() override
Definition: ServiceRep.cc:173
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
Definition: MemBuf.h:23
libecap::shared_ptr< libecap::adapter::Xaction > AdapterXaction
Definition: XactionRep.h:44
void RegisterAdapterService(const ServiceRep::AdapterService &adapterService)
register loaded eCAP module service
Definition: ServiceRep.cc:325
eCAP service configuration
Definition: Config.h:25
virtual const char * status() const
Definition: ServiceRep.cc:281
bool detached() const override
whether detached() was called
Definition: ServiceRep.cc:308
#define assert(EX)
Definition: assert.h:17
int checkEvents(int timeout) override
Definition: ServiceRep.cc:117
const char * c_str()
Definition: SBuf.cc:516
void master(const AdapterXaction &aMaster)
Definition: XactionRep.cc:80
void CheckUnusedAdapterServices(const Services &services)
check for loaded eCAP services without matching ecap_service in squid.conf
Definition: ServiceRep.cc:344
#define EVENT_LOOP_TIMEOUT
Definition: EventLoop.h:16
Adaptation::Initiate * makeXactLauncher(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp) override
Definition: ServiceRep.cc:260
const char * termedBuf() const
Definition: SquidString.h:92
libecap::shared_ptr< libecap::adapter::Service > AdapterService
Definition: ServiceRep.h:39
char * content()
start of the added data
Definition: MemBuf.h:41
wraps Adaptation::Ecap::ServiceConfig to allow eCAP visitors
Definition: ServiceRep.cc:50
Adaptation::Ecap::ServiceConfig Master
Definition: ServiceRep.cc:53
#define Must(condition)
Definition: TextException.h:75
std::vector< Adaptation::ServicePointer > Services
Definition: Service.h:70
manages async eCAP transactions
Definition: ServiceRep.cc:67
#define DBG_IMPORTANT
Definition: Stream.h:38
void reset()
Definition: MemBuf.cc:129
bool up() const override
Definition: ServiceRep.cc:246
void registerEngine(AsyncEngine *engine)
Definition: EventLoop.cc:70
ConfigRep(const Master &aMaster)
Definition: ServiceRep.cc:80
void kickAsyncServices(timeval &timeout)
resumes async transactions (if any) and returns true if they set a timeout
Definition: ServiceRep.cc:141
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const Master & master
the configuration being wrapped
Definition: ServiceRep.cc:63
static AdapterServices TheServices
all loaded services
Definition: ServiceRep.cc:40

 

Introduction

Documentation

Support

Miscellaneous