]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ICAP/ICAPServiceRep.cc
I get compile errors on many tests because Squid defines its own assert()
[thirdparty/squid.git] / src / ICAP / ICAPServiceRep.cc
CommitLineData
774c051c 1/*
2 * DEBUG: section 93 ICAP (RFC 3507) Client
3 */
4
5#include "squid.h"
6#include "TextException.h"
7#include "ICAPServiceRep.h"
8#include "ICAPOptions.h"
9#include "ICAPOptXact.h"
10#include "ConfigParser.h"
11
12CBDATA_CLASS_INIT(ICAPServiceRep);
13
14ICAPServiceRep::ICAPServiceRep(): method(ICAP::methodNone),
15 point(ICAP::pointNone), port(-1), bypass(false), unreachable(false),
16 theOptions(NULL), theState(stateInit), notifying(false), self(NULL)
17{}
18
19ICAPServiceRep::~ICAPServiceRep()
20{
21 Must(!waiting());
22 changeOptions(0);
23}
24
25const char *
26ICAPServiceRep::methodStr() const
27{
28 return ICAP::methodStr(method);
29}
30
31ICAP::Method
32ICAPServiceRep::parseMethod(const char *str) const
33{
34 if (!strncasecmp(str, "REQMOD", 6))
35 return ICAP::methodReqmod;
36
37 if (!strncasecmp(str, "RESPMOD", 7))
38 return ICAP::methodRespmod;
39
40 return ICAP::methodNone;
41}
42
43
44const char *
45ICAPServiceRep::vectPointStr() const
46{
47 return ICAP::vectPointStr(point);
48}
49
50ICAP::VectPoint
51ICAPServiceRep::parseVectPoint(const char *service) const
52{
53 const char *t = service;
54 const char *q = strchr(t, '_');
55
56 if (q)
57 t = q + 1;
58
59 if (!strcasecmp(t, "precache"))
60 return ICAP::pointPreCache;
61
62 if (!strcasecmp(t, "postcache"))
63 return ICAP::pointPostCache;
64
65 return ICAP::pointNone;
66}
67
68bool
69ICAPServiceRep::configure(Pointer &aSelf)
70{
71 assert(!self && aSelf != NULL);
72 self = aSelf;
73
74 char *service_type = NULL;
75
76 ConfigParser::ParseString(&key);
77 ConfigParser::ParseString(&service_type);
78 ConfigParser::ParseBool(&bypass);
79 ConfigParser::ParseString(&uri);
80
81 debug(3, 5) ("ICAPService::parseConfigLine (line %d): %s %s %d\n", config_lineno, key.buf(), service_type, bypass);
82
83 method = parseMethod(service_type);
84 point = parseVectPoint(service_type);
85
86 debug(3, 5) ("ICAPService::parseConfigLine (line %d): service is %s_%s\n", config_lineno, methodStr(), vectPointStr());
87
88 if (uri.cmp("icap://", 7) != 0) {
89 debug(3, 0) ("ICAPService::parseConfigLine (line %d): wrong uri: %s\n", config_lineno, uri.buf());
90 return false;
91 }
92
93 const char *s = uri.buf() + 7;
94
95 const char *e;
96
97 bool have_port = false;
98
99 if ((e = strchr(s, ':')) != NULL) {
100 have_port = true;
101 } else if ((e = strchr(s, '/')) != NULL) {
102 have_port = false;
103 } else {
104 return false;
105 }
106
107 int len = e - s;
108 host.limitInit(s, len);
109 s = e;
110
111 if (have_port) {
112 s++;
113
114 if ((e = strchr(s, '/')) != NULL) {
115 char *t;
116 port = strtoul(s, &t, 0) % 65536;
117
118 if (t != e) {
119 return false;
120 }
121
122 s = e;
123
124 if (s[0] != '/') {
125 return false;
126 }
127 }
128 } else {
129
130 struct servent *serv = getservbyname("icap", "tcp");
131
132 if (serv) {
133 port = htons(serv->s_port);
134 } else {
135 port = 1344;
136 }
137 }
138
139 s++;
140 e = strchr(s, '\0');
141 len = e - s;
142
143 if (len > 1024) {
144 debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno);
145 }
146
147 resource.limitInit(s, len + 1);
148
149 if ((bypass != 0) && (bypass != 1)) {
150 return false;
151 }
152
153 return true;
154
155};
156
157void ICAPServiceRep::invalidate()
158{
159 assert(self != NULL);
160 self = NULL; // may destroy us and, hence, invalidate cbdata(this)
161 // TODO: it would be nice to invalidate cbdata(this) when not destroyed
162}
163
164bool ICAPServiceRep::up() const
165{
166 return self != NULL && theState == stateUp;
167}
168
169bool ICAPServiceRep::wantsPreview(size_t &wantedSize) const
170{
171 Must(up());
172
173 if (theOptions->preview < 0)
174 return false;
175
176 wantedSize = theOptions->preview;
177
178 return true;
179}
180
181bool ICAPServiceRep::allows204() const
182{
183 Must(up());
184 return true; // in the future, we may have ACLs to prevent 204s
185}
186
187
188static
189void ICAPServiceRep_noteTimeToUpdate(void *data)
190{
191 ICAPServiceRep *service = static_cast<ICAPServiceRep*>(data);
192 Must(service);
193 service->noteTimeToUpdate();
194}
195
196void ICAPServiceRep::noteTimeToUpdate()
197{
198 if (!self || waiting()) {
199 debugs(93,5, "ICAPService ignores options update " << status());
200 return;
201 }
202
203 debugs(93,5, "ICAPService performs a regular options update " << status());
204 startGettingOptions();
205}
206
207static
208void ICAPServiceRep_noteTimeToNotify(void *data)
209{
210 ICAPServiceRep *service = static_cast<ICAPServiceRep*>(data);
211 Must(service);
212 service->noteTimeToNotify();
213}
214
215void ICAPServiceRep::noteTimeToNotify()
216{
217 Must(!notifying);
218 notifying = true;
219 debugs(93,7, "ICAPService notifies " << theClients.size() << " clients " <<
220 status());
221
222 // note: we must notify even if we are invalidated
223
224 Pointer us = NULL;
225
226 while (!theClients.empty()) {
227 Client i = theClients.pop_back();
228 us = i.service; // prevent callbacks from destroying us while we loop
229
230 if (cbdataReferenceValid(i.data))
231 (*i.callback)(i.data, us);
232
233 cbdataReferenceDone(i.data);
234 }
235
236 notifying = false;
237}
238
239void ICAPServiceRep::callWhenReady(Callback *cb, void *data)
240{
241 Must(cb);
242 Must(self != NULL);
243
244 Client i;
245 i.service = self;
246 i.callback = cb;
247 i.data = cbdataReference(data);
248 theClients.push_back(i);
249
250 if (waiting() || notifying)
251 return; // do nothing, we will be picked up in noteTimeToNotify()
252
253 if (needNewOptions())
254 startGettingOptions();
255 else
256 scheduleNotification();
257}
258
259void ICAPServiceRep::scheduleNotification()
260{
261 debugs(93,7, "ICAPService will notify " << theClients.size() << " clients");
262 eventAdd("ICAPServiceRep::noteTimeToNotify", &ICAPServiceRep_noteTimeToNotify, this, 0, 0, true);
263}
264
265bool ICAPServiceRep::waiting() const
266{
267 return theState == stateWait;
268}
269
270bool ICAPServiceRep::needNewOptions() const
271{
272 return !theOptions || !theOptions->fresh();
273}
274
275void ICAPServiceRep::changeOptions(ICAPOptions *newOptions)
276{
277 debugs(93,9, "ICAPService changes options from " << theOptions << " to " <<
278 newOptions);
279 delete theOptions;
280 theOptions = newOptions;
281}
282
283static
284void ICAPServiceRep_noteNewOptions(ICAPOptXact *x, void *data)
285{
286 ICAPServiceRep *service = static_cast<ICAPServiceRep*>(data);
287 Must(service);
288 service->noteNewOptions(x);
289}
290
291void ICAPServiceRep::noteNewOptions(ICAPOptXact *x)
292{
293 Must(x);
294 Must(waiting());
295
296 theState = stateDown; // default in case we fail to set new options
297
298 changeOptions(x->options);
299 x->options = NULL;
300 delete x;
301
302 if (theOptions && theOptions->valid())
303 theState = stateUp;
304
305 debugs(93,6, "ICAPService got new options and is now " <<
306 (up() ? "up" : "down"));
307
308 scheduleUpdate();
309
310 scheduleNotification();
311}
312
313void ICAPServiceRep::startGettingOptions()
314{
315 debugs(93,6, "ICAPService will get new options " << status());
316 theState = stateWait;
317
318 ICAPOptXact *x = new ICAPOptXact;
319 x->start(self, &ICAPServiceRep_noteNewOptions, this);
320 // TODO: timeout incase ICAPOptXact never calls us back?
321}
322
323void ICAPServiceRep::scheduleUpdate()
324{
325 int delay = -1;
326
327 if (theOptions && theOptions->valid()) {
328 const time_t expire = theOptions->expire();
329
330 if (expire > squid_curtime)
331 delay = expire - squid_curtime;
332 else
333 if (expire >= 0)
334 delay = 1; // delay for expired or 'expiring now' options
335 else
336 delay = 60*60; // default for options w/o known expiration time
337 } else {
338 delay = 5*60; // delay for a down service
339 }
340
341 if (delay <= 0) {
342 debugs(93,0, "internal error: ICAPServiceRep failed to compute options update schedule");
343 delay = 5*60; // delay for an internal error
344 }
345
346 // with zero delay, the state changes to stateWait before
347 // notifications are sent out to clients
348 assert(delay > 0);
349
350 debugs(93,7, "ICAPService will update options in " << delay << " sec");
351
352 eventAdd("ICAPServiceRep::noteTimeToUpdate",
353 &ICAPServiceRep_noteTimeToUpdate, this, delay, 0, true);
354
355 // XXX: prompt updates of valid options should not disable concurrent ICAP
356 // xactions. 'Wait' state should not mark the service 'down'! This will
357 // also remove 'delay == 0' as a special case above.
358}
359
360const char *ICAPServiceRep::status() const
361{
362 if (!self)
363 return "[invalidated]";
364
365 switch (theState) {
366
367 case stateInit:
368 return "[init]";
369
370 case stateWait:
371 return "[wait]";
372
373 case stateUp:
374 return "[up]";
375
376 case stateDown:
377 return "[down]";
378 }
379
380 return "[unknown]";
381}