Squid Web Cache master
Loading...
Searching...
No Matches
ServiceRep.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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.
7 */
8
9/* DEBUG: section 93 ICAP (RFC 3507) Client */
10
11#include "squid.h"
12#include "adaptation/Answer.h"
18#include "base/TextException.h"
19#include "comm/Connection.h"
20#include "compat/netdb.h"
21#include "ConfigParser.h"
22#include "debug/Stream.h"
23#include "fde.h"
24#include "globals.h"
25#include "HttpReply.h"
26#include "ip/tools.h"
27#include "SquidConfig.h"
28
29#define DEFAULT_ICAP_PORT 1344
30#define DEFAULT_ICAPS_PORT 11344
31
33
35 AsyncJob("Adaptation::Icap::ServiceRep"), Adaptation::Service(svcCfg),
36 tlsContext(writeableCfg().secure, sslContext),
37 theOptions(nullptr), theOptionsFetcher(nullptr), theLastUpdate(0),
38 theBusyConns(0),
39 theAllWaiters(0),
40 connOverloadReported(false),
41 theIdleConns(nullptr),
42 isSuspended(nullptr), notifying(false),
43 updateScheduled(false),
44 wasAnnouncedUp(true), // do not announce an "up" service at startup
45 isDetached(false)
46{
48 theIdleConns = new IdleConnList("ICAP Service", nullptr);
49}
50
52{
54 delete theIdleConns;
55 Must(!theOptionsFetcher);
56 delete theOptions;
57 });
58}
59
60void
62{
64
65 // use /etc/services or default port if needed
66 const bool have_port = cfg().port >= 0;
67 if (!have_port) {
68 struct servent *serv;
69 if (cfg().protocol.caseCmp("icaps") == 0)
70 serv = xgetservbyname("icaps", "tcp");
71 else
72 serv = xgetservbyname("icap", "tcp");
73
74 if (serv) {
75 writeableCfg().port = htons(serv->s_port);
76 } else {
77 writeableCfg().port = cfg().protocol.caseCmp("icaps") == 0 ? DEFAULT_ICAPS_PORT : DEFAULT_ICAP_PORT;
78 }
79 }
80
81 if (cfg().protocol.caseCmp("icaps") == 0)
82 writeableCfg().secure.encryptTransport = true;
83
84 if (cfg().secure.encryptTransport) {
85 debugs(3, 2, "initializing service " << cfg().resource << " SSL context");
86 sslContext = writeableCfg().secure.createClientContext(true);
87 }
88
89 if (!cfg().connectionEncryption.configured())
90 writeableCfg().connectionEncryption.defaultTo(cfg().secure.encryptTransport);
91
92 theSessionFailures.configure(TheConfig.oldest_service_failure > 0 ?
94}
95
97{
98 const int failures = theSessionFailures.count(1);
99 debugs(93,4, " failure " << failures << " out of " <<
100 TheConfig.service_failure_limit << " allowed in " <<
101 TheConfig.oldest_service_failure << "sec " << status());
102
103 if (isSuspended)
104 return;
105
108 suspend("too many failures");
109
110 // TODO: Should bypass setting affect how much Squid tries to talk to
111 // the ICAP service that is currently unusable and is likely to remain
112 // so for some time? The current code says "no". Perhaps the answer
113 // should be configurable.
114}
115
116// TODO: getIdleConnection() and putConnection()/noteConnectionFailed() manage a
117// "used connection slot" resource. Automate that resource tracking (RAII/etc.).
120{
121 Comm::ConnectionPointer connection;
122
123 /* 2011-06-17: rousskov:
124 * There are two things that happen at the same time in pop(). Both are important.
125 * 1) Ensure that we can use a pconn for this transaction.
126 * 2) Ensure that the number of idle pconns does not grow without bounds.
127 *
128 * Both happen in the beginning of the transaction. Both are dictated by real-world problems.
129 * retriable means you can repeat the request if you suspect the first try failed due to a pconn race.
130 * HTTP and ICAP rules prohibit the use of pconns for non-retriable requests.
131 *
132 * If there are zero idle connections, (2) is irrelevant. (2) is only relevant when there are many
133 * idle connections and we should not open more connections without closing some idle ones,
134 * or instead of just opening a new connection and leaving idle connections as is.
135 * In other words, (2) tells us to close one FD for each new one we open due to retriable.
136 */
137 if (retriableXact)
138 connection = theIdleConns->pop();
139 else
140 theIdleConns->closeN(1);
141
142 ++theBusyConns;
143 debugs(93,3, "got connection: " << connection);
144 return connection;
145}
146
147// pools connection if it is reusable or closes it
148void Adaptation::Icap::ServiceRep::putConnection(const Comm::ConnectionPointer &conn, bool isReusable, bool sendReset, const char *comment)
149{
150 Must(Comm::IsConnOpen(conn));
151 // do not pool an idle connection if we owe connections
152 if (isReusable && excessConnections() == 0) {
153 debugs(93, 3, "pushing pconn" << comment);
154 theIdleConns->push(conn);
155 } else {
156 debugs(93, 3, (sendReset ? "RST" : "FIN") << "-closing " <<
157 comment);
158 // comm_close called from Connection::close will clear timeout
159 // TODO: add "bool sendReset = false" to Connection::close()?
160 if (sendReset)
161 comm_reset_close(conn);
162 else
163 conn->close();
164 }
165
166 Must(theBusyConns > 0);
167 --theBusyConns;
168 // a connection slot released. Check if there are waiters....
169 busyCheckpoint();
170}
171
172// a wrapper to avoid exposing theIdleConns
174{
175 Must(Comm::IsConnOpen(conn));
176 fd_table[conn->fd].noteUse(); // pconn reuse, albeit not via PconnPool API
177}
178
180{
181 debugs(93, 3, "Connection failed: " << comment);
182 --theBusyConns;
183}
184
186{
187 if (cfg().maxConn >= 0)
188 theMaxConnections = cfg().maxConn;
189 else if (theOptions && theOptions->max_connections >= 0)
190 theMaxConnections = theOptions->max_connections;
191 else {
192 theMaxConnections = -1;
193 return;
194 }
195
196 if (::Config.workers > 1 )
197 theMaxConnections /= ::Config.workers;
198}
199
201{
202 if (theMaxConnections < 0)
203 return -1;
204
205 // we are available if we can open or reuse connections
206 // in other words, if we will not create debt
207 int available = max(0, theMaxConnections - theBusyConns);
208
209 if (!available && !connOverloadReported) {
210 debugs(93, DBG_IMPORTANT, "WARNING: ICAP Max-Connections limit " <<
211 "exceeded for service " << cfg().uri << ". Open connections now: " <<
212 theBusyConns + theIdleConns->count() << ", including " <<
213 theIdleConns->count() << " idle persistent connections.");
214 connOverloadReported = true;
215 }
216
217 if (cfg().onOverload == srvForce)
218 return -1;
219
220 return available;
221}
222
223// The number of connections which excess the Max-Connections limit
225{
226 if (theMaxConnections < 0)
227 return 0;
228
229 // Waiters affect the number of needed connections but a needed
230 // connection may still be excessive from Max-Connections p.o.v.
231 // so we should not account for waiting transaction needs here.
232 const int debt = theBusyConns + theIdleConns->count() - theMaxConnections;
233 if (debt > 0)
234 return debt;
235 else
236 return 0;
237}
238
240{
241 --theAllWaiters;
242
243 // in case the notified transaction did not take the connection slot
244 busyCheckpoint();
245}
246
247// called when a connection slot may become available
249{
250 if (theNotificationWaiters.empty()) // nobody is waiting for a slot
251 return;
252
253 int freed = 0;
254 int available = availableConnections();
255
256 if (available < 0) {
257 // It is possible to have waiters when no limit on connections exist in
258 // case of reconfigure or because new Options received.
259 // In this case, notify all waiting transactions.
260 freed = theNotificationWaiters.size();
261 } else {
262 // avoid notifying more waiters than there will be available slots
263 const int notifiedWaiters = theAllWaiters - theNotificationWaiters.size();
264 freed = available - notifiedWaiters;
265 }
266
267 debugs(93,7, "Available connections: " << available <<
268 " freed slots: " << freed <<
269 " waiting in queue: " << theNotificationWaiters.size());
270
271 while (freed > 0 && !theNotificationWaiters.empty()) {
272 Client i = theNotificationWaiters.front();
273 theNotificationWaiters.pop_front();
275 i.callback = nullptr;
276 --freed;
277 }
278}
279
281{
282 if (isSuspended) {
283 debugs(93,4, "keeping suspended, also for " << reason);
284 } else {
285 isSuspended = reason;
286 debugs(93, DBG_IMPORTANT, "suspending ICAP service for " << reason);
288 announceStatusChange("suspended", true);
289 }
290}
291
293{
294 return theLastUpdate != 0;
295}
296
298{
299 return theOptions && theOptions->valid() && theOptions->fresh();
300}
301
303{
304 return !isSuspended && hasOptions();
305}
306
308{
309 Must(up());
310 int available = availableConnections();
311 if (available < 0)
312 return true;
313 else
314 return (available - theAllWaiters > 0);
315}
316
318{
319 Must(up());
320
321 int available = availableConnections();
322 return (available != 0); // it is -1 (no limit) or has available slots
323}
324
326{
327 Must(hasOptions());
328 return theOptions->transferKind(urlPath) != Adaptation::Icap::Options::xferIgnore;
329}
330
331bool Adaptation::Icap::ServiceRep::wantsPreview(const SBuf &urlPath, size_t &wantedSize) const
332{
333 Must(hasOptions());
334
335 if (theOptions->preview < 0)
336 return false;
337
338 if (theOptions->transferKind(urlPath) != Adaptation::Icap::Options::xferPreview)
339 return false;
340
341 wantedSize = theOptions->preview;
342
343 return true;
344}
345
347{
348 Must(hasOptions());
349 return true; // in the future, we may have ACLs to prevent 204s
350}
351
353{
354 Must(hasOptions());
355 if (theOptions->allow206)
356 return true; // in the future, we may have ACLs to prevent 206s
357 return false;
358}
359
360static
362{
363 Adaptation::Icap::ServiceRep *service = static_cast<Adaptation::Icap::ServiceRep*>(data);
364 Must(service);
365 service->noteTimeToUpdate();
366}
367
369{
370 if (!detached())
371 updateScheduled = false;
372
373 if (detached() || theOptionsFetcher.set()) {
374 debugs(93,5, "ignores options update " << status());
375 return;
376 }
377
378 debugs(93,5, "performs a regular options update " << status());
379 startGettingOptions();
380}
381
383{
384 Must(!notifying);
385 notifying = true;
386 debugs(93,7, "notifies " << theClients.size() << " clients " <<
387 status());
388
389 // note: we must notify even if we are invalidated
390
391 Pointer us = nullptr;
392
393 while (!theClients.empty()) {
394 Client i = theClients.back();
395 theClients.pop_back();
397 i.callback = nullptr;
398 }
399
400 notifying = false;
401}
402
404{
405 debugs(93,8, "ICAPServiceRep::callWhenAvailable");
406 Must(cb!=nullptr);
407 Must(up());
408 Must(!theIdleConns->count()); // or we should not be waiting
409
410 Client i;
411 i.service = Pointer(this);
412 i.callback = cb;
413 if (priority)
414 theNotificationWaiters.push_front(i);
415 else
416 theNotificationWaiters.push_back(i);
417
418 busyCheckpoint();
419}
420
422{
423 Must(cb!=nullptr);
424
425 debugs(93,5, "Adaptation::Icap::Service is asked to call " << *cb <<
426 " when ready " << status());
427
428 Must(!broken()); // we do not wait for a broken service
429
430 Client i;
431 i.service = Pointer(this); // TODO: is this really needed?
432 i.callback = cb;
433 theClients.push_back(i);
434
435 if (theOptionsFetcher.set() || notifying)
436 return; // do nothing, we will be picked up in noteTimeToNotify()
437
438 if (needNewOptions())
439 startGettingOptions();
440 else
441 scheduleNotification();
442}
443
445{
446 debugs(93,7, "will notify " << theClients.size() << " clients");
447 CallJobHere(93, 5, this, Adaptation::Icap::ServiceRep, noteTimeToNotify);
448}
449
451{
452 return !detached() && !up();
453}
454
456{
457 debugs(93,8, "changes options from " << theOptions << " to " <<
458 newOptions << ' ' << status());
459
460 delete theOptions;
461 theOptions = newOptions;
462 theSessionFailures.clear();
463 isSuspended = nullptr;
464 theLastUpdate = squid_curtime;
465
466 checkOptions();
467 announceStatusChange("down after an options fetch failure", true);
468}
469
471{
472 if (theOptions == nullptr)
473 return;
474
475 if (!theOptions->valid()) {
476 debugs(93, DBG_IMPORTANT, "WARNING: Squid got an invalid ICAP OPTIONS response " <<
477 "from service " << cfg().uri << "; error: " << theOptions->error);
478 return;
479 }
480
481 /*
482 * Issue a warning if the ICAP server returned methods in the
483 * options response that don't match the method from squid.conf.
484 */
485
486 if (!theOptions->methods.empty()) {
487 bool method_found = false;
488 String method_list;
489 std::vector <ICAP::Method>::iterator iter = theOptions->methods.begin();
490
491 while (iter != theOptions->methods.end()) {
492
493 if (*iter == cfg().method) {
494 method_found = true;
495 break;
496 }
497
498 method_list.append(ICAP::methodStr(*iter));
499 method_list.append(" ", 1);
500 ++iter;
501 }
502
503 if (!method_found) {
504 debugs(93, DBG_IMPORTANT, "WARNING: Squid is configured to use ICAP method " <<
505 cfg().methodStr() <<
506 " for service " << cfg().uri <<
507 " but OPTIONS response declares the methods are " << method_list);
508 }
509 }
510
511 /*
512 * Check the ICAP server's date header for clock skew
513 */
514 const int skew = (int)(theOptions->timestamp() - squid_curtime);
515 if (abs(skew) > theOptions->ttl()) {
516 // TODO: If skew is negative, the option will be considered down
517 // because of stale options. We should probably change this.
518 debugs(93, DBG_IMPORTANT, "ICAP service's clock is skewed by " << skew <<
519 " seconds: " << cfg().uri);
520 }
521}
522
523void Adaptation::Icap::ServiceRep::announceStatusChange(const char *downPhrase, bool important) const
524{
525 if (wasAnnouncedUp == up()) // no significant changes to announce
526 return;
527
528 const char *what = cfg().bypass ? "optional" : "essential";
529 const char *state = wasAnnouncedUp ? downPhrase : "up";
530 const int level = important ? 1 :2;
531 debugs(93,level, what << " ICAP service is " << state << ": " <<
532 cfg().uri << ' ' << status());
533
534 wasAnnouncedUp = !wasAnnouncedUp;
535}
536
537// we are receiving ICAP OPTIONS response headers here or NULL on failures
539{
540 Must(initiated(theOptionsFetcher));
541 clearAdaptation(theOptionsFetcher);
542
543 if (answer.kind == Answer::akError) {
544 debugs(93,3, "failed to fetch options " << status());
545 handleNewOptions(nullptr);
546 return;
547 }
548
549 Must(answer.kind == Answer::akForward); // no akBlock for OPTIONS requests
550 const Http::Message *msg = answer.message.getRaw();
551 Must(msg);
552
553 debugs(93,5, "is interpreting new options " << status());
554
555 Adaptation::Icap::Options *newOptions = nullptr;
556 if (const HttpReply *r = dynamic_cast<const HttpReply*>(msg)) {
557 newOptions = new Adaptation::Icap::Options;
558 newOptions->configure(r);
559 } else {
560 debugs(93, DBG_IMPORTANT, "ICAP service got wrong options message " << status());
561 }
562
563 handleNewOptions(newOptions);
564}
565
566// we (a) must keep trying to get OPTIONS and (b) are RefCounted so we
567// must keep our job alive (XXX: until nobody needs us)
569{
570 clearAdaptation(theOptionsFetcher);
571 debugs(93,2, "ICAP probably failed to fetch options (" << e.what() <<
572 ")" << status());
573 handleNewOptions(nullptr);
574}
575
577{
578 // new options may be NULL
579 changeOptions(newOptions);
580
581 debugs(93,3, "got new options and is now " << status());
582
583 scheduleUpdate(optionsFetchTime());
584
585 // XXX: this whole feature bases on the false assumption a service only has one IP
586 setMaxConnections();
587 const int excess = excessConnections();
588 // if we owe connections and have idle pconns, close the latter
589 if (excess && theIdleConns->count() > 0) {
590 const int n = min(excess, theIdleConns->count());
591 debugs(93,5, "closing " << n << " pconns to relief debt");
592 theIdleConns->closeN(n);
593 }
594
595 scheduleNotification();
596}
597
599{
600 Must(!theOptionsFetcher);
601 debugs(93,6, "will get new options " << status());
602
603 // XXX: "this" here is "self"; works until refcounting API changes
604 theOptionsFetcher = initiateAdaptation(
606 // TODO: timeout in case Adaptation::Icap::OptXact never calls us back?
607 // Such a timeout should probably be a generic AsyncStart feature.
608}
609
611{
612 if (updateScheduled) {
613 debugs(93,7, "reschedules update");
614 // XXX: check whether the event is there because AR saw
615 // an unreproducible eventDelete assertion on 2007/06/18
618 else
619 debugs(93, DBG_IMPORTANT, "ERROR: Squid BUG: ICAP service lost an update event.");
620 updateScheduled = false;
621 }
622
623 debugs(93,7, "raw OPTIONS fetch at " << when << " or in " <<
624 (when - squid_curtime) << " sec");
625 debugs(93,9, "last fetched at " << theLastUpdate << " or " <<
626 (squid_curtime - theLastUpdate) << " sec ago");
627
628 /* adjust update time to prevent too-frequent updates */
629
630 if (when < squid_curtime)
631 when = squid_curtime;
632
633 // XXX: move hard-coded constants from here to Adaptation::Icap::TheConfig
634 const int minUpdateGap = 30; // seconds
635 if (when < theLastUpdate + minUpdateGap)
636 when = theLastUpdate + minUpdateGap;
637
638 const int delay = when - squid_curtime;
639 debugs(93,5, "will fetch OPTIONS in " << delay << " sec");
640
641 eventAdd("Adaptation::Icap::ServiceRep::noteTimeToUpdate",
642 &ServiceRep_noteTimeToUpdate, this, delay, 0, true);
643 updateScheduled = true;
644}
645
646// returns absolute time when OPTIONS should be fetched
647time_t
649{
650 if (theOptions && theOptions->valid()) {
651 const time_t expire = theOptions->expire();
652 debugs(93,7, "options expire on " << expire << " >= " << squid_curtime);
653
654 // conservative estimate of how long the OPTIONS transaction will take
655 // XXX: move hard-coded constants from here to Adaptation::Icap::TheConfig
656 const int expectedWait = 20; // seconds
657
658 // Unknown or invalid (too small) expiration times should not happen.
659 // Adaptation::Icap::Options should use the default TTL, and ICAP servers should not
660 // send invalid TTLs, but bugs and attacks happen.
661 if (expire < expectedWait)
662 return squid_curtime;
663 else
664 return expire - expectedWait; // before the current options expire
665 }
666
667 // use revival delay as "expiration" time for a service w/o valid options
669}
670
677
678// returns a temporary string depicting service status, for debugging
680{
681 static MemBuf buf;
682
683 buf.reset();
684 buf.append("[", 1);
685
686 if (up())
687 buf.append("up", 2);
688 else {
689 buf.append("down", 4);
690 if (isSuspended)
691 buf.append(",susp", 5);
692
693 if (!theOptions)
694 buf.append(",!opt", 5);
695 else if (!theOptions->valid())
696 buf.append(",!valid", 7);
697 else if (!theOptions->fresh())
698 buf.append(",stale", 6);
699 }
700
701 if (detached())
702 buf.append(",detached", 9);
703
704 if (theOptionsFetcher.set())
705 buf.append(",fetch", 6);
706
707 if (notifying)
708 buf.append(",notif", 6);
709
710 if (const int failures = theSessionFailures.remembered())
711 buf.appendf(",fail%d", failures);
712
713 buf.append("]", 1);
714 buf.terminate();
715
716 return buf.content();
717}
718
720{
721 debugs(93,3, "detaching ICAP service: " << cfg().uri <<
722 ' ' << status());
723 isDetached = true;
724}
725
727{
728 return isDetached;
729}
730
738
740{
741 theService = aConnWaiter.theService;
742 theService->noteNewWaiter();
743}
744
749
#define ScheduleCallHere(call)
Definition AsyncCall.h:166
#define CallJobHere(debugSection, debugLevel, job, Class, method)
time_t squid_curtime
class SquidConfig Config
#define SWALLOW_EXCEPTIONS(code)
#define Must(condition)
#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type)
Definition cbdata.h:333
summarizes adaptation service answer for the noteAdaptationAnswer() API
Definition Answer.h:25
Kind kind
the type of the answer
Definition Answer.h:47
Http::MessagePointer message
HTTP request or response to forward.
Definition Answer.h:44
@ akForward
forward the supplied adapted HTTP message
Definition Answer.h:29
@ akError
no adapted message will come; see bypassable
Definition Answer.h:31
time_t oldest_service_failure
Definition Config.h:55
int service_failure_limit
Definition Config.h:54
int service_revival_delay
Definition Config.h:56
ConnWaiterDialer(const CbcPointer< Adaptation::Icap::ModXact > &xact, Adaptation::Icap::ConnWaiterDialer::Parent::Method aHandler)
void configure(const HttpReply *reply)
Definition Options.cc:84
void scheduleUpdate(time_t when)
void noteFailure() override
Definition ServiceRep.cc:96
void noteAdaptationAnswer(const Answer &answer) override
bool availableForOld() const
a transaction notified about connection slot availability may start communicating with the service
void noteConnectionUse(const Comm::ConnectionPointer &conn)
void suspend(const char *reason)
time_t optionsFetchTime() const
void changeOptions(Options *newOptions)
IdleConnList * theIdleConns
idle persistent connection pool
Definition ServiceRep.h:145
void callException(const std::exception &e) override
called when the job throws during an async call
bool up() const override
ServiceRep(const ServiceConfigPointer &aConfig)
Definition ServiceRep.cc:34
void callWhenAvailable(AsyncCall::Pointer &cb, bool priority=false)
bool detached() const override
whether detached() was called
bool wantsUrl(const SBuf &urlPath) const override
const char * status() const override
internal cleanup; do not call directly
void setMaxConnections()
Set the maximum allowed connections for the service.
void callWhenReady(AsyncCall::Pointer &cb)
void announceStatusChange(const char *downPhrase, bool important) const
void noteGoneWaiter()
An xaction is not waiting any more for service to be available.
bool probed() const override
Initiate * makeXactLauncher(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp) override
bool wantsPreview(const SBuf &urlPath, size_t &wantedSize) const
Comm::ConnectionPointer getIdleConnection(bool isRetriable)
bool availableForNew() const
a new transaction may start communicating with the service
void handleNewOptions(Options *newOptions)
void noteConnectionFailed(const char *comment)
void putConnection(const Comm::ConnectionPointer &conn, bool isReusable, bool sendReset, const char *comment)
int excessConnections() const
The number of connections which excess the Max-Connections limit.
virtual void finalize()
Definition Service.cc:26
common parts of HttpRequest and HttpReply
Definition Message.h:26
void append(const char *c, int sz) override
Definition MemBuf.cc:209
char * content()
start of the added data
Definition MemBuf.h:41
void reset()
Definition MemBuf.cc:129
void terminate()
Definition MemBuf.cc:241
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition Packable.h:61
C * getRaw() const
Definition RefCount.h:89
Definition SBuf.h:94
void append(char const *buf, int len)
Definition String.cc:131
void comm_reset_close(const Comm::ConnectionPointer &conn)
Definition comm.cc:787
A const & max(A const &lhs, A const &rhs)
A const & min(A const &lhs, A const &rhs)
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
int eventFind(EVH *func, void *arg)
Definition event.cc:145
void eventDelete(EVH *func, void *arg)
Definition event.cc:127
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition event.cc:107
#define fd_table
Definition fde.h:189
#define DEFAULT_ICAP_PORT
Definition ServiceRep.cc:29
#define DEFAULT_ICAPS_PORT
Definition ServiceRep.cc:30
static void ServiceRep_noteTimeToUpdate(void *data)
Config TheConfig
Definition Config.cc:19
const char * methodStr(Method)
Definition Elements.cc:15
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition Connection.cc:27
struct servent * xgetservbyname(const char *name, const char *proto)
POSIX getservbyname(3) equivalent.
Definition netdb.h:31
int unsigned int
Definition stub_fd.cc:19