~clint-fewbar/ubuntu/precise/squid3/ignore-sighup-early

« back to all changes in this revision

Viewing changes to src/ICAP/ICAPServiceRep.cc

  • Committer: Bazaar Package Importer
  • Author(s): Luigi Gangitano
  • Date: 2006-11-11 10:32:06 UTC
  • Revision ID: james.westby@ubuntu.com-20061111103206-f3p0r9g0vq44rp3r
Tags: upstream-3.0.PRE5
ImportĀ upstreamĀ versionĀ 3.0.PRE5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#include "SquidTime.h"
 
12
 
 
13
CBDATA_CLASS_INIT(ICAPServiceRep);
 
14
 
 
15
// XXX: move to squid.conf
 
16
const int ICAPServiceRep::TheSessionFailureLimit = 10;
 
17
 
 
18
ICAPServiceRep::ICAPServiceRep(): method(ICAP::methodNone),
 
19
        point(ICAP::pointNone), port(-1), bypass(false),
 
20
        theOptions(NULL), theLastUpdate(0),
 
21
        theSessionFailures(0), isSuspended(0),
 
22
        waiting(false), notifying(false),
 
23
        updateScheduled(false), self(NULL),
 
24
        wasAnnouncedUp(true) // do not announce an "up" service at startup
 
25
{}
 
26
 
 
27
ICAPServiceRep::~ICAPServiceRep()
 
28
{
 
29
    Must(!waiting);
 
30
    changeOptions(0);
 
31
}
 
32
 
 
33
const char *
 
34
ICAPServiceRep::methodStr() const
 
35
{
 
36
    return ICAP::methodStr(method);
 
37
}
 
38
 
 
39
ICAP::Method
 
40
ICAPServiceRep::parseMethod(const char *str) const
 
41
{
 
42
    if (!strncasecmp(str, "REQMOD", 6))
 
43
        return ICAP::methodReqmod;
 
44
 
 
45
    if (!strncasecmp(str, "RESPMOD", 7))
 
46
        return ICAP::methodRespmod;
 
47
 
 
48
    return ICAP::methodNone;
 
49
}
 
50
 
 
51
 
 
52
const char *
 
53
ICAPServiceRep::vectPointStr() const
 
54
{
 
55
    return ICAP::vectPointStr(point);
 
56
}
 
57
 
 
58
ICAP::VectPoint
 
59
ICAPServiceRep::parseVectPoint(const char *service) const
 
60
{
 
61
    const char *t = service;
 
62
    const char *q = strchr(t, '_');
 
63
 
 
64
    if (q)
 
65
        t = q + 1;
 
66
 
 
67
    if (!strcasecmp(t, "precache"))
 
68
        return ICAP::pointPreCache;
 
69
 
 
70
    if (!strcasecmp(t, "postcache"))
 
71
        return ICAP::pointPostCache;
 
72
 
 
73
    return ICAP::pointNone;
 
74
}
 
75
 
 
76
bool
 
77
ICAPServiceRep::configure(Pointer &aSelf)
 
