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