]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/ecap/ServiceRep.cc
Fix eCAP header includes (#1753)
[thirdparty/squid.git] / src / adaptation / ecap / ServiceRep.cc
CommitLineData
b510f3a1 1/*
b8ae064d 2 * Copyright (C) 1996-2023 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"
675b8408 18#include "debug/Stream.h"
86c63190 19#include "EventLoop.h"
fdc96a39 20
8800478b 21#if HAVE_LIBECAP_ADAPTER_SERVICE_H
0a720258 22#include <libecap/adapter/service.h>
8800478b
AJ
23#endif
24#if HAVE_LIBECAP_COMMON_OPTIONS_H
0a720258 25#include <libecap/common/options.h>
8800478b
AJ
26#endif
27#if HAVE_LIBECAP_COMMON_NAME_H
0a720258 28#include <libecap/common/name.h>
8800478b
AJ
29#endif
30#if HAVE_LIBECAP_COMMON_NAMED_VALUES_H
0a720258 31#include <libecap/common/named_values.h>
8800478b
AJ
32#endif
33
0a720258 34#include <limits>
0a720258
AR
35#include <map>
36
37/// libecap::adapter::services indexed by their URI
38typedef std::map<std::string, Adaptation::Ecap::ServiceRep::AdapterService> AdapterServices;
39/// all loaded services
40static AdapterServices TheServices;
41/// configured services producing async transactions
42static AdapterServices AsyncServices;
76fc7e57 43
22fff3bf
AR
44namespace Adaptation
45{
46namespace Ecap
47{
48
e1e90d26 49/// wraps Adaptation::Ecap::ServiceConfig to allow eCAP visitors
22fff3bf 50class ConfigRep: public libecap::Options
e1e90d26
AR
51{
52public:
53 typedef Adaptation::Ecap::ServiceConfig Master;
54 typedef libecap::Name Name;
55 typedef libecap::Area Area;
56
22fff3bf 57 ConfigRep(const Master &aMaster);
e1e90d26 58
22fff3bf 59 // libecap::Options API
337b9aa4
AR
60 const libecap::Area option(const libecap::Name &name) const override;
61 void visitEachOption(libecap::NamedValueVisitor &visitor) const override;
e1e90d26
AR
62
63 const Master &master; ///< the configuration being wrapped
64};
65
0a720258
AR
66/// manages async eCAP transactions
67class Engine: public AsyncEngine
68{
69public:
70 /* AsyncEngine API */
337b9aa4 71 int checkEvents(int timeout) override;
0a720258
AR
72
73private:
74 void kickAsyncServices(timeval &timeout);
75};
76
22fff3bf
AR
77} // namespace Ecap
78} // namespace Adaptation
79
22fff3bf
AR
80Adaptation::Ecap::ConfigRep::ConfigRep(const Master &aMaster): master(aMaster)
81{
82}
83
84const libecap::Area
85Adaptation::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
e1e90d26 102void
22fff3bf 103Adaptation::Ecap::ConfigRep::visitEachOption(libecap::NamedValueVisitor &visitor) const
e1e90d26
AR
104{
105 // we may supply the params we know about too, but only if we set host ID
22fff3bf 106 visitor.visit(metaBypassable, Area(master.bypass ? "1" : "0", 1));
e1e90d26
AR
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
0a720258
AR
114/* Adaptation::Ecap::Engine */
115
116int
117Adaptation::Ecap::Engine::checkEvents(int)
118{
119 // Start with the default I/O loop timeout, convert from milliseconds.
fd3f5eac 120 static const struct timeval maxTimeout = {
0a720258 121 EVENT_LOOP_TIMEOUT/1000, // seconds
86c63190 122 (EVENT_LOOP_TIMEOUT % 1000)*1000
0a720258
AR
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)
134 return std::numeric_limits<int>::max();
135 else
136 return timeout.tv_sec*1000 + timeout.tv_usec/1000;
137}
138
139/// resumes async transactions (if any) and returns true if they set a timeout
140void
141Adaptation::Ecap::Engine::kickAsyncServices(timeval &timeout)
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
6666da11 163Adaptation::Ecap::ServiceRep::ServiceRep(const ServiceConfigPointer &cfg):
b6388dfd 164 /*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg),
f53969cc 165 isDetached(false)
fdc96a39
AR
166{
167}
168
574b508c 169Adaptation::Ecap::ServiceRep::~ServiceRep()
fdc96a39
AR
170{
171}
172
574b508c 173void Adaptation::Ecap::ServiceRep::noteFailure()
26ac0430
AJ
174{
175 assert(false); // XXX: should this be ICAP-specific?
fdc96a39
AR
176}
177
178void
574b508c 179Adaptation::Ecap::ServiceRep::finalize()
fdc96a39 180{
26ac0430 181 Adaptation::Service::finalize();
88df846b 182 if (!cfg().connectionEncryption.configured())
c50b35b5 183 writeableCfg().connectionEncryption.defaultTo(true);
76fc7e57 184 theService = FindAdapterService(cfg().uri);
e1d1bd27 185 if (theService) {
45d2da8b
AR
186 try {
187 tryConfigureAndStart();
188 Must(up());
aa420fc9 189 } catch (const std::exception &e) { // standardized exceptions
45d2da8b
AR
190 if (!handleFinalizeFailure(e.what()))
191 throw; // rethrow for upper layers to handle
aa420fc9 192 } catch (...) { // all other exceptions
45d2da8b
AR
193 if (!handleFinalizeFailure("unrecognized exception"))
194 throw; // rethrow for upper layers to handle
195 }
196 return; // success or handled exception
e1d1bd27 197 } else {
1159e19b 198 debugs(93,DBG_IMPORTANT, "WARNING: configured ecap_service was not loaded: " << cfg().uri);
26ac0430 199 }
fdc96a39
AR
200}
201
45d2da8b
AR
202/// attempts to configure and start eCAP service; the caller handles exceptions
203void
204Adaptation::Ecap::ServiceRep::tryConfigureAndStart()
205{
bf95c10a 206 debugs(93,2, "configuring eCAP service: " << theService->uri());
45d2da8b
AR
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();
0a720258
AR
212
213 if (theService->makesAsyncXactions()) {
214 AsyncServices[theService->uri()] = theService;
215 debugs(93, 5, "asyncs: " << AsyncServices.size());
216 }
45d2da8b
AR
217}
218
219/// handles failures while configuring or starting an eCAP service;
220/// returns false if the error must be propagated to higher levels
221bool
222Adaptation::Ecap::ServiceRep::handleFinalizeFailure(const char *error)
223{
224 const bool salvage = cfg().bypass;
aa420fc9 225 const int level = salvage ? DBG_IMPORTANT :DBG_CRITICAL;
45d2da8b
AR
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
aa420fc9 238 return true; // tell the caller to ignore the problem because we handled it
45d2da8b
AR
239}
240
574b508c 241bool Adaptation::Ecap::ServiceRep::probed() const
fdc96a39
AR
242{
243 return true; // we "probe" the adapter in finalize().
244}
245
574b508c 246bool Adaptation::Ecap::ServiceRep::up() const
fdc96a39 247{
eaba85cb 248 return bool(theService);
fdc96a39
AR
249}
250
51b5dcf5 251bool Adaptation::Ecap::ServiceRep::wantsUrl(const SBuf &urlPath) const
fdc96a39
AR
252{
253 Must(up());
51b5dcf5
AJ
254 SBuf nonConstUrlPath = urlPath;
255 // c_str() reallocates and terminates for libecap API
256 return theService->wantsUrl(nonConstUrlPath.c_str());
fdc96a39
AR
257}
258
259Adaptation::Initiate *
63df1d28 260Adaptation::Ecap::ServiceRep::makeXactLauncher(Http::Message *virgin,
af0ded40 261 HttpRequest *cause, AccessLogEntry::Pointer &alp)
fdc96a39 262{
26ac0430 263 Must(up());
0a720258
AR
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.
aee3523a 267 static AsyncEngine *TheEngine = nullptr;
0a720258
AR
268 if (AsyncServices.size() && !TheEngine && EventLoop::Running) {
269 TheEngine = new Engine;
270 EventLoop::Running->registerEngine(TheEngine);
271 debugs(93, 3, "asyncs: " << AsyncServices.size() << ' ' << TheEngine);
272 }
273
af0ded40 274 XactionRep *rep = new XactionRep(virgin, cause, alp, Pointer(this));
26ac0430 275 XactionRep::AdapterXaction x(theService->makeXaction(rep));
fdc96a39
AR
276 rep->master(x);
277 return rep;
278}
279
280// returns a temporary string depicting service status, for debugging
574b508c 281const char *Adaptation::Ecap::ServiceRep::status() const
fdc96a39 282{
76fc7e57
AJ
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
303void Adaptation::Ecap::ServiceRep::detach()
304{
305 isDetached = true;
306}
d090e020 307
76fc7e57
AJ
308bool Adaptation::Ecap::ServiceRep::detached() const
309{
310 return isDetached;
311}
312
313Adaptation::Ecap::ServiceRep::AdapterService
314Adaptation::Ecap::FindAdapterService(const String& serviceUri)
315{
0a720258
AR
316 AdapterServices::const_iterator pos = TheServices.find(serviceUri.termedBuf());
317 if (pos != TheServices.end()) {
318 Must(pos->second);
319 return pos->second;
76fc7e57
AJ
320 }
321 return ServiceRep::AdapterService();
322}
323
324void
325Adaptation::Ecap::RegisterAdapterService(const Adaptation::Ecap::ServiceRep::AdapterService& adapterService)
326{
0a720258
AR
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.
76fc7e57
AJ
330}
331
332void
333Adaptation::Ecap::UnregisterAdapterService(const String& serviceUri)
334{
0a720258
AR
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;
76fc7e57
AJ
339 }
340 debugs(93, 3, "failed to unregister eCAP module service: " << serviceUri);
341}
342
343void
344Adaptation::Ecap::CheckUnusedAdapterServices(const Adaptation::Services& cfgs)
345{
0a720258 346 typedef AdapterServices::const_iterator ASCI;
76fc7e57 347 for (ASCI loaded = TheServices.begin(); loaded != TheServices.end();
d090e020 348 ++loaded) {
76fc7e57
AJ
349 bool found = false;
350 for (Services::const_iterator cfged = cfgs.begin();
d090e020 351 cfged != cfgs.end() && !found; ++cfged) {
0a720258 352 found = (*cfged)->cfg().uri == loaded->second->uri().c_str();
76fc7e57
AJ
353 }
354 if (!found)
d816f28d 355 debugs(93, DBG_IMPORTANT, "WARNING: loaded eCAP service has no matching " <<
0a720258 356 "ecap_service config option: " << loaded->second->uri());
76fc7e57 357 }
fdc96a39 358}
f53969cc 359