]>
Commit | Line | Data |
---|---|---|
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 | 30 | Adaptation::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 | 54 | Adaptation::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 |
61 | Adaptation::Icap::ServiceRep & |
62 | Adaptation::Icap::Xaction::service() | |
0bef8dd7 | 63 | { |
a22e6cd3 AR |
64 | Must(theService != NULL); |
65 | return *theService; | |
0bef8dd7 AR |
66 | } |
67 | ||
26cc52cb | 68 | void 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 |
75 | void 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 | 82 | void 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 |
92 | static void |
93 | icapLookupDnsResults(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 |
100 | void |
101 | Adaptation::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 |
137 | void |
138 | Adaptation::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 | 181 | void |
26cc52cb | 182 | Adaptation::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 | 190 | void 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 | 224 | void 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 | 253 | void 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 | 262 | void 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 | 275 | void 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 | 293 | void Adaptation::Icap::Xaction::noteCommTimedout(const CommTimeoutCbParams &io) |
774c051c | 294 | { |
774c051c | 295 | handleCommTimedout(); |
774c051c | 296 | } |
297 | ||
26cc52cb | 298 | void 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 | 316 | void Adaptation::Icap::Xaction::noteCommClosed(const CommCloseCbParams &io) |
774c051c | 317 | { |
318 | closer = NULL; | |
774c051c | 319 | handleCommClosed(); |
774c051c | 320 | } |
321 | ||
26cc52cb | 322 | void 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 |
328 | void 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 | 335 | void 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 | 344 | bool Adaptation::Icap::Xaction::doneAll() const |
774c051c | 345 | { |
0bef8dd7 | 346 | return !connector && !reader && !writer && Adaptation::Initiate::doneAll(); |
774c051c | 347 | } |
348 | ||
26cc52cb | 349 | void 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 | 367 | void 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 | 386 | void 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 | 423 | void 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 | 432 | bool 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 | 450 | bool 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 | 456 | bool Adaptation::Icap::Xaction::doneReading() const |
774c051c | 457 | { |
458 | return commEof; | |
459 | } | |
460 | ||
26cc52cb | 461 | bool Adaptation::Icap::Xaction::doneWriting() const |
c99de607 | 462 | { |
463 | return !writer; | |
464 | } | |
465 | ||
26cc52cb | 466 | bool 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 |
473 | bool Adaptation::Icap::Xaction::haveConnection() const |
474 | { | |
475 | return connection != NULL && connection->isOpen(); | |
476 | } | |
477 | ||
c824c43b | 478 | // initiator aborted |
26cc52cb | 479 | void 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 |
492 | void 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 | 506 | void 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 |
525 | void 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 |
538 | void 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 | ||
550 | void 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 | 571 | const 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 | 589 | void 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 | 604 | void 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 | 613 | bool Adaptation::Icap::Xaction::fillVirginHttpHeader(MemBuf &buf) const |
3cfc19b3 | 614 | { |
615 | return false; | |
616 | } |