]>
Commit | Line | Data |
---|---|---|
b510f3a1 | 1 | /* |
bbc27441 AJ |
2 | * Copyright (C) 1996-2014 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. | |
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): |
76fc7e57 AJ |
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(); |
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 |
192 | void | |
193 | Adaptation::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 | |
210 | bool | |
211 | Adaptation::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 | 230 | bool Adaptation::Ecap::ServiceRep::probed() const |
fdc96a39 AR |
231 | { |
232 | return true; // we "probe" the adapter in finalize(). | |
233 | } | |
234 | ||
574b508c | 235 | bool Adaptation::Ecap::ServiceRep::up() const |
fdc96a39 AR |
236 | { |
237 | return theService != NULL; | |
238 | } | |
239 | ||
574b508c | 240 | bool 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 | ||
246 | Adaptation::Initiate * | |
4299f876 | 247 | Adaptation::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 | 268 | const 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 | ||
290 | void Adaptation::Ecap::ServiceRep::detach() | |
291 | { | |
292 | isDetached = true; | |
293 | } | |
d090e020 | 294 | |
76fc7e57 AJ |
295 | bool Adaptation::Ecap::ServiceRep::detached() const |
296 | { | |
297 | return isDetached; | |
298 | } | |
299 | ||
300 | Adaptation::Ecap::ServiceRep::AdapterService | |
301 | Adaptation::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 | ||
311 | void | |
312 | Adaptation::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 | ||
319 | void | |
320 | Adaptation::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 | ||
330 | void | |
331 | Adaptation::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 | } |