]>
Commit | Line | Data |
---|---|---|
b510f3a1 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 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 | |
29 | typedef std::map<std::string, Adaptation::Ecap::ServiceRep::AdapterService> AdapterServices; | |
30 | /// all loaded services | |
31 | static AdapterServices TheServices; | |
32 | /// configured services producing async transactions | |
33 | static AdapterServices AsyncServices; | |
76fc7e57 | 34 | |
22fff3bf AR |
35 | namespace Adaptation |
36 | { | |
37 | namespace Ecap | |
38 | { | |
39 | ||
e1e90d26 | 40 | /// wraps Adaptation::Ecap::ServiceConfig to allow eCAP visitors |
22fff3bf | 41 | class ConfigRep: public libecap::Options |
e1e90d26 AR |
42 | { |
43 | public: | |
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 |
58 | class Engine: public AsyncEngine | |
59 | { | |
60 | public: | |
61 | /* AsyncEngine API */ | |
62 | virtual int checkEvents(int timeout); | |
63 | ||
64 | private: | |
65 | void kickAsyncServices(timeval &timeout); | |
66 | }; | |
67 | ||
22fff3bf AR |
68 | } // namespace Ecap |
69 | } // namespace Adaptation | |
70 | ||
22fff3bf AR |
71 | Adaptation::Ecap::ConfigRep::ConfigRep(const Master &aMaster): master(aMaster) |
72 | { | |
73 | } | |
74 | ||
75 | const libecap::Area | |
76 | Adaptation::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 | 93 | void |
22fff3bf | 94 | Adaptation::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 | ||
107 | int | |
108 | Adaptation::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 | |
131 | void | |
132 | Adaptation::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 | 154 | Adaptation::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 | 160 | Adaptation::Ecap::ServiceRep::~ServiceRep() |
fdc96a39 AR |
161 | { |
162 | } | |
163 | ||
574b508c | 164 | void Adaptation::Ecap::ServiceRep::noteFailure() |
26ac0430 AJ |
165 | { |
166 | assert(false); // XXX: should this be ICAP-specific? | |
fdc96a39 AR |
167 | } |
168 | ||
169 | void | |
574b508c | 170 | Adaptation::Ecap::ServiceRep::finalize() |
fdc96a39 | 171 | { |
26ac0430 | 172 | Adaptation::Service::finalize(); |
88df846b | 173 | if (!cfg().connectionEncryption.configured()) |
c50b35b5 | 174 | writeableCfg().connectionEncryption.defaultTo(true); |
76fc7e57 | 175 | theService = FindAdapterService(cfg().uri); |
e1d1bd27 | 176 | if (theService) { |
45d2da8b AR |
177 | try { |
178 | tryConfigureAndStart(); | |
179 | Must(up()); | |
aa420fc9 | 180 | } catch (const std::exception &e) { // standardized exceptions |
45d2da8b AR |
181 | if (!handleFinalizeFailure(e.what())) |
182 | throw; // rethrow for upper layers to handle | |
aa420fc9 | 183 | } catch (...) { // all other exceptions |
45d2da8b AR |
184 | if (!handleFinalizeFailure("unrecognized exception")) |
185 | throw; // rethrow for upper layers to handle | |
186 | } | |
187 | return; // success or handled exception | |
e1d1bd27 | 188 | } else { |
1159e19b | 189 | debugs(93,DBG_IMPORTANT, "WARNING: configured ecap_service was not loaded: " << cfg().uri); |
26ac0430 | 190 | } |
fdc96a39 AR |
191 | } |
192 | ||
45d2da8b AR |
193 | /// attempts to configure and start eCAP service; the caller handles exceptions |
194 | void | |
195 | Adaptation::Ecap::ServiceRep::tryConfigureAndStart() | |
196 | { | |
197 | debugs(93,2, HERE << "configuring eCAP service: " << theService->uri()); | |
198 | const ConfigRep cfgRep(dynamic_cast<const ServiceConfig&>(cfg())); | |
199 | theService->configure(cfgRep); | |
200 | ||
201 | debugs(93,DBG_IMPORTANT, "Starting eCAP service: " << theService->uri()); | |
202 | theService->start(); | |
0a720258 AR |
203 | |
204 | if (theService->makesAsyncXactions()) { | |
205 | AsyncServices[theService->uri()] = theService; | |
206 | debugs(93, 5, "asyncs: " << AsyncServices.size()); | |
207 | } | |
45d2da8b AR |
208 | } |
209 | ||
210 | /// handles failures while configuring or starting an eCAP service; | |
211 | /// returns false if the error must be propagated to higher levels | |
212 | bool | |
213 | Adaptation::Ecap::ServiceRep::handleFinalizeFailure(const char *error) | |
214 | { | |
215 | const bool salvage = cfg().bypass; | |
aa420fc9 | 216 | const int level = salvage ? DBG_IMPORTANT :DBG_CRITICAL; |
45d2da8b AR |
217 | const char *kind = salvage ? "optional" : "essential"; |
218 | debugs(93, level, "ERROR: failed to start " << kind << " eCAP service: " << | |
219 | cfg().uri << ":\n" << error); | |
220 | ||
221 | if (!salvage) | |
222 | return false; // we cannot handle the problem; the caller may escalate | |
223 | ||
224 | // make up() false, preventing new adaptation requests and enabling bypass | |
225 | theService.reset(); | |
226 | debugs(93, level, "WARNING: " << kind << " eCAP service is " << | |
227 | "down after initialization failure: " << cfg().uri); | |
228 | ||
aa420fc9 | 229 | return true; // tell the caller to ignore the problem because we handled it |
45d2da8b AR |
230 | } |
231 | ||
574b508c | 232 | bool Adaptation::Ecap::ServiceRep::probed() const |
fdc96a39 AR |
233 | { |
234 | return true; // we "probe" the adapter in finalize(). | |
235 | } | |
236 | ||
574b508c | 237 | bool Adaptation::Ecap::ServiceRep::up() const |
fdc96a39 | 238 | { |
eaba85cb | 239 | return bool(theService); |
fdc96a39 AR |
240 | } |
241 | ||
51b5dcf5 | 242 | bool Adaptation::Ecap::ServiceRep::wantsUrl(const SBuf &urlPath) const |
fdc96a39 AR |
243 | { |
244 | Must(up()); | |
51b5dcf5 AJ |
245 | SBuf nonConstUrlPath = urlPath; |
246 | // c_str() reallocates and terminates for libecap API | |
247 | return theService->wantsUrl(nonConstUrlPath.c_str()); | |
fdc96a39 AR |
248 | } |
249 | ||
250 | Adaptation::Initiate * | |
4299f876 | 251 | Adaptation::Ecap::ServiceRep::makeXactLauncher(HttpMsg *virgin, |
af0ded40 | 252 | HttpRequest *cause, AccessLogEntry::Pointer &alp) |
fdc96a39 | 253 | { |
26ac0430 | 254 | Must(up()); |
0a720258 AR |
255 | |
256 | // register now because (a) we need EventLoop::Running and (b) we do not | |
257 | // want to add more main loop overheads unless an async service is used. | |
258 | static AsyncEngine *TheEngine = NULL; | |
259 | if (AsyncServices.size() && !TheEngine && EventLoop::Running) { | |
260 | TheEngine = new Engine; | |
261 | EventLoop::Running->registerEngine(TheEngine); | |
262 | debugs(93, 3, "asyncs: " << AsyncServices.size() << ' ' << TheEngine); | |
263 | } | |
264 | ||
af0ded40 | 265 | XactionRep *rep = new XactionRep(virgin, cause, alp, Pointer(this)); |
26ac0430 | 266 | XactionRep::AdapterXaction x(theService->makeXaction(rep)); |
fdc96a39 AR |
267 | rep->master(x); |
268 | return rep; | |
269 | } | |
270 | ||
271 | // returns a temporary string depicting service status, for debugging | |
574b508c | 272 | const char *Adaptation::Ecap::ServiceRep::status() const |
fdc96a39 | 273 | { |
76fc7e57 AJ |
274 | // TODO: move generic stuff from eCAP and ICAP to Adaptation |
275 | static MemBuf buf; | |
276 | ||
277 | buf.reset(); | |
278 | buf.append("[", 1); | |
279 | ||
280 | if (up()) | |
281 | buf.append("up", 2); | |
282 | else | |
283 | buf.append("down", 4); | |
284 | ||
285 | if (detached()) | |
286 | buf.append(",detached", 9); | |
287 | ||
288 | buf.append("]", 1); | |
289 | buf.terminate(); | |
290 | ||
291 | return buf.content(); | |
292 | } | |
293 | ||
294 | void Adaptation::Ecap::ServiceRep::detach() | |
295 | { | |
296 | isDetached = true; | |
297 | } | |
d090e020 | 298 | |
76fc7e57 AJ |
299 | bool Adaptation::Ecap::ServiceRep::detached() const |
300 | { | |
301 | return isDetached; | |
302 | } | |
303 | ||
304 | Adaptation::Ecap::ServiceRep::AdapterService | |
305 | Adaptation::Ecap::FindAdapterService(const String& serviceUri) | |
306 | { | |
0a720258 AR |
307 | AdapterServices::const_iterator pos = TheServices.find(serviceUri.termedBuf()); |
308 | if (pos != TheServices.end()) { | |
309 | Must(pos->second); | |
310 | return pos->second; | |
76fc7e57 AJ |
311 | } |
312 | return ServiceRep::AdapterService(); | |
313 | } | |
314 | ||
315 | void | |
316 | Adaptation::Ecap::RegisterAdapterService(const Adaptation::Ecap::ServiceRep::AdapterService& adapterService) | |
317 | { | |
0a720258 AR |
318 | TheServices[adapterService->uri()] = adapterService; // may update old one |
319 | debugs(93, 3, "stored eCAP module service: " << adapterService->uri()); | |
320 | // We do not update AsyncServices here in case they are not configured. | |
76fc7e57 AJ |
321 | } |
322 | ||
323 | void | |
324 | Adaptation::Ecap::UnregisterAdapterService(const String& serviceUri) | |
325 | { | |
0a720258 AR |
326 | if (TheServices.erase(serviceUri.termedBuf())) { |
327 | debugs(93, 3, "unregistered eCAP module service: " << serviceUri); | |
328 | AsyncServices.erase(serviceUri.termedBuf()); // no-op for non-async | |
329 | return; | |
76fc7e57 AJ |
330 | } |
331 | debugs(93, 3, "failed to unregister eCAP module service: " << serviceUri); | |
332 | } | |
333 | ||
334 | void | |
335 | Adaptation::Ecap::CheckUnusedAdapterServices(const Adaptation::Services& cfgs) | |
336 | { | |
0a720258 | 337 | typedef AdapterServices::const_iterator ASCI; |
76fc7e57 | 338 | for (ASCI loaded = TheServices.begin(); loaded != TheServices.end(); |
d090e020 | 339 | ++loaded) { |
76fc7e57 AJ |
340 | bool found = false; |
341 | for (Services::const_iterator cfged = cfgs.begin(); | |
d090e020 | 342 | cfged != cfgs.end() && !found; ++cfged) { |
0a720258 | 343 | found = (*cfged)->cfg().uri == loaded->second->uri().c_str(); |
76fc7e57 AJ |
344 | } |
345 | if (!found) | |
e0236918 | 346 | debugs(93, DBG_IMPORTANT, "Warning: loaded eCAP service has no matching " << |
0a720258 | 347 | "ecap_service config option: " << loaded->second->uri()); |
76fc7e57 | 348 | } |
fdc96a39 | 349 | } |
f53969cc | 350 |