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