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