]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/icap/Xaction.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / adaptation / icap / Xaction.cc
CommitLineData
774c051c 1/*
507d0a78 2 * DEBUG: section 93 ICAP (RFC 3507) Client
774c051c 3 */
4
582c2af2
FC
5#include "squid.h"
6#include "acl/FilledChecklist.h"
7#include "adaptation/icap/Config.h"
8#include "adaptation/icap/Launcher.h"
9#include "adaptation/icap/Xaction.h"
10#include "base/TextException.h"
774c051c 11#include "comm.h"
d6327017 12#include "comm/Connection.h"
aed188fd 13#include "comm/ConnOpener.h"
ec41b64c 14#include "comm/Write.h"
bd7f2ede 15#include "CommCalls.h"
582c2af2
FC
16#include "err_detail_type.h"
17#include "fde.h"
18#include "globals.h"
5f8252d2 19#include "HttpMsg.h"
3ff65596 20#include "HttpReply.h"
582c2af2 21#include "HttpRequest.h"
3ff65596 22#include "icap_log.h"
582c2af2
FC
23#include "ipcache.h"
24#include "pconn.h"
25#include "protos.h"
3ff65596 26#include "SquidTime.h"
781ce8ff 27
26cc52cb 28//CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, Xaction);
5f8252d2 29
d1c7f781 30Adaptation::Icap::Xaction::Xaction(const char *aTypeName, Adaptation::Icap::ServiceRep::Pointer &aService):
bd7f2ede 31 AsyncJob(aTypeName),
4299f876 32 Adaptation::Initiate(aTypeName),
3ff65596
AR
33 icapRequest(NULL),
34 icapReply(NULL),
35 attempts(0),
2413d60a 36 connection(NULL),
a22e6cd3 37 theService(aService),
774c051c 38 commBuf(NULL), commBufSize(0),
39 commEof(false),
2dfede9e 40 reuseConnection(true),
c824c43b 41 isRetriable(true),
3ff65596 42 isRepeatable(true),
cfc68405 43 ignoreLastWrite(false),
41ebd397
CT
44 connector(NULL), reader(NULL), writer(NULL), closer(NULL),
45 alep(new AccessLogEntry),
46 al(*alep)
774c051c 47{
5f8252d2 48 debugs(93,3, typeName << " constructed, this=" << this <<
9e008dda 49 " [icapx" << id << ']'); // we should not call virtual status() here
3ff65596
AR
50 icapRequest = HTTPMSGLOCK(new HttpRequest);
51 icap_tr_start = current_time;
774c051c 52}
53
26cc52cb 54Adaptation::Icap::Xaction::~Xaction()
774c051c 55{
5f8252d2 56 debugs(93,3, typeName << " destructed, this=" << this <<
9e008dda 57 " [icapx" << id << ']'); // we should not call virtual status() here
3ff65596 58 HTTPMSGUNLOCK(icapRequest);
5f8252d2 59}
60
26cc52cb
AR
61Adaptation::Icap::ServiceRep &
62Adaptation::Icap::Xaction::service()
0bef8dd7 63{
a22e6cd3
AR
64 Must(theService != NULL);
65 return *theService;
0bef8dd7
AR
66}
67
26cc52cb 68void Adaptation::Icap::Xaction::disableRetries()
9e008dda 69{
3ff65596
AR
70 debugs(93,5, typeName << (isRetriable ? " from now on" : " still") <<
71 " cannot be retried " << status());
c824c43b 72 isRetriable = false;
73}
74
3ff65596
AR
75void Adaptation::Icap::Xaction::disableRepeats(const char *reason)
76{
77 debugs(93,5, typeName << (isRepeatable ? " from now on" : " still") <<
78 " cannot be repeated because " << reason << status());
79 isRepeatable = false;
80}
81
26cc52cb 82void Adaptation::Icap::Xaction::start()
5f8252d2 83{
0bef8dd7 84 Adaptation::Initiate::start();
5f8252d2 85
86 readBuf.init(SQUID_TCP_SO_RCVBUF, SQUID_TCP_SO_RCVBUF);
87 commBuf = (char*)memAllocBuf(SQUID_TCP_SO_RCVBUF, &commBufSize);
88 // make sure maximum readBuf space does not exceed commBuf size
89 Must(static_cast<size_t>(readBuf.potentialSpaceSize()) <= commBufSize);
774c051c 90}
91
fb505fa1
CT
92static void
93icapLookupDnsResults(const ipcache_addrs *ia, const DnsLookupDetails &, void *data)
94{
95 Adaptation::Icap::Xaction *xa = static_cast<Adaptation::Icap::Xaction *>(data);
96 xa->dnsLookupDone(ia);
97}
98
774c051c 99// TODO: obey service-specific, OPTIONS-reported connection limit
642a305c
AJ
100void
101Adaptation::Icap::Xaction::openConnection()
774c051c 102{
aed188fd 103 Must(!haveConnection());
c824c43b 104
2dba5b8e 105 Adaptation::Icap::ServiceRep &s = service();
774c051c 106
26cc52cb 107 if (!TheConfig.reuse_connections)
560d7d2d 108 disableRetries(); // this will also safely drain pconn pool
109
2dba5b8e
CT
110 bool wasReused = false;
111 connection = s.getConnection(isRetriable, wasReused);
2413d60a 112
983983ce 113 if (wasReused && Comm::IsConnOpen(connection)) {
2dba5b8e 114 // Set comm Close handler
bd7f2ede 115 // fake the connect callback
26cc52cb
AR
116 // TODO: can we sync call Adaptation::Icap::Xaction::noteCommConnected here instead?
117 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
4299f876
AR
118 CbcPointer<Xaction> self(this);
119 Dialer dialer(self, &Adaptation::Icap::Xaction::noteCommConnected);
642a305c 120 dialer.params.conn = connection;
bd7f2ede 121 dialer.params.flag = COMM_OK;
122 // fake other parameters by copying from the existing connection
26cc52cb 123 connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected", dialer);
9e008dda 124 ScheduleCallHere(connector);
c8ceec27 125 return;
2dfede9e 126 }
127
c8ceec27 128 disableRetries(); // we only retry pconn failures
129
983983ce 130 // Attempt to open a new connection...
2dba5b8e 131 debugs(93,3, typeName << " opens connection to " << s.cfg().host.termedBuf() << ":" << s.cfg().port);
774c051c 132
fb505fa1
CT
133 // Locate the Service IP(s) to open
134 ipcache_nbgethostbyname(s.cfg().host.termedBuf(), icapLookupDnsResults, this);
135}
919fc80d 136
fb505fa1
CT
137void
138Adaptation::Icap::Xaction::dnsLookupDone(const ipcache_addrs *ia)
139{
140 Adaptation::Icap::ServiceRep &s = service();
2dba5b8e 141
fb505fa1
CT
142 if (ia == NULL) {
143 debugs(44, DBG_IMPORTANT, "ICAP: Unknown service host: " << s.cfg().host);
774c051c 144
fb505fa1
CT
145#if WHEN_IPCACHE_NBGETHOSTBYNAME_USES_ASYNC_CALLS
146 dieOnConnectionFailure(); // throws
147#else // take a step back into protected Async call dialing.
148 // fake the connect callback
149 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
150 CbcPointer<Xaction> self(this);
151 Dialer dialer(self, &Adaptation::Icap::Xaction::noteCommConnected);
152 dialer.params.conn = connection;
153 dialer.params.flag = COMM_ERROR;
154 // fake other parameters by copying from the existing connection
155 connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected", dialer);
156 ScheduleCallHere(connector);
157#endif
158 return;
159 }
bd7f2ede 160
fb505fa1
CT
161 assert(ia->cur < ia->count);
162
163 connection = new Comm::Connection;
164 connection->remote = ia->in_addrs[ia->cur];
165 connection->remote.SetPort(s.cfg().port);
166 getOutgoingAddress(NULL, connection);
167
168 // TODO: service bypass status may differ from that of a transaction
26cc52cb 169 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> ConnectDialer;
d1c7f781 170 connector = JobCallback(93,3, ConnectDialer, this, Adaptation::Icap::Xaction::noteCommConnected);
4accd6d8 171 Comm::ConnOpener *cs = new Comm::ConnOpener(connection, connector, TheConfig.connect_timeout(service().cfg().bypass));
aed188fd 172 cs->setHost(s.cfg().host.termedBuf());
a42bff56 173 AsyncJob::Start(cs);
774c051c 174}
175
2dfede9e 176/*
177 * This event handler is necessary to work around the no-rentry policy
26cc52cb 178 * of Adaptation::Icap::Xaction::callStart()
2dfede9e 179 */
bd7f2ede 180#if 0
2dfede9e 181void
26cc52cb 182Adaptation::Icap::Xaction::reusedConnection(void *data)
2dfede9e 183{
192378eb 184 debugs(93, 5, HERE << "reused connection");
26cc52cb 185 Adaptation::Icap::Xaction *x = (Adaptation::Icap::Xaction*)data;
2dfede9e 186 x->noteCommConnected(COMM_OK);
187}
bd7f2ede 188#endif
2dfede9e 189
26cc52cb 190void Adaptation::Icap::Xaction::closeConnection()
774c051c 191{
aed188fd 192 if (haveConnection()) {
774c051c 193
bd7f2ede 194 if (closer != NULL) {
2413d60a 195 comm_remove_close_handler(connection->fd, closer);
774c051c 196 closer = NULL;
197 }
198
c99de607 199 cancelRead(); // may not work
200
5f8252d2 201 if (reuseConnection && !doneWithIo()) {
dd6e6148 202 //status() adds leading spaces.
5f8252d2 203 debugs(93,5, HERE << "not reusing pconn due to pending I/O" << status());
c99de607 204 reuseConnection = false;
205 }
774c051c 206
2dba5b8e 207 if (reuseConnection)
c824c43b 208 disableRetries();
2dba5b8e 209
a32c060f 210 const bool reset = !reuseConnection &&
ee5216b8 211 (al.icap.outcome == xoGone || al.icap.outcome == xoError);
a32c060f 212
2dba5b8e 213 Adaptation::Icap::ServiceRep &s = service();
a32c060f 214 s.putConnection(connection, reuseConnection, reset, status());
774c051c 215
c99de607 216 writer = NULL;
217 reader = NULL;
774c051c 218 connector = NULL;
5d779c44 219 connection = NULL;
774c051c 220 }
221}
222
223// connection with the ICAP service established
26cc52cb 224void Adaptation::Icap::Xaction::noteCommConnected(const CommConnectCbParams &io)
774c051c 225{
cfd66529
AJ
226 if (io.flag == COMM_TIMEOUT) {
227 handleCommTimedout();
228 return;
229 }
230
bd7f2ede 231 Must(connector != NULL);
774c051c 232 connector = NULL;
c99de607 233
bd7f2ede 234 if (io.flag != COMM_OK)
c99de607 235 dieOnConnectionFailure(); // throws
236
fb505fa1
CT
237 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
238 AsyncCall::Pointer timeoutCall = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommTimedout",
239 TimeoutDialer(this,&Adaptation::Icap::Xaction::noteCommTimedout));
933dd095 240 commSetConnTimeout(io.conn, TheConfig.connect_timeout(service().cfg().bypass), timeoutCall);
fb505fa1 241
cfd66529
AJ
242 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommCloseCbParams> CloseDialer;
243 closer = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommClosed",
244 CloseDialer(this,&Adaptation::Icap::Xaction::noteCommClosed));
245 comm_add_close_handler(io.conn->fd, closer);
246
983983ce 247// ?? fd_table[io.conn->fd].noteUse(icapPconnPool);
2dba5b8e 248 service().noteConnectionUse(connection);
774c051c 249
250 handleCommConnected();
774c051c 251}
252
26cc52cb 253void Adaptation::Icap::Xaction::dieOnConnectionFailure()
9e008dda 254{
4932ad93 255 debugs(93, 2, HERE << typeName <<
9e008dda 256 " failed to connect to " << service().cfg().uri);
fb505fa1 257 service().noteConnectionFailed("failure");
64b66b76 258 detailError(ERR_DETAIL_ICAP_XACT_START);
c99de607 259 throw TexcHere("cannot connect to the ICAP service");
260}
261
26cc52cb 262void Adaptation::Icap::Xaction::scheduleWrite(MemBuf &buf)
774c051c 263{
aed188fd
AJ
264 Must(haveConnection());
265
774c051c 266 // comm module will free the buffer
26cc52cb 267 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
1b76e6c1 268 writer = JobCallback(93, 3,
4cb2536f 269 Dialer, this, Adaptation::Icap::Xaction::noteCommWrote);
bd7f2ede 270
ec41b64c 271 Comm::Write(connection, &buf, writer);
c99de607 272 updateTimeout();
774c051c 273}
274
26cc52cb 275void Adaptation::Icap::Xaction::noteCommWrote(const CommIoCbParams &io)
774c051c 276{
bd7f2ede 277 Must(writer != NULL);
774c051c 278 writer = NULL;
9e008dda 279
cfc68405 280 if (ignoreLastWrite) {
281 // a hack due to comm inability to cancel a pending write
9e008dda 282 ignoreLastWrite = false;
bd7f2ede 283 debugs(93, 7, HERE << "ignoring last write; status: " << io.flag);
cfc68405 284 } else {
bd7f2ede 285 Must(io.flag == COMM_OK);
3ff65596 286 al.icap.bytesSent += io.size;
cfc68405 287 updateTimeout();
bd7f2ede 288 handleCommWrote(io.size);
cfc68405 289 }
774c051c 290}
291
292// communication timeout with the ICAP service
26cc52cb 293void Adaptation::Icap::Xaction::noteCommTimedout(const CommTimeoutCbParams &io)
774c051c 294{
774c051c 295 handleCommTimedout();
774c051c 296}
297
26cc52cb 298void Adaptation::Icap::Xaction::handleCommTimedout()
774c051c 299{
4932ad93 300 debugs(93, 2, HERE << typeName << " failed: timeout with " <<
9e008dda 301 theService->cfg().methodStr() << " " <<
a7a42b14 302 theService->cfg().uri << status());
fe3e2600 303 reuseConnection = false;
3f832a99 304 const bool whileConnecting = connector != NULL;
fb505fa1
CT
305 if (whileConnecting) {
306 assert(!haveConnection());
307 theService->noteConnectionFailed("timedout");
308 } else
309 closeConnection(); // so that late Comm callbacks do not disturb bypass
3f832a99 310 throw TexcHere(whileConnecting ?
9e008dda
AJ
311 "timed out while connecting to the ICAP service" :
312 "timed out while talking to the ICAP service");
774c051c 313}
314
315// unexpected connection close while talking to the ICAP service
26cc52cb 316void Adaptation::Icap::Xaction::noteCommClosed(const CommCloseCbParams &io)
774c051c 317{
318 closer = NULL;
774c051c 319 handleCommClosed();
774c051c 320}
321
26cc52cb 322void Adaptation::Icap::Xaction::handleCommClosed()
774c051c 323{
64b66b76 324 detailError(ERR_DETAIL_ICAP_XACT_CLOSE);
774c051c 325 mustStop("ICAP service connection externally closed");
326}
327
3ff65596
AR
328void Adaptation::Icap::Xaction::callException(const std::exception &e)
329{
330 setOutcome(xoError);
8277060a 331 service().noteFailure();
3ff65596
AR
332 Adaptation::Initiate::callException(e);
333}
334
26cc52cb 335void Adaptation::Icap::Xaction::callEnd()
774c051c 336{
c824c43b 337 if (doneWithIo()) {
338 debugs(93, 5, HERE << typeName << " done with I/O" << status());
339 closeConnection();
340 }
0bef8dd7 341 Adaptation::Initiate::callEnd(); // may destroy us
774c051c 342}
343
26cc52cb 344bool Adaptation::Icap::Xaction::doneAll() const
774c051c 345{
0bef8dd7 346 return !connector && !reader && !writer && Adaptation::Initiate::doneAll();
774c051c 347}
348
26cc52cb 349void Adaptation::Icap::Xaction::updateTimeout()
9e008dda 350{
aed188fd
AJ
351 Must(haveConnection());
352
bd7f2ede 353 if (reader != NULL || writer != NULL) {
c99de607 354 // restart the timeout before each I/O
355 // XXX: why does Config.Timeout lacks a write timeout?
cfc68405 356 // TODO: service bypass status may differ from that of a transaction
26cc52cb 357 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
d1c7f781 358 AsyncCall::Pointer call = JobCallback(93, 5, TimeoutDialer, this, Adaptation::Icap::Xaction::noteCommTimedout);
8d77a37c 359 commSetConnTimeout(connection, TheConfig.io_timeout(service().cfg().bypass), call);
c99de607 360 } else {
361 // clear timeout when there is no I/O
362 // Do we need a lifetime timeout?
8d77a37c 363 commUnsetConnTimeout(connection);
c99de607 364 }
365}
366
26cc52cb 367void Adaptation::Icap::Xaction::scheduleRead()
774c051c 368{
aed188fd 369 Must(haveConnection());
774c051c 370 Must(!reader);
371 Must(readBuf.hasSpace());
372
774c051c 373 /*
26cc52cb 374 * See comments in Adaptation::Icap::Xaction.h about why we use commBuf
bb790702 375 * here instead of reading directly into readBuf.buf.
774c051c 376 */
26cc52cb 377 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
1b76e6c1 378 reader = JobCallback(93, 3,
4cb2536f 379 Dialer, this, Adaptation::Icap::Xaction::noteCommRead);
774c051c 380
ec20038e 381 comm_read(connection, commBuf, readBuf.spaceSize(), reader);
c99de607 382 updateTimeout();
774c051c 383}
384
385// comm module read a portion of the ICAP response for us
26cc52cb 386void Adaptation::Icap::Xaction::noteCommRead(const CommIoCbParams &io)
774c051c 387{
bd7f2ede 388 Must(reader != NULL);
774c051c 389 reader = NULL;
390
bd7f2ede 391 Must(io.flag == COMM_OK);
774c051c 392
3f832a99
AR
393 if (!io.size) {
394 commEof = true;
395 reuseConnection = false;
3ff65596 396
3f832a99
AR
397 // detect a pconn race condition: eof on the first pconn read
398 if (!al.icap.bytesRead && retriable()) {
399 setOutcome(xoRace);
400 mustStop("pconn race");
401 return;
402 }
403 } else {
c99de607 404
5be49f98 405 al.icap.bytesRead+=io.size;
774c051c 406
5be49f98 407 updateTimeout();
c99de607 408
5be49f98 409 debugs(93, 3, HERE << "read " << io.size << " bytes");
774c051c 410
5be49f98
A
411 /*
412 * See comments in Adaptation::Icap::Xaction.h about why we use commBuf
413 * here instead of reading directly into readBuf.buf.
414 */
774c051c 415
bd7f2ede 416 readBuf.append(commBuf, io.size);
c824c43b 417 disableRetries(); // because pconn did not fail
c824c43b 418 }
774c051c 419
bd7f2ede 420 handleCommRead(io.size);
774c051c 421}
422
26cc52cb 423void Adaptation::Icap::Xaction::cancelRead()
774c051c 424{
bd7f2ede 425 if (reader != NULL) {
aed188fd 426 Must(haveConnection());
2413d60a 427 comm_read_cancel(connection->fd, reader);
bd7f2ede 428 reader = NULL;
774c051c 429 }
430}
431
26cc52cb 432bool Adaptation::Icap::Xaction::parseHttpMsg(HttpMsg *msg)
774c051c 433{
def17b6a 434 debugs(93, 5, HERE << "have " << readBuf.contentSize() << " head bytes to parse");
774c051c 435
436 http_status error = HTTP_STATUS_NONE;
437 const bool parsed = msg->parse(&readBuf, commEof, &error);
438 Must(parsed || !error); // success or need more data
439
440 if (!parsed) { // need more data
441 Must(mayReadMore());
442 msg->reset();
443 return false;
444 }
445
446 readBuf.consume(msg->hdr_sz);
447 return true;
448}
449
26cc52cb 450bool Adaptation::Icap::Xaction::mayReadMore() const
774c051c 451{
452 return !doneReading() && // will read more data
453 readBuf.hasSpace(); // have space for more data
454}
455
26cc52cb 456bool Adaptation::Icap::Xaction::doneReading() const
774c051c 457{
458 return commEof;
459}
460
26cc52cb 461bool Adaptation::Icap::Xaction::doneWriting() const
c99de607 462{
463 return !writer;
464}
465
26cc52cb 466bool Adaptation::Icap::Xaction::doneWithIo() const
c99de607 467{
aed188fd 468 return haveConnection() &&
9e008dda
AJ
469 !connector && !reader && !writer && // fast checks, some redundant
470 doneReading() && doneWriting();
c99de607 471}
472
aed188fd
AJ
473bool Adaptation::Icap::Xaction::haveConnection() const
474{
475 return connection != NULL && connection->isOpen();
476}
477
c824c43b 478// initiator aborted
26cc52cb 479void Adaptation::Icap::Xaction::noteInitiatorAborted()
774c051c 480{
c824c43b 481
4299f876 482 if (theInitiator.set()) {
07e72f8e 483 debugs(93,4, HERE << "Initiator gone before ICAP transaction ended");
c824c43b 484 clearInitiator();
64b66b76 485 detailError(ERR_DETAIL_ICAP_INIT_GONE);
07e72f8e 486 setOutcome(xoGone);
c824c43b 487 mustStop("initiator aborted");
c99de607 488 }
c824c43b 489
774c051c 490}
491
3ff65596
AR
492void Adaptation::Icap::Xaction::setOutcome(const Adaptation::Icap::XactOutcome &xo)
493{
494 if (al.icap.outcome != xoUnknown) {
495 debugs(93, 3, HERE << "Warning: reseting outcome: from " <<
e1381638 496 al.icap.outcome << " to " << xo);
3ff65596
AR
497 } else {
498 debugs(93, 4, HERE << xo);
499 }
500 al.icap.outcome = xo;
501}
502
5f8252d2 503// This 'last chance' method is called before a 'done' transaction is deleted.
504// It is wrong to call virtual methods from a destructor. Besides, this call
505// indicates that the transaction will terminate as planned.
26cc52cb 506void Adaptation::Icap::Xaction::swanSong()
774c051c 507{
5f8252d2 508 // kids should sing first and then call the parent method.
509
510 closeConnection(); // TODO: rename because we do not always close
511
512 if (!readBuf.isNull())
513 readBuf.clean();
514
515 if (commBuf)
516 memFreeBuf(commBufSize, commBuf);
774c051c 517
4299f876 518 tellQueryAborted();
3ff65596
AR
519
520 maybeLog();
774c051c 521
0bef8dd7 522 Adaptation::Initiate::swanSong();
774c051c 523}
524
e1381638
AJ
525void Adaptation::Icap::Xaction::tellQueryAborted()
526{
4299f876
AR
527 if (theInitiator.set()) {
528 Adaptation::Icap::XactAbortInfo abortInfo(icapRequest, icapReply,
4cb2536f 529 retriable(), repeatable());
4299f876
AR
530 Launcher *launcher = dynamic_cast<Launcher*>(theInitiator.get());
531 // launcher may be nil if initiator is invalid
532 CallJobHere1(91,5, CbcPointer<Launcher>(launcher),
4cb2536f 533 Launcher, noteXactAbort, abortInfo);
4299f876
AR
534 clearInitiator();
535 }
3ff65596
AR
536}
537
e1381638
AJ
538void Adaptation::Icap::Xaction::maybeLog()
539{
540 if (IcapLogfileStatus == LOG_ENABLE) {
3ff65596 541 ACLChecklist *checklist = new ACLFilledChecklist(::Config.accessList.icap, al.request, dash_str);
2efeb0b7 542 if (!::Config.accessList.icap || checklist->fastCheck() == ACCESS_ALLOWED) {
3ff65596 543 finalizeLogInfo();
41ebd397 544 icapLogLog(alep, checklist);
3ff65596 545 }
3ff65596
AR
546 delete checklist;
547 }
548}
549
550void Adaptation::Icap::Xaction::finalizeLogInfo()
551{
552 //prepare log data
553 al.icp.opcode = ICP_INVALID;
e1381638 554
3ff65596
AR
555 const Adaptation::Icap::ServiceRep &s = service();
556 al.icap.hostAddr = s.cfg().host.termedBuf();
557 al.icap.serviceName = s.cfg().key;
558 al.icap.reqUri = s.cfg().uri;
e1381638 559
3ff65596
AR
560 al.icap.ioTime = tvSubMsec(icap_tio_start, icap_tio_finish);
561 al.icap.trTime = tvSubMsec(icap_tr_start, current_time);
562
563 al.icap.request = HTTPMSGLOCK(icapRequest);
564 if (icapReply) {
565 al.icap.reply = HTTPMSGLOCK(icapReply);
566 al.icap.resStatus = icapReply->sline.status;
567 }
568}
569
774c051c 570// returns a temporary string depicting transaction status, for debugging
26cc52cb 571const char *Adaptation::Icap::Xaction::status() const
774c051c 572{
573 static MemBuf buf;
574 buf.reset();
575
5f8252d2 576 buf.append(" [", 2);
774c051c 577
578 fillPendingStatus(buf);
579 buf.append("/", 1);
580 fillDoneStatus(buf);
581
52ed047a 582 buf.Printf(" %s%u]", id.Prefix, id.value);
774c051c 583
584 buf.terminate();
585
586 return buf.content();
587}
588
26cc52cb 589void Adaptation::Icap::Xaction::fillPendingStatus(MemBuf &buf) const
774c051c 590{
aed188fd 591 if (haveConnection()) {
2413d60a 592 buf.Printf("FD %d", connection->fd);
774c051c 593
bd7f2ede 594 if (writer != NULL)
774c051c 595 buf.append("w", 1);
596
bd7f2ede 597 if (reader != NULL)
774c051c 598 buf.append("r", 1);
599
5f8252d2 600 buf.append(";", 1);
774c051c 601 }
602}
603
26cc52cb 604void Adaptation::Icap::Xaction::fillDoneStatus(MemBuf &buf) const
774c051c 605{
aed188fd 606 if (haveConnection() && commEof)
2413d60a 607 buf.Printf("Comm(%d)", connection->fd);
774c051c 608
609 if (stopReason != NULL)
610 buf.Printf("Stopped");
611}
3cfc19b3 612
26cc52cb 613bool Adaptation::Icap::Xaction::fillVirginHttpHeader(MemBuf &buf) const
3cfc19b3 614{
615 return false;
616}