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