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