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