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