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