]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/icap/Xaction.cc
SourceLayout: move HttpMsg into libhttp as Http::Message
[thirdparty/squid.git] / src / adaptation / icap / Xaction.cc
CommitLineData
774c051c 1/*
4ac4a490 2 * Copyright (C) 1996-2017 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"
1b091aec 12#include "acl/FilledChecklist.h"
582c2af2
FC
13#include "adaptation/icap/Config.h"
14#include "adaptation/icap/Launcher.h"
15#include "adaptation/icap/Xaction.h"
16#include "base/TextException.h"
774c051c 17#include "comm.h"
d6327017 18#include "comm/Connection.h"
aed188fd 19#include "comm/ConnOpener.h"
e34e40b5 20#include "comm/Read.h"
3347e998 21#include "comm/Write.h"
bd7f2ede 22#include "CommCalls.h"
582c2af2
FC
23#include "err_detail_type.h"
24#include "fde.h"
eb13c21e 25#include "FwdState.h"
7fe542fe 26#include "globals.h"
3ff65596 27#include "HttpReply.h"
3ff65596 28#include "icap_log.h"
582c2af2
FC
29#include "ipcache.h"
30#include "pconn.h"
a72b6e88 31#include "security/PeerConnector.h"
4d5904f7 32#include "SquidConfig.h"
3ff65596 33#include "SquidTime.h"
781ce8ff 34
a72b6e88 35/// Gives Security::PeerConnector access to Answer in the PeerPoolMgr callback dialer.
1b091aec 36class MyIcapAnswerDialer: public UnaryMemFunT<Adaptation::Icap::Xaction, Security::EncryptorAnswer, Security::EncryptorAnswer&>,
a72b6e88 37 public Security::PeerConnector::CbDialer
1b091aec
CT
38{
39public:
40 MyIcapAnswerDialer(const JobPointer &aJob, Method aMethod):
41 UnaryMemFunT<Adaptation::Icap::Xaction, Security::EncryptorAnswer, Security::EncryptorAnswer&>(aJob, aMethod, Security::EncryptorAnswer()) {}
42
a72b6e88 43 /* Security::PeerConnector::CbDialer API */
1b091aec
CT
44 virtual Security::EncryptorAnswer &answer() { return arg1; }
45};
46
47namespace Ssl
48{
49/// A simple PeerConnector for Secure ICAP services. No SslBump capabilities.
a72b6e88 50class IcapPeerConnector: public Security::PeerConnector {
1b091aec
CT
51 CBDATA_CLASS(IcapPeerConnector);
52public:
53 IcapPeerConnector(
54 Adaptation::Icap::ServiceRep::Pointer &service,
55 const Comm::ConnectionPointer &aServerConn,
d4ddb3e6
CT
56 AsyncCall::Pointer &aCallback,
57 AccessLogEntry::Pointer const &alp,
58 const time_t timeout = 0):
1b091aec 59 AsyncJob("Ssl::IcapPeerConnector"),
0166128b 60 Security::PeerConnector(aServerConn, aCallback, alp, timeout), icapService(service) {}
1b091aec 61
a72b6e88 62 /* Security::PeerConnector API */
0166128b 63 virtual bool initialize(Security::SessionPointer &);
1b091aec 64 virtual void noteNegotiationDone(ErrorState *error);
b23f5f9c
AJ
65 virtual Security::ContextPointer getTlsContext() {
66 return icapService->sslContext;
900daee3 67 }
1b091aec
CT
68
69private:
70 Adaptation::Icap::ServiceRep::Pointer icapService;
71};
72} // namespace Ssl
73
74CBDATA_NAMESPACED_CLASS_INIT(Ssl, IcapPeerConnector);
1b091aec 75
d1c7f781 76Adaptation::Icap::Xaction::Xaction(const char *aTypeName, Adaptation::Icap::ServiceRep::Pointer &aService):
f53969cc
SM
77 AsyncJob(aTypeName),
78 Adaptation::Initiate(aTypeName),
79 icapRequest(NULL),
80 icapReply(NULL),
81 attempts(0),
82 connection(NULL),
83 theService(aService),
f53969cc
SM
84 commEof(false),
85 reuseConnection(true),
86 isRetriable(true),
87 isRepeatable(true),
88 ignoreLastWrite(false),
89 stopReason(NULL),
90 connector(NULL),
91 reader(NULL),
92 writer(NULL),
93 closer(NULL),
94 alep(new AccessLogEntry),
95 al(*alep),
96 cs(NULL)
774c051c 97{
5f8252d2 98 debugs(93,3, typeName << " constructed, this=" << this <<
9e008dda 99 " [icapx" << id << ']'); // we should not call virtual status() here
b248c2a3
AJ
100 icapRequest = new HttpRequest;
101 HTTPMSGLOCK(icapRequest);
3ff65596 102 icap_tr_start = current_time;
23541b3e
AJ
103 memset(&icap_tio_start, 0, sizeof(icap_tio_start));
104 memset(&icap_tio_finish, 0, sizeof(icap_tio_finish));
774c051c 105}
106
26cc52cb 107Adaptation::Icap::Xaction::~Xaction()
774c051c 108{
5f8252d2 109 debugs(93,3, typeName << " destructed, this=" << this <<
9e008dda 110 " [icapx" << id << ']'); // we should not call virtual status() here
3ff65596 111 HTTPMSGUNLOCK(icapRequest);
5f8252d2 112}
113
d4ddb3e6
CT
114AccessLogEntry::Pointer
115Adaptation::Icap::Xaction::masterLogEntry()
116{
117 AccessLogEntry::Pointer nil;
118 return nil;
119}
120
26cc52cb
AR
121Adaptation::Icap::ServiceRep &
122Adaptation::Icap::Xaction::service()
0bef8dd7 123{
a22e6cd3
AR
124 Must(theService != NULL);
125 return *theService;
0bef8dd7
AR
126}
127
26cc52cb 128void Adaptation::Icap::Xaction::disableRetries()
9e008dda 129{
3ff65596
AR
130 debugs(93,5, typeName << (isRetriable ? " from now on" : " still") <<
131 " cannot be retried " << status());
c824c43b 132 isRetriable = false;
133}
134
3ff65596
AR
135void Adaptation::Icap::Xaction::disableRepeats(const char *reason)
136{
137 debugs(93,5, typeName << (isRepeatable ? " from now on" : " still") <<
138 " cannot be repeated because " << reason << status());
139 isRepeatable = false;
140}
141
26cc52cb 142void Adaptation::Icap::Xaction::start()
5f8252d2 143{
0bef8dd7 144 Adaptation::Initiate::start();
774c051c 145}
146
fb505fa1 147static void
4a3b98d7 148icapLookupDnsResults(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data)
fb505fa1
CT
149{
150 Adaptation::Icap::Xaction *xa = static_cast<Adaptation::Icap::Xaction *>(data);
151 xa->dnsLookupDone(ia);
152}
153
774c051c 154// TODO: obey service-specific, OPTIONS-reported connection limit
642a305c
AJ
155void
156Adaptation::Icap::Xaction::openConnection()
774c051c 157{
aed188fd 158 Must(!haveConnection());
c824c43b 159
2dba5b8e 160 Adaptation::Icap::ServiceRep &s = service();
774c051c 161
26cc52cb 162 if (!TheConfig.reuse_connections)
560d7d2d 163 disableRetries(); // this will also safely drain pconn pool
164
2dba5b8e
CT
165 bool wasReused = false;
166 connection = s.getConnection(isRetriable, wasReused);
2413d60a 167
983983ce 168 if (wasReused && Comm::IsConnOpen(connection)) {
2dba5b8e 169 // Set comm Close handler
bd7f2ede 170 // fake the connect callback
26cc52cb
AR
171 // TODO: can we sync call Adaptation::Icap::Xaction::noteCommConnected here instead?
172 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
4299f876
AR
173 CbcPointer<Xaction> self(this);
174 Dialer dialer(self, &Adaptation::Icap::Xaction::noteCommConnected);
642a305c 175 dialer.params.conn = connection;
23ff0bee 176 dialer.params.flag = Comm::OK;
bd7f2ede 177 // fake other parameters by copying from the existing connection
26cc52cb 178 connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected", dialer);
9e008dda 179 ScheduleCallHere(connector);
c8ceec27 180 return;
2dfede9e 181 }
182
c8ceec27 183 disableRetries(); // we only retry pconn failures
184
983983ce 185 // Attempt to open a new connection...
2dba5b8e 186 debugs(93,3, typeName << " opens connection to " << s.cfg().host.termedBuf() << ":" << s.cfg().port);
774c051c 187
fb505fa1
CT
188 // Locate the Service IP(s) to open
189 ipcache_nbgethostbyname(s.cfg().host.termedBuf(), icapLookupDnsResults, this);
190}
919fc80d 191
fb505fa1
CT
192void
193Adaptation::Icap::Xaction::dnsLookupDone(const ipcache_addrs *ia)
194{
195 Adaptation::Icap::ServiceRep &s = service();
2dba5b8e 196
fb505fa1
CT
197 if (ia == NULL) {
198 debugs(44, DBG_IMPORTANT, "ICAP: Unknown service host: " << s.cfg().host);
774c051c 199
fb505fa1
CT
200#if WHEN_IPCACHE_NBGETHOSTBYNAME_USES_ASYNC_CALLS
201 dieOnConnectionFailure(); // throws
202#else // take a step back into protected Async call dialing.
203 // fake the connect callback
204 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
205 CbcPointer<Xaction> self(this);
206 Dialer dialer(self, &Adaptation::Icap::Xaction::noteCommConnected);
207 dialer.params.conn = connection;
4ee57cbe 208 dialer.params.flag = Comm::COMM_ERROR;
fb505fa1
CT
209 // fake other parameters by copying from the existing connection
210 connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected", dialer);
211 ScheduleCallHere(connector);
212#endif
213 return;
214 }
bd7f2ede 215
fb505fa1
CT
216 assert(ia->cur < ia->count);
217
218 connection = new Comm::Connection;
219 connection->remote = ia->in_addrs[ia->cur];
4dd643d5 220 connection->remote.port(s.cfg().port);
fb505fa1
CT
221 getOutgoingAddress(NULL, connection);
222
223 // TODO: service bypass status may differ from that of a transaction
26cc52cb 224 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> ConnectDialer;
d1c7f781 225 connector = JobCallback(93,3, ConnectDialer, this, Adaptation::Icap::Xaction::noteCommConnected);
d471b08a 226 cs = new Comm::ConnOpener(connection, connector, TheConfig.connect_timeout(service().cfg().bypass));
aed188fd 227 cs->setHost(s.cfg().host.termedBuf());
5c720aa0 228 AsyncJob::Start(cs.get());
774c051c 229}
230
2dfede9e 231/*
232 * This event handler is necessary to work around the no-rentry policy
26cc52cb 233 * of Adaptation::Icap::Xaction::callStart()
2dfede9e 234 */
bd7f2ede 235#if 0
2dfede9e 236void
26cc52cb 237Adaptation::Icap::Xaction::reusedConnection(void *data)
2dfede9e 238{
192378eb 239 debugs(93, 5, HERE << "reused connection");
26cc52cb 240 Adaptation::Icap::Xaction *x = (Adaptation::Icap::Xaction*)data;
23ff0bee 241 x->noteCommConnected(Comm::OK);
2dfede9e 242}
bd7f2ede 243#endif
2dfede9e 244
26cc52cb 245void Adaptation::Icap::Xaction::closeConnection()
774c051c 246{
aed188fd 247 if (haveConnection()) {
774c051c 248
bd7f2ede 249 if (closer != NULL) {
2413d60a 250 comm_remove_close_handler(connection->fd, closer);
774c051c 251 closer = NULL;
252 }
253
c99de607 254 cancelRead(); // may not work
255
5f8252d2 256 if (reuseConnection && !doneWithIo()) {
dd6e6148 257 //status() adds leading spaces.
5f8252d2 258 debugs(93,5, HERE << "not reusing pconn due to pending I/O" << status());
c99de607 259 reuseConnection = false;
260 }
774c051c 261
2dba5b8e 262 if (reuseConnection)
c824c43b 263 disableRetries();
2dba5b8e 264
a32c060f 265 const bool reset = !reuseConnection &&
ee5216b8 266 (al.icap.outcome == xoGone || al.icap.outcome == xoError);
a32c060f 267
2dba5b8e 268 Adaptation::Icap::ServiceRep &s = service();
a32c060f 269 s.putConnection(connection, reuseConnection, reset, status());
774c051c 270
c99de607 271 writer = NULL;
272 reader = NULL;
774c051c 273 connector = NULL;
5d779c44 274 connection = NULL;
774c051c 275 }
276}
277
278// connection with the ICAP service established
26cc52cb 279void Adaptation::Icap::Xaction::noteCommConnected(const CommConnectCbParams &io)
774c051c 280{
d471b08a
AK
281 cs = NULL;
282
23ff0bee 283 if (io.flag == Comm::TIMEOUT) {
cfd66529
AJ
284 handleCommTimedout();
285 return;
286 }
287
bd7f2ede 288 Must(connector != NULL);
774c051c 289 connector = NULL;
c99de607 290
23ff0bee 291 if (io.flag != Comm::OK)
c99de607 292 dieOnConnectionFailure(); // throws
293
fb505fa1
CT
294 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
295 AsyncCall::Pointer timeoutCall = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommTimedout",
296 TimeoutDialer(this,&Adaptation::Icap::Xaction::noteCommTimedout));
933dd095 297 commSetConnTimeout(io.conn, TheConfig.connect_timeout(service().cfg().bypass), timeoutCall);
fb505fa1 298
cfd66529
AJ
299 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommCloseCbParams> CloseDialer;
300 closer = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommClosed",
301 CloseDialer(this,&Adaptation::Icap::Xaction::noteCommClosed));
302 comm_add_close_handler(io.conn->fd, closer);
303
0166128b
AJ
304 // If it is a reused connection and the TLS object is built
305 // we should not negotiate new TLS session
33cc0629 306 const auto &ssl = fd_table[io.conn->fd].ssl;
1b091aec
CT
307 if (!ssl && service().cfg().secure.encryptTransport) {
308 CbcPointer<Adaptation::Icap::Xaction> me(this);
309 securer = asyncCall(93, 4, "Adaptation::Icap::Xaction::handleSecuredPeer",
310 MyIcapAnswerDialer(me, &Adaptation::Icap::Xaction::handleSecuredPeer));
311
a72b6e88 312 auto *sslConnector = new Ssl::IcapPeerConnector(theService, io.conn, securer, masterLogEntry(), TheConfig.connect_timeout(service().cfg().bypass));
1b091aec
CT
313 AsyncJob::Start(sslConnector); // will call our callback
314 return;
315 }
1b091aec 316
983983ce 317// ?? fd_table[io.conn->fd].noteUse(icapPconnPool);
2dba5b8e 318 service().noteConnectionUse(connection);
774c051c 319
320 handleCommConnected();
774c051c 321}
322
26cc52cb 323void Adaptation::Icap::Xaction::dieOnConnectionFailure()
9e008dda 324{
4932ad93 325 debugs(93, 2, HERE << typeName <<
9e008dda 326 " failed to connect to " << service().cfg().uri);
fb505fa1 327 service().noteConnectionFailed("failure");
64b66b76 328 detailError(ERR_DETAIL_ICAP_XACT_START);
c99de607 329 throw TexcHere("cannot connect to the ICAP service");
330}
331
26cc52cb 332void Adaptation::Icap::Xaction::scheduleWrite(MemBuf &buf)
774c051c 333{
aed188fd
AJ
334 Must(haveConnection());
335
774c051c 336 // comm module will free the buffer
26cc52cb 337 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
1b76e6c1 338 writer = JobCallback(93, 3,
4cb2536f 339 Dialer, this, Adaptation::Icap::Xaction::noteCommWrote);
bd7f2ede 340
ec41b64c 341 Comm::Write(connection, &buf, writer);
c99de607 342 updateTimeout();
774c051c 343}
344
26cc52cb 345void Adaptation::Icap::Xaction::noteCommWrote(const CommIoCbParams &io)
774c051c 346{
bd7f2ede 347 Must(writer != NULL);
774c051c 348 writer = NULL;
9e008dda 349
cfc68405 350 if (ignoreLastWrite) {
351 // a hack due to comm inability to cancel a pending write
9e008dda 352 ignoreLastWrite = false;
bd7f2ede 353 debugs(93, 7, HERE << "ignoring last write; status: " << io.flag);
cfc68405 354 } else {
23ff0bee 355 Must(io.flag == Comm::OK);
3ff65596 356 al.icap.bytesSent += io.size;
cfc68405 357 updateTimeout();
bd7f2ede 358 handleCommWrote(io.size);
cfc68405 359 }
774c051c 360}
361
362// communication timeout with the ICAP service
ced8def3 363void Adaptation::Icap::Xaction::noteCommTimedout(const CommTimeoutCbParams &)
774c051c 364{
774c051c 365 handleCommTimedout();
774c051c 366}
367
26cc52cb 368void Adaptation::Icap::Xaction::handleCommTimedout()
774c051c 369{
4932ad93 370 debugs(93, 2, HERE << typeName << " failed: timeout with " <<
9e008dda 371 theService->cfg().methodStr() << " " <<
a7a42b14 372 theService->cfg().uri << status());
fe3e2600 373 reuseConnection = false;
3f832a99 374 const bool whileConnecting = connector != NULL;
fb505fa1
CT
375 if (whileConnecting) {
376 assert(!haveConnection());
377 theService->noteConnectionFailed("timedout");
378 } else
379 closeConnection(); // so that late Comm callbacks do not disturb bypass
3f832a99 380 throw TexcHere(whileConnecting ?
9e008dda
AJ
381 "timed out while connecting to the ICAP service" :
382 "timed out while talking to the ICAP service");
774c051c 383}
384
385// unexpected connection close while talking to the ICAP service
ced8def3 386void Adaptation::Icap::Xaction::noteCommClosed(const CommCloseCbParams &)
774c051c 387{
1b091aec
CT
388 if (securer != NULL) {
389 securer->cancel("Connection closed before SSL negotiation finished");
390 securer = NULL;
391 }
774c051c 392 closer = NULL;
774c051c 393 handleCommClosed();
774c051c 394}
395
26cc52cb 396void Adaptation::Icap::Xaction::handleCommClosed()
774c051c 397{
64b66b76 398 detailError(ERR_DETAIL_ICAP_XACT_CLOSE);
774c051c 399 mustStop("ICAP service connection externally closed");
400}
401
3ff65596
AR
402void Adaptation::Icap::Xaction::callException(const std::exception &e)
403{
404 setOutcome(xoError);
8277060a 405 service().noteFailure();
3ff65596
AR
406 Adaptation::Initiate::callException(e);
407}
408
26cc52cb 409void Adaptation::Icap::Xaction::callEnd()
774c051c 410{
c824c43b 411 if (doneWithIo()) {
412 debugs(93, 5, HERE << typeName << " done with I/O" << status());
413 closeConnection();
414 }
0bef8dd7 415 Adaptation::Initiate::callEnd(); // may destroy us
774c051c 416}
417
26cc52cb 418bool Adaptation::Icap::Xaction::doneAll() const
774c051c 419{
1b091aec 420 return !connector && !securer && !reader && !writer && Adaptation::Initiate::doneAll();
774c051c 421}
422
26cc52cb 423void Adaptation::Icap::Xaction::updateTimeout()
9e008dda 424{
aed188fd
AJ
425 Must(haveConnection());
426
bd7f2ede 427 if (reader != NULL || writer != NULL) {
c99de607 428 // restart the timeout before each I/O
429 // XXX: why does Config.Timeout lacks a write timeout?
cfc68405 430 // TODO: service bypass status may differ from that of a transaction
26cc52cb 431 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
d1c7f781 432 AsyncCall::Pointer call = JobCallback(93, 5, TimeoutDialer, this, Adaptation::Icap::Xaction::noteCommTimedout);
8d77a37c 433 commSetConnTimeout(connection, TheConfig.io_timeout(service().cfg().bypass), call);
c99de607 434 } else {
435 // clear timeout when there is no I/O
436 // Do we need a lifetime timeout?
8d77a37c 437 commUnsetConnTimeout(connection);
c99de607 438 }
439}
440
26cc52cb 441void Adaptation::Icap::Xaction::scheduleRead()
774c051c 442{
aed188fd 443 Must(haveConnection());
774c051c 444 Must(!reader);
284e8d8d 445 Must(readBuf.length() < SQUID_TCP_SO_RCVBUF); // will expand later if needed
774c051c 446
84ae6223
AJ
447 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
448 reader = JobCallback(93, 3, Dialer, this, Adaptation::Icap::Xaction::noteCommRead);
449 Comm::Read(connection, reader);
c99de607 450 updateTimeout();
774c051c 451}
452
453// comm module read a portion of the ICAP response for us
26cc52cb 454void Adaptation::Icap::Xaction::noteCommRead(const CommIoCbParams &io)
774c051c 455{
bd7f2ede 456 Must(reader != NULL);
774c051c 457 reader = NULL;
458
23ff0bee 459 Must(io.flag == Comm::OK);
774c051c 460
284e8d8d
AJ
461 // TODO: tune this better to expected message sizes
462 readBuf.reserveCapacity(SQUID_TCP_SO_RCVBUF);
2b42f3fd
AR
463 // we are not asked to grow beyond the allowed maximum
464 Must(readBuf.length() < SQUID_TCP_SO_RCVBUF);
465 // now we can ensure that there is space to read new data,
466 // even if readBuf.spaceSize() currently returns zero.
467 readBuf.rawSpace(1);
284e8d8d 468
84ae6223
AJ
469 CommIoCbParams rd(this); // will be expanded with ReadNow results
470 rd.conn = io.conn;
84ae6223 471
284e8d8d 472 switch (Comm::ReadNow(rd, readBuf)) {
84ae6223
AJ
473 case Comm::INPROGRESS:
474 if (readBuf.isEmpty())
475 debugs(33, 2, io.conn << ": no data to process, " << xstrerr(rd.xerrno));
476 scheduleRead();
477 return;
478
479 case Comm::OK:
480 al.icap.bytesRead += rd.size;
481
482 updateTimeout();
483
484 debugs(93, 3, "read " << rd.size << " bytes");
485
486 disableRetries(); // because pconn did not fail
487
488 /* Continue to process previously read data */
489 break;
490
491 case Comm::ENDFILE: // close detected by 0-byte read
3f832a99
AR
492 commEof = true;
493 reuseConnection = false;
3ff65596 494
3f832a99
AR
495 // detect a pconn race condition: eof on the first pconn read
496 if (!al.icap.bytesRead && retriable()) {
497 setOutcome(xoRace);
498 mustStop("pconn race");
499 return;
500 }
774c051c 501
84ae6223 502 break;
774c051c 503
94c0aeac 504 // case Comm::COMM_ERROR:
84ae6223
AJ
505 default: // no other flags should ever occur
506 debugs(11, 2, io.conn << ": read failure: " << xstrerr(rd.xerrno));
507 mustStop("unknown ICAP I/O read error");
508 return;
c824c43b 509 }
774c051c 510
bd7f2ede 511 handleCommRead(io.size);
774c051c 512}
513
26cc52cb 514void Adaptation::Icap::Xaction::cancelRead()
774c051c 515{
bd7f2ede 516 if (reader != NULL) {
aed188fd 517 Must(haveConnection());
0d4e382b 518 Comm::ReadCancel(connection->fd, reader);
bd7f2ede 519 reader = NULL;
774c051c 520 }
521}
522
63df1d28
AJ
523bool
524Adaptation::Icap::Xaction::parseHttpMsg(Http::Message *msg)
774c051c 525{
84ae6223 526 debugs(93, 5, "have " << readBuf.length() << " head bytes to parse");
774c051c 527
955394ce 528 Http::StatusCode error = Http::scNone;
84ae6223
AJ
529 // XXX: performance regression c_str() data copies
530 const char *buf = readBuf.c_str();
531 const bool parsed = msg->parse(buf, readBuf.length(), commEof, &error);
774c051c 532 Must(parsed || !error); // success or need more data
533
f53969cc 534 if (!parsed) { // need more data
774c051c 535 Must(mayReadMore());
536 msg->reset();
537 return false;
538 }
539
540 readBuf.consume(msg->hdr_sz);
541 return true;
542}
543
26cc52cb 544bool Adaptation::Icap::Xaction::mayReadMore() const
774c051c 545{
546 return !doneReading() && // will read more data
2b42f3fd 547 readBuf.length() < SQUID_TCP_SO_RCVBUF; // have space for more data
774c051c 548}
549
26cc52cb 550bool Adaptation::Icap::Xaction::doneReading() const
774c051c 551{
552 return commEof;
553}
554
26cc52cb 555bool Adaptation::Icap::Xaction::doneWriting() const
c99de607 556{
557 return !writer;
558}
559
26cc52cb 560bool Adaptation::Icap::Xaction::doneWithIo() const
c99de607 561{
aed188fd 562 return haveConnection() &&
9e008dda
AJ
563 !connector && !reader && !writer && // fast checks, some redundant
564 doneReading() && doneWriting();
c99de607 565}
566
aed188fd
AJ
567bool Adaptation::Icap::Xaction::haveConnection() const
568{
569 return connection != NULL && connection->isOpen();
570}
571
c824c43b 572// initiator aborted
26cc52cb 573void Adaptation::Icap::Xaction::noteInitiatorAborted()
774c051c 574{
c824c43b 575
4299f876 576 if (theInitiator.set()) {
07e72f8e 577 debugs(93,4, HERE << "Initiator gone before ICAP transaction ended");
c824c43b 578 clearInitiator();
64b66b76 579 detailError(ERR_DETAIL_ICAP_INIT_GONE);
07e72f8e 580 setOutcome(xoGone);
c824c43b 581 mustStop("initiator aborted");
c99de607 582 }
c824c43b 583
774c051c 584}
585
3ff65596
AR
586void Adaptation::Icap::Xaction::setOutcome(const Adaptation::Icap::XactOutcome &xo)
587{
588 if (al.icap.outcome != xoUnknown) {
589 debugs(93, 3, HERE << "Warning: reseting outcome: from " <<
e1381638 590 al.icap.outcome << " to " << xo);
3ff65596
AR
591 } else {
592 debugs(93, 4, HERE << xo);
593 }
594 al.icap.outcome = xo;
595}
596
5f8252d2 597// This 'last chance' method is called before a 'done' transaction is deleted.
598// It is wrong to call virtual methods from a destructor. Besides, this call
599// indicates that the transaction will terminate as planned.
26cc52cb 600void Adaptation::Icap::Xaction::swanSong()
774c051c 601{
5f8252d2 602 // kids should sing first and then call the parent method.
5c720aa0 603 if (cs.valid()) {
45f2e27f
A
604 debugs(93,6, HERE << id << " about to notify ConnOpener!");
605 CallJobHere(93, 3, cs, Comm::ConnOpener, noteAbort);
606 cs = NULL;
607 service().noteConnectionFailed("abort");
d471b08a 608 }
5f8252d2 609
610 closeConnection(); // TODO: rename because we do not always close
611
84ae6223 612 readBuf.clear();
774c051c 613
4299f876 614 tellQueryAborted();
3ff65596
AR
615
616 maybeLog();
774c051c 617
0bef8dd7 618 Adaptation::Initiate::swanSong();
774c051c 619}
620
e1381638
AJ
621void Adaptation::Icap::Xaction::tellQueryAborted()
622{
4299f876 623 if (theInitiator.set()) {
b248c2a3 624 Adaptation::Icap::XactAbortInfo abortInfo(icapRequest, icapReply.getRaw(),
4cb2536f 625 retriable(), repeatable());
4299f876
AR
626 Launcher *launcher = dynamic_cast<Launcher*>(theInitiator.get());
627 // launcher may be nil if initiator is invalid
628 CallJobHere1(91,5, CbcPointer<Launcher>(launcher),
4cb2536f 629 Launcher, noteXactAbort, abortInfo);
4299f876
AR
630 clearInitiator();
631 }
3ff65596
AR
632}
633
e1381638
AJ
634void Adaptation::Icap::Xaction::maybeLog()
635{
636 if (IcapLogfileStatus == LOG_ENABLE) {
8ebad780
CT
637 finalizeLogInfo();
638 icapLogLog(alep);
3ff65596
AR
639 }
640}
641
642void Adaptation::Icap::Xaction::finalizeLogInfo()
643{
644 //prepare log data
645 al.icp.opcode = ICP_INVALID;
e1381638 646
3ff65596
AR
647 const Adaptation::Icap::ServiceRep &s = service();
648 al.icap.hostAddr = s.cfg().host.termedBuf();
649 al.icap.serviceName = s.cfg().key;
650 al.icap.reqUri = s.cfg().uri;
e1381638 651
01bd87d8
CT
652 tvSub(al.icap.ioTime, icap_tio_start, icap_tio_finish);
653 tvSub(al.icap.trTime, icap_tr_start, current_time);
3ff65596 654
b248c2a3
AJ
655 al.icap.request = icapRequest;
656 HTTPMSGLOCK(al.icap.request);
657 if (icapReply != NULL) {
658 al.icap.reply = icapReply.getRaw();
659 HTTPMSGLOCK(al.icap.reply);
9b769c67 660 al.icap.resStatus = icapReply->sline.status();
3ff65596
AR
661 }
662}
663
774c051c 664// returns a temporary string depicting transaction status, for debugging
26cc52cb 665const char *Adaptation::Icap::Xaction::status() const
774c051c 666{
667 static MemBuf buf;
668 buf.reset();
5f8252d2 669 buf.append(" [", 2);
774c051c 670 fillPendingStatus(buf);
671 buf.append("/", 1);
672 fillDoneStatus(buf);
f2e41480 673 buf.appendf(" %s%u]", id.prefix(), id.value);
774c051c 674 buf.terminate();
675
676 return buf.content();
677}
678
26cc52cb 679void Adaptation::Icap::Xaction::fillPendingStatus(MemBuf &buf) const
774c051c 680{
aed188fd 681 if (haveConnection()) {
4391cd15 682 buf.appendf("FD %d", connection->fd);
774c051c 683
bd7f2ede 684 if (writer != NULL)
774c051c 685 buf.append("w", 1);
686
bd7f2ede 687 if (reader != NULL)
774c051c 688 buf.append("r", 1);
689
5f8252d2 690 buf.append(";", 1);
774c051c 691 }
692}
693
26cc52cb 694void Adaptation::Icap::Xaction::fillDoneStatus(MemBuf &buf) const
774c051c 695{
aed188fd 696 if (haveConnection() && commEof)
4391cd15 697 buf.appendf("Comm(%d)", connection->fd);
774c051c 698
699 if (stopReason != NULL)
4391cd15 700 buf.append("Stopped", 7);
774c051c 701}
3cfc19b3 702
ced8def3 703bool Adaptation::Icap::Xaction::fillVirginHttpHeader(MemBuf &) const
3cfc19b3 704{
705 return false;
706}
f53969cc 707
eba8d9bb 708bool
0166128b 709Ssl::IcapPeerConnector::initialize(Security::SessionPointer &serverSession)
1b091aec 710{
0166128b 711 if (!Security::PeerConnector::initialize(serverSession))
eba8d9bb 712 return false;
1b091aec
CT
713
714 assert(!icapService->cfg().secure.sslDomain.isEmpty());
5d9a65df 715#if USE_OPENSSL
1b091aec 716 SBuf *host = new SBuf(icapService->cfg().secure.sslDomain);
eba8d9bb 717 SSL_set_ex_data(serverSession.get(), ssl_ex_index_server, host);
1b091aec 718
eba8d9bb 719 ACLFilledChecklist *check = static_cast<ACLFilledChecklist *>(SSL_get_ex_data(serverSession.get(), ssl_ex_index_cert_error_check));
1b091aec
CT
720 if (check)
721 check->dst_peer_name = *host;
5d9a65df 722#endif
1b091aec 723
5d9a65df 724 Security::SetSessionResumeData(serverSession, icapService->sslSession);
eba8d9bb 725 return true;
1b091aec
CT
726}
727
728void
729Ssl::IcapPeerConnector::noteNegotiationDone(ErrorState *error)
730{
731 if (error)
732 return;
733
734 const int fd = serverConnection()->fd;
5d9a65df 735 Security::MaybeGetSessionResumeData(fd_table[fd].ssl, icapService->sslSession);
1b091aec
CT
736}
737
738void
739Adaptation::Icap::Xaction::handleSecuredPeer(Security::EncryptorAnswer &answer)
740{
741 Must(securer != NULL);
742 securer = NULL;
743
744 if (closer != NULL) {
745 if (answer.conn != NULL)
746 comm_remove_close_handler(answer.conn->fd, closer);
747 else
748 closer->cancel("securing completed");
749 closer = NULL;
750 }
751
752 if (answer.error.get()) {
753 if (answer.conn != NULL)
754 answer.conn->close();
755 debugs(93, 2, typeName <<
0166128b 756 " TLS negotiation to " << service().cfg().uri << " failed");
1b091aec
CT
757 service().noteConnectionFailed("failure");
758 detailError(ERR_DETAIL_ICAP_XACT_SSL_START);
0166128b 759 throw TexcHere("cannot connect to the TLS ICAP service");
1b091aec
CT
760 }
761
0166128b 762 debugs(93, 5, "TLS negotiation to " << service().cfg().uri << " complete");
1b091aec
CT
763
764 service().noteConnectionUse(answer.conn);
765
766 handleCommConnected();
767}
1b091aec 768