78
{
 
79
    assert(!self && aSelf != NULL);
 
80
    self = aSelf;
 
81
 
 
82
    char *service_type = NULL;
 
83
 
 
84
    ConfigParser::ParseString(&key);
 
85
    ConfigParser::ParseString(&service_type);
 
86
    ConfigParser::ParseBool(&bypass);
 
87
    ConfigParser::ParseString(&uri);
 
88
 
 
89
    debug(3, 5) ("ICAPService::parseConfigLine (line %d): %s %s %d\n", config_lineno, key.buf(), service_type, bypass);
 
90
 
 
91
    method = parseMethod(service_type);
 
92
    point = parseVectPoint(service_type);
 
93
 
 
94
    debug(3, 5) ("ICAPService::parseConfigLine (line %d): service is %s_%s\n", config_lineno, methodStr(), vectPointStr());
 
95
 
 
96
    if (uri.cmp("icap://", 7) != 0) {
 
97
        debug(3, 0) ("ICAPService::parseConfigLine (line %d): wrong uri: %s\n", config_lineno, uri.buf());
 
98
        return false;
 
99
    }
 
100
 
 
101
    const char *s = uri.buf() + 7;
 
102
 
 
103
    const char *e;
 
104
 
 
105
    bool have_port = false;
 
106
 
 
107
    if ((e = strchr(s, ':')) != NULL) {
 
108
        have_port = true;
 
109
    } else if ((e = strchr(s, '/')) != NULL) {
 
110
        have_port = false;
 
111
    } else {
 
112
        return false;
 
113
    }
 
114
 
 
115
    int len = e - s;
 
116
    host.limitInit(s, len);
 
117
    s = e;
 
118
 
 
119
    if (have_port) {
 
120
        s++;
 
121
 
 
122
        if ((e = strchr(s, '/')) != NULL) {
 
123
            char *t;
 
124
            port = strtoul(s, &t, 0) % 65536;
 
125
 
 
126
            if (t != e) {
 
127
                return false;
 
128
            }
 
129
 
 
130
            s = e;
 
131
 
 
132
            if (s[0] != '/') {
 
133
                return false;
 
134
            }
 
135
        }
 
136
    } else {
 
137
 
 
138
        struct servent *serv = getservbyname("icap", "tcp");
 
139
 
 
140
        if (serv) {
 
141
            port = htons(serv->s_port);
 
142
        } else {
 
143
            port = 1344;
 
144
        }
 
145
    }
 
146
 
 
147
    s++;
 
148
    e = strchr(s, '\0');
 
149
    len = e - s;
 
150
 
 
151
    if (len > 1024) {
 
152
        debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno);
 
153
    }
 
154
 
 
155
    resource.limitInit(s, len + 1);
 
156
 
 
157
    if ((bypass != 0) && (bypass != 1)) {
 
158
        return false;
 
159
    }
 
160
 
 
161
    return true;
 
162
 
 
163
};
 
164
 
 
165
void ICAPServiceRep::invalidate()
 
166
{
 
167
    assert(self != NULL);
 
168
    Pointer savedSelf = self; // to prevent destruction when we nullify self
 
169
    self = NULL;
 
170
 
 
171
    announceStatusChange("invalidated by reconfigure", false);
 
172
 
 
173
    savedSelf = NULL; // may destroy us and, hence, invalidate cbdata(this)
 
174
    // TODO: it would be nice to invalidate cbdata(this) when not destroyed
 
175
}
 
176
 
 
177
void ICAPServiceRep::noteFailure() {
 
178
    ++theSessionFailures;
 
179
    debugs(93,4, "ICAPService failure " << theSessionFailures <<
 
180
        ", out of " << TheSessionFailureLimit << " allowed");
 
181
 
 
182
    if (theSessionFailures > TheSessionFailureLimit)
 
183
        suspend("too many failures");
 
184
 
 
185
    // TODO: Should bypass setting affect how much Squid tries to talk to
 
186
    // the ICAP service that is currently unusable and is likely to remain 
 
187
    // so for some time? The current code says "no". Perhaps the answer 
 
188
    // should be configurable.
 
189
}
 
190
 
 
191
void ICAPServiceRep::suspend(const char *reason) {
 
192
    if (isSuspended) {
 
193
        debugs(93,4, "keeping ICAPService suspended, also for " << reason);
 
194
    } else {
 
195
        isSuspended = reason;
 
196
        debugs(93,1, "suspending ICAPService for " << reason);
 
197
        announceStatusChange("suspended", true);
 
198
    }
 
199
}
 
200
 
 
201
bool ICAPServiceRep::probed() const
 
202
{
 
203
    return theLastUpdate != 0;
 
204
}
 
205
 
 
206
bool ICAPServiceRep::hasOptions() const {
 
207
    return theOptions && theOptions->valid() && theOptions->fresh();
 
208
}
 
209
 
 
210
bool ICAPServiceRep::up() const
 
211
{
 
212
    return self != NULL && !isSuspended && hasOptions();
 
213
}
 
214
 
 
215
bool ICAPServiceRep::broken() const
 
216
{
 
217
    return probed() && !up();
 
218
}
 
219
 
 
220
bool ICAPServiceRep::wantsUrl(const String &urlPath) const
 
221
{
 
222
    Must(hasOptions());
 
223
    return theOptions->transferKind(urlPath) != ICAPOptions::xferIgnore;
 
224
}
 
225
 
 
226
bool ICAPServiceRep::wantsPreview(const String &urlPath, size_t &wantedSize) const
 
227
{
 
228
    Must(hasOptions());
 
229
 
 
230
    if (theOptions->preview < 0)
 
231
        return false;
 
232
 
 
233
    if (theOptions->transferKind(urlPath) != ICAPOptions::xferPreview)
 
234
        return false;
 
235
 
 
236
    wantedSize = theOptions->preview;
 
237
 
 
238
    return true;
 
239
}
 
240
 
 
241
bool ICAPServiceRep::allows204() const
 
242
{
 
243
    Must(hasOptions());
 
244
    return true; // in the future, we may have ACLs to prevent 204s
 
245
}
 
246
 
 
247
 
 
248
static
 
249
void ICAPServiceRep_noteTimeToUpdate(void *data)
 
250
{
 
251
    ICAPServiceRep *service = static_cast<ICAPServiceRep*>(data);
 
252
    Must(service);
 
253
    service->noteTimeToUpdate();
 
254
}
 
255
 
 
256
void ICAPServiceRep::noteTimeToUpdate()
 
257
{
 
258
    if (self != NULL)
 
259
        updateScheduled = false;
 
260
 
 
261
    if (!self || waiting) {
 
262
        debugs(93,5, "ICAPService ignores options update " << status());
 
263
        return;
 
264
    }
 
265
 
 
266
    debugs(93,5, "ICAPService performs a regular options update " << status());
 
267
    startGettingOptions();
 
268
}
 
269
 
 
270
static
 
271
void ICAPServiceRep_noteTimeToNotify(void *data)
 
272
{
 
273
    ICAPServiceRep *service = static_cast<ICAPServiceRep*>(data);
 
274
    Must(service);
 
275
    service->noteTimeToNotify();
 
276
}
 
277
 
 
278
void ICAPServiceRep::noteTimeToNotify()
 
279
{
 
280
    Must(!notifying);
 
281
    notifying = true;
 
282
    debugs(93,7, "ICAPService notifies " << theClients.size() << " clients " <<
 
283
           status());
 
284
 
 
285
    // note: we must notify even if we are invalidated
 
286
 
 
287
    Pointer us = NULL;
 
288
 
 
289
    while (!theClients.empty()) {
 
290
        Client i = theClients.pop_back();
 
291
        us = i.service; // prevent callbacks from destroying us while we loop
 
292
 
 
293
        if (cbdataReferenceValid(i.data))
 
294
            (*i.callback)(i.data, us);
 
295
 
 
296
        cbdataReferenceDone(i.data);
 
297
    }
 
298
 
 
299
    notifying = false;
 
300
}
 
301
 
 
302
void ICAPServiceRep::callWhenReady(Callback *cb, void *data)
 
303
{
 
304
    Must(cb);
 
305
    Must(self != NULL);
 
306
    Must(!broken()); // we do not wait for a broken service
 
307
 
 
308
    Client i;
 
309
    i.service = self;
 
310
    i.callback = cb;
 
311
    i.data = cbdataReference(data);
 
312
    theClients.push_back(i);
 
313
 
 
314
    if (waiting || notifying)
 
315
        return; // do nothing, we will be picked up in noteTimeToNotify()
 
316
 
 
317
    if (needNewOptions())
 
318
        startGettingOptions();
 
319
    else
 
320
        scheduleNotification();
 
321
}
 
322
 
 
323
void ICAPServiceRep::scheduleNotification()
 
324
{
 
325
    debugs(93,7, "ICAPService will notify " << theClients.size() << " clients");
 
326
    eventAdd("ICAPServiceRep::noteTimeToNotify", &ICAPServiceRep_noteTimeToNotify, this, 0, 0, true);
 
327
}
 
328
 
 
329
bool ICAPServiceRep::needNewOptions() const
 
330
{
 
331
    return self != NULL && !up();
 
332
}
 
333
 
 
334
void ICAPServiceRep::changeOptions(ICAPOptions *newOptions)
 
335
{
 
336
    debugs(93,9, "ICAPService changes options from " << theOptions << " to " <<
 
337
           newOptions);
 
338
 
 
339
    delete theOptions;
 
340
    theOptions = newOptions;
 
341
    theSessionFailures = 0;
 
342
    isSuspended = 0;
 
343
    theLastUpdate = squid_curtime;
 
344
 
 
345
    checkOptions();
 
346
    announceStatusChange("down after an options fetch failure", true);
 
347
}
 
348
 
 
349
void ICAPServiceRep::checkOptions()
 
350
{
 
351
    if (theOptions == NULL)
 
352
        return;
 
353
 
 
354
    /*
 
355
     * Issue a warning if the ICAP server returned methods in the
 
356
     * options response that don't match the method from squid.conf.
 
357
     */
 
358
 
 
359
    if (!theOptions->methods.empty()) {
 
360
        bool method_found = false;
 
361
        String method_list;
 
362
        Vector <ICAP::Method>::iterator iter = theOptions->methods.begin();
 
363
 
 
364
        while (iter != theOptions->methods.end()) {
 
365
 
 
366
            if (*iter == method) {
 
367
                method_found = true;
 
368
                break;
 
369
            }
 
370
 
 
371
            method_list.append(ICAP::methodStr(*iter));
 
372
            method_list.append(" ", 1);
 
373
            iter++;
 
374
        }
 
375
 
 
376
        if (!method_found) {
 
377
            debugs(93,1, "WARNING: Squid is configured to use ICAP method " <<
 
378
                   ICAP::methodStr(method) <<
 
379
                   " for service " << uri.buf() <<
 
380
                   " but OPTIONS response declares the methods are " << method_list.buf());
 
381
        }
 
382
    }
 
383
 
 
384
 
 
385
    /*
 
386
     *  Check the ICAP server's date header for clock skew
 
387
     */
 
388
    int skew = abs((int)(theOptions->timestamp() - squid_curtime));
 
389
    if (skew > theOptions->ttl())
 
390
        debugs(93, 1, host.buf() << "'s clock is skewed by " << skew << " seconds!");
 
391
}
 
392
 
 
393
void ICAPServiceRep::announceStatusChange(const char *downPhrase, bool important) const
 
394
{
 
395
    if (wasAnnouncedUp == up()) // no significant changes to announce
 
396
        return;
 
397
 
 
398
    const char *what = bypass ? "optional" : "essential";
 
399
    const char *state = wasAnnouncedUp ? downPhrase : "up";
 
400
    const int level = important ? 1 : 2;
 
401
    debugs(93,level, what << " ICAP service is " << state << ": " << uri);
 
402
 
 
403
    wasAnnouncedUp = !wasAnnouncedUp;
 
404
}
 
405
 
 
406
static
 
407
void ICAPServiceRep_noteNewOptions(ICAPOptXact *x, void *data)
 
408
{
 
409
    ICAPServiceRep *service = static_cast<ICAPServiceRep*>(data);
 
410
    Must(service);
 
411
    service->noteNewOptions(x);
 
412
}
 
413
 
 
414
void ICAPServiceRep::noteNewOptions(ICAPOptXact *x)
 
415
{
 
416
    Must(x);
 
417
    Must(waiting);
 
418
    waiting = false;
 
419
 
 
420
    changeOptions(x->options);
 
421
    x->options = NULL;
 
422
    delete x;
 
423
 
 
424
    debugs(93,3, "ICAPService got new options and is now " << status());
 
425
 
 
426
    scheduleUpdate();
 
427
    scheduleNotification();
 
428
}
 
429
 
 
430
void ICAPServiceRep::startGettingOptions()
 
431
{
 
432
    Must(!waiting);
 
433
    debugs(93,6, "ICAPService will get new options " << status());
 
434
    waiting = true;
 
435
 
 
436
    ICAPOptXact *x = new ICAPOptXact;
 
437
    x->start(self, &ICAPServiceRep_noteNewOptions, this);
 
438
    // TODO: timeout in case ICAPOptXact never calls us back?
 
439
}
 
440
 
 
441
void ICAPServiceRep::scheduleUpdate()
 
442
{
 
443
    if (updateScheduled)
 
444
        return; // already scheduled
 
445
 
 
446
    // XXX: move hard-coded constants from here to TheICAPConfig
 
447
 
 
448
    // conservative estimate of how long the OPTIONS transaction will take
 
449
    const int expectedWait = 20; // seconds
 
450
 
 
451
    time_t when = 0;
 
452
 
 
453
    if (theOptions && theOptions->valid()) {
 
454
        const time_t expire = theOptions->expire();
 
455
        debugs(93,7, "ICAPService options expire on " << expire << " >= " << squid_curtime);
 
456
 
 
457
        if (expire < 0) // unknown expiration time
 
458
            when = squid_curtime + 60*60;
 
459
        else
 
460
        if (expire < expectedWait) // invalid expiration time
 
461
            when = squid_curtime + 60*60;
 
462
        else
 
463
            when = expire - expectedWait; // before the current options expire
 
464
    } else {
 
465
        when = squid_curtime + 3*60; // delay for a down service
 
466
    }
 
467
 
 
468
    debugs(93,7, "ICAPService options raw update on " << when << " or " << (when - squid_curtime));
 
469
    if (when < squid_curtime)
 
470
        when = squid_curtime;
 
471
 
 
472
    const int minUpdateGap = 1*60; // seconds
 
473
    if (when < theLastUpdate + minUpdateGap)
 
474
        when = theLastUpdate + minUpdateGap;
 
475
 
 
476
    // TODO: keep the time of the last update to prevet too-frequent updates
 
477
 
 
478
    const int delay = when - squid_curtime;
 
479
 
 
480
    debugs(93,5, "ICAPService will update options in " << delay << " sec");
 
481
 
 
482
    eventAdd("ICAPServiceRep::noteTimeToUpdate",
 
483
             &ICAPServiceRep_noteTimeToUpdate, this, delay, 0, true);
 
484
    updateScheduled = true;
 
485
}
 
486
 
 
487
// returns a temporary string depicting service status, for debugging
 
488
const char *ICAPServiceRep::status() const
 
489
{
 
490
    static MemBuf buf;
 
491
 
 
492
    buf.reset();
 
493
    buf.append("[", 1);
 
494
 
 
495
    if (up())
 
496
        buf.append("up", 2);
 
497
    else
 
498
        buf.append("down", 4);
 
499
 
 
500
    if (!self)
 
501
        buf.append(",gone", 5);
 
502
 
 
503
    if (waiting)
 
504
        buf.append(",wait", 5);
 
505
 
 
506
    if (notifying)
 
507
        buf.append(",notif", 6);
 
508
 
 
509
    if (theSessionFailures > 0)
 
510
        buf.Printf(",F%d", theSessionFailures);
 
511
 
 
512
    if (isSuspended)
 
513
        buf.append(",susp", 5);
 
514
 
 
515
    buf.append("]", 1);
 
516
    buf.terminate();
 
517
 
 
518
    return buf.content();
 
519
}