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