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