]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/ecap/ServiceRep.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / adaptation / ecap / ServiceRep.cc
1 /*
2 * DEBUG: section 93 eCAP Interface
3 */
4 #include "squid.h"
5 #include <list>
6 #include <libecap/adapter/service.h>
7 #include <libecap/common/options.h>
8 #include <libecap/common/name.h>
9 #include <libecap/common/named_values.h>
10 #include "adaptation/ecap/Config.h"
11 #include "adaptation/ecap/Host.h"
12 #include "adaptation/ecap/ServiceRep.h"
13 #include "adaptation/ecap/XactionRep.h"
14 #include "base/TextException.h"
15
16 // configured eCAP service wrappers
17 static std::list<Adaptation::Ecap::ServiceRep::AdapterService> TheServices;
18
19 namespace Adaptation
20 {
21 namespace Ecap
22 {
23
24 /// wraps Adaptation::Ecap::ServiceConfig to allow eCAP visitors
25 class ConfigRep: public libecap::Options
26 {
27 public:
28 typedef Adaptation::Ecap::ServiceConfig Master;
29 typedef libecap::Name Name;
30 typedef libecap::Area Area;
31
32 ConfigRep(const Master &aMaster);
33
34 // libecap::Options API
35 virtual const libecap::Area option(const libecap::Name &name) const;
36 virtual void visitEachOption(libecap::NamedValueVisitor &visitor) const;
37
38 const Master &master; ///< the configuration being wrapped
39 };
40
41 } // namespace Ecap
42 } // namespace Adaptation
43
44
45 Adaptation::Ecap::ConfigRep::ConfigRep(const Master &aMaster): master(aMaster)
46 {
47 }
48
49 const libecap::Area
50 Adaptation::Ecap::ConfigRep::option(const libecap::Name &name) const
51 {
52 // we may supply the params we know about, but only when names have host ID
53 if (name == metaBypassable)
54 return Area(master.bypass ? "1" : "0", 1);
55
56 // TODO: We could build a by-name index, but is it worth it? Good adapters
57 // should use visitEachOption() instead, to check for name typos/errors.
58 typedef Master::Extensions::const_iterator MECI;
59 for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i) {
60 if (name == i->first)
61 return Area(i->second.data(), i->second.size());
62 }
63
64 return Area();
65 }
66
67 void
68 Adaptation::Ecap::ConfigRep::visitEachOption(libecap::NamedValueVisitor &visitor) const
69 {
70 // we may supply the params we know about too, but only if we set host ID
71 visitor.visit(metaBypassable, Area(master.bypass ? "1" : "0", 1));
72
73 // visit adapter-specific options (i.e., those not recognized by Squid)
74 typedef Master::Extensions::const_iterator MECI;
75 for (MECI i = master.extensions.begin(); i != master.extensions.end(); ++i)
76 visitor.visit(Name(i->first), Area::FromTempString(i->second));
77 }
78
79
80
81 Adaptation::Ecap::ServiceRep::ServiceRep(const ServiceConfigPointer &cfg):
82 /*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg),
83 isDetached(false)
84 {
85 }
86
87 Adaptation::Ecap::ServiceRep::~ServiceRep()
88 {
89 }
90
91 void Adaptation::Ecap::ServiceRep::noteFailure()
92 {
93 assert(false); // XXX: should this be ICAP-specific?
94 }
95
96 void
97 Adaptation::Ecap::ServiceRep::finalize()
98 {
99 Adaptation::Service::finalize();
100 theService = FindAdapterService(cfg().uri);
101 if (theService) {
102 try {
103 tryConfigureAndStart();
104 Must(up());
105 } catch (const std::exception &e) { // standardized exceptions
106 if (!handleFinalizeFailure(e.what()))
107 throw; // rethrow for upper layers to handle
108 } catch (...) { // all other exceptions
109 if (!handleFinalizeFailure("unrecognized exception"))
110 throw; // rethrow for upper layers to handle
111 }
112 return; // success or handled exception
113 } else {
114 debugs(93,DBG_IMPORTANT, "WARNING: configured ecap_service was not loaded: " << cfg().uri);
115 }
116 }
117
118 /// attempts to configure and start eCAP service; the caller handles exceptions
119 void
120 Adaptation::Ecap::ServiceRep::tryConfigureAndStart()
121 {
122 debugs(93,2, HERE << "configuring eCAP service: " << theService->uri());
123 const ConfigRep cfgRep(dynamic_cast<const ServiceConfig&>(cfg()));
124 theService->configure(cfgRep);
125
126 debugs(93,DBG_IMPORTANT, "Starting eCAP service: " << theService->uri());
127 theService->start();
128 }
129
130 /// handles failures while configuring or starting an eCAP service;
131 /// returns false if the error must be propagated to higher levels
132 bool
133 Adaptation::Ecap::ServiceRep::handleFinalizeFailure(const char *error)
134 {
135 const bool salvage = cfg().bypass;
136 const int level = salvage ? DBG_IMPORTANT :DBG_CRITICAL;
137 const char *kind = salvage ? "optional" : "essential";
138 debugs(93, level, "ERROR: failed to start " << kind << " eCAP service: " <<
139 cfg().uri << ":\n" << error);
140
141 if (!salvage)
142 return false; // we cannot handle the problem; the caller may escalate
143
144 // make up() false, preventing new adaptation requests and enabling bypass
145 theService.reset();
146 debugs(93, level, "WARNING: " << kind << " eCAP service is " <<
147 "down after initialization failure: " << cfg().uri);
148
149 return true; // tell the caller to ignore the problem because we handled it
150 }
151
152 bool Adaptation::Ecap::ServiceRep::probed() const
153 {
154 return true; // we "probe" the adapter in finalize().
155 }
156
157 bool Adaptation::Ecap::ServiceRep::up() const
158 {
159 return theService != NULL;
160 }
161
162 bool Adaptation::Ecap::ServiceRep::wantsUrl(const String &urlPath) const
163 {
164 Must(up());
165 return theService->wantsUrl(urlPath.termedBuf());
166 }
167
168 Adaptation::Initiate *
169 Adaptation::Ecap::ServiceRep::makeXactLauncher(HttpMsg *virgin,
170 HttpRequest *cause)
171 {
172 Must(up());
173 XactionRep *rep = new XactionRep(virgin, cause, Pointer(this));
174 XactionRep::AdapterXaction x(theService->makeXaction(rep));
175 rep->master(x);
176 return rep;
177 }
178
179 // returns a temporary string depicting service status, for debugging
180 const char *Adaptation::Ecap::ServiceRep::status() const
181 {
182 // TODO: move generic stuff from eCAP and ICAP to Adaptation
183 static MemBuf buf;
184
185 buf.reset();
186 buf.append("[", 1);
187
188 if (up())
189 buf.append("up", 2);
190 else
191 buf.append("down", 4);
192
193 if (detached())
194 buf.append(",detached", 9);
195
196 buf.append("]", 1);
197 buf.terminate();
198
199 return buf.content();
200 }
201
202 void Adaptation::Ecap::ServiceRep::detach()
203 {
204 isDetached = true;
205 }
206
207 bool Adaptation::Ecap::ServiceRep::detached() const
208 {
209 return isDetached;
210 }
211
212 Adaptation::Ecap::ServiceRep::AdapterService
213 Adaptation::Ecap::FindAdapterService(const String& serviceUri)
214 {
215 typedef std::list<ServiceRep::AdapterService>::const_iterator ASCI;
216 for (ASCI s = TheServices.begin(); s != TheServices.end(); ++s) {
217 Must(*s);
218 if (serviceUri == (*s)->uri().c_str())
219 return *s;
220 }
221 return ServiceRep::AdapterService();
222 }
223
224 void
225 Adaptation::Ecap::RegisterAdapterService(const Adaptation::Ecap::ServiceRep::AdapterService& adapterService)
226 {
227 typedef std::list<ServiceRep::AdapterService>::iterator ASI;
228 for (ASI s = TheServices.begin(); s != TheServices.end(); ++s) {
229 Must(*s);
230 if (adapterService->uri() == (*s)->uri()) {
231 *s = adapterService;
232 debugs(93, 3, "updated eCAP module service: " <<
233 adapterService->uri());
234 return;
235 }
236 }
237 TheServices.push_back(adapterService);
238 debugs(93, 3, "registered eCAP module service: " << adapterService->uri());
239 }
240
241 void
242 Adaptation::Ecap::UnregisterAdapterService(const String& serviceUri)
243 {
244 typedef std::list<ServiceRep::AdapterService>::iterator ASI;
245 for (ASI s = TheServices.begin(); s != TheServices.end(); ++s) {
246 if (serviceUri == (*s)->uri().c_str()) {
247 TheServices.erase(s);
248 debugs(93, 3, "unregistered eCAP module service: " << serviceUri);
249 return;
250 }
251 }
252 debugs(93, 3, "failed to unregister eCAP module service: " << serviceUri);
253 }
254
255 void
256 Adaptation::Ecap::CheckUnusedAdapterServices(const Adaptation::Services& cfgs)
257 {
258 typedef std::list<ServiceRep::AdapterService>::const_iterator ASCI;
259 for (ASCI loaded = TheServices.begin(); loaded != TheServices.end();
260 ++loaded) {
261 bool found = false;
262 for (Services::const_iterator cfged = cfgs.begin();
263 cfged != cfgs.end() && !found; ++cfged) {
264 found = (*cfged)->cfg().uri == (*loaded)->uri().c_str();
265 }
266 if (!found)
267 debugs(93, 1, "Warning: loaded eCAP service has no matching " <<
268 "ecap_service config option: " << (*loaded)->uri());
269 }
270 }