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