]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
Revert r14137 due to compile errors
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
dd11e0b7 1/*
bde978a6 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
dd11e0b7 3 *
bbc27441
AJ
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.
dd11e0b7 7 */
f88bb09c 8
bbc27441
AJ
9/* DEBUG: section 33 Client-side Routines */
10
63be0a78 11/**
12 \defgroup ClientSide Client-Side Logics
13 *
d85b8894 14 \section cserrors Errors and client side
edce4d98 15 *
63be0a78 16 \par Problem the first:
17 * the store entry is no longer authoritative on the
edce4d98 18 * reply status. EBITTEST (E_ABORT) is no longer a valid test outside
19 * of client_side_reply.c.
20 * Problem the second: resources are wasted if we delay in cleaning up.
21 * Problem the third we can't depend on a connection close to clean up.
9e008dda 22 *
63be0a78 23 \par Nice thing the first:
9e008dda 24 * Any step in the stream can callback with data
edce4d98 25 * representing an error.
26 * Nice thing the second: once you stop requesting reads from upstream,
27 * upstream can be stopped too.
28 *
63be0a78 29 \par Solution #1:
30 * Error has a callback mechanism to hand over a membuf
9e008dda 31 * with the error content. The failing node pushes that back as the
edce4d98 32 * reply. Can this be generalised to reduce duplicate efforts?
33 * A: Possibly. For now, only one location uses this.
34 * How to deal with pre-stream errors?
35 * Tell client_side_reply that we *want* an error page before any
36 * stream calls occur. Then we simply read as normal.
63be0a78 37 *
38 *
d85b8894 39 \section pconn_logic Persistent connection logic:
63be0a78 40 *
41 \par
42 * requests (httpClientRequest structs) get added to the connection
43 * list, with the current one being chr
9e008dda 44 *
63be0a78 45 \par
46 * The request is *immediately* kicked off, and data flows through
47 * to clientSocketRecipient.
9e008dda 48 *
63be0a78 49 \par
50 * If the data that arrives at clientSocketRecipient is not for the current
51 * request, clientSocketRecipient simply returns, without requesting more
52 * data, or sending it.
53 *
54 \par
9e008dda
AJ
55 * ClientKeepAliveNextRequest will then detect the presence of data in
56 * the next ClientHttpRequest, and will send it, restablishing the
63be0a78 57 * data flow.
edce4d98 58 */
59
582c2af2 60#include "squid.h"
04f55905 61#include "acl/FilledChecklist.h"
65d448bc 62#include "anyp/PortCfg.h"
00406b24 63#include "base/Subscription.h"
d841c88d 64#include "base/TextException.h"
a011edee 65#include "CachePeer.h"
95e6d864 66#include "client_db.h"
602d9612 67#include "client_side.h"
04f55905
AJ
68#include "client_side_reply.h"
69#include "client_side_request.h"
70#include "ClientRequestContext.h"
c8be6d7b 71#include "clientStream.h"
c4b7a5a9 72#include "comm.h"
cfd66529 73#include "comm/Connection.h"
d841c88d 74#include "comm/Loops.h"
7e66d5e2 75#include "comm/Read.h"
cbff89ba 76#include "comm/TcpAcceptor.h"
582c2af2
FC
77#include "comm/Write.h"
78#include "CommCalls.h"
8eb0a7ee 79#include "errorpage.h"
c4ad1349 80#include "fd.h"
04f55905 81#include "fde.h"
95e6d864 82#include "fqdncache.h"
eb13c21e 83#include "FwdState.h"
67679543 84#include "globals.h"
24438ec5
AJ
85#include "helper.h"
86#include "helper/Reply.h"
5c0c642e 87#include "http.h"
c99510dd 88#include "http/one/RequestParser.h"
db1720f8 89#include "http/one/TeChunkedParser.h"
25b6a907 90#include "HttpHdrContRange.h"
a5bac1d2 91#include "HttpHeaderTools.h"
528b2c61 92#include "HttpReply.h"
93#include "HttpRequest.h"
4daaf3cb
AJ
94#include "ident/Config.h"
95#include "ident/Ident.h"
308e60be 96#include "internal.h"
cbff89ba 97#include "ipc/FdNotes.h"
fe090a86 98#include "ipc/StartListening.h"
1c7ae5ff 99#include "log/access_log.h"
0eb49b6d 100#include "MemBuf.h"
04f55905 101#include "MemObject.h"
b6149797 102#include "mime_header.h"
b1cef121 103#include "parser/Tokenizer.h"
582c2af2 104#include "profiler/Profiler.h"
1fa9b1a7 105#include "rfc1738.h"
92ae4c86 106#include "servers/forward.h"
4d5904f7 107#include "SquidConfig.h"
582c2af2 108#include "SquidTime.h"
e1656dc4 109#include "StatCounters.h"
00a7574e 110#include "StatHist.h"
582c2af2
FC
111#include "Store.h"
112#include "TimeOrTag.h"
4e540555 113#include "tools.h"
b1bd952a 114#include "URL.h"
582c2af2
FC
115
116#if USE_AUTH
117#include "auth/UserRequest.h"
118#endif
119#if USE_DELAY_POOLS
120#include "ClientInfo.h"
121#endif
cb4f4424 122#if USE_OPENSSL
d620ae0e 123#include "ssl/bio.h"
95d2589c 124#include "ssl/context_storage.h"
602d9612 125#include "ssl/gadgets.h"
95d2589c 126#include "ssl/helper.h"
602d9612 127#include "ssl/ProxyCerts.h"
fd4624d7 128#include "ssl/ServerBump.h"
4db984be 129#include "ssl/support.h"
95d2589c
CT
130#endif
131#if USE_SSL_CRTD
95d2589c 132#include "ssl/certificate_db.h"
602d9612 133#include "ssl/crtd_message.h"
95d2589c
CT
134#endif
135
ed6e9fb9
AJ
136// for tvSubUsec() which should be in SquidTime.h
137#include "util.h"
138
074d6a40
AJ
139#include <climits>
140#include <cmath>
95d2589c 141#include <limits>
95d2589c 142
5492ad1d 143#if LINGERING_CLOSE
144#define comm_close comm_lingering_close
145#endif
146
cbff89ba 147/// dials clientListenerConnectionOpened call
fe090a86
AR
148class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
149{
150public:
fa720bfb
AJ
151 typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
152 ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
f53969cc 153 handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}
fe090a86 154
5667a628
AR
155 virtual void print(std::ostream &os) const {
156 startPrint(os) <<
f53969cc 157 ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
5667a628 158 }
fe090a86
AR
159
160 virtual bool canDial(AsyncCall &) const { return true; }
8bbb16e3 161 virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }
fe090a86
AR
162
163public:
164 Handler handler;
165
166private:
fa720bfb 167 AnyP::PortCfgPointer portCfg; ///< from HttpPortList
cbff89ba 168 Ipc::FdNoteId portTypeNote; ///< Type of IPC socket being opened
cbff89ba 169 Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
fe090a86
AR
170};
171
fa720bfb 172static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);
fe090a86 173
edce4d98 174/* our socket-related context */
62e76326 175
0655fa4d 176CBDATA_CLASS_INIT(ClientSocketContext);
62e76326 177
edce4d98 178/* Local functions */
2b663917 179static IOCB clientWriteComplete;
180static IOCB clientWriteBodyComplete;
e0d28505 181static IOACB httpAccept;
cb4f4424 182#if USE_OPENSSL
e0d28505 183static IOACB httpsAccept;
40d34a62 184#endif
8d77a37c 185static CTCB clientLifetimeTimeout;
3898f57f 186#if USE_IDENT
05832ae1 187static IDCB clientIdentDone;
3898f57f 188#endif
190154cf 189static int clientIsContentLengthValid(HttpRequest * r);
47f6e231 190static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
50c09fc4 191
02c8dde5
AJ
192static void clientUpdateStatHistCounters(LogTags logType, int svc_time);
193static void clientUpdateStatCounters(LogTags logType);
c8be6d7b 194static void clientUpdateHierCounters(HierarchyLogEntry *);
528b2c61 195static bool clientPingHasFinished(ping_data const *aPing);
41ebd397 196void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);
1cf238db 197static void ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn);
02c8dde5 198static void clientUpdateSocketStats(LogTags logType, size_t size);
c8be6d7b 199
84cc2635 200char *skipLeadingSpace(char *aString);
62e76326 201
c8be6d7b 202clientStreamNode *
528b2c61 203ClientSocketContext::getTail() const
c8be6d7b 204{
0655fa4d 205 if (http->client_stream.tail)
206 return (clientStreamNode *)http->client_stream.tail->data;
207
208 return NULL;
c8be6d7b 209}
210
211clientStreamNode *
528b2c61 212ClientSocketContext::getClientReplyContext() const
c8be6d7b 213{
528b2c61 214 return (clientStreamNode *)http->client_stream.tail->prev->data;
c8be6d7b 215}
216
434a79b0
DK
217ConnStateData *
218ClientSocketContext::getConn() const
219{
220 return http->getConn();
221}
222
63be0a78 223/**
7e66d5e2
AJ
224 * This routine should be called to grow the in.buf and then
225 * call Comm::Read().
c4b7a5a9 226 */
227void
a46d2c0e 228ConnStateData::readSomeData()
c4b7a5a9 229{
a46d2c0e 230 if (reading())
62e76326 231 return;
232
73c36fd9 233 debugs(33, 4, HERE << clientConnection << ": reading request...");
62e76326 234
4d1376d7
MM
235 // we can only read if there is more than 1 byte of space free
236 if (Config.maxRequestBufferSize - in.buf.length() < 2)
1368d115 237 return;
62e76326 238
1cf238db 239 typedef CommCbMemFunT<ConnStateData, CommIoCbParams> Dialer;
d1c7f781 240 reader = JobCallback(33, 5, Dialer, this, ConnStateData::clientReadRequest);
7e66d5e2 241 Comm::Read(clientConnection, reader);
c4b7a5a9 242}
62e76326 243
c8be6d7b 244void
1cf238db 245ClientSocketContext::removeFromConnectionList(ConnStateData * conn)
c8be6d7b 246{
0655fa4d 247 ClientSocketContext::Pointer *tempContextPointer;
1cf238db 248 assert(conn != NULL && cbdataReferenceValid(conn));
94a396a3 249 assert(conn->getCurrentContext() != NULL);
c8be6d7b 250 /* Unlink us from the connection request list */
0655fa4d 251 tempContextPointer = & conn->currentobject;
62e76326 252
0655fa4d 253 while (tempContextPointer->getRaw()) {
62e76326 254 if (*tempContextPointer == this)
255 break;
256
257 tempContextPointer = &(*tempContextPointer)->next;
c8be6d7b 258 }
62e76326 259
0655fa4d 260 assert(tempContextPointer->getRaw() != NULL);
528b2c61 261 *tempContextPointer = next;
262 next = NULL;
c8be6d7b 263}
ea6f43cd 264
0655fa4d 265ClientSocketContext::~ClientSocketContext()
f88bb09c 266{
0655fa4d 267 clientStreamNode *node = getTail();
268
269 if (node) {
270 ClientSocketContext *streamContext = dynamic_cast<ClientSocketContext *> (node->data.getRaw());
271
272 if (streamContext) {
273 /* We are *always* the tail - prevent recursive free */
274 assert(this == streamContext);
275 node->data = NULL;
276 }
277 }
278
279 if (connRegistered_)
280 deRegisterWithConn();
281
282 httpRequestFree(http);
283
edce4d98 284 /* clean up connection links to us */
c53577d9 285 assert(this != next.getRaw());
0655fa4d 286}
62e76326 287
0655fa4d 288void
289ClientSocketContext::registerWithConn()
290{
291 assert (!connRegistered_);
292 assert (http);
94a396a3 293 assert (http->getConn() != NULL);
0655fa4d 294 connRegistered_ = true;
98242069 295 http->getConn()->addContextToQueue(this);
0655fa4d 296}
297
298void
299ClientSocketContext::deRegisterWithConn()
300{
301 assert (connRegistered_);
98242069 302 removeFromConnectionList(http->getConn());
0655fa4d 303 connRegistered_ = false;
304}
305
306void
307ClientSocketContext::connIsFinished()
308{
309 assert (http);
94a396a3 310 assert (http->getConn() != NULL);
0655fa4d 311 deRegisterWithConn();
312 /* we can't handle any more stream data - detach */
313 clientStreamDetach(getTail(), http);
314}
315
bead5d2c 316ClientSocketContext::ClientSocketContext(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq) :
f53969cc
SM
317 clientConnection(aConn),
318 http(aReq),
319 reply(NULL),
320 next(NULL),
321 writtenToSocket(0),
322 mayUseConnection_ (false),
323 connRegistered_ (false)
0655fa4d 324{
bead5d2c 325 assert(http != NULL);
0655fa4d 326 memset (reqbuf, '\0', sizeof (reqbuf));
327 flags.deferred = 0;
328 flags.parsed_ok = 0;
329 deferredparams.node = NULL;
330 deferredparams.rep = NULL;
f88bb09c 331}
332
655daa06
AR
333void
334ClientSocketContext::writeControlMsg(HttpControlMsg &msg)
335{
92ae4c86 336 HttpReply::Pointer rep(msg.reply);
b248c2a3 337 Must(rep != NULL);
655daa06 338
655daa06
AR
339 // remember the callback
340 cbControlMsgSent = msg.cbSuccess;
341
655daa06
AR
342 AsyncCall::Pointer call = commCbCall(33, 5, "ClientSocketContext::wroteControlMsg",
343 CommIoCbPtrFun(&WroteControlMsg, this));
655daa06 344
92ae4c86 345 getConn()->writeControlMsgAndCall(this, rep.getRaw(), call);
655daa06
AR
346}
347
348/// called when we wrote the 1xx response
349void
c8407295 350ClientSocketContext::wroteControlMsg(const Comm::ConnectionPointer &conn, char *, size_t, Comm::Flag errflag, int xerrno)
655daa06 351{
c8407295 352 if (errflag == Comm::ERR_CLOSING)
655daa06
AR
353 return;
354
c8407295 355 if (errflag == Comm::OK) {
655daa06
AR
356 ScheduleCallHere(cbControlMsgSent);
357 return;
358 }
359
360 debugs(33, 3, HERE << "1xx writing failed: " << xstrerr(xerrno));
361 // no error notification: see HttpControlMsg.h for rationale and
362 // note that some errors are detected elsewhere (e.g., close handler)
363
364 // close on 1xx errors to be conservative and to simplify the code
365 // (if we do not close, we must notify the source of a failure!)
80463bb4 366 conn->close();
92ae4c86
AR
367
368 // XXX: writeControlMsgAndCall() should handle writer-specific writing
369 // results, including errors and then call us with success/failure outcome.
655daa06
AR
370}
371
372/// wroteControlMsg() wrapper: ClientSocketContext is not an AsyncJob
373void
c8407295 374ClientSocketContext::WroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag, int xerrno, void *data)
655daa06
AR
375{
376 ClientSocketContext *context = static_cast<ClientSocketContext*>(data);
e0d28505 377 context->wroteControlMsg(conn, bufnotused, size, errflag, xerrno);
655daa06
AR
378}
379
edce4d98 380#if USE_IDENT
4d55827a 381static void
edce4d98 382clientIdentDone(const char *ident, void *data)
4d55827a 383{
cc59d02a 384 ConnStateData *conn = (ConnStateData *)data;
73c36fd9 385 xstrncpy(conn->clientConnection->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
e81957b7 386}
447e176b 387#endif
7a2f978b 388
c8be6d7b 389void
02c8dde5 390clientUpdateStatCounters(LogTags logType)
a7c05555 391{
e4f1fdae 392 ++statCounter.client_http.requests;
62e76326 393
c8be6d7b 394 if (logTypeIsATcpHit(logType))
e4f1fdae 395 ++statCounter.client_http.hits;
62e76326 396
c8be6d7b 397 if (logType == LOG_TCP_HIT)
e4f1fdae 398 ++statCounter.client_http.disk_hits;
c8be6d7b 399 else if (logType == LOG_TCP_MEM_HIT)
e4f1fdae 400 ++statCounter.client_http.mem_hits;
c8be6d7b 401}
402
403void
02c8dde5 404clientUpdateStatHistCounters(LogTags logType, int svc_time)
c8be6d7b 405{
e8baef82 406 statCounter.client_http.allSvcTime.count(svc_time);
63be0a78 407 /**
ee1679df 408 * The idea here is not to be complete, but to get service times
409 * for only well-defined types. For example, we don't include
1d7ab0f4 410 * LOG_TCP_REFRESH_FAIL because its not really a cache hit
ee1679df 411 * (we *tried* to validate it, but failed).
412 */
62e76326 413
c8be6d7b 414 switch (logType) {
62e76326 415
1d7ab0f4 416 case LOG_TCP_REFRESH_UNMODIFIED:
e8baef82 417 statCounter.client_http.nearHitSvcTime.count(svc_time);
62e76326 418 break;
419
ee1679df 420 case LOG_TCP_IMS_HIT:
e8baef82 421 statCounter.client_http.nearMissSvcTime.count(svc_time);
62e76326 422 break;
423
ee1679df 424 case LOG_TCP_HIT:
62e76326 425
ee1679df 426 case LOG_TCP_MEM_HIT:
62e76326 427
b540e168 428 case LOG_TCP_OFFLINE_HIT:
e8baef82 429 statCounter.client_http.hitSvcTime.count(svc_time);
62e76326 430 break;
431
ee1679df 432 case LOG_TCP_MISS:
62e76326 433
ee1679df 434 case LOG_TCP_CLIENT_REFRESH_MISS:
e8baef82 435 statCounter.client_http.missSvcTime.count(svc_time);
62e76326 436 break;
437
ee1679df 438 default:
62e76326 439 /* make compiler warnings go away */
440 break;
ee1679df 441 }
c8be6d7b 442}
443
528b2c61 444bool
c8be6d7b 445clientPingHasFinished(ping_data const *aPing)
446{
447 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
62e76326 448 return true;
449
528b2c61 450 return false;
c8be6d7b 451}
452
453void
454clientUpdateHierCounters(HierarchyLogEntry * someEntry)
455{
456 ping_data *i;
62e76326 457
c8547a11 458 switch (someEntry->code) {
459#if USE_CACHE_DIGESTS
62e76326 460
c8547a11 461 case CD_PARENT_HIT:
62e76326 462
a196e1e4 463 case CD_SIBLING_HIT:
95dc7ff4 464 ++ statCounter.cd.times_used;
62e76326 465 break;
c8547a11 466#endif
62e76326 467
c8547a11 468 case SIBLING_HIT:
62e76326 469
c8547a11 470 case PARENT_HIT:
62e76326 471
c8547a11 472 case FIRST_PARENT_MISS:
62e76326 473
c8547a11 474 case CLOSEST_PARENT_MISS:
95dc7ff4 475 ++ statCounter.icp.times_used;
62e76326 476 i = &someEntry->ping;
477
478 if (clientPingHasFinished(i))
e8baef82 479 statCounter.icp.querySvcTime.count(tvSubUsec(i->start, i->stop));
62e76326 480
481 if (i->timeout)
95dc7ff4 482 ++ statCounter.icp.query_timeouts;
62e76326 483
484 break;
485
c8547a11 486 case CLOSEST_PARENT:
62e76326 487
c8547a11 488 case CLOSEST_DIRECT:
95dc7ff4 489 ++ statCounter.netdb.times_used;
62e76326 490
491 break;
492
69c95dd3 493 default:
62e76326 494 break;
17b6e784 495 }
a7c05555 496}
497
528b2c61 498void
499ClientHttpRequest::updateCounters()
c8be6d7b 500{
528b2c61 501 clientUpdateStatCounters(logType);
62e76326 502
528b2c61 503 if (request->errType != ERR_NONE)
95dc7ff4 504 ++ statCounter.client_http.errors;
62e76326 505
528b2c61 506 clientUpdateStatHistCounters(logType,
af0ded40 507 tvSubMsec(al->cache.start_time, current_time));
62e76326 508
528b2c61 509 clientUpdateHierCounters(&request->hier);
c8be6d7b 510}
511
edce4d98 512void
41ebd397 513prepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry::Pointer &aLogEntry)
7a2f978b 514{
c8be6d7b 515 assert(request);
41ebd397 516 assert(aLogEntry != NULL);
7684c4b1 517
518 if (Config.onoff.log_mime_hdrs) {
7684c4b1 519 MemBuf mb;
2fe7eff9 520 mb.init();
10201568 521 request->header.packInto(&mb);
105d1937
A
522 //This is the request after adaptation or redirection
523 aLogEntry->headers.adapted_request = xstrdup(mb.buf);
524
525 // the virgin request is saved to aLogEntry->request
526 if (aLogEntry->request) {
105d1937 527 mb.reset();
10201568 528 aLogEntry->request->header.packInto(&mb);
105d1937
A
529 aLogEntry->headers.request = xstrdup(mb.buf);
530 }
3ff65596 531
5038f9d8
AR
532#if USE_ADAPTATION
533 const Adaptation::History::Pointer ah = request->adaptLogHistory();
534 if (ah != NULL) {
5038f9d8 535 mb.reset();
10201568 536 ah->lastMeta.packInto(&mb);
99690f32 537 aLogEntry->adapt.last_meta = xstrdup(mb.buf);
5038f9d8 538 }
3ff65596
AR
539#endif
540
2fe7eff9 541 mb.clean();
7684c4b1 542 }
543
3ff65596 544#if ICAP_CLIENT
5038f9d8 545 const Adaptation::Icap::History::Pointer ih = request->icapHistory();
3ff65596 546 if (ih != NULL)
01bd87d8 547 ih->processingTime(aLogEntry->icap.processingTime);
3ff65596
AR
548#endif
549
c8be6d7b 550 aLogEntry->http.method = request->method;
551 aLogEntry->http.version = request->http_ver;
c8be6d7b 552 aLogEntry->hier = request->hier;
3ff65596 553 if (request->content_length > 0) // negative when no body or unknown length
cc0ca3b9 554 aLogEntry->http.clientRequestSz.payloadData += request->content_length; // XXX: actually adaptedRequest payload size ??
5b4117d8 555 aLogEntry->cache.extuser = request->extacl_user.termedBuf();
abb929f0 556
a119c6ad
AR
557 // Adapted request, if any, inherits and then collects all the stats, but
558 // the virgin request gets logged instead; copy the stats to log them.
559 // TODO: avoid losses by keeping these stats in a shared history object?
64b66b76 560 if (aLogEntry->request) {
a119c6ad 561 aLogEntry->request->dnsWait = request->dnsWait;
64b66b76
CT
562 aLogEntry->request->errType = request->errType;
563 aLogEntry->request->errDetail = request->errDetail;
564 }
c8be6d7b 565}
566
567void
528b2c61 568ClientHttpRequest::logRequest()
569{
f5f9e44c 570 if (!out.size && !logType)
6fd1086a
AR
571 debugs(33, 5, HERE << "logging half-baked transaction: " << log_uri);
572
41ebd397
CT
573 al->icp.opcode = ICP_INVALID;
574 al->url = log_uri;
575 debugs(33, 9, "clientLogRequest: al.url='" << al->url << "'");
62e76326 576
41ebd397 577 if (al->reply) {
9b769c67 578 al->http.code = al->reply->sline.status();
41ebd397 579 al->http.content_type = al->reply->content_type.termedBuf();
6fd1086a 580 } else if (loggingEntry() && loggingEntry()->mem_obj) {
9b769c67 581 al->http.code = loggingEntry()->mem_obj->getReply()->sline.status();
41ebd397 582 al->http.content_type = loggingEntry()->mem_obj->getReply()->content_type.termedBuf();
6fd1086a 583 }
5f8252d2 584
41ebd397 585 debugs(33, 9, "clientLogRequest: http.code='" << al->http.code << "'");
90a8964c 586
3a646078 587 if (loggingEntry() && loggingEntry()->mem_obj && loggingEntry()->objectLen() >= 0)
d6df21d2 588 al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
90a8964c 589
cc0ca3b9
AJ
590 al->http.clientRequestSz.header = req_sz;
591 al->http.clientReplySz.header = out.headers_sz;
d6df21d2 592 // XXX: calculate without payload encoding or headers !!
cc0ca3b9 593 al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
90a8964c 594
41ebd397 595 al->cache.highOffset = out.offset;
90a8964c 596
41ebd397 597 al->cache.code = logType;
62e76326 598
01bd87d8 599 tvSub(al->cache.trTime, al->cache.start_time, current_time);
62e76326 600
6fd1086a 601 if (request)
41ebd397 602 prepareLogWithRequestDetails(request, al);
62e76326 603
1b76e6c1 604 if (getConn() != NULL && getConn()->clientConnection != NULL && getConn()->clientConnection->rfc931[0])
41ebd397 605 al->cache.rfc931 = getConn()->clientConnection->rfc931;
62e76326 606
cb4f4424 607#if USE_OPENSSL && 0
62e76326 608
6fd1086a
AR
609 /* This is broken. Fails if the connection has been closed. Needs
610 * to snarf the ssl details some place earlier..
611 */
612 if (getConn() != NULL)
41ebd397 613 al->cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl);
62e76326 614
a7ad6e4e 615#endif
62e76326 616
f8a2338f
AJ
617 /* Add notes (if we have a request to annotate) */
618 if (request) {
619 // The al->notes and request->notes must point to the same object.
620 (void)SyncNotes(*al, *request);
621 for (auto i = Config.notes.begin(); i != Config.notes.end(); ++i) {
622 if (const char *value = (*i)->match(request, al->reply, NULL)) {
623 NotePairs &notes = SyncNotes(*al, *request);
624 notes.add((*i)->key.termedBuf(), value);
625 debugs(33, 3, (*i)->key.termedBuf() << " " << value);
626 }
d7f4a0b7
CT
627 }
628 }
629
8ebad780 630 ACLFilledChecklist checklist(NULL, request, NULL);
b248c2a3 631 if (al->reply) {
8ebad780
CT
632 checklist.reply = al->reply;
633 HTTPMSGLOCK(checklist.reply);
b248c2a3 634 }
62e76326 635
8ebad780
CT
636 if (request) {
637 al->adapted_request = request;
638 HTTPMSGLOCK(al->adapted_request);
639 }
640 accessLogLog(al, &checklist);
641
642 bool updatePerformanceCounters = true;
643 if (Config.accessList.stats_collection) {
644 ACLFilledChecklist statsCheck(Config.accessList.stats_collection, request, NULL);
645 if (al->reply) {
646 statsCheck.reply = al->reply;
647 HTTPMSGLOCK(statsCheck.reply);
b248c2a3 648 }
8ebad780
CT
649 updatePerformanceCounters = (statsCheck.fastCheck() == ACCESS_ALLOWED);
650 }
651
652 if (updatePerformanceCounters) {
2ae98e09
TH
653 if (request)
654 updateCounters();
7684c4b1 655
1b76e6c1
AJ
656 if (getConn() != NULL && getConn()->clientConnection != NULL)
657 clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size);
7a2f978b 658 }
c8be6d7b 659}
660
661void
528b2c61 662ClientHttpRequest::freeResources()
c8be6d7b 663{
528b2c61 664 safe_free(uri);
665 safe_free(log_uri);
666 safe_free(redirect.location);
30abd221 667 range_iter.boundary.clean();
6dd9f4bd 668 HTTPMSGUNLOCK(request);
62e76326 669
528b2c61 670 if (client_stream.tail)
62e76326 671 clientStreamAbort((clientStreamNode *)client_stream.tail->data, this);
c8be6d7b 672}
673
674void
675httpRequestFree(void *data)
676{
59a1efb2 677 ClientHttpRequest *http = (ClientHttpRequest *)data;
c8be6d7b 678 assert(http != NULL);
528b2c61 679 delete http;
7a2f978b 680}
681
a46d2c0e 682bool
683ConnStateData::areAllContextsForThisConnection() const
c8be6d7b 684{
0655fa4d 685 ClientSocketContext::Pointer context = getCurrentContext();
62e76326 686
0655fa4d 687 while (context.getRaw()) {
98242069 688 if (context->http->getConn() != this)
a46d2c0e 689 return false;
62e76326 690
691 context = context->next;
c8be6d7b 692 }
62e76326 693
a46d2c0e 694 return true;
c8be6d7b 695}
696
697void
a46d2c0e 698ConnStateData::freeAllContexts()
c8be6d7b 699{
0655fa4d 700 ClientSocketContext::Pointer context;
62e76326 701
c53577d9 702 while ((context = getCurrentContext()).getRaw() != NULL) {
0655fa4d 703 assert(getCurrentContext() !=
704 getCurrentContext()->next);
705 context->connIsFinished();
706 assert (context != currentobject);
c8be6d7b 707 }
708}
709
f692498b
AJ
710/// propagates abort event to all contexts
711void
712ConnStateData::notifyAllContexts(int xerrno)
713{
714 typedef ClientSocketContext::Pointer CSCP;
715 for (CSCP c = getCurrentContext(); c.getRaw(); c = c->next)
716 c->noteIoError(xerrno);
717}
718
7a2f978b 719/* This is a handler normally called by comm_close() */
ced8def3 720void ConnStateData::connStateClosed(const CommCloseCbParams &)
7a2f978b 721{
6e1d409c 722 deleteThis("ConnStateData::connStateClosed");
a46d2c0e 723}
62e76326 724
cc1e110a
AJ
725#if USE_AUTH
726void
727ConnStateData::setAuth(const Auth::UserRequest::Pointer &aur, const char *by)
728{
729 if (auth_ == NULL) {
730 if (aur != NULL) {
731 debugs(33, 2, "Adding connection-auth to " << clientConnection << " from " << by);
732 auth_ = aur;
733 }
734 return;
735 }
736
737 // clobered with self-pointer
738 // NP: something nasty is going on in Squid, but harmless.
739 if (aur == auth_) {
740 debugs(33, 2, "WARNING: Ignoring duplicate connection-auth for " << clientConnection << " from " << by);
741 return;
742 }
743
744 /*
745 * Connection-auth relies on a single set of credentials being preserved
746 * for all requests on a connection once they have been setup.
747 * There are several things which need to happen to preserve security
748 * when connection-auth credentials change unexpectedly or are unset.
749 *
750 * 1) auth helper released from any active state
751 *
752 * They can only be reserved by a handshake process which this
753 * connection can now never complete.
754 * This prevents helpers hanging when their connections close.
755 *
756 * 2) pinning is expected to be removed and server conn closed
757 *
758 * The upstream link is authenticated with the same credentials.
759 * Expecting the same level of consistency we should have received.
760 * This prevents upstream being faced with multiple or missing
761 * credentials after authentication.
762 * NP: un-pin is left to the cleanup in ConnStateData::swanSong()
763 * we just trigger that cleanup here via comm_reset_close() or
764 * ConnStateData::stopReceiving()
765 *
766 * 3) the connection needs to close.
767 *
768 * This prevents attackers injecting requests into a connection,
769 * or gateways wrongly multiplexing users into a single connection.
770 *
771 * When credentials are missing closure needs to follow an auth
772 * challenge for best recovery by the client.
773 *
774 * When credentials change there is nothing we can do but abort as
775 * fast as possible. Sending TCP RST instead of an HTTP response
776 * is the best-case action.
777 */
778
779 // clobbered with nul-pointer
780 if (aur == NULL) {
781 debugs(33, 2, "WARNING: Graceful closure on " << clientConnection << " due to connection-auth erase from " << by);
782 auth_->releaseAuthServer();
783 auth_ = NULL;
784 // XXX: need to test whether the connection re-auth challenge is sent. If not, how to trigger it from here.
785 // NP: the current situation seems to fix challenge loops in Safari without visible issues in others.
786 // we stop receiving more traffic but can leave the Job running to terminate after the error or challenge is delivered.
787 stopReceiving("connection-auth removed");
788 return;
789 }
790
791 // clobbered with alternative credentials
792 if (aur != auth_) {
793 debugs(33, 2, "ERROR: Closing " << clientConnection << " due to change of connection-auth from " << by);
794 auth_->releaseAuthServer();
795 auth_ = NULL;
796 // this is a fatal type of problem.
797 // Close the connection immediately with TCP RST to abort all traffic flow
798 comm_reset_close(clientConnection);
799 return;
800 }
801
802 /* NOT REACHABLE */
803}
804#endif
805
6e1d409c 806// cleans up before destructor is called
a2ac85d9 807void
6e1d409c 808ConnStateData::swanSong()
a46d2c0e 809{
73c36fd9 810 debugs(33, 2, HERE << clientConnection);
f35961af 811 flags.readMore = false;
f53969cc 812 clientdbEstablished(clientConnection->remote, -1); /* decrement */
a46d2c0e 813 assert(areAllContextsForThisConnection());
814 freeAllContexts();
6e1d409c 815
89b1d7a2 816 unpinConnection(true);
e3a4aecc 817
73c36fd9
AJ
818 if (Comm::IsConnOpen(clientConnection))
819 clientConnection->close();
d67acb4e 820
cc1e110a
AJ
821#if USE_AUTH
822 // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
823 setAuth(NULL, "ConnStateData::SwanSong cleanup");
824#endif
825
6e1d409c
AR
826 BodyProducer::swanSong();
827 flags.swanSang = true;
a2ac85d9 828}
829
830bool
831ConnStateData::isOpen() const
832{
10b06767 833 return cbdataReferenceValid(this) && // XXX: checking "this" in a method
73c36fd9
AJ
834 Comm::IsConnOpen(clientConnection) &&
835 !fd_table[clientConnection->fd].closing();
a2ac85d9 836}
837
838ConnStateData::~ConnStateData()
839{
73c36fd9 840 debugs(33, 3, HERE << clientConnection);
a2ac85d9 841
842 if (isOpen())
e0236918 843 debugs(33, DBG_IMPORTANT, "BUG: ConnStateData did not close " << clientConnection);
6e1d409c
AR
844
845 if (!flags.swanSang)
e0236918 846 debugs(33, DBG_IMPORTANT, "BUG: ConnStateData was not destroyed properly; " << clientConnection);
62e76326 847
279152e7
AJ
848 if (bodyPipe != NULL)
849 stopProducingFor(bodyPipe, false);
87f237a9 850
cb4f4424 851#if USE_OPENSSL
fd4624d7 852 delete sslServerBump;
8a7fe008 853#endif
7a2f978b 854}
855
63be0a78 856/**
450fe1cb 857 * clientSetKeepaliveFlag() sets request->flags.proxyKeepalive.
c68e9c6b 858 * This is the client-side persistent connection flag. We need
859 * to set this relatively early in the request processing
860 * to handle hacks for broken servers and clients.
861 */
92ae4c86 862void
59a1efb2 863clientSetKeepaliveFlag(ClientHttpRequest * http)
c68e9c6b 864{
190154cf 865 HttpRequest *request = http->request;
f6329bc3 866
7f06a3d8
AJ
867 debugs(33, 3, "http_ver = " << request->http_ver);
868 debugs(33, 3, "method = " << request->method);
62e76326 869
4a1acc56 870 // TODO: move to HttpRequest::hdrCacheInit, just like HttpReply.
e857372a 871 request->flags.proxyKeepalive = request->persistent();
c68e9c6b 872}
873
31be8b80 874static int
190154cf 875clientIsContentLengthValid(HttpRequest * r)
31be8b80 876{
914b89a2 877 switch (r->method.id()) {
62e76326 878
c2a7cefd 879 case Http::METHOD_GET:
62e76326 880
c2a7cefd 881 case Http::METHOD_HEAD:
62e76326 882 /* We do not want to see a request entity on GET/HEAD requests */
883 return (r->content_length <= 0 || Config.onoff.request_entities);
884
ffc128c4 885 default:
62e76326 886 /* For other types of requests we don't care */
887 return 1;
ffc128c4 888 }
62e76326 889
ffc128c4 890 /* NOT REACHED */
31be8b80 891}
892
c8be6d7b 893int
47f6e231 894clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength)
efd900cb 895{
c8be6d7b 896 if (Config.maxRequestBodySize &&
62e76326 897 bodyLength > Config.maxRequestBodySize)
f53969cc 898 return 1; /* too large */
62e76326 899
efd900cb 900 return 0;
901}
902
39cb8c41 903// careful: the "current" context may be gone if we wrote an early response
0655fa4d 904ClientSocketContext::Pointer
905ConnStateData::getCurrentContext() const
c8be6d7b 906{
0655fa4d 907 return currentobject;
c8be6d7b 908}
909
910void
2324cda2 911ClientSocketContext::deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData)
528b2c61 912{
bf8fe701 913 debugs(33, 2, "clientSocketRecipient: Deferring request " << http->uri);
528b2c61 914 assert(flags.deferred == 0);
915 flags.deferred = 1;
916 deferredparams.node = node;
917 deferredparams.rep = rep;
2324cda2 918 deferredparams.queuedBuffer = receivedData;
c8be6d7b 919 return;
920}
921
0655fa4d 922bool
923ClientSocketContext::startOfOutput() const
c8be6d7b 924{
0655fa4d 925 return http->out.size == 0;
c8be6d7b 926}
927
528b2c61 928size_t
47f6e231 929ClientSocketContext::lengthToSend(Range<int64_t> const &available)
528b2c61 930{
47f6e231 931 /*the size of available range can always fit in a size_t type*/
932 size_t maximum = (size_t)available.size();
2512d159 933
528b2c61 934 if (!http->request->range)
62e76326 935 return maximum;
936
528b2c61 937 assert (canPackMoreRanges());
62e76326 938
528b2c61 939 if (http->range_iter.debt() == -1)
62e76326 940 return maximum;
941
528b2c61 942 assert (http->range_iter.debt() > 0);
62e76326 943
2512d159 944 /* TODO this + the last line could be a range intersection calculation */
47f6e231 945 if (available.start < http->range_iter.currentSpec()->offset)
2512d159 946 return 0;
947
d85c3078 948 return min(http->range_iter.debt(), (int64_t)maximum);
528b2c61 949}
950
c8be6d7b 951void
528b2c61 952ClientSocketContext::noteSentBodyBytes(size_t bytes)
953{
954 http->out.offset += bytes;
62e76326 955
528b2c61 956 if (!http->request->range)
62e76326 957 return;
958
528b2c61 959 if (http->range_iter.debt() != -1) {
62e76326 960 http->range_iter.debt(http->range_iter.debt() - bytes);
961 assert (http->range_iter.debt() >= 0);
528b2c61 962 }
62e76326 963
dd272b8e 964 /* debt() always stops at -1, below that is a bug */
965 assert (http->range_iter.debt() >= -1);
528b2c61 966}
62e76326 967
528b2c61 968bool
969ClientHttpRequest::multipartRangeRequest() const
970{
971 return request->multipartRangeRequest();
972}
973
974bool
975ClientSocketContext::multipartRangeRequest() const
976{
977 return http->multipartRangeRequest();
978}
979
980void
981ClientSocketContext::sendBody(HttpReply * rep, StoreIOBuffer bodyData)
c8be6d7b 982{
983 assert(rep == NULL);
528b2c61 984
450fe1cb 985 if (!multipartRangeRequest() && !http->request->flags.chunkedReply) {
2512d159 986 size_t length = lengthToSend(bodyData.range());
62e76326 987 noteSentBodyBytes (length);
9e008dda
AJ
988 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteBodyComplete",
989 CommIoCbPtrFun(clientWriteBodyComplete, this));
73c36fd9 990 Comm::Write(clientConnection, bodyData.data, length, call, NULL);
62e76326 991 return;
528b2c61 992 }
993
994 MemBuf mb;
2fe7eff9 995 mb.init();
4ad60609
AR
996 if (multipartRangeRequest())
997 packRange(bodyData, &mb);
998 else
999 packChunk(bodyData, mb);
2512d159 1000
9e008dda 1001 if (mb.contentSize()) {
2512d159 1002 /* write */
9e008dda
AJ
1003 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
1004 CommIoCbPtrFun(clientWriteComplete, this));
73c36fd9 1005 Comm::Write(clientConnection, &mb, call);
1cf238db 1006 } else
c8407295 1007 writeComplete(clientConnection, NULL, 0, Comm::OK);
c8be6d7b 1008}
1009
4ad60609
AR
1010/**
1011 * Packs bodyData into mb using chunked encoding. Packs the last-chunk
1012 * if bodyData is empty.
1013 */
1014void
1015ClientSocketContext::packChunk(const StoreIOBuffer &bodyData, MemBuf &mb)
1016{
1017 const uint64_t length =
1018 static_cast<uint64_t>(lengthToSend(bodyData.range()));
1019 noteSentBodyBytes(length);
1020
4391cd15 1021 mb.appendf("%" PRIX64 "\r\n", length);
4ad60609 1022 mb.append(bodyData.data, length);
4391cd15 1023 mb.append("\r\n", 2);
4ad60609
AR
1024}
1025
63be0a78 1026/** put terminating boundary for multiparts */
528b2c61 1027static void
30abd221 1028clientPackTermBound(String boundary, MemBuf * mb)
528b2c61 1029{
4391cd15 1030 mb->appendf("\r\n--" SQUIDSTRINGPH "--\r\n", SQUIDSTRINGPRINT(boundary));
4a7a3d56 1031 debugs(33, 6, "clientPackTermBound: buf offset: " << mb->size);
528b2c61 1032}
1033
63be0a78 1034/** appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
528b2c61 1035static void
30abd221 1036clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
528b2c61 1037{
75faaa7a 1038 HttpHeader hdr(hoReply);
528b2c61 1039 assert(rep);
1040 assert(spec);
1041
1042 /* put boundary */
5b4117d8 1043 debugs(33, 5, "clientPackRangeHdr: appending boundary: " << boundary);
528b2c61 1044 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
4391cd15 1045 mb->appendf("\r\n--" SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(boundary));
528b2c61 1046
1047 /* stuff the header with required entries and pack it */
62e76326 1048
a9925b40 1049 if (rep->header.has(HDR_CONTENT_TYPE))
1050 hdr.putStr(HDR_CONTENT_TYPE, rep->header.getStr(HDR_CONTENT_TYPE));
62e76326 1051
528b2c61 1052 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
62e76326 1053
10201568 1054 hdr.packInto(mb);
519e0948 1055 hdr.clean();
528b2c61 1056
1057 /* append <crlf> (we packed a header, not a reply) */
10201568 1058 mb->append("\r\n", 2);
528b2c61 1059}
1060
63be0a78 1061/**
bb790702 1062 * extracts a "range" from *buf and appends them to mb, updating
528b2c61 1063 * all offsets and such.
1064 */
c8be6d7b 1065void
2512d159 1066ClientSocketContext::packRange(StoreIOBuffer const &source, MemBuf * mb)
528b2c61 1067{
1068 HttpHdrRangeIter * i = &http->range_iter;
47f6e231 1069 Range<int64_t> available (source.range());
b65351fa 1070 char const *buf = source.data;
62e76326 1071
2512d159 1072 while (i->currentSpec() && available.size()) {
62e76326 1073 const size_t copy_sz = lengthToSend(available);
62e76326 1074
2512d159 1075 if (copy_sz) {
1076 /*
1077 * intersection of "have" and "need" ranges must not be empty
1078 */
1079 assert(http->out.offset < i->currentSpec()->offset + i->currentSpec()->length);
13c2229a 1080 assert(http->out.offset + (int64_t)available.size() > i->currentSpec()->offset);
2512d159 1081
1082 /*
1083 * put boundary and headers at the beginning of a range in a
1084 * multi-range
1085 */
1086
1087 if (http->multipartRangeRequest() && i->debt() == i->currentSpec()->length) {
1088 assert(http->memObject());
1089 clientPackRangeHdr(
f53969cc
SM
1090 http->memObject()->getReply(), /* original reply */
1091 i->currentSpec(), /* current range */
1092 i->boundary, /* boundary, the same for all */
2512d159 1093 mb);
1094 }
62e76326 1095
2512d159 1096 /*
1097 * append content
1098 */
4a7a3d56 1099 debugs(33, 3, "clientPackRange: appending " << copy_sz << " bytes");
62e76326 1100
2512d159 1101 noteSentBodyBytes (copy_sz);
62e76326 1102
2fe7eff9 1103 mb->append(buf, copy_sz);
62e76326 1104
2512d159 1105 /*
1106 * update offsets
1107 */
1108 available.start += copy_sz;
62e76326 1109
2512d159 1110 buf += copy_sz;
62e76326 1111
2512d159 1112 }
62e76326 1113
c7329b75 1114 if (!canPackMoreRanges()) {
bf8fe701 1115 debugs(33, 3, "clientPackRange: Returning because !canPackMoreRanges.");
c7329b75 1116
1117 if (i->debt() == 0)
1118 /* put terminating boundary for multiparts */
1119 clientPackTermBound(i->boundary, mb);
62e76326 1120
62e76326 1121 return;
c7329b75 1122 }
62e76326 1123
8bcf08e0 1124 int64_t nextOffset = getNextRangeOffset();
62e76326 1125
8bcf08e0 1126 assert (nextOffset >= http->out.offset);
62e76326 1127
8bcf08e0 1128 int64_t skip = nextOffset - http->out.offset;
62e76326 1129
2512d159 1130 /* adjust for not to be transmitted bytes */
8bcf08e0 1131 http->out.offset = nextOffset;
2512d159 1132
c16f948b 1133 if (available.size() <= (uint64_t)skip)
62e76326 1134 return;
1135
2512d159 1136 available.start += skip;
62e76326 1137
2512d159 1138 buf += skip;
1139
1140 if (copy_sz == 0)
1141 return;
528b2c61 1142 }
1143}
1144
63be0a78 1145/** returns expected content length for multi-range replies
528b2c61 1146 * note: assumes that httpHdrRangeCanonize has already been called
1147 * warning: assumes that HTTP headers for individual ranges at the
1148 * time of the actuall assembly will be exactly the same as
1149 * the headers when clientMRangeCLen() is called */
1150int
1151ClientHttpRequest::mRangeCLen()
c8be6d7b 1152{
47f6e231 1153 int64_t clen = 0;
c8be6d7b 1154 MemBuf mb;
528b2c61 1155
86a2f789 1156 assert(memObject());
528b2c61 1157
2fe7eff9 1158 mb.init();
528b2c61 1159 HttpHdrRange::iterator pos = request->range->begin();
62e76326 1160
528b2c61 1161 while (pos != request->range->end()) {
62e76326 1162 /* account for headers for this range */
2fe7eff9 1163 mb.reset();
86a2f789 1164 clientPackRangeHdr(memObject()->getReply(),
62e76326 1165 *pos, range_iter.boundary, &mb);
1166 clen += mb.size;
528b2c61 1167
62e76326 1168 /* account for range content */
1169 clen += (*pos)->length;
528b2c61 1170
4a7a3d56 1171 debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
62e76326 1172 ++pos;
528b2c61 1173 }
62e76326 1174
528b2c61 1175 /* account for the terminating boundary */
2fe7eff9 1176 mb.reset();
62e76326 1177
528b2c61 1178 clientPackTermBound(range_iter.boundary, &mb);
62e76326 1179
528b2c61 1180 clen += mb.size;
1181
2fe7eff9 1182 mb.clean();
62e76326 1183
528b2c61 1184 return clen;
1185}
1186
63be0a78 1187/**
528b2c61 1188 * returns true if If-Range specs match reply, false otherwise
1189 */
1190static int
59a1efb2 1191clientIfRangeMatch(ClientHttpRequest * http, HttpReply * rep)
528b2c61 1192{
a9925b40 1193 const TimeOrTag spec = http->request->header.getTimeOrTag(HDR_IF_RANGE);
528b2c61 1194 /* check for parsing falure */
62e76326 1195
528b2c61 1196 if (!spec.valid)
62e76326 1197 return 0;
1198
528b2c61 1199 /* got an ETag? */
1200 if (spec.tag.str) {
a9925b40 1201 ETag rep_tag = rep->header.getETag(HDR_ETAG);
bf8fe701 1202 debugs(33, 3, "clientIfRangeMatch: ETags: " << spec.tag.str << " and " <<
1203 (rep_tag.str ? rep_tag.str : "<none>"));
62e76326 1204
1205 if (!rep_tag.str)
f53969cc 1206 return 0; /* entity has no etag to compare with! */
62e76326 1207
1208 if (spec.tag.weak || rep_tag.weak) {
e0236918 1209 debugs(33, DBG_IMPORTANT, "clientIfRangeMatch: Weak ETags are not allowed in If-Range: " << spec.tag.str << " ? " << rep_tag.str);
f53969cc 1210 return 0; /* must use strong validator for sub-range requests */
62e76326 1211 }
1212
bd76482d 1213 return etagIsStrongEqual(rep_tag, spec.tag);
528b2c61 1214 }
62e76326 1215
528b2c61 1216 /* got modification time? */
1217 if (spec.time >= 0) {
86a2f789 1218 return http->storeEntry()->lastmod <= spec.time;
528b2c61 1219 }
62e76326 1220
f53969cc 1221 assert(0); /* should not happen */
528b2c61 1222 return 0;
1223}
1224
63be0a78 1225/**
1226 * generates a "unique" boundary string for multipart responses
528b2c61 1227 * the caller is responsible for cleaning the string */
30abd221 1228String
528b2c61 1229ClientHttpRequest::rangeBoundaryStr() const
1230{
528b2c61 1231 const char *key;
7dbca7a4
AJ
1232 String b(APP_FULLNAME);
1233 b.append(":",1);
86a2f789 1234 key = storeEntry()->getMD5Text();
528b2c61 1235 b.append(key, strlen(key));
1236 return b;
1237}
1238
63be0a78 1239/** adds appropriate Range headers if needed */
528b2c61 1240void
1241ClientSocketContext::buildRangeHeader(HttpReply * rep)
1242{
1243 HttpHeader *hdr = rep ? &rep->header : 0;
1244 const char *range_err = NULL;
190154cf 1245 HttpRequest *request = http->request;
528b2c61 1246 assert(request->range);
1247 /* check if we still want to do ranges */
62e76326 1248
11e3fa1c
AJ
1249 int64_t roffLimit = request->getRangeOffsetLimit();
1250
528b2c61 1251 if (!rep)
62e76326 1252 range_err = "no [parse-able] reply";
9b769c67 1253 else if ((rep->sline.status() != Http::scOkay) && (rep->sline.status() != Http::scPartialContent))
62e76326 1254 range_err = "wrong status code";
a9925b40 1255 else if (hdr->has(HDR_CONTENT_RANGE))
62e76326 1256 range_err = "origin server does ranges";
528b2c61 1257 else if (rep->content_length < 0)
62e76326 1258 range_err = "unknown length";
86a2f789 1259 else if (rep->content_length != http->memObject()->getReply()->content_length)
f53969cc 1260 range_err = "INCONSISTENT length"; /* a bug? */
96cbbe03 1261
a3c6762c 1262 /* hits only - upstream CachePeer determines correct behaviour on misses, and client_side_reply determines
9e008dda 1263 * hits candidates
96cbbe03 1264 */
a9925b40 1265 else if (logTypeIsATcpHit(http->logType) && http->request->header.has(HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
62e76326 1266 range_err = "If-Range match failed";
528b2c61 1267 else if (!http->request->range->canonize(rep))
62e76326 1268 range_err = "canonization failed";
528b2c61 1269 else if (http->request->range->isComplex())
62e76326 1270 range_err = "too complex range header";
11e3fa1c 1271 else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded(roffLimit))
62e76326 1272 range_err = "range outside range_offset_limit";
1273
528b2c61 1274 /* get rid of our range specs on error */
1275 if (range_err) {
b631f31c 1276 /* XXX We do this here because we need canonisation etc. However, this current
9e008dda 1277 * code will lead to incorrect store offset requests - the store will have the
b631f31c 1278 * offset data, but we won't be requesting it.
1279 * So, we can either re-request, or generate an error
1280 */
f0baf149 1281 http->request->ignoreRange(range_err);
c8be6d7b 1282 } else {
9b769c67
AJ
1283 /* XXX: TODO: Review, this unconditional set may be wrong. */
1284 rep->sline.set(rep->sline.version, Http::scPartialContent);
fedd1531 1285 // web server responded with a valid, but unexpected range.
1286 // will (try-to) forward as-is.
1287 //TODO: we should cope with multirange request/responses
1288 bool replyMatchRequest = rep->content_range != NULL ?
1289 request->range->contains(rep->content_range->spec) :
1290 true;
4c9eadc2 1291 const int spec_count = http->request->range->specs.size();
47f6e231 1292 int64_t actual_clen = -1;
62e76326 1293
47f6e231 1294 debugs(33, 3, "clientBuildRangeHeader: range spec count: " <<
9e008dda 1295 spec_count << " virgin clen: " << rep->content_length);
62e76326 1296 assert(spec_count > 0);
62e76326 1297 /* append appropriate header(s) */
1298
1299 if (spec_count == 1) {
fedd1531 1300 if (!replyMatchRequest) {
1301 hdr->delById(HDR_CONTENT_RANGE);
1302 hdr->putContRange(rep->content_range);
1303 actual_clen = rep->content_length;
1304 //http->range_iter.pos = rep->content_range->spec.begin();
1305 (*http->range_iter.pos)->offset = rep->content_range->spec.offset;
1306 (*http->range_iter.pos)->length = rep->content_range->spec.length;
1307
1308 } else {
1309 HttpHdrRange::iterator pos = http->request->range->begin();
1310 assert(*pos);
1311 /* append Content-Range */
1312
1313 if (!hdr->has(HDR_CONTENT_RANGE)) {
1314 /* No content range, so this was a full object we are
1315 * sending parts of.
1316 */
1317 httpHeaderAddContRange(hdr, **pos, rep->content_length);
1318 }
1319
1320 /* set new Content-Length to the actual number of bytes
1321 * transmitted in the message-body */
1322 actual_clen = (*pos)->length;
62e76326 1323 }
62e76326 1324 } else {
1325 /* multipart! */
1326 /* generate boundary string */
1327 http->range_iter.boundary = http->rangeBoundaryStr();
1328 /* delete old Content-Type, add ours */
a9925b40 1329 hdr->delById(HDR_CONTENT_TYPE);
62e76326 1330 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
826a1fed
FC
1331 "multipart/byteranges; boundary=\"" SQUIDSTRINGPH "\"",
1332 SQUIDSTRINGPRINT(http->range_iter.boundary));
62e76326 1333 /* Content-Length is not required in multipart responses
1334 * but it is always nice to have one */
1335 actual_clen = http->mRangeCLen();
2512d159 1336 /* http->out needs to start where we want data at */
1337 http->out.offset = http->range_iter.currentSpec()->offset;
62e76326 1338 }
1339
1340 /* replace Content-Length header */
1341 assert(actual_clen >= 0);
1342
a9925b40 1343 hdr->delById(HDR_CONTENT_LENGTH);
62e76326 1344
47f6e231 1345 hdr->putInt64(HDR_CONTENT_LENGTH, actual_clen);
62e76326 1346
bf8fe701 1347 debugs(33, 3, "clientBuildRangeHeader: actual content length: " << actual_clen);
62e76326 1348
1349 /* And start the range iter off */
1350 http->range_iter.updateSpec();
c8be6d7b 1351 }
528b2c61 1352}
1353
1354void
1355ClientSocketContext::prepareReply(HttpReply * rep)
1356{
fedd1531 1357 reply = rep;
1358
528b2c61 1359 if (http->request->range)
62e76326 1360 buildRangeHeader(rep);
528b2c61 1361}
1362
1363void
1364ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData)
1365{
1366 prepareReply(rep);
528b2c61 1367 assert (rep);
06a5ae20 1368 MemBuf *mb = rep->pack();
1ce34ddd
AJ
1369
1370 // dump now, so we dont output any body.
1371 debugs(11, 2, "HTTP Client " << clientConnection);
1372 debugs(11, 2, "HTTP Client REPLY:\n---------\n" << mb->buf << "\n----------");
1373
528b2c61 1374 /* Save length of headers for persistent conn checks */
032785bf 1375 http->out.headers_sz = mb->contentSize();
528b2c61 1376#if HEADERS_LOG
62e76326 1377
528b2c61 1378 headersLog(0, 0, http->request->method, rep);
1379#endif
62e76326 1380
c8be6d7b 1381 if (bodyData.data && bodyData.length) {
4ad60609
AR
1382 if (multipartRangeRequest())
1383 packRange(bodyData, mb);
450fe1cb 1384 else if (http->request->flags.chunkedReply) {
4ad60609
AR
1385 packChunk(bodyData, *mb);
1386 } else {
2512d159 1387 size_t length = lengthToSend(bodyData.range());
62e76326 1388 noteSentBodyBytes (length);
1389
2fe7eff9 1390 mb->append(bodyData.data, length);
62e76326 1391 }
c8be6d7b 1392 }
62e76326 1393
c8be6d7b 1394 /* write */
425802c8 1395 debugs(33,7, HERE << "sendStartOfMessage schedules clientWriteComplete");
9e008dda
AJ
1396 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
1397 CommIoCbPtrFun(clientWriteComplete, this));
73c36fd9 1398 Comm::Write(clientConnection, mb, call);
032785bf 1399 delete mb;
c8be6d7b 1400}
1401
63be0a78 1402/**
7dc5f514 1403 * Write a chunk of data to a client socket. If the reply is present,
1404 * send the reply headers down the wire too, and clean them up when
1405 * finished.
9e008dda 1406 * Pre-condition:
edce4d98 1407 * The request is one backed by a connection, not an internal request.
1408 * data context is not NULL
1409 * There are no more entries in the stream chain.
2246b732 1410 */
92ae4c86 1411void
59a1efb2 1412clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http,
2324cda2 1413 HttpReply * rep, StoreIOBuffer receivedData)
edce4d98 1414{
51571330
AJ
1415 // dont tryt to deliver if client already ABORTED
1416 if (!http->getConn() || !cbdataReferenceValid(http->getConn()) || !Comm::IsConnOpen(http->getConn()->clientConnection))
1417 return;
1418
edce4d98 1419 /* Test preconditions */
1420 assert(node != NULL);
559da936 1421 PROF_start(clientSocketRecipient);
62e76326 1422 /* TODO: handle this rather than asserting
9e008dda
AJ
1423 * - it should only ever happen if we cause an abort and
1424 * the callback chain loops back to here, so we can simply return.
1425 * However, that itself shouldn't happen, so it stays as an assert for now.
edce4d98 1426 */
1427 assert(cbdataReferenceValid(node));
edce4d98 1428 assert(node->node.next == NULL);
0655fa4d 1429 ClientSocketContext::Pointer context = dynamic_cast<ClientSocketContext *>(node->data.getRaw());
94a396a3 1430 assert(context != NULL);
5c336a3b 1431
528b2c61 1432 /* TODO: check offset is what we asked for */
62e76326 1433
92ae4c86 1434 if (context != http->getConn()->getCurrentContext())
2324cda2 1435 context->deferRecipientForLater(node, rep, receivedData);
92ae4c86
AR
1436 else
1437 http->getConn()->handleReply(rep, receivedData);
fc68f6b1 1438
559da936 1439 PROF_stop(clientSocketRecipient);
edce4d98 1440}
1441
63be0a78 1442/**
1443 * Called when a downstream node is no longer interested in
edce4d98 1444 * our data. As we are a terminal node, this means on aborts
1445 * only
1446 */
1447void
59a1efb2 1448clientSocketDetach(clientStreamNode * node, ClientHttpRequest * http)
edce4d98 1449{
edce4d98 1450 /* Test preconditions */
1451 assert(node != NULL);
62e76326 1452 /* TODO: handle this rather than asserting
9e008dda
AJ
1453 * - it should only ever happen if we cause an abort and
1454 * the callback chain loops back to here, so we can simply return.
edce4d98 1455 * However, that itself shouldn't happen, so it stays as an assert for now.
1456 */
1457 assert(cbdataReferenceValid(node));
1458 /* Set null by ContextFree */
edce4d98 1459 assert(node->node.next == NULL);
0655fa4d 1460 /* this is the assert discussed above */
e4a67a80 1461 assert(NULL == dynamic_cast<ClientSocketContext *>(node->data.getRaw()));
edce4d98 1462 /* We are only called when the client socket shutsdown.
1463 * Tell the prev pipeline member we're finished
1464 */
1465 clientStreamDetach(node, http);
7a2f978b 1466}
1467
f4f278b5 1468static void
ced8def3 1469clientWriteBodyComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int xerrno, void *data)
f4f278b5 1470{
ced8def3 1471 debugs(33,7, "schedule clientWriteComplete");
e0d28505 1472 clientWriteComplete(conn, NULL, size, errflag, xerrno, data);
c8be6d7b 1473}
1474
1475void
a46d2c0e 1476ConnStateData::readNextRequest()
c8be6d7b 1477{
73c36fd9 1478 debugs(33, 5, HERE << clientConnection << " reading next req");
bf8fe701 1479
97b32442 1480 fd_note(clientConnection->fd, "Idle client: Waiting for next request");
63be0a78 1481 /**
5ddf7edc 1482 * Set the timeout BEFORE calling readSomeData().
c8be6d7b 1483 */
1cf238db 1484 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876 1485 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
4cb2536f 1486 TimeoutDialer, this, ConnStateData::requestTimeout);
c5c06f02 1487 commSetConnTimeout(clientConnection, clientConnection->timeLeft(idleTimeout()), timeoutCall);
1cf238db 1488
a46d2c0e 1489 readSomeData();
63be0a78 1490 /** Please don't do anything with the FD past here! */
c8be6d7b 1491}
1492
09d3938c 1493static void
1cf238db 1494ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn)
c8be6d7b 1495{
73c36fd9 1496 debugs(33, 2, HERE << conn->clientConnection << " Sending next");
bf8fe701 1497
63be0a78 1498 /** If the client stream is waiting on a socket write to occur, then */
62e76326 1499
c8be6d7b 1500 if (deferredRequest->flags.deferred) {
63be0a78 1501 /** NO data is allowed to have been sent. */
62e76326 1502 assert(deferredRequest->http->out.size == 0);
63be0a78 1503 /** defer now. */
62e76326 1504 clientSocketRecipient(deferredRequest->deferredparams.node,
1505 deferredRequest->http,
1506 deferredRequest->deferredparams.rep,
1507 deferredRequest->deferredparams.queuedBuffer);
c8be6d7b 1508 }
62e76326 1509
63be0a78 1510 /** otherwise, the request is still active in a callbacksomewhere,
c8be6d7b 1511 * and we are done
f4f278b5 1512 */
f4f278b5 1513}
1514
cf6eb29e 1515/// called when we have successfully finished writing the response
0655fa4d 1516void
1517ClientSocketContext::keepaliveNextRequest()
1a92a1e2 1518{
1cf238db 1519 ConnStateData * conn = http->getConn();
bd4e6ec8 1520
73c36fd9 1521 debugs(33, 3, HERE << "ConnnStateData(" << conn->clientConnection << "), Context(" << clientConnection << ")");
0655fa4d 1522 connIsFinished();
1523
73c36fd9
AJ
1524 if (conn->pinning.pinned && !Comm::IsConnOpen(conn->pinning.serverConnection)) {
1525 debugs(33, 2, HERE << conn->clientConnection << " Connection was pinned but server side gone. Terminating client connection");
1526 conn->clientConnection->close();
d67acb4e
AJ
1527 return;
1528 }
1529
cf6eb29e
CT
1530 /** \par
1531 * We are done with the response, and we are either still receiving request
1532 * body (early response!) or have already stopped receiving anything.
1533 *
1534 * If we are still receiving, then clientParseRequest() below will fail.
1535 * (XXX: but then we will call readNextRequest() which may succeed and
1536 * execute a smuggled request as we are not done with the current request).
1537 *
1538 * If we stopped because we got everything, then try the next request.
1539 *
1540 * If we stopped receiving because of an error, then close now to avoid
1541 * getting stuck and to prevent accidental request smuggling.
1542 */
1543
1544 if (const char *reason = conn->stoppedReceiving()) {
1545 debugs(33, 3, HERE << "closing for earlier request error: " << reason);
1546 conn->clientConnection->close();
1547 return;
1548 }
1549
63be0a78 1550 /** \par
f900210a 1551 * Attempt to parse a request from the request buffer.
1552 * If we've been fed a pipelined request it may already
1553 * be in our read buffer.
1554 *
63be0a78 1555 \par
f900210a 1556 * This needs to fall through - if we're unlucky and parse the _last_ request
1557 * from our read buffer we may never re-register for another client read.
1558 */
1559
f35961af 1560 if (conn->clientParseRequests()) {
73c36fd9 1561 debugs(33, 3, HERE << conn->clientConnection << ": parsed next request from buffer");
f900210a 1562 }
1563
63be0a78 1564 /** \par
f900210a 1565 * Either we need to kick-start another read or, if we have
1566 * a half-closed connection, kill it after the last request.
1567 * This saves waiting for half-closed connections to finished being
1568 * half-closed _AND_ then, sometimes, spending "Timeout" time in
1569 * the keepalive "Waiting for next request" state.
1570 */
73c36fd9 1571 if (commIsHalfClosed(conn->clientConnection->fd) && (conn->getConcurrentRequestCount() == 0)) {
bf8fe701 1572 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: half-closed client with no pending requests, closing");
73c36fd9 1573 conn->clientConnection->close();
f900210a 1574 return;
1575 }
1576
0655fa4d 1577 ClientSocketContext::Pointer deferredRequest;
62e76326 1578
63be0a78 1579 /** \par
f900210a 1580 * At this point we either have a parsed request (which we've
1581 * kicked off the processing for) or not. If we have a deferred
1582 * request (parsed but deferred for pipeling processing reasons)
1583 * then look at processing it. If not, simply kickstart
1584 * another read.
1585 */
1586
1587 if ((deferredRequest = conn->getCurrentContext()).getRaw()) {
73c36fd9 1588 debugs(33, 3, HERE << conn->clientConnection << ": calling PushDeferredIfNeeded");
62e76326 1589 ClientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
f35961af 1590 } else if (conn->flags.readMore) {
73c36fd9 1591 debugs(33, 3, HERE << conn->clientConnection << ": calling conn->readNextRequest()");
f900210a 1592 conn->readNextRequest();
f35961af
AR
1593 } else {
1594 // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
1b76e6c1 1595 debugs(33, DBG_IMPORTANT, HERE << "abandoning " << conn->clientConnection);
f900210a 1596 }
1a92a1e2 1597}
1598
c8be6d7b 1599void
02c8dde5 1600clientUpdateSocketStats(LogTags logType, size_t size)
c8be6d7b 1601{
1602 if (size == 0)
62e76326 1603 return;
1604
c8be6d7b 1605 kb_incr(&statCounter.client_http.kbytes_out, size);
62e76326 1606
c8be6d7b 1607 if (logTypeIsATcpHit(logType))
62e76326 1608 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
c8be6d7b 1609}
1610
63be0a78 1611/**
528b2c61 1612 * increments iterator "i"
63be0a78 1613 * used by clientPackMoreRanges
1614 *
1615 \retval true there is still data available to pack more ranges
9e008dda 1616 \retval false
63be0a78 1617 */
528b2c61 1618bool
1619ClientSocketContext::canPackMoreRanges() const
1620{
63be0a78 1621 /** first update iterator "i" if needed */
62e76326 1622
528b2c61 1623 if (!http->range_iter.debt()) {
73c36fd9 1624 debugs(33, 5, HERE << "At end of current range spec for " << clientConnection);
62e76326 1625
24a8eaae 1626 if (http->range_iter.pos != http->range_iter.end)
62e76326 1627 ++http->range_iter.pos;
1628
1629 http->range_iter.updateSpec();
528b2c61 1630 }
62e76326 1631
528b2c61 1632 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
63be0a78 1633
528b2c61 1634 /* paranoid sync condition */
1635 /* continue condition: need_more_data */
bf8fe701 1636 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: returning " << (http->range_iter.currentSpec() ? true : false));
528b2c61 1637 return http->range_iter.currentSpec() ? true : false;
1638}
1639
47f6e231 1640int64_t
528b2c61 1641ClientSocketContext::getNextRangeOffset() const
1642{
f0baf149
AR
1643 debugs (33, 5, "range: " << http->request->range <<
1644 "; http offset " << http->out.offset <<
1645 "; reply " << reply);
1646
1647 // XXX: This method is called from many places, including pullData() which
1648 // may be called before prepareReply() [on some Squid-generated errors].
1649 // Hence, we may not even know yet whether we should honor/do ranges.
1650
528b2c61 1651 if (http->request->range) {
62e76326 1652 /* offset in range specs does not count the prefix of an http msg */
62e76326 1653 /* check: reply was parsed and range iterator was initialized */
1654 assert(http->range_iter.valid);
1655 /* filter out data according to range specs */
1656 assert (canPackMoreRanges());
1657 {
f53969cc 1658 int64_t start; /* offset of still missing data */
62e76326 1659 assert(http->range_iter.currentSpec());
1660 start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
47f6e231 1661 debugs(33, 3, "clientPackMoreRanges: in: offset: " << http->out.offset);
1662 debugs(33, 3, "clientPackMoreRanges: out:"
9e008dda
AJ
1663 " start: " << start <<
1664 " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
1665 " [" << http->range_iter.currentSpec()->offset <<
1666 ", " << http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length << "),"
1667 " len: " << http->range_iter.currentSpec()->length <<
1668 " debt: " << http->range_iter.debt());
62e76326 1669 if (http->range_iter.currentSpec()->length != -1)
f53969cc 1670 assert(http->out.offset <= start); /* we did not miss it */
62e76326 1671
1672 return start;
1673 }
1674
fedd1531 1675 } else if (reply && reply->content_range) {
1676 /* request does not have ranges, but reply does */
63be0a78 1677 /** \todo FIXME: should use range_iter_pos on reply, as soon as reply->content_range
1678 * becomes HttpHdrRange rather than HttpHdrRangeSpec.
1679 */
fedd1531 1680 return http->out.offset + reply->content_range->spec.offset;
528b2c61 1681 }
1682
1683 return http->out.offset;
1684}
1685
c8be6d7b 1686void
528b2c61 1687ClientSocketContext::pullData()
c8be6d7b 1688{
f0baf149 1689 debugs(33, 5, reply << " written " << http->out.size << " into " << clientConnection);
bf8fe701 1690
c8be6d7b 1691 /* More data will be coming from the stream. */
528b2c61 1692 StoreIOBuffer readBuffer;
1693 /* XXX: Next requested byte in the range sequence */
1694 /* XXX: length = getmaximumrangelenfgth */
1695 readBuffer.offset = getNextRangeOffset();
c8be6d7b 1696 readBuffer.length = HTTP_REQBUF_SZ;
528b2c61 1697 readBuffer.data = reqbuf;
1698 /* we may note we have reached the end of the wanted ranges */
1699 clientStreamRead(getTail(), http, readBuffer);
1700}
1701
a77f4765 1702/** Adapt stream status to account for Range cases
86c63190 1703 *
a77f4765 1704 */
62e76326 1705clientStream_status_t
528b2c61 1706ClientSocketContext::socketState()
1707{
1708 switch (clientStreamStatus(getTail(), http)) {
62e76326 1709
1710 case STREAM_NONE:
528b2c61 1711 /* check for range support ending */
62e76326 1712
528b2c61 1713 if (http->request->range) {
62e76326 1714 /* check: reply was parsed and range iterator was initialized */
1715 assert(http->range_iter.valid);
1716 /* filter out data according to range specs */
1717
1718 if (!canPackMoreRanges()) {
920ba08d 1719 debugs(33, 5, HERE << "Range request at end of returnable " <<
73c36fd9 1720 "range sequence on " << clientConnection);
a77f4765
FC
1721 // we got everything we wanted from the store
1722 return STREAM_COMPLETE;
62e76326 1723 }
fedd1531 1724 } else if (reply && reply->content_range) {
425802c8 1725 /* reply has content-range, but Squid is not managing ranges */
1726 const int64_t &bytesSent = http->out.offset;
1727 const int64_t &bytesExpected = reply->content_range->spec.length;
fedd1531 1728
425802c8 1729 debugs(33, 7, HERE << "body bytes sent vs. expected: " <<
9e008dda
AJ
1730 bytesSent << " ? " << bytesExpected << " (+" <<
1731 reply->content_range->spec.offset << ")");
425802c8 1732
1733 // did we get at least what we expected, based on range specs?
1734
a77f4765
FC
1735 if (bytesSent == bytesExpected) // got everything
1736 return STREAM_COMPLETE;
425802c8 1737
a77f4765
FC
1738 if (bytesSent > bytesExpected) // Error: Sent more than expected
1739 return STREAM_UNPLANNED_COMPLETE;
62e76326 1740 }
1741
1742 return STREAM_NONE;
1743
1744 case STREAM_COMPLETE:
528b2c61 1745 return STREAM_COMPLETE;
62e76326 1746
1747 case STREAM_UNPLANNED_COMPLETE:
1748 return STREAM_UNPLANNED_COMPLETE;
1749
1750 case STREAM_FAILED:
1751 return STREAM_FAILED;
528b2c61 1752 }
62e76326 1753
528b2c61 1754 fatal ("unreachable code\n");
1755 return STREAM_NONE;
c8be6d7b 1756}
edce4d98 1757
63be0a78 1758/**
1759 * A write has just completed to the client, or we have just realised there is
edce4d98 1760 * no more data to send.
1761 */
e6ccf245 1762void
ced8def3 1763clientWriteComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag, int, void *data)
7a2f978b 1764{
528b2c61 1765 ClientSocketContext *context = (ClientSocketContext *)data;
e0d28505 1766 context->writeComplete(conn, bufnotused, size, errflag);
0655fa4d 1767}
1768
f692498b
AJ
1769/// remembers the abnormal connection termination for logging purposes
1770void
1771ClientSocketContext::noteIoError(const int xerrno)
1772{
1773 if (http) {
1774 if (xerrno == ETIMEDOUT)
41ebd397 1775 http->al->http.timedout = true;
f692498b 1776 else // even if xerrno is zero (which means read abort/eof)
41ebd397 1777 http->al->http.aborted = true;
f692498b
AJ
1778 }
1779}
1780
55e44db9 1781void
1782ClientSocketContext::doClose()
1783{
73c36fd9 1784 clientConnection->close();
55e44db9 1785}
1786
cf6eb29e 1787/// called when we encounter a response-related error
55e44db9 1788void
5f8252d2 1789ClientSocketContext::initiateClose(const char *reason)
55e44db9 1790{
cf6eb29e
CT
1791 http->getConn()->stopSending(reason); // closes ASAP
1792}
5f8252d2 1793
cf6eb29e
CT
1794void
1795ConnStateData::stopSending(const char *error)
1796{
1797 debugs(33, 4, HERE << "sending error (" << clientConnection << "): " << error <<
1798 "; old receiving error: " <<
1799 (stoppedReceiving() ? stoppedReceiving_ : "none"));
fc68f6b1 1800
cf6eb29e
CT
1801 if (const char *oldError = stoppedSending()) {
1802 debugs(33, 3, HERE << "already stopped sending: " << oldError);
1803 return; // nothing has changed as far as this connection is concerned
1804 }
1805 stoppedSending_ = error;
fc68f6b1 1806
cf6eb29e
CT
1807 if (!stoppedReceiving()) {
1808 if (const int64_t expecting = mayNeedToReadMoreBody()) {
1809 debugs(33, 5, HERE << "must still read " << expecting <<
e7287625 1810 " request body bytes with " << in.buf.length() << " unused");
cf6eb29e 1811 return; // wait for the request receiver to finish reading
3b299123 1812 }
55e44db9 1813 }
1814
cf6eb29e 1815 clientConnection->close();
55e44db9 1816}
1817
0655fa4d 1818void
ced8def3 1819ClientSocketContext::writeComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag)
0655fa4d 1820{
5ad038e1 1821 const StoreEntry *entry = http->storeEntry();
7a2f978b 1822 http->out.size += size;
e0d28505 1823 debugs(33, 5, HERE << conn << ", sz " << size <<
e4049756 1824 ", err " << errflag << ", off " << http->out.size << ", len " <<
5ad038e1 1825 (entry ? entry->objectLen() : 0));
c8be6d7b 1826 clientUpdateSocketStats(http->logType, size);
62e76326 1827
c8407295 1828 /* Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up */
fc68f6b1 1829
c8407295 1830 if (errflag == Comm::ERR_CLOSING || !Comm::IsConnOpen(conn))
5f8252d2 1831 return;
1832
e0d28505 1833 if (errflag || clientHttpRequestStatus(conn->fd, http)) {
5f8252d2 1834 initiateClose("failure or true request status");
62e76326 1835 /* Do we leak here ? */
1836 return;
edce4d98 1837 }
62e76326 1838
0655fa4d 1839 switch (socketState()) {
62e76326 1840
edce4d98 1841 case STREAM_NONE:
0655fa4d 1842 pullData();
62e76326 1843 break;
1844
edce4d98 1845 case STREAM_COMPLETE:
61489dd0 1846 debugs(33, 5, conn << " Stream complete, keepalive is " << http->request->flags.proxyKeepalive);
a77f4765
FC
1847 if (http->request->flags.proxyKeepalive)
1848 keepaliveNextRequest();
1849 else
1850 initiateClose("STREAM_COMPLETE NOKEEPALIVE");
62e76326 1851 return;
1852
edce4d98 1853 case STREAM_UNPLANNED_COMPLETE:
6e1d409c
AR
1854 initiateClose("STREAM_UNPLANNED_COMPLETE");
1855 return;
62e76326 1856
edce4d98 1857 case STREAM_FAILED:
6e1d409c 1858 initiateClose("STREAM_FAILED");
62e76326 1859 return;
1860
edce4d98 1861 default:
62e76326 1862 fatal("Hit unreachable code in clientWriteComplete\n");
7a2f978b 1863 }
1864}
1865
eacfca83
AR
1866ClientSocketContext *
1867ConnStateData::abortRequestParsing(const char *const uri)
038eb4ed 1868{
eacfca83
AR
1869 ClientHttpRequest *http = new ClientHttpRequest(this);
1870 http->req_sz = in.buf.length();
edce4d98 1871 http->uri = xstrdup(uri);
c4b7a5a9 1872 setLogUri (http, uri);
eacfca83
AR
1873 ClientSocketContext *context = new ClientSocketContext(clientConnection, http);
1874 StoreIOBuffer tempBuffer;
c8be6d7b 1875 tempBuffer.data = context->reqbuf;
1876 tempBuffer.length = HTTP_REQBUF_SZ;
edce4d98 1877 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 1878 clientReplyStatus, new clientReplyContext(http), clientSocketRecipient,
62e76326 1879 clientSocketDetach, context, tempBuffer);
edce4d98 1880 return context;
038eb4ed 1881}
1882
c8be6d7b 1883char *
1884skipLeadingSpace(char *aString)
1885{
1886 char *result = aString;
62e76326 1887
c8be6d7b 1888 while (xisspace(*aString))
62e76326 1889 ++aString;
1890
c8be6d7b 1891 return result;
1892}
1893
63be0a78 1894/**
d4a04ed5 1895 * 'end' defaults to NULL for backwards compatibility
1896 * remove default value if we ever get rid of NULL-terminated
1897 * request buffers.
1898 */
1899const char *
1900findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
c8be6d7b 1901{
d4a04ed5 1902 if (NULL == end) {
1903 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1904 assert(end);
1905 }
62e76326 1906
5e263176 1907 for (; end > uriAndHTTPVersion; --end) {
d4a04ed5 1908 if (*end == '\n' || *end == '\r')
62e76326 1909 continue;
1910
d4a04ed5 1911 if (xisspace(*end)) {
1912 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1913 return end + 1;
62e76326 1914 else
1915 break;
1916 }
c8be6d7b 1917 }
62e76326 1918
3f38a55e 1919 return NULL;
c8be6d7b 1920}
1921
c8be6d7b 1922void
727552f4 1923setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl)
c8be6d7b 1924{
a46d0227 1925 safe_free(http->log_uri);
62e76326 1926
727552f4
CT
1927 if (!cleanUrl)
1928 // The uri is already clean just dump it.
62e76326 1929 http->log_uri = xstrndup(uri, MAX_URL);
727552f4 1930 else {
2bea559d
A
1931 int flags = 0;
1932 switch (Config.uri_whitespace) {
1933 case URI_WHITESPACE_ALLOW:
1934 flags |= RFC1738_ESCAPE_NOSPACE;
1935
1936 case URI_WHITESPACE_ENCODE:
1937 flags |= RFC1738_ESCAPE_UNESCAPED;
1938 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1939 break;
727552f4 1940
2bea559d
A
1941 case URI_WHITESPACE_CHOP: {
1942 flags |= RFC1738_ESCAPE_NOSPACE;
1943 flags |= RFC1738_ESCAPE_UNESCAPED;
1944 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1945 int pos = strcspn(http->log_uri, w_space);
1946 http->log_uri[pos] = '\0';
1947 }
1948 break;
1949
1950 case URI_WHITESPACE_DENY:
1951 case URI_WHITESPACE_STRIP:
1952 default: {
1953 const char *t;
1954 char *tmp_uri = static_cast<char*>(xmalloc(strlen(uri) + 1));
1955 char *q = tmp_uri;
1956 t = uri;
1957 while (*t) {
a38ec4b1
FC
1958 if (!xisspace(*t)) {
1959 *q = *t;
1960 ++q;
1961 }
95dc7ff4 1962 ++t;
727552f4 1963 }
2bea559d
A
1964 *q = '\0';
1965 http->log_uri = xstrndup(rfc1738_escape_unescaped(tmp_uri), MAX_URL);
1966 xfree(tmp_uri);
1967 }
1968 break;
1969 }
727552f4 1970 }
c8be6d7b 1971}
1972
3f38a55e 1973static void
9bafa70d 1974prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, const Http1::RequestParserPointer &hp)
62e76326 1975{
3f38a55e 1976 int vhost = conn->port->vhost;
1977 int vport = conn->port->vport;
9ff1b8ca 1978 static char ipbuf[MAX_IPSTRLEN];
c8be6d7b 1979
be4d35dc 1980 http->flags.accel = true;
c8be6d7b 1981
3f38a55e 1982 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
c8be6d7b 1983
5f3cc9a2 1984 static const SBuf cache_object("cache_object://");
9bafa70d 1985 if (hp->requestUri().startsWith(cache_object))
34399323 1986 return; /* already in good shape */
1987
b1cef121 1988 // XXX: re-use proper URL parser for this
9bafa70d 1989 SBuf url = hp->requestUri(); // use full provided URI if we abort
b1cef121
AJ
1990 do { // use a loop so we can break out of it
1991 ::Parser::Tokenizer tok(url);
f9688132 1992 if (tok.skip('/')) // origin-form URL already.
b1cef121
AJ
1993 break;
1994
62e76326 1995 if (conn->port->vhost)
1996 return; /* already in good shape */
1997
b1cef121 1998 // skip the URI scheme
9bafa70d 1999 static const CharacterSet uriScheme = CharacterSet("URI-scheme","+-.") + CharacterSet::ALPHA + CharacterSet::DIGIT;
b1cef121 2000 static const SBuf uriSchemeEnd("://");
9bafa70d 2001 if (!tok.skipAll(uriScheme) || !tok.skip(uriSchemeEnd))
b1cef121 2002 break;
62e76326 2003
b1cef121
AJ
2004 // skip the authority segment
2005 // RFC 3986 complex nested ABNF for "authority" boils down to this:
2006 static const CharacterSet authority = CharacterSet("authority","-._~%:@[]!$&'()*+,;=") +
f53969cc 2007 CharacterSet::HEXDIG + CharacterSet::ALPHA + CharacterSet::DIGIT;
9bafa70d 2008 if (!tok.skipAll(authority))
b1cef121 2009 break;
62e76326 2010
63ccea28 2011 static const SBuf slashUri("/");
83e3a594 2012 const SBuf t = tok.remaining();
b1cef121 2013 if (t.isEmpty())
63ccea28 2014 url = slashUri;
b1cef121
AJ
2015 else if (t[0]=='/') // looks like path
2016 url = t;
2017 else if (t[0]=='?' || t[0]=='#') { // looks like query or fragment. fix '/'
63ccea28 2018 url = slashUri;
b1cef121
AJ
2019 url.append(t);
2020 } // else do nothing. invalid path
62e76326 2021
b1cef121 2022 } while(false);
62e76326 2023
b1cef121
AJ
2024#if SHOULD_REJECT_UNKNOWN_URLS
2025 // reject URI which are not well-formed even after the processing above
f9688132 2026 if (url.isEmpty() || url[0] != '/') {
f1d5359e 2027 hp->parseStatusCode = Http::scBadRequest;
943cdf6d 2028 return conn->abortRequestParsing("error:invalid-request");
3f38a55e 2029 }
b1cef121 2030#endif
3f38a55e 2031
5463e4b9 2032 if (vport < 0)
4dd643d5 2033 vport = http->getConn()->clientConnection->local.port();
5463e4b9 2034
ae7ff0b8 2035 const bool switchedToHttps = conn->switchedToHttps();
2036 const bool tryHostHeader = vhost || switchedToHttps;
9ff1b8ca 2037 char *host = NULL;
9bafa70d 2038 if (tryHostHeader && (host = hp->getHeaderField("Host"))) {
5463e4b9
AJ
2039 debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
2040 char thost[256];
2041 if (vport > 0) {
2042 thost[0] = '\0';
2043 char *t = NULL;
2044 if (host[strlen(host)] != ']' && (t = strrchr(host,':')) != NULL) {
2045 strncpy(thost, host, (t-host));
2046 snprintf(thost+(t-host), sizeof(thost)-(t-host), ":%d", vport);
2047 host = thost;
2048 } else if (!t) {
2049 snprintf(thost, sizeof(thost), "%s:%d",host, vport);
2050 host = thost;
2051 }
2052 } // else nothing to alter port-wise.
9bafa70d 2053 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen + strlen(host);
62e76326 2054 http->uri = (char *)xcalloc(url_sz, 1);
de95413f 2055 snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH, AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(url));
b1cef121 2056 debugs(33, 5, "ACCEL VHOST REWRITE: " << http->uri);
5463e4b9
AJ
2057 } else if (conn->port->defaultsite /* && !vhost */) {
2058 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
9bafa70d 2059 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen +
f53969cc 2060 strlen(conn->port->defaultsite);
62e76326 2061 http->uri = (char *)xcalloc(url_sz, 1);
5463e4b9
AJ
2062 char vportStr[32];
2063 vportStr[0] = '\0';
2064 if (vport > 0) {
2065 snprintf(vportStr, sizeof(vportStr),":%d",vport);
2066 }
b1cef121 2067 snprintf(http->uri, url_sz, "%s://%s%s" SQUIDSBUFPH,
de95413f 2068 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
b1cef121 2069 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << http->uri);
5463e4b9 2070 } else if (vport > 0 /* && (!vhost || no Host:) */) {
3cc0f4e7 2071 debugs(33, 5, "ACCEL VPORT REWRITE: *_port IP + vport=" << vport);
5463e4b9 2072 /* Put the local socket IP address as the hostname, with whatever vport we found */
9bafa70d 2073 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen;
62e76326 2074 http->uri = (char *)xcalloc(url_sz, 1);
4dd643d5 2075 http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
b1cef121 2076 snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
4599cded 2077 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(),
b1cef121
AJ
2078 ipbuf, vport, SQUIDSBUFPRINT(url));
2079 debugs(33, 5, "ACCEL VPORT REWRITE: " << http->uri);
3f38a55e 2080 }
2081}
2082
2083static void
9bafa70d 2084prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, const Http1::RequestParserPointer &hp)
62e76326 2085{
b47500dc 2086 // TODO Must() on URI !empty when the parser supports throw. For now avoid assert().
9bafa70d 2087 if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
62e76326 2088 return; /* already in good shape */
3f38a55e 2089
2090 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
2091
9bafa70d
AJ
2092 if (const char *host = hp->getHeaderField("Host")) {
2093 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen +
f53969cc 2094 strlen(host);
62e76326 2095 http->uri = (char *)xcalloc(url_sz, 1);
b47500dc 2096 snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH,
f53969cc 2097 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(hp->requestUri()));
b47500dc 2098 debugs(33, 5, "TRANSPARENT HOST REWRITE: " << http->uri);
c8be6d7b 2099 } else {
62e76326 2100 /* Put the local socket IP address as the hostname. */
9bafa70d 2101 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen;
62e76326 2102 http->uri = (char *)xcalloc(url_sz, 1);
f9688132 2103 static char ipbuf[MAX_IPSTRLEN];
4dd643d5 2104 http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
b47500dc 2105 snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
4599cded 2106 AnyP::UriScheme(http->getConn()->transferProtocol.protocol).c_str(),
9bafa70d 2107 ipbuf, http->getConn()->clientConnection->local.port(), SQUIDSBUFPRINT(hp->requestUri()));
b47500dc 2108 debugs(33, 5, "TRANSPARENT REWRITE: " << http->uri);
c8be6d7b 2109 }
c8be6d7b 2110}
2111
7830d88a 2112/** Parse an HTTP request
9e008dda 2113 *
7830d88a
FC
2114 * \note Sets result->flags.parsed_ok to 0 if failed to parse the request,
2115 * to 1 if the request was correctly parsed.
2116 * \param[in] csd a ConnStateData. The caller must make sure it is not null
1b51ee7b 2117 * \param[in] hp an Http1::RequestParser
7830d88a
FC
2118 * \param[out] mehtod_p will be set as a side-effect of the parsing.
2119 * Pointed-to value will be set to Http::METHOD_NONE in case of
2120 * parsing failure
2121 * \param[out] http_ver will be set as a side-effect of the parsing
2122 * \return NULL on incomplete requests,
2123 * a ClientSocketContext structure on success or failure.
7a2f978b 2124 */
92ae4c86 2125ClientSocketContext *
9bafa70d 2126parseHttpRequest(ConnStateData *csd, const Http1::RequestParserPointer &hp)
7a2f978b 2127{
87abd755
AJ
2128 /* Attempt to parse the first line; this will define where the method, url, version and header begin */
2129 {
9bafa70d 2130 const bool parsedOk = hp->parse(csd->in.buf);
fc68f6b1 2131
3248e962
CT
2132 if (csd->port->flags.isIntercepted() && Config.accessList.on_unsupported_protocol)
2133 csd->preservedClientData = csd->in.buf;
36a9c964 2134 // sync the buffers after parsing.
9bafa70d 2135 csd->in.buf = hp->remaining();
7a4fa6a0 2136
9bafa70d 2137 if (hp->needsMoreData()) {
87abd755
AJ
2138 debugs(33, 5, "Incomplete request, waiting for end of request line");
2139 return NULL;
2140 }
fc68f6b1 2141
016a316b 2142 if (!parsedOk) {
f1d5359e 2143 if (hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge || hp->parseStatusCode == Http::scUriTooLong)
943cdf6d 2144 return csd->abortRequestParsing("error:request-too-large");
016a316b 2145
943cdf6d 2146 return csd->abortRequestParsing("error:invalid-request");
016a316b 2147 }
84cc2635 2148 }
fc68f6b1 2149
6d0613b2
AJ
2150 /* We know the whole request is in parser now */
2151 debugs(11, 2, "HTTP Client " << csd->clientConnection);
2152 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
9bafa70d
AJ
2153 hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol() << "\n" <<
2154 hp->mimeHeader() <<
6d0613b2 2155 "\n----------");
fc68f6b1 2156
adf29627 2157 /* deny CONNECT via accelerated ports */
9bafa70d 2158 if (hp->method() == Http::METHOD_CONNECT && csd->port != NULL && csd->port->flags.accelSurrogate) {
4599cded 2159 debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << csd->transferProtocol << " Accelerator port " << csd->port->s.port());
9bafa70d 2160 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
f1d5359e 2161 hp->parseStatusCode = Http::scMethodNotAllowed;
eacfca83 2162 return csd->abortRequestParsing("error:method-not-allowed");
adf29627
AJ
2163 }
2164
c27e1e37 2165 /* RFC 7540 section 11.6 registers the method PRI as HTTP/2 specific
5de5c2d0
AJ
2166 * Deny "PRI" method if used in HTTP/1.x or 0.9 versions.
2167 * If seen it signals a broken client or proxy has corrupted the traffic.
2168 */
2169 if (hp->method() == Http::METHOD_PRI && hp->messageProtocol() < Http::ProtocolVersion(2,0)) {
2170 debugs(33, DBG_IMPORTANT, "WARNING: PRI method received on " << csd->transferProtocol << " port " << csd->port->s.port());
2171 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
093c6381 2172 hp->parseStatusCode = Http::scMethodNotAllowed;
5de5c2d0
AJ
2173 return csd->abortRequestParsing("error:method-not-allowed");
2174 }
2175
9bafa70d
AJ
2176 if (hp->method() == Http::METHOD_NONE) {
2177 debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
f1d5359e 2178 hp->parseStatusCode = Http::scMethodNotAllowed;
eacfca83 2179 return csd->abortRequestParsing("error:unsupported-request-method");
3f38a55e 2180 }
7a2f978b 2181
6d0613b2
AJ
2182 // Process headers after request line
2183 debugs(33, 3, "complete request received. " <<
9bafa70d
AJ
2184 "prefix_sz = " << hp->messageHeaderSize() <<
2185 ", request-line-size=" << hp->firstLineSize() <<
2186 ", mime-header-size=" << hp->headerBlockSize() <<
2187 ", mime header block:\n" << hp->mimeHeader() << "\n----------");
62e76326 2188
7a2f978b 2189 /* Ok, all headers are received */
9ff1b8ca
AJ
2190 ClientHttpRequest *http = new ClientHttpRequest(csd);
2191
9bafa70d 2192 http->req_sz = hp->messageHeaderSize();
b9f85998 2193 ClientSocketContext *result = new ClientSocketContext(csd->clientConnection, http);
62e76326 2194
9ff1b8ca 2195 StoreIOBuffer tempBuffer;
c8be6d7b 2196 tempBuffer.data = result->reqbuf;
2197 tempBuffer.length = HTTP_REQBUF_SZ;
62e76326 2198
0655fa4d 2199 ClientStreamData newServer = new clientReplyContext(http);
0655fa4d 2200 ClientStreamData newClient = result;
edce4d98 2201 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 2202 clientReplyStatus, newServer, clientSocketRecipient,
2203 clientSocketDetach, newClient, tempBuffer);
62e76326 2204
3ff65596 2205 /* set url */
b1cef121
AJ
2206 // XXX: c_str() does re-allocate but here replaces explicit malloc/free.
2207 // when internalCheck() accepts SBuf removing this will be a net gain for performance.
f9688132
AJ
2208 SBuf tmp(hp->requestUri());
2209 const char *url = tmp.c_str();
7a2f978b 2210
450e09f9 2211 debugs(33,5, "Prepare absolute URL from " <<
2ee34428 2212 (csd->transparent()?"intercept":(csd->port->flags.accelSurrogate ? "accel":"")));
3f38a55e 2213 /* Rewrite the URL in transparent or accelerator mode */
89272111
AJ
2214 /* NP: there are several cases to traverse here:
2215 * - standard mode (forward proxy)
2216 * - transparent mode (TPROXY)
2217 * - transparent mode with failures
2218 * - intercept mode (NAT)
2219 * - intercept mode with failures
2220 * - accelerator mode (reverse proxy)
2221 * - internal URL
2222 * - mixed combos of the above with internal URL
151ba0d4
AJ
2223 * - remote interception with PROXY protocol
2224 * - remote reverse-proxy with PROXY protocol
89272111 2225 */
be364179 2226 if (csd->transparent()) {
89272111 2227 /* intercept or transparent mode, properly working with no failures */
5f3cc9a2 2228 prepareTransparentURL(csd, http, hp);
89272111 2229
2f2749d7 2230 } else if (internalCheck(url)) {
89272111 2231 /* internal URL mode */
2f2749d7 2232 /* prepend our name & port */
2233 http->uri = xstrdup(internalLocalUri(NULL, url));
59c59acf
AJ
2234 // We just re-wrote the URL. Must replace the Host: header.
2235 // But have not parsed there yet!! flag for local-only handling.
be4d35dc 2236 http->flags.internal = true;
59c59acf 2237
6a25a046 2238 } else if (csd->port->flags.accelSurrogate || csd->switchedToHttps()) {
59c59acf 2239 /* accelerator mode */
5f3cc9a2 2240 prepareAcceleratedURL(csd, http, hp);
3f38a55e 2241 }
2242
2243 if (!http->uri) {
62e76326 2244 /* No special rewrites have been applied above, use the
2245 * requested url. may be rewritten later, so make extra room */
9bafa70d 2246 int url_sz = hp->requestUri().length() + Config.appendDomainLen + 5;
62e76326 2247 http->uri = (char *)xcalloc(url_sz, 1);
2248 strcpy(http->uri, url);
3f38a55e 2249 }
62e76326 2250
c4b7a5a9 2251 result->flags.parsed_ok = 1;
c8be6d7b 2252 return result;
7a2f978b 2253}
2254
1368d115 2255bool
8a64f7f1 2256ConnStateData::In::maybeMakeSpaceAvailable()
c8be6d7b 2257{
8a64f7f1
AJ
2258 if (buf.spaceSize() < 2) {
2259 const SBuf::size_type haveCapacity = buf.length() + buf.spaceSize();
e7287625 2260 if (haveCapacity >= Config.maxRequestBufferSize) {
90737510
A
2261 debugs(33, 4, "request buffer full: client_request_buffer_max_size=" << Config.maxRequestBufferSize);
2262 return false;
2263 }
50ae17d6
AJ
2264 if (haveCapacity == 0) {
2265 // haveCapacity is based on the SBuf visible window of the MemBlob buffer, which may fill up.
2266 // at which point bump the buffer back to default. This allocates a new MemBlob with any un-parsed bytes.
2267 buf.reserveCapacity(CLIENT_REQ_BUF_SZ);
2268 } else {
2269 const SBuf::size_type wantCapacity = min(static_cast<SBuf::size_type>(Config.maxRequestBufferSize), haveCapacity*2);
2270 buf.reserveCapacity(wantCapacity);
2271 }
8a64f7f1 2272 debugs(33, 2, "growing request buffer: available=" << buf.spaceSize() << " used=" << buf.length());
c8be6d7b 2273 }
8a64f7f1 2274 return (buf.spaceSize() >= 2);
c8be6d7b 2275}
2276
2277void
0655fa4d 2278ConnStateData::addContextToQueue(ClientSocketContext * context)
c8be6d7b 2279{
0655fa4d 2280 ClientSocketContext::Pointer *S;
62e76326 2281
0655fa4d 2282 for (S = (ClientSocketContext::Pointer *) & currentobject; S->getRaw();
3d0ac046 2283 S = &(*S)->next);
c8be6d7b 2284 *S = context;
62e76326 2285
0655fa4d 2286 ++nrequests;
c8be6d7b 2287}
2288
2289int
0655fa4d 2290ConnStateData::getConcurrentRequestCount() const
c8be6d7b 2291{
2292 int result = 0;
0655fa4d 2293 ClientSocketContext::Pointer *T;
62e76326 2294
0655fa4d 2295 for (T = (ClientSocketContext::Pointer *) &currentobject;
3d0ac046 2296 T->getRaw(); T = &(*T)->next, ++result);
c8be6d7b 2297 return result;
2298}
2299
c8be6d7b 2300int
1cf238db 2301ConnStateData::connFinishedWithConn(int size)
c8be6d7b 2302{
2303 if (size == 0) {
e7287625 2304 if (getConcurrentRequestCount() == 0 && in.buf.isEmpty()) {
62e76326 2305 /* no current or pending requests */
73c36fd9 2306 debugs(33, 4, HERE << clientConnection << " closed");
62e76326 2307 return 1;
2308 } else if (!Config.onoff.half_closed_clients) {
2309 /* admin doesn't want to support half-closed client sockets */
73c36fd9 2310 debugs(33, 3, HERE << clientConnection << " aborted (half_closed_clients disabled)");
f692498b 2311 notifyAllContexts(0); // no specific error implies abort
62e76326 2312 return 1;
2313 }
c8be6d7b 2314 }
62e76326 2315
c8be6d7b 2316 return 0;
2317}
2318
92ae4c86
AR
2319void
2320ConnStateData::consumeInput(const size_t byteCount)
2321{
2322 assert(byteCount > 0 && byteCount <= in.buf.length());
2323 in.buf.consume(byteCount);
2324 debugs(33, 5, "in.buf has " << in.buf.length() << " unused bytes");
2325}
2326
1cf238db 2327void
f35961af 2328ConnStateData::clientAfterReadingRequests()
c4b7a5a9 2329{
39cb8c41 2330 // Were we expecting to read more request body from half-closed connection?
73c36fd9
AJ
2331 if (mayNeedToReadMoreBody() && commIsHalfClosed(clientConnection->fd)) {
2332 debugs(33, 3, HERE << "truncated body: closing half-closed " << clientConnection);
2333 clientConnection->close();
39cb8c41 2334 return;
c4b7a5a9 2335 }
2336
f35961af
AR
2337 if (flags.readMore)
2338 readSomeData();
c4b7a5a9 2339}
2340
84c77748
AR
2341void
2342ConnStateData::quitAfterError(HttpRequest *request)
2343{
2344 // From HTTP p.o.v., we do not have to close after every error detected
2345 // at the client-side, but many such errors do require closure and the
2346 // client-side code is bad at handling errors so we play it safe.
2347 if (request)
e857372a 2348 request->flags.proxyKeepalive = false;
84c77748
AR
2349 flags.readMore = false;
2350 debugs(33,4, HERE << "Will close after error: " << clientConnection);
2351}
2352
cb4f4424 2353#if USE_OPENSSL
8eb0a7ee
CT
2354bool ConnStateData::serveDelayedError(ClientSocketContext *context)
2355{
2356 ClientHttpRequest *http = context->http;
fd4624d7
CT
2357
2358 if (!sslServerBump)
8eb0a7ee
CT
2359 return false;
2360
fd4624d7 2361 assert(sslServerBump->entry);
7a957a93 2362 // Did we create an error entry while processing CONNECT?
fd4624d7 2363 if (!sslServerBump->entry->isEmpty()) {
84c77748
AR
2364 quitAfterError(http->request);
2365
7a957a93
AR
2366 // Get the saved error entry and send it to the client by replacing the
2367 // ClientHttpRequest store entry with it.
8eb0a7ee
CT
2368 clientStreamNode *node = context->getClientReplyContext();
2369 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
7a957a93
AR
2370 assert(repContext);
2371 debugs(33, 5, "Responding with delated error for " << http->uri);
f0baf149 2372 repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
7a957a93
AR
2373
2374 // save the original request for logging purposes
b248c2a3
AJ
2375 if (!context->http->al->request) {
2376 context->http->al->request = http->request;
2377 HTTPMSGLOCK(context->http->al->request);
2378 }
7a957a93
AR
2379
2380 // Get error details from the fake certificate-peeking request.
129fe2a1 2381 http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
8eb0a7ee 2382 context->pullData();
8eb0a7ee
CT
2383 return true;
2384 }
2385
7a957a93
AR
2386 // In bump-server-first mode, we have not necessarily seen the intended
2387 // server name at certificate-peeking time. Check for domain mismatch now,
2388 // when we can extract the intended name from the bumped HTTP request.
62a7607e 2389 if (X509 *srvCert = sslServerBump->serverCert.get()) {
8eb0a7ee 2390 HttpRequest *request = http->request;
5c51bffb 2391 if (!Ssl::checkX509ServerValidity(srvCert, request->url.host())) {
7a957a93 2392 debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
5c51bffb 2393 "does not match domainname " << request->url.host());
8eb0a7ee 2394
d4a56c34 2395 bool allowDomainMismatch = false;
638402dd
AJ
2396 if (Config.ssl_client.cert_error) {
2397 ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
62a7607e 2398 check.sslErrors = new Ssl::CertErrors(Ssl::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
d4a56c34 2399 allowDomainMismatch = (check.fastCheck() == ACCESS_ALLOWED);
638402dd
AJ
2400 delete check.sslErrors;
2401 check.sslErrors = NULL;
2402 }
8eb0a7ee 2403
7d82c5b8 2404 if (!allowDomainMismatch) {
84c77748
AR
2405 quitAfterError(request);
2406
8eb0a7ee
CT
2407 clientStreamNode *node = context->getClientReplyContext();
2408 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2409 assert (repContext);
2410
7a957a93 2411 // Fill the server IP and hostname for error page generation.
fd4624d7 2412 HttpRequest::Pointer const & peekerRequest = sslServerBump->request;
5c51bffb 2413 request->hier.note(peekerRequest->hier.tcpServer, request->url.host());
7a957a93 2414
8eb0a7ee 2415 // Create an error object and fill it
955394ce 2416 ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request);
8eb0a7ee 2417 err->src_addr = clientConnection->remote;
7a957a93
AR
2418 Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
2419 SQUID_X509_V_ERR_DOMAIN_MISMATCH,
62a7607e 2420 srvCert, NULL);
8eb0a7ee 2421 err->detail = errDetail;
7a957a93 2422 // Save the original request for logging purposes.
b248c2a3
AJ
2423 if (!context->http->al->request) {
2424 context->http->al->request = request;
2425 HTTPMSGLOCK(context->http->al->request);
2426 }
8eb0a7ee
CT
2427 repContext->setReplyToError(request->method, err);
2428 assert(context->http->out.offset == 0);
2429 context->pullData();
8eb0a7ee
CT
2430 return true;
2431 }
2432 }
2433 }
2434
2435 return false;
2436}
cb4f4424 2437#endif // USE_OPENSSL
8eb0a7ee 2438
3248e962
CT
2439/**
2440 * Check on_unsupported_protocol checklist and return true if tunnel mode selected
2441 * or false otherwise
2442 */
2443bool
2444clientTunnelOnError(ConnStateData *conn, ClientSocketContext *context, HttpRequest *request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
2445{
2446 if (conn->port->flags.isIntercepted() &&
44352c16 2447 Config.accessList.on_unsupported_protocol && conn->nrequests <= 1) {
3248e962
CT
2448 ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, request, NULL);
2449 checklist.requestErrorType = requestError;
2450 checklist.src_addr = conn->clientConnection->remote;
2451 checklist.my_addr = conn->clientConnection->local;
2452 checklist.conn(conn);
2453 allow_t answer = checklist.fastCheck();
2454 if (answer == ACCESS_ALLOWED && answer.kind == 1) {
2455 debugs(33, 3, "Request will be tunneled to server");
2456 if (context)
2457 context->removeFromConnectionList(conn);
2458 Comm::SetSelect(conn->clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
44352c16 2459
3248e962
CT
2460 SBuf preReadData;
2461 if (conn->preservedClientData.length())
2462 preReadData.append(conn->preservedClientData);
2463 static char ip[MAX_IPSTRLEN];
2464 conn->clientConnection->local.toUrl(ip, sizeof(ip));
2465 conn->in.buf.assign("CONNECT ").append(ip).append(" HTTP/1.1\r\nHost: ").append(ip).append("\r\n\r\n").append(preReadData);
2466
2467 bool ret = conn->handleReadData();
2468 if (ret)
2469 ret = conn->clientParseRequests();
2470
2471 if (!ret) {
2472 debugs(33, 2, "Failed to start fake CONNECT request for on_unsupported_protocol: " << conn->clientConnection);
2473 conn->clientConnection->close();
2474 }
2475 return true;
2476 } else {
2477 debugs(33, 3, "Continue with returning the error: " << requestError);
2478 }
2479 }
2480
2481 if (context) {
2482 conn->quitAfterError(request);
2483 clientStreamNode *node = context->getClientReplyContext();
2484 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2485 assert (repContext);
2486
2487 repContext->setReplyToError(requestError, errStatusCode, method, context->http->uri, conn->clientConnection->remote, NULL, requestErrorBytes, NULL);
2488
2489 assert(context->http->out.offset == 0);
2490 context->pullData();
2491 } // else Probably an ERR_REQUEST_START_TIMEOUT error so just return.
2492 return false;
2493}
2494
ec69bdb2 2495void
28fd6d0b
AJ
2496clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
2497{
2498 /*
2499 * DPW 2007-05-18
2500 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
2501 * to here because calling comm_reset_close() causes http to
48a37aee 2502 * be freed before accessing.
28fd6d0b
AJ
2503 */
2504 if (request != NULL && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
2505 debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
2506 conn->flags.readMore = false;
2507 comm_reset_close(conn->clientConnection);
2508 }
2509}
2510
9bafa70d
AJ
2511void
2512clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, ClientSocketContext *context)
c4b7a5a9 2513{
59a1efb2 2514 ClientHttpRequest *http = context->http;
39cb8c41 2515 bool chunked = false;
e18b8316 2516 bool mustReplyToOptions = false;
3ff65596 2517 bool unsupportedTe = false;
39cb8c41 2518 bool expectBody = false;
5f8252d2 2519
ec69bdb2
CT
2520 // We already have the request parsed and checked, so we
2521 // only need to go through the final body/conn setup to doCallouts().
2522 assert(http->request);
2523 HttpRequest::Pointer request = http->request;
2524
92ae4c86 2525 // temporary hack to avoid splitting this huge function with sensitive code
f9688132 2526 const bool isFtp = !hp;
9bafa70d 2527
92ae4c86
AR
2528 // Some blobs below are still HTTP-specific, but we would have to rewrite
2529 // this entire function to remove them from the FTP code path. Connection
2530 // setup and body_pipe preparation blobs are needed for FTP.
2531
40d34a62
AJ
2532 request->clientConnectionManager = conn;
2533
c4b7a5a9 2534 request->flags.accelerated = http->flags.accel;
450fe1cb 2535 request->flags.sslBumped=conn->switchedToHttps();
450fe1cb 2536 request->flags.ignoreCc = conn->port->ignore_cc;
7fb65ee4 2537 // TODO: decouple http->flags.accel from request->flags.sslBumped
450fe1cb 2538 request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
e4a14600 2539 !conn->port->allow_direct : 0;
21512911
CT
2540#if USE_AUTH
2541 if (request->flags.sslBumped) {
cc1e110a
AJ
2542 if (conn->getAuth() != NULL)
2543 request->auth_user_request = conn->getAuth();
21512911
CT
2544 }
2545#endif
2ad20b4f
AJ
2546
2547 /** \par
2548 * If transparent or interception mode is working clone the transparent and interception flags
2549 * from the port settings to the request.
2550 */
40d34a62 2551 if (http->clientConnection != NULL) {
304a6180 2552 request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
0d901ef4 2553 request->flags.interceptTproxy = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
13edf4cc
AJ
2554 static const bool proxyProtocolPort = (conn->port != NULL) ? conn->port->flags.proxySurrogate : false;
2555 if (request->flags.interceptTproxy && !proxyProtocolPort) {
0d901ef4
SH
2556 if (Config.accessList.spoof_client_ip) {
2557 ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.spoof_client_ip, http);
2558 request->flags.spoofClientIp = (checklist->fastCheck() == ACCESS_ALLOWED);
2559 delete checklist;
2560 } else
2561 request->flags.spoofClientIp = true;
2562 } else
2563 request->flags.spoofClientIp = false;
2ad20b4f 2564 }
fc68f6b1 2565
5b4117d8 2566 if (internalCheck(request->urlpath.termedBuf())) {
5c51bffb
AJ
2567 if (internalHostnameIs(request->url.host()) && request->url.port() == getMyPort()) {
2568 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
be4d35dc 2569 http->flags.internal = true;
9c175897 2570 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.termedBuf())) {
5c51bffb 2571 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
f6894d37 2572 request->url.setScheme(AnyP::PROTO_HTTP);
cc192b50 2573 request->SetHost(internalHostname());
5c51bffb 2574 request->url.port(getMyPort());
be4d35dc 2575 http->flags.internal = true;
4e3f4dc7 2576 } else
5c51bffb 2577 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
f024c970 2578 }
e72a0ec0 2579
c4b7a5a9 2580 request->flags.internal = http->flags.internal;
b248c2a3 2581 setLogUri (http, urlCanonicalClean(request.getRaw()));
73c36fd9 2582 request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
3d674977 2583#if FOLLOW_X_FORWARDED_FOR
40d34a62
AJ
2584 // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
2585 // not a details about teh TCP connection itself
73c36fd9 2586 request->indirect_client_addr = conn->clientConnection->remote;
3d674977 2587#endif /* FOLLOW_X_FORWARDED_FOR */
73c36fd9 2588 request->my_addr = conn->clientConnection->local;
35fb56c9 2589 request->myportname = conn->port->name;
9bafa70d
AJ
2590
2591 if (!isFtp) {
2592 // XXX: for non-HTTP messages instantiate a different HttpMsg child type
2593 // for now Squid only supports HTTP requests
2594 const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
2595 assert(request->http_ver.protocol == http_ver.protocol);
2596 request->http_ver.major = http_ver.major;
2597 request->http_ver.minor = http_ver.minor;
2598 }
62e76326 2599
9174ba3d
AJ
2600 // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
2601 // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
2602 request->clientConnectionManager = conn;
2603
39cb8c41
AR
2604 if (request->header.chunked()) {
2605 chunked = true;
2606 } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
2607 const String te = request->header.getList(HDR_TRANSFER_ENCODING);
2608 // HTTP/1.1 requires chunking to be the last encoding if there is one
2609 unsupportedTe = te.size() && te != "identity";
2610 } // else implied identity coding
de48b288 2611
f9688132 2612 mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
d916dcb8 2613 (request->header.getInt64(HDR_MAX_FORWARDS) == 0);
b248c2a3 2614 if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions || unsupportedTe) {
62e76326 2615 clientStreamNode *node = context->getClientReplyContext();
b248c2a3 2616 conn->quitAfterError(request.getRaw());
0655fa4d 2617 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2618 assert (repContext);
955394ce 2619 repContext->setReplyToError(ERR_UNSUP_REQ, Http::scNotImplemented, request->method, NULL,
b248c2a3 2620 conn->clientConnection->remote, request.getRaw(), NULL, NULL);
62e76326 2621 assert(context->http->out.offset == 0);
2622 context->pullData();
28fd6d0b
AJ
2623 clientProcessRequestFinished(conn, request);
2624 return;
c4b7a5a9 2625 }
2626
b248c2a3 2627 if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
62e76326 2628 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2629 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2630 assert (repContext);
b248c2a3 2631 conn->quitAfterError(request.getRaw());
0655fa4d 2632 repContext->setReplyToError(ERR_INVALID_REQ,
955394ce 2633 Http::scLengthRequired, request->method, NULL,
b248c2a3 2634 conn->clientConnection->remote, request.getRaw(), NULL, NULL);
62e76326 2635 assert(context->http->out.offset == 0);
2636 context->pullData();
28fd6d0b
AJ
2637 clientProcessRequestFinished(conn, request);
2638 return;
c4b7a5a9 2639 }
2640
92ae4c86 2641 clientSetKeepaliveFlag(http);
f35961af 2642 // Let tunneling code be fully responsible for CONNECT requests
c2a7cefd 2643 if (http->request->method == Http::METHOD_CONNECT) {
b66e0e86 2644 context->mayUseConnection(true);
f35961af
AR
2645 conn->flags.readMore = false;
2646 }
fc68f6b1 2647
cb4f4424 2648#if USE_OPENSSL
28fd6d0b 2649 if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
28fd6d0b
AJ
2650 clientProcessRequestFinished(conn, request);
2651 return;
2652 }
061bbdec
CT
2653#endif
2654
b66e0e86 2655 /* Do we expect a request-body? */
39cb8c41
AR
2656 expectBody = chunked || request->content_length > 0;
2657 if (!context->mayUseConnection() && expectBody) {
2658 request->body_pipe = conn->expectRequestBody(
de48b288 2659 chunked ? -1 : request->content_length);
5f8252d2 2660
62e76326 2661 /* Is it too large? */
39cb8c41 2662 if (!chunked && // if chunked, we will check as we accumulate
de48b288 2663 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
62e76326 2664 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2665 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2666 assert (repContext);
b248c2a3 2667 conn->quitAfterError(request.getRaw());
0655fa4d 2668 repContext->setReplyToError(ERR_TOO_BIG,
f11c8e2f 2669 Http::scPayloadTooLarge, Http::METHOD_NONE, NULL,
73c36fd9 2670 conn->clientConnection->remote, http->request, NULL, NULL);
62e76326 2671 assert(context->http->out.offset == 0);
2672 context->pullData();
28fd6d0b
AJ
2673 clientProcessRequestFinished(conn, request);
2674 return;
62e76326 2675 }
2676
92ae4c86 2677 if (!isFtp) {
a5d444a5
DK
2678 // We may stop producing, comm_close, and/or call setReplyToError()
2679 // below, so quit on errors to avoid http->doCallouts()
28fd6d0b
AJ
2680 if (!conn->handleRequestBodyData()) {
2681 clientProcessRequestFinished(conn, request);
2682 return;
2683 }
39cb8c41 2684
a5d444a5 2685 if (!request->body_pipe->productionEnded()) {
e7ce227f 2686 debugs(33, 5, "need more request body");
a5d444a5
DK
2687 context->mayUseConnection(true);
2688 assert(conn->flags.readMore);
2689 }
f35961af 2690 }
c4b7a5a9 2691 }
2692
de31d06f 2693 http->calloutContext = new ClientRequestContext(http);
2694
2695 http->doCallouts();
9e008dda 2696
28fd6d0b 2697 clientProcessRequestFinished(conn, request);
c4b7a5a9 2698}
2699
92ae4c86
AR
2700int
2701ConnStateData::pipelinePrefetchMax() const
2702{
efbea402
CT
2703 // TODO: Support pipelined requests through pinned connections.
2704 if (pinning.pinned)
2705 return 0;
92ae4c86
AR
2706 return Config.pipeline_max_prefetch;
2707}
2708
079a8480
AJ
2709/**
2710 * Limit the number of concurrent requests.
2711 * \return true when there are available position(s) in the pipeline queue for another request.
2712 * \return false when the pipeline queue is full or disabled.
2713 */
2714bool
2715ConnStateData::concurrentRequestQueueFilled() const
c4b7a5a9 2716{
079a8480
AJ
2717 const int existingRequestCount = getConcurrentRequestCount();
2718
2719 // default to the configured pipeline size.
2720 // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
0de57497 2721#if USE_OPENSSL
43b43c46 2722 const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
0de57497 2723#else
06211564 2724 const int internalRequest = 0;
0de57497 2725#endif
43b43c46 2726 const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
62e76326 2727
079a8480
AJ
2728 // when queue filled already we cant add more.
2729 if (existingRequestCount >= concurrentRequestLimit) {
2730 debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
2731 debugs(33, 5, clientConnection << " deferring new request until one is done");
2732 return true;
c4b7a5a9 2733 }
62e76326 2734
079a8480 2735 return false;
c4b7a5a9 2736}
2737
00d0ce87 2738/**
d3d92daa 2739 * Perform proxy_protocol_access ACL tests on the client which
3bd97e7e
AJ
2740 * connected to PROXY protocol port to see if we trust the
2741 * sender enough to accept their PROXY header claim.
00d0ce87 2742 */
6658cc16 2743bool
00d0ce87
AJ
2744ConnStateData::proxyProtocolValidateClient()
2745{
d3d92daa
AJ
2746 if (!Config.accessList.proxyProtocol)
2747 return proxyProtocolError("PROXY client not permitted by default ACL");
2748
2749 ACLFilledChecklist ch(Config.accessList.proxyProtocol, NULL, clientConnection->rfc931);
00d0ce87
AJ
2750 ch.src_addr = clientConnection->remote;
2751 ch.my_addr = clientConnection->local;
6658cc16 2752 ch.conn(this);
00d0ce87 2753
3bd97e7e
AJ
2754 if (ch.fastCheck() != ACCESS_ALLOWED)
2755 return proxyProtocolError("PROXY client not permitted by ACLs");
2756
6658cc16 2757 return true;
00d0ce87
AJ
2758}
2759
2760/**
2761 * Perform cleanup on PROXY protocol errors.
2762 * If header parsing hits a fatal error terminate the connection,
2763 * otherwise wait for more data.
2764 */
2765bool
3bd97e7e 2766ConnStateData::proxyProtocolError(const char *msg)
00d0ce87 2767{
3c082dbe 2768 if (msg) {
70a16fea
AJ
2769 // This is important to know, but maybe not so much that flooding the log is okay.
2770#if QUIET_PROXY_PROTOCOL
2771 // display the first of every 32 occurances at level 1, the others at level 2.
2772 static uint8_t hide = 0;
2773 debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
2774#else
2775 debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
2776#endif
3bd97e7e 2777 mustStop(msg);
3c082dbe 2778 }
00d0ce87
AJ
2779 return false;
2780}
2781
3bd97e7e 2782/// magic octet prefix for PROXY protocol version 1
70a16fea 2783static const SBuf Proxy1p0magic("PROXY ", 6);
3bd97e7e
AJ
2784
2785/// magic octet prefix for PROXY protocol version 2
70a16fea 2786static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
3bd97e7e 2787
00d0ce87 2788/**
3bd97e7e
AJ
2789 * Test the connection read buffer for PROXY protocol header.
2790 * Version 1 and 2 header currently supported.
00d0ce87
AJ
2791 */
2792bool
3d74cb1f 2793ConnStateData::parseProxyProtocolHeader()
00d0ce87 2794{
00d0ce87 2795 // http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
cbae7ab9 2796
70a16fea
AJ
2797 // detect and parse PROXY/2.0 protocol header
2798 if (in.buf.startsWith(Proxy2p0magic))
2799 return parseProxy2p0();
00d0ce87 2800
70a16fea
AJ
2801 // detect and parse PROXY/1.0 protocol header
2802 if (in.buf.startsWith(Proxy1p0magic))
bd1be2ae 2803 return parseProxy1p0();
00d0ce87 2804
70a16fea
AJ
2805 // detect and terminate other protocols
2806 if (in.buf.length() >= Proxy2p0magic.length()) {
2807 // PROXY/1.0 magic is shorter, so we know that
2808 // the input does not start with any PROXY magic
3bd97e7e
AJ
2809 return proxyProtocolError("PROXY protocol error: invalid header");
2810 }
00d0ce87 2811
3d74cb1f
AJ
2812 // TODO: detect short non-magic prefixes earlier to avoid
2813 // waiting for more data which may never come
2814
3bd97e7e
AJ
2815 // not enough bytes to parse yet.
2816 return false;
2817}
00d0ce87 2818
3bd97e7e
AJ
2819/// parse the PROXY/1.0 protocol header from the connection read buffer
2820bool
70a16fea 2821ConnStateData::parseProxy1p0()
3bd97e7e
AJ
2822{
2823 ::Parser::Tokenizer tok(in.buf);
70a16fea 2824 tok.skip(Proxy1p0magic);
3bd97e7e 2825
154ea566
AJ
2826 // skip to first LF (assumes it is part of CRLF)
2827 static const CharacterSet lineContent = CharacterSet::LF.complement("non-LF");
2828 SBuf line;
2829 if (tok.prefix(line, lineContent, 107-Proxy1p0magic.length())) {
2830 if (tok.skip('\n')) {
2831 // found valid header
2832 in.buf = tok.remaining();
2833 needProxyProtocolHeader_ = false;
2834 // reset the tokenizer to work on found line only.
2835 tok.reset(line);
2836 } else
2837 return false; // no LF yet
2838
2839 } else // protocol error only if there are more than 107 bytes prefix header
2840 return proxyProtocolError(in.buf.length() > 107? "PROXY/1.0 error: missing CRLF" : NULL);
2841
1689cdbc
AJ
2842 static const SBuf unknown("UNKNOWN"), tcpName("TCP");
2843 if (tok.skip(tcpName)) {
3bd97e7e 2844
1689cdbc
AJ
2845 // skip TCP/IP version number
2846 static const CharacterSet tcpVersions("TCP-version","46");
bd1be2ae 2847 if (!tok.skipOne(tcpVersions))
154ea566 2848 return proxyProtocolError("PROXY/1.0 error: missing TCP version");
00d0ce87 2849
3bd97e7e
AJ
2850 // skip SP after protocol version
2851 if (!tok.skip(' '))
154ea566 2852 return proxyProtocolError("PROXY/1.0 error: missing SP");
00d0ce87 2853
3bd97e7e
AJ
2854 SBuf ipa, ipb;
2855 int64_t porta, portb;
1689cdbc
AJ
2856 static const CharacterSet ipChars = CharacterSet("IP Address",".:") + CharacterSet::HEXDIG;
2857
2858 // parse: src-IP SP dst-IP SP src-port SP dst-port CR
2859 // leave the LF until later.
2860 const bool correct = tok.prefix(ipa, ipChars) && tok.skip(' ') &&
2861 tok.prefix(ipb, ipChars) && tok.skip(' ') &&
2862 tok.int64(porta) && tok.skip(' ') &&
2863 tok.int64(portb) &&
2864 tok.skip('\r');
2865 if (!correct)
154ea566 2866 return proxyProtocolError("PROXY/1.0 error: invalid syntax");
00d0ce87 2867
3bd97e7e
AJ
2868 // parse IP and port strings
2869 Ip::Address originalClient, originalDest;
00d0ce87 2870
3bd97e7e
AJ
2871 if (!originalClient.GetHostByName(ipa.c_str()))
2872 return proxyProtocolError("PROXY/1.0 error: invalid src-IP address");
00d0ce87 2873
3bd97e7e
AJ
2874 if (!originalDest.GetHostByName(ipb.c_str()))
2875 return proxyProtocolError("PROXY/1.0 error: invalid dst-IP address");
00d0ce87 2876
3bd97e7e
AJ
2877 if (porta > 0 && porta <= 0xFFFF) // max uint16_t
2878 originalClient.port(static_cast<uint16_t>(porta));
2879 else
2880 return proxyProtocolError("PROXY/1.0 error: invalid src port");
cbae7ab9 2881
3bd97e7e
AJ
2882 if (portb > 0 && portb <= 0xFFFF) // max uint16_t
2883 originalDest.port(static_cast<uint16_t>(portb));
2884 else
2885 return proxyProtocolError("PROXY/1.0 error: invalid dst port");
cbae7ab9 2886
3bd97e7e
AJ
2887 // we have original client and destination details now
2888 // replace the client connection values
2889 debugs(33, 5, "PROXY/1.0 protocol on connection " << clientConnection);
2890 clientConnection->local = originalDest;
2891 clientConnection->remote = originalClient;
65984b09
YW
2892 if ((clientConnection->flags & COMM_TRANSPARENT))
2893 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
3bd97e7e 2894 debugs(33, 5, "PROXY/1.0 upgrade: " << clientConnection);
cbae7ab9 2895
3bd97e7e
AJ
2896 // repeat fetch ensuring the new client FQDN can be logged
2897 if (Config.onoff.log_fqdn)
2898 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
cbae7ab9 2899
3bd97e7e 2900 return true;
1689cdbc
AJ
2901
2902 } else if (tok.skip(unknown)) {
154ea566
AJ
2903 // found valid but unusable header
2904 return true;
1689cdbc
AJ
2905
2906 } else
154ea566 2907 return proxyProtocolError("PROXY/1.0 error: invalid protocol family");
cbae7ab9 2908
3bd97e7e
AJ
2909 return false;
2910}
cbae7ab9 2911
3bd97e7e
AJ
2912/// parse the PROXY/2.0 protocol header from the connection read buffer
2913bool
70a16fea 2914ConnStateData::parseProxy2p0()
3bd97e7e 2915{
1689cdbc
AJ
2916 static const SBuf::size_type prefixLen = Proxy2p0magic.length();
2917 if (in.buf.length() < prefixLen + 4)
2918 return false; // need more bytes
2919
2920 if ((in.buf[prefixLen] & 0xF0) != 0x20) // version == 2 is mandatory
3bd97e7e 2921 return proxyProtocolError("PROXY/2.0 error: invalid version");
cbae7ab9 2922
1689cdbc 2923 const char command = (in.buf[prefixLen] & 0x0F);
3bd97e7e
AJ
2924 if ((command & 0xFE) != 0x00) // values other than 0x0-0x1 are invalid
2925 return proxyProtocolError("PROXY/2.0 error: invalid command");
cbae7ab9 2926
1689cdbc 2927 const char family = (in.buf[prefixLen+1] & 0xF0) >>4;
3bd97e7e
AJ
2928 if (family > 0x3) // values other than 0x0-0x3 are invalid
2929 return proxyProtocolError("PROXY/2.0 error: invalid family");
cbae7ab9 2930
1689cdbc 2931 const char proto = (in.buf[prefixLen+1] & 0x0F);
3bd97e7e
AJ
2932 if (proto > 0x2) // values other than 0x0-0x2 are invalid
2933 return proxyProtocolError("PROXY/2.0 error: invalid protocol type");
cbae7ab9 2934
1689cdbc 2935 const char *clen = in.buf.rawContent() + prefixLen + 2;
17954dbd
AJ
2936 uint16_t len;
2937 memcpy(&len, clen, sizeof(len));
2938 len = ntohs(len);
cbae7ab9 2939
1689cdbc 2940 if (in.buf.length() < prefixLen + 4 + len)
3bd97e7e 2941 return false; // need more bytes
cbae7ab9 2942
1689cdbc 2943 in.buf.consume(prefixLen + 4); // 4 being the extra bytes
3bd97e7e
AJ
2944 const SBuf extra = in.buf.consume(len);
2945 needProxyProtocolHeader_ = false; // found successfully
cbae7ab9 2946
3bd97e7e
AJ
2947 // LOCAL connections do nothing with the extras
2948 if (command == 0x00/* LOCAL*/)
cbae7ab9
AJ
2949 return true;
2950
1689cdbc 2951 union pax {
3bd97e7e
AJ
2952 struct { /* for TCP/UDP over IPv4, len = 12 */
2953 struct in_addr src_addr;
2954 struct in_addr dst_addr;
2955 uint16_t src_port;
2956 uint16_t dst_port;
2957 } ipv4_addr;
2958 struct { /* for TCP/UDP over IPv6, len = 36 */
bd1be2ae
A
2959 struct in6_addr src_addr;
2960 struct in6_addr dst_addr;
2961 uint16_t src_port;
2962 uint16_t dst_port;
3bd97e7e
AJ
2963 } ipv6_addr;
2964#if NOT_SUPPORTED
2965 struct { /* for AF_UNIX sockets, len = 216 */
bd1be2ae
A
2966 uint8_t src_addr[108];
2967 uint8_t dst_addr[108];
3bd97e7e
AJ
2968 } unix_addr;
2969#endif
1689cdbc 2970 };
3bd97e7e 2971
17954dbd
AJ
2972 pax ipu;
2973 memcpy(&ipu, extra.rawContent(), sizeof(pax));
3bd97e7e
AJ
2974
2975 // replace the client connection values
2976 debugs(33, 5, "PROXY/2.0 protocol on connection " << clientConnection);
bd1be2ae 2977 switch (family) {
3bd97e7e 2978 case 0x1: // IPv4
17954dbd
AJ
2979 clientConnection->local = ipu.ipv4_addr.dst_addr;
2980 clientConnection->local.port(ntohs(ipu.ipv4_addr.dst_port));
2981 clientConnection->remote = ipu.ipv4_addr.src_addr;
2982 clientConnection->remote.port(ntohs(ipu.ipv4_addr.src_port));
65984b09
YW
2983 if ((clientConnection->flags & COMM_TRANSPARENT))
2984 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
3bd97e7e
AJ
2985 break;
2986 case 0x2: // IPv6
17954dbd
AJ
2987 clientConnection->local = ipu.ipv6_addr.dst_addr;
2988 clientConnection->local.port(ntohs(ipu.ipv6_addr.dst_port));
2989 clientConnection->remote = ipu.ipv6_addr.src_addr;
2990 clientConnection->remote.port(ntohs(ipu.ipv6_addr.src_port));
65984b09
YW
2991 if ((clientConnection->flags & COMM_TRANSPARENT))
2992 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
3bd97e7e
AJ
2993 break;
2994 default: // do nothing
2995 break;
00d0ce87 2996 }
3bd97e7e 2997 debugs(33, 5, "PROXY/2.0 upgrade: " << clientConnection);
00d0ce87 2998
3bd97e7e
AJ
2999 // repeat fetch ensuring the new client FQDN can be logged
3000 if (Config.onoff.log_fqdn)
3001 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
3002
3003 return true;
00d0ce87
AJ
3004}
3005
3248e962
CT
3006void
3007ConnStateData::receivedFirstByte()
3008{
3009 if (receivedFirstByte_)
3010 return;
3011
3012 receivedFirstByte_ = true;
3013 // Set timeout to Config.Timeout.request
3014 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3015 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3016 TimeoutDialer, this, ConnStateData::requestTimeout);
3017 commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
3018}
3019
63be0a78 3020/**
f900210a 3021 * Attempt to parse one or more requests from the input buffer.
eacfca83
AR
3022 * Returns true after completing parsing of at least one request [header]. That
3023 * includes cases where parsing ended with an error (e.g., a huge request).
f900210a 3024 */
4959e21e 3025bool
f35961af 3026ConnStateData::clientParseRequests()
f900210a 3027{
f900210a 3028 bool parsed_req = false;
3029
1b76e6c1 3030 debugs(33, 5, HERE << clientConnection << ": attempting to parse");
f900210a 3031
39cb8c41 3032 // Loop while we have read bytes that are not needed for producing the body
f35961af 3033 // On errors, bodyPipe may become nil, but readMore will be cleared
e7287625 3034 while (!in.buf.isEmpty() && !bodyPipe && flags.readMore) {
f900210a 3035
fc68f6b1 3036 /* Don't try to parse if the buffer is empty */
e7287625 3037 if (in.buf.isEmpty())
fc68f6b1 3038 break;
4681057e 3039
079a8480
AJ
3040 /* Limit the number of concurrent requests */
3041 if (concurrentRequestQueueFilled())
f900210a 3042 break;
f900210a 3043
00d0ce87 3044 // try to parse the PROXY protocol header magic bytes
3d74cb1f 3045 if (needProxyProtocolHeader_ && !parseProxyProtocolHeader())
00d0ce87
AJ
3046 break;
3047
db5a5382 3048 if (ClientSocketContext *context = parseOneRequest()) {
eacfca83 3049 debugs(33, 5, clientConnection << ": done parsing a request");
f900210a 3050
8d77a37c 3051 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
dc49061a 3052 CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
1b76e6c1 3053 commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
f900210a 3054
eacfca83
AR
3055 context->registerWithConn();
3056
9bafa70d 3057 processParsedRequest(context);
f900210a 3058
4959e21e 3059 parsed_req = true; // XXX: do we really need to parse everything right NOW ?
f900210a 3060
3061 if (context->mayUseConnection()) {
f35961af 3062 debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
f900210a 3063 break;
3064 }
eacfca83
AR
3065 } else {
3066 debugs(33, 5, clientConnection << ": not enough request data: " <<
3067 in.buf.length() << " < " << Config.maxRequestHeaderSize);
3068 Must(in.buf.length() < Config.maxRequestHeaderSize);
3069 break;
f900210a 3070 }
39cb8c41 3071 }
fc68f6b1 3072
a5baffba 3073 /* XXX where to 'finish' the parsing pass? */
f900210a 3074 return parsed_req;
3075}
3076
1cf238db 3077void
3078ConnStateData::clientReadRequest(const CommIoCbParams &io)
c4b7a5a9 3079{
7e66d5e2 3080 debugs(33,5, io.conn);
f84dd7eb
AR
3081 Must(reading());
3082 reader = NULL;
c4b7a5a9 3083
c8407295
AJ
3084 /* Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up */
3085 if (io.flag == Comm::ERR_CLOSING) {
7e66d5e2 3086 debugs(33,5, io.conn << " closing Bailout.");
c4b7a5a9 3087 return;
3088 }
62e76326 3089
73c36fd9
AJ
3090 assert(Comm::IsConnOpen(clientConnection));
3091 assert(io.conn->fd == clientConnection->fd);
5c336a3b 3092
44db45e8 3093 /*
7e66d5e2
AJ
3094 * Don't reset the timeout value here. The value should be
3095 * counting Config.Timeout.request and applies to the request
3096 * as a whole, not individual read() calls.
3097 * Plus, it breaks our lame *HalfClosed() detection
44db45e8 3098 */
7e66d5e2 3099
4d1376d7 3100 in.maybeMakeSpaceAvailable();
7e66d5e2
AJ
3101 CommIoCbParams rd(this); // will be expanded with ReadNow results
3102 rd.conn = io.conn;
470b1598 3103 switch (Comm::ReadNow(rd, in.buf)) {
c8407295 3104 case Comm::INPROGRESS:
7e66d5e2
AJ
3105 if (in.buf.isEmpty())
3106 debugs(33, 2, io.conn << ": no data to process, " << xstrerr(rd.xerrno));
3107 readSomeData();
62e76326 3108 return;
c4b7a5a9 3109
c8407295 3110 case Comm::OK:
7e66d5e2 3111 kb_incr(&(statCounter.client_http.kbytes_in), rd.size);
3248e962
CT
3112 if (!receivedFirstByte_)
3113 receivedFirstByte();
7e66d5e2
AJ
3114 // may comm_close or setReplyToError
3115 if (!handleReadData())
3116 return;
3b299123 3117
7e66d5e2
AJ
3118 /* Continue to process previously read data */
3119 break;
a31a78fb 3120
c8407295 3121 case Comm::ENDFILE: // close detected by 0-byte read
7e66d5e2 3122 debugs(33, 5, io.conn << " closed?");
62e76326 3123
7e66d5e2
AJ
3124 if (connFinishedWithConn(rd.size)) {
3125 clientConnection->close();
3126 return;
3127 }
62e76326 3128
7e66d5e2
AJ
3129 /* It might be half-closed, we can't tell */
3130 fd_table[io.conn->fd].flags.socket_eof = true;
3131 commMarkHalfClosed(io.conn->fd);
3132 fd_note(io.conn->fd, "half-closed");
62e76326 3133
7e66d5e2
AJ
3134 /* There is one more close check at the end, to detect aborted
3135 * (partial) requests. At this point we can't tell if the request
3136 * is partial.
3137 */
a46d2c0e 3138
7e66d5e2
AJ
3139 /* Continue to process previously read data */
3140 break;
62e76326 3141
f53969cc 3142 // case Comm::COMM_ERROR:
7e66d5e2
AJ
3143 default: // no other flags should ever occur
3144 debugs(33, 2, io.conn << ": got flag " << rd.flag << "; " << xstrerr(rd.xerrno));
3145 notifyAllContexts(rd.xerrno);
3146 io.conn->close();
3147 return;
c4b7a5a9 3148 }
3149
94439e4e 3150 /* Process next request */
1cf238db 3151 if (getConcurrentRequestCount() == 0)
5c336a3b 3152 fd_note(io.fd, "Reading next request");
c8be6d7b 3153
f35961af 3154 if (!clientParseRequests()) {
9e008dda
AJ
3155 if (!isOpen())
3156 return;
f900210a 3157 /*
3158 * If the client here is half closed and we failed
3159 * to parse a request, close the connection.
3160 * The above check with connFinishedWithConn() only
3161 * succeeds _if_ the buffer is empty which it won't
3162 * be if we have an incomplete request.
6e1d409c 3163 * XXX: This duplicates ClientSocketContext::keepaliveNextRequest
f900210a 3164 */
5c336a3b 3165 if (getConcurrentRequestCount() == 0 && commIsHalfClosed(io.fd)) {
be364179 3166 debugs(33, 5, HERE << io.conn << ": half-closed connection, no completed request parsed, connection closing.");
73c36fd9 3167 clientConnection->close();
ee6f0213 3168 return;
62e76326 3169 }
f900210a 3170 }
ee6f0213 3171
1cf238db 3172 if (!isOpen())
2e216b1d 3173 return;
3174
f35961af 3175 clientAfterReadingRequests();
94439e4e 3176}
3177
63be0a78 3178/**
3179 * called when new request data has been read from the socket
39cb8c41
AR
3180 *
3181 * \retval false called comm_close or setReplyToError (the caller should bail)
3182 * \retval true we did not call comm_close or setReplyToError
63be0a78 3183 */
39cb8c41 3184bool
7e66d5e2 3185ConnStateData::handleReadData()
94439e4e 3186{
5f8252d2 3187 // if we are reading a body, stuff data into the body pipe
3188 if (bodyPipe != NULL)
39cb8c41
AR
3189 return handleRequestBodyData();
3190 return true;
94439e4e 3191}
3192
63be0a78 3193/**
bb790702 3194 * called when new request body data has been buffered in in.buf
63be0a78 3195 * may close the connection if we were closing and piped everything out
39cb8c41
AR
3196 *
3197 * \retval false called comm_close or setReplyToError (the caller should bail)
3198 * \retval true we did not call comm_close or setReplyToError
63be0a78 3199 */
39cb8c41 3200bool
5f8252d2 3201ConnStateData::handleRequestBodyData()
94439e4e 3202{
5f8252d2 3203 assert(bodyPipe != NULL);
3204
39cb8c41 3205 if (in.bodyParser) { // chunked encoding
be29ee33 3206 if (const err_type error = handleChunkedRequestBody()) {
39cb8c41
AR
3207 abortChunkedRequestBody(error);
3208 return false;
3ff65596 3209 }
39cb8c41 3210 } else { // identity encoding
73c36fd9 3211 debugs(33,5, HERE << "handling plain request body for " << clientConnection);
be29ee33
AJ
3212 const size_t putSize = bodyPipe->putMoreData(in.buf.c_str(), in.buf.length());
3213 if (putSize > 0)
3214 consumeInput(putSize);
3215
3ff65596
AR
3216 if (!bodyPipe->mayNeedMoreData()) {
3217 // BodyPipe will clear us automagically when we produced everything
3218 bodyPipe = NULL;
3219 }
3220 }
3221
3ff65596 3222 if (!bodyPipe) {
73c36fd9 3223 debugs(33,5, HERE << "produced entire request body for " << clientConnection);
62e76326 3224
cf6eb29e 3225 if (const char *reason = stoppedSending()) {
5f8252d2 3226 /* we've finished reading like good clients,
3227 * now do the close that initiateClose initiated.
5f8252d2 3228 */
cf6eb29e 3229 debugs(33, 3, HERE << "closing for earlier sending error: " << reason);
73c36fd9 3230 clientConnection->close();
39cb8c41
AR
3231 return false;
3232 }
3233 }
3234
3235 return true;
3236}
3237
3238/// parses available chunked encoded body bytes, checks size, returns errors
3239err_type
be29ee33 3240ConnStateData::handleChunkedRequestBody()
39cb8c41 3241{
e7287625 3242 debugs(33, 7, "chunked from " << clientConnection << ": " << in.buf.length());
39cb8c41
AR
3243
3244 try { // the parser will throw on errors
3245
e7287625 3246 if (in.buf.isEmpty()) // nothing to do
39cb8c41
AR
3247 return ERR_NONE;
3248
39cb8c41 3249 BodyPipeCheckout bpc(*bodyPipe);
be29ee33
AJ
3250 in.bodyParser->setPayloadBuffer(&bpc.buf);
3251 const bool parsed = in.bodyParser->parse(in.buf);
3252 in.buf = in.bodyParser->remaining(); // sync buffers
39cb8c41 3253 bpc.checkIn();
39cb8c41
AR
3254
3255 // dechunk then check: the size limit applies to _dechunked_ content
3256 if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
3257 return ERR_TOO_BIG;
3258
3259 if (parsed) {
3260 finishDechunkingRequest(true);
3261 Must(!bodyPipe);
3262 return ERR_NONE; // nil bodyPipe implies body end for the caller
5f8252d2 3263 }
39cb8c41
AR
3264
3265 // if chunk parser needs data, then the body pipe must need it too
3266 Must(!in.bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
3267
3268 // if parser needs more space and we can consume nothing, we will stall
3269 Must(!in.bodyParser->needsMoreSpace() || bodyPipe->buf().hasContent());
3270 } catch (...) { // TODO: be more specific
3271 debugs(33, 3, HERE << "malformed chunks" << bodyPipe->status());
3272 return ERR_INVALID_REQ;
94439e4e 3273 }
39cb8c41
AR
3274
3275 debugs(33, 7, HERE << "need more chunked data" << *bodyPipe->status());
3276 return ERR_NONE;
3277}
3278
3279/// quit on errors related to chunked request body handling
3280void
3281ConnStateData::abortChunkedRequestBody(const err_type error)
3282{
3283 finishDechunkingRequest(false);
3284
3285 // XXX: The code below works if we fail during initial request parsing,
d5430dc8 3286 // but if we fail when the server connection is used already, the server may send
39cb8c41
AR
3287 // us its response too, causing various assertions. How to prevent that?
3288#if WE_KNOW_HOW_TO_SEND_ERRORS
3289 ClientSocketContext::Pointer context = getCurrentContext();
3290 if (context != NULL && !context->http->out.offset) { // output nothing yet
3291 clientStreamNode *node = context->getClientReplyContext();
3292 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
3293 assert(repContext);
955394ce 3294 const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
f11c8e2f 3295 Http::scPayloadTooLarge : HTTP_BAD_REQUEST;
39cb8c41
AR
3296 repContext->setReplyToError(error, scode,
3297 repContext->http->request->method,
3298 repContext->http->uri,
a3c6762c 3299 CachePeer,
39cb8c41
AR
3300 repContext->http->request,
3301 in.buf, NULL);
3302 context->pullData();
3303 } else {
3304 // close or otherwise we may get stuck as nobody will notice the error?
73c36fd9 3305 comm_reset_close(clientConnection);
39cb8c41
AR
3306 }
3307#else
3308 debugs(33, 3, HERE << "aborting chunked request without error " << error);
73c36fd9 3309 comm_reset_close(clientConnection);
39cb8c41 3310#endif
f35961af 3311 flags.readMore = false;
5f8252d2 3312}
55e44db9 3313
5f8252d2 3314void
1cf238db 3315ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer )
5f8252d2 3316{
cf6eb29e
CT
3317 // request reader may get stuck waiting for space if nobody consumes body
3318 if (bodyPipe != NULL)
3319 bodyPipe->enableAutoConsumption();
3320
92ae4c86 3321 // kids extend
94439e4e 3322}
3323
63be0a78 3324/** general lifetime handler for HTTP requests */
1cf238db 3325void
3326ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
7a2f978b 3327{
3248e962
CT
3328 if (Config.accessList.on_unsupported_protocol && !receivedFirstByte_) {
3329#if USE_OPENSSL
3330 if (serverBump() && (serverBump()->act.step1 == Ssl::bumpPeek || serverBump()->act.step1 == Ssl::bumpStare)) {
3331 if (spliceOnError(ERR_REQUEST_START_TIMEOUT)) {
3332 receivedFirstByte();
3333 return;
3334 }
3335 } else if (fd_table[io.conn->fd].ssl == NULL)
3336#endif
3337 {
3338 const HttpRequestMethod method;
3339 if (clientTunnelOnError(this, NULL, NULL, method, ERR_REQUEST_START_TIMEOUT, Http::scNone, NULL)) {
3340 // Tunnel established. Set receivedFirstByte to avoid loop.
3341 receivedFirstByte();
3342 return;
3343 }
3344 }
3345 }
af57a2e3 3346 /*
62e76326 3347 * Just close the connection to not confuse browsers
6177a89f
HN
3348 * using persistent connections. Some browsers open
3349 * a connection and then do not use it until much
62e76326 3350 * later (presumeably because the request triggering
3351 * the open has already been completed on another
3352 * connection)
3353 */
1cf238db 3354 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
8d77a37c 3355 io.conn->close();
7a2f978b 3356}
3357
b5c39993 3358static void
8d77a37c 3359clientLifetimeTimeout(const CommTimeoutCbParams &io)
b5c39993 3360{
8d77a37c 3361 ClientHttpRequest *http = static_cast<ClientHttpRequest *>(io.data);
cb61ec47
AJ
3362 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout");
3363 debugs(33, DBG_IMPORTANT, "\t" << http->uri);
41ebd397 3364 http->al->http.timedout = true;
8d77a37c
AJ
3365 if (Comm::IsConnOpen(io.conn))
3366 io.conn->close();
b5c39993 3367}
3368
94bfd31f 3369ConnStateData::ConnStateData(const MasterXaction::Pointer &xact) :
f53969cc
SM
3370 AsyncJob("ConnStateData"), // kids overwrite
3371 nrequests(0),
cb4f4424 3372#if USE_OPENSSL
f53969cc 3373 sslBumpMode(Ssl::bumpEnd),
4579a6d0 3374#endif
f53969cc 3375 needProxyProtocolHeader_(false),
4579a6d0 3376#if USE_OPENSSL
f53969cc
SM
3377 switchedToHttps_(false),
3378 sslServerBump(NULL),
3379 signAlgorithm(Ssl::algSignTrusted),
94bfd31f 3380#endif
f53969cc 3381 stoppedSending_(NULL),
3248e962
CT
3382 stoppedReceiving_(NULL),
3383 receivedFirstByte_(false)
c8be6d7b 3384{
4579a6d0
AJ
3385 flags.readMore = true; // kids may overwrite
3386 flags.swanSang = false;
3387
94bfd31f
AJ
3388 pinning.host = NULL;
3389 pinning.port = -1;
3390 pinning.pinned = false;
3391 pinning.auth = false;
3392 pinning.zeroReply = false;
3393 pinning.peer = NULL;
3394
3395 // store the details required for creating more MasterXaction objects as new requests come in
3396 clientConnection = xact->tcpClient;
fa720bfb 3397 port = xact->squidPort;
4599cded 3398 transferProtocol = port->transport; // default to the *_port protocol= setting. may change later.
94bfd31f
AJ
3399 log_addr = xact->tcpClient->remote;
3400 log_addr.applyMask(Config.Addrs.client_netmask);
92ae4c86
AR
3401}
3402
3403void
3404ConnStateData::start()
3405{
3406 BodyProducer::start();
3407 HttpControlMsgSink::start();
3408
5529ca8a 3409 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
94bfd31f 3410 (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
5529ca8a 3411#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
3412 int i = IP_PMTUDISC_DONT;
94bfd31f
AJ
3413 if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0)
3414 debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerror());
5529ca8a 3415#else
9bb67276 3416 static bool reported = false;
5529ca8a 3417
3418 if (!reported) {
9bb67276
AJ
3419 debugs(33, DBG_IMPORTANT, "NOTICE: Path MTU discovery disabling is not supported on your platform.");
3420 reported = true;
5529ca8a 3421 }
89aec9b6
AJ
3422#endif
3423 }
5529ca8a 3424
89aec9b6 3425 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
94bfd31f
AJ
3426 AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, ConnStateData::connStateClosed);
3427 comm_add_close_handler(clientConnection->fd, call);
5529ca8a 3428
89aec9b6 3429 if (Config.onoff.log_fqdn)
94bfd31f 3430 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
89aec9b6
AJ
3431
3432#if USE_IDENT
3433 if (Ident::TheConfig.identLookup) {
3434 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
92ae4c86
AR
3435 identChecklist.src_addr = clientConnection->remote;
3436 identChecklist.my_addr = clientConnection->local;
2efeb0b7 3437 if (identChecklist.fastCheck() == ACCESS_ALLOWED)
92ae4c86 3438 Ident::Start(clientConnection, clientIdentDone, this);
89aec9b6 3439 }
5529ca8a 3440#endif
3441
94bfd31f 3442 clientdbEstablished(clientConnection->remote, 1);
5529ca8a 3443
6658cc16
AJ
3444 needProxyProtocolHeader_ = port->flags.proxySurrogate;
3445 if (needProxyProtocolHeader_) {
3446 if (!proxyProtocolValidateClient()) // will close the connection on failure
3447 return;
3448 }
3449
9a0a18de 3450#if USE_DELAY_POOLS
92ae4c86 3451 fd_table[clientConnection->fd].clientInfo = NULL;
b4cd430a 3452
f33d34a8 3453 if (Config.onoff.client_db) {
b4cd430a
CT
3454 /* it was said several times that client write limiter does not work if client_db is disabled */
3455
3456 ClientDelayPools& pools(Config.ClientDelay.pools);
2efeb0b7 3457 ACLFilledChecklist ch(NULL, NULL, NULL);
b4cd430a 3458
2efeb0b7
AJ
3459 // TODO: we check early to limit error response bandwith but we
3460 // should recheck when we can honor delay_pool_uses_indirect
3461 // TODO: we should also pass the port details for myportname here.
92ae4c86
AR
3462 ch.src_addr = clientConnection->remote;
3463 ch.my_addr = clientConnection->local;
b4cd430a 3464
95dc7ff4 3465 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
b4cd430a 3466
2efeb0b7
AJ
3467 /* pools require explicit 'allow' to assign a client into them */
3468 if (pools[pool].access) {
3469 ch.accessList = pools[pool].access;
3470 allow_t answer = ch.fastCheck();
3471 if (answer == ACCESS_ALLOWED) {
3472
3473 /* request client information from db after we did all checks
3474 this will save hash lookup if client failed checks */
92ae4c86 3475 ClientInfo * cli = clientdbGetInfo(clientConnection->remote);
2efeb0b7
AJ
3476 assert(cli);
3477
3478 /* put client info in FDE */
92ae4c86 3479 fd_table[clientConnection->fd].clientInfo = cli;
2efeb0b7
AJ
3480
3481 /* setup write limiter for this request */
3482 const double burst = floor(0.5 +
3483 (pools[pool].highwatermark * Config.ClientDelay.initial)/100.0);
3484 cli->setWriteLimiter(pools[pool].rate, burst, pools[pool].highwatermark);
3485 break;
3486 } else {
3487 debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
3488 }
b4cd430a
CT
3489 }
3490 }
3491 }
3492#endif
92ae4c86
AR
3493
3494 // kids must extend to actually start doing something (e.g., reading)
7a2f978b 3495}
3496
e7ce227f 3497/** Handle a new connection on an HTTP socket. */
92ae4c86
AR
3498void
3499httpAccept(const CommAcceptCbParams &params)
6afea0a4
AR
3500{
3501 MasterXaction::Pointer xact = params.xaction;
3502 AnyP::PortCfgPointer s = xact->squidPort;
3503
3504 // NP: it is possible the port was reconfigured when the call or accept() was queued.
3505
3506 if (params.flag != Comm::OK) {
3507 // Its possible the call was still queued when the client disconnected
e7ce227f 3508 debugs(33, 2, s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
6afea0a4
AR
3509 return;
3510 }
3511
e7ce227f 3512 debugs(33, 4, params.conn << ": accepted");
92ae4c86 3513 fd_note(params.conn->fd, "client http connect");
6afea0a4 3514
e7ce227f 3515 if (s->tcp_keepalive.enabled)
6afea0a4 3516 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
6afea0a4 3517
e7ce227f 3518 ++incoming_sockets_accepted;
6afea0a4
AR
3519
3520 // Socket is ready, setup the connection manager to start using it
92ae4c86 3521 ConnStateData *connState = Http::NewServer(xact);
e7ce227f 3522 AsyncJob::Start(connState); // usually async-calls readSomeData()
7a2f978b 3523}
3524
cb4f4424 3525#if USE_OPENSSL
1f7c9178 3526
63be0a78 3527/** Create SSL connection structure and update fd_table */
ae7ff0b8 3528static SSL *
449f0115 3529httpsCreate(const Comm::ConnectionPointer &conn, SSL_CTX *sslContext)
ae7ff0b8 3530{
d620ae0e 3531 if (SSL *ssl = Ssl::CreateServer(sslContext, conn->fd, "client https start")) {
8693472e 3532 debugs(33, 5, "will negotate SSL on " << conn);
b3a8ae1b 3533 return ssl;
ae7ff0b8 3534 }
3535
b3a8ae1b
AR
3536 conn->close();
3537 return NULL;
ae7ff0b8 3538}
3539
3248e962 3540/**
44352c16 3541 *
3248e962
CT
3542 * \retval 1 on success
3543 * \retval 0 when needs more data
3544 * \retval -1 on error
3545 */
3546static int
d620ae0e 3547Squid_SSL_accept(ConnStateData *conn, PF *callback)
1f7c9178 3548{
d620ae0e 3549 int fd = conn->clientConnection->fd;
a7ad6e4e 3550 SSL *ssl = fd_table[fd].ssl;
1f7c9178 3551 int ret;
3552
e9447f01 3553 errno = 0;
a7ad6e4e 3554 if ((ret = SSL_accept(ssl)) <= 0) {
e9447f01 3555 int xerrno = errno;
62e76326 3556 int ssl_error = SSL_get_error(ssl, ret);
3557
3558 switch (ssl_error) {
3559
3560 case SSL_ERROR_WANT_READ:
d620ae0e 3561 Comm::SetSelect(fd, COMM_SELECT_READ, callback, conn, 0);
3248e962 3562 return 0;
62e76326 3563
3564 case SSL_ERROR_WANT_WRITE:
d620ae0e 3565 Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, conn, 0);
3248e962 3566 return 0;
62e76326 3567
6de9e64b 3568 case SSL_ERROR_SYSCALL:
6de9e64b 3569 if (ret == 0) {
d620ae0e 3570 debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
6de9e64b 3571 } else {
e9447f01 3572 debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
13251f8d 3573 (xerrno == 0 ? ERR_error_string(ssl_error, NULL) : xstrerr(xerrno)));
6de9e64b 3574 }
3248e962 3575 return -1;
6de9e64b 3576
3577 case SSL_ERROR_ZERO_RETURN:
d620ae0e 3578 debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
3248e962 3579 return -1;
6de9e64b 3580
62e76326 3581 default:
d620ae0e 3582 debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
9e008dda
AJ
3583 fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
3584 " (" << ssl_error << "/" << ret << ")");
3248e962 3585 return -1;
62e76326 3586 }
3587
3588 /* NOTREACHED */
1f7c9178 3589 }
3248e962 3590 return 1;
d620ae0e
CT
3591}
3592
3593/** negotiate an SSL connection */
3594static void
3595clientNegotiateSSL(int fd, void *data)
3596{
3597 ConnStateData *conn = (ConnStateData *)data;
3598 X509 *client_cert;
3599 SSL *ssl = fd_table[fd].ssl;
3600
3248e962
CT
3601 int ret;
3602 if ((ret = Squid_SSL_accept(conn, clientNegotiateSSL)) <= 0) {
3603 if (ret < 0) // An error
b54a7c5a 3604 conn->clientConnection->close();
d620ae0e 3605 return;
3248e962 3606 }
62e76326 3607
6de9e64b 3608 if (SSL_session_reused(ssl)) {
bf8fe701 3609 debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
3610 " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
6de9e64b 3611 } else {
3612 if (do_debug(83, 4)) {
3613 /* Write out the SSL session details.. actually the call below, but
3614 * OpenSSL headers do strange typecasts confusing GCC.. */
3615 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
afdd443f 3616#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
9f3de01a 3617 PEM_ASN1_write((i2d_of_void *)i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
2930f303 3618
0fd2205b 3619#elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2930f303 3620
0fd2205b 3621 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
3622 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
3623 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
3624 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
3625 * Because there are two possible usable cast, if you get an error here, try the other
3626 * commented line. */
3627
3628 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
3629 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
2930f303 3630
0e33d58c 3631#else
3632
bf8fe701 3633 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
0fd2205b 3634
0e33d58c 3635#endif
6de9e64b 3636 /* Note: This does not automatically fflush the log file.. */
3637 }
3638
bf8fe701 3639 debugs(83, 2, "clientNegotiateSSL: New session " <<
3640 SSL_get_session(ssl) << " on FD " << fd << " (" <<
3641 fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
3642 ")");
6de9e64b 3643 }
3644
bf8fe701 3645 debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
3646 SSL_get_cipher(ssl));
1f7c9178 3647
6de9e64b 3648 client_cert = SSL_get_peer_certificate(ssl);
62e76326 3649
1f7c9178 3650 if (client_cert != NULL) {
bf8fe701 3651 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3652 " client certificate: subject: " <<
3653 X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
3654
3655 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3656 " client certificate: issuer: " <<
3657 X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
1f7c9178 3658
62e76326 3659 X509_free(client_cert);
1f7c9178 3660 } else {
bf8fe701 3661 debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
3662 " has no certificate.");
1f7c9178 3663 }
3664
69f69080
CT
3665#if defined(TLSEXT_NAMETYPE_host_name)
3666 if (!conn->serverBump()) {
3667 // when in bumpClientFirst mode, get the server name from SNI
3668 if (const char *server = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))
3669 conn->resetSslCommonName(server);
3670 }
3671#endif
3672
a46d2c0e 3673 conn->readSomeData();
1f7c9178 3674}
3675
379e8c1c 3676/**
ab2e0682
CT
3677 * If SSL_CTX is given, starts reading the SSL handshake.
3678 * Otherwise, calls switchToHttps to generate a dynamic SSL_CTX.
379e8c1c
AR
3679 */
3680static void
36048c42 3681httpsEstablish(ConnStateData *connState, SSL_CTX *sslContext)
379e8c1c
AR
3682{
3683 SSL *ssl = NULL;
3684 assert(connState);
3685 const Comm::ConnectionPointer &details = connState->clientConnection;
3686
36048c42 3687 if (!sslContext || !(ssl = httpsCreate(details, sslContext)))
379e8c1c
AR
3688 return;
3689
7a957a93
AR
3690 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3691 AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
87f237a9 3692 connState, ConnStateData::requestTimeout);
379e8c1c
AR
3693 commSetConnTimeout(details, Config.Timeout.request, timeoutCall);
3694
36048c42 3695 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
379e8c1c
AR
3696}
3697
3698/**
87f237a9 3699 * A callback function to use with the ACLFilledChecklist callback.
94bfd31f 3700 * In the case of ACCESS_ALLOWED answer initializes a bumped SSL connection,
7a957a93 3701 * else reverts the connection to tunnel mode.
379e8c1c
AR
3702 */
3703static void
3704httpsSslBumpAccessCheckDone(allow_t answer, void *data)
3705{
3706 ConnStateData *connState = (ConnStateData *) data;
3707
7a957a93 3708 // if the connection is closed or closing, just return.
379e8c1c
AR
3709 if (!connState->isOpen())
3710 return;
3711
7a957a93 3712 // Require both a match and a positive bump mode to work around exceptional
caf3666d 3713 // cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
5d65362c 3714 if (answer == ACCESS_ALLOWED && (answer.kind != Ssl::bumpNone && answer.kind != Ssl::bumpSplice)) {
e4f14091 3715 debugs(33, 2, "sslBump needed for " << connState->clientConnection << " method " << answer.kind);
08097970 3716 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
379e8c1c 3717 } else {
08097970
AR
3718 debugs(33, 2, HERE << "sslBump not needed for " << connState->clientConnection);
3719 connState->sslBumpMode = Ssl::bumpNone;
9e104535 3720 }
379e8c1c 3721
9e104535
CT
3722 // fake a CONNECT request to force connState to tunnel
3723 static char ip[MAX_IPSTRLEN];
3724 connState->clientConnection->local.toUrl(ip, sizeof(ip));
3725 // Pre-pend this fake request to the TLS bits already in the buffer
3726 SBuf retStr;
3727 retStr.append("CONNECT ").append(ip).append(" HTTP/1.1\r\nHost: ").append(ip).append("\r\n\r\n");
3728 connState->in.buf = retStr.append(connState->in.buf);
3729 bool ret = connState->handleReadData();
3730 if (ret)
3731 ret = connState->clientParseRequests();
379e8c1c 3732
9e104535
CT
3733 if (!ret) {
3734 debugs(33, 2, "Failed to start fake CONNECT request for SSL bumped connection: " << connState->clientConnection);
3735 connState->clientConnection->close();
379e8c1c
AR
3736 }
3737}
3738
63be0a78 3739/** handle a new HTTPS connection */
1f7c9178 3740static void
449f0115 3741httpsAccept(const CommAcceptCbParams &params)
1f7c9178 3742{
94bfd31f
AJ
3743 MasterXaction::Pointer xact = params.xaction;
3744 const AnyP::PortCfgPointer s = xact->squidPort;
3745
c0ff709f 3746 // NP: it is possible the port was reconfigured when the call or accept() was queued.
c4b7a5a9 3747
c8407295 3748 if (params.flag != Comm::OK) {
cbff89ba 3749 // Its possible the call was still queued when the client disconnected
449f0115 3750 debugs(33, 2, "httpsAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
62e76326 3751 return;
c4b7a5a9 3752 }
62e76326 3753
449f0115
AJ
3754 debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
3755 fd_note(params.conn->fd, "client https connect");
62e76326 3756
859741ed
AJ
3757 if (s->tcp_keepalive.enabled) {
3758 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
b2130d58 3759 }
3760
95dc7ff4 3761 ++incoming_sockets_accepted;
62e76326 3762
89aec9b6 3763 // Socket is ready, setup the connection manager to start using it
92ae4c86 3764 ConnStateData *connState = Https::NewServer(xact);
e7ce227f 3765 AsyncJob::Start(connState); // usually async-calls postHttpsAccept()
92ae4c86 3766}
62e76326 3767
92ae4c86
AR
3768void
3769ConnStateData::postHttpsAccept()
3770{
b3cb9958 3771 if (port->flags.tunnelSslBumping) {
e7ce227f 3772 debugs(33, 5, "accept transparent connection: " << clientConnection);
62e76326 3773
379e8c1c 3774 if (!Config.accessList.ssl_bump) {
b3cb9958 3775 httpsSslBumpAccessCheckDone(ACCESS_DENIED, this);
379e8c1c
AR
3776 return;
3777 }
87f237a9 3778
379e8c1c 3779 // Create a fake HTTP request for ssl_bump ACL check,
38450a50 3780 // using tproxy/intercept provided destination IP and port.
379e8c1c
AR
3781 HttpRequest *request = new HttpRequest();
3782 static char ip[MAX_IPSTRLEN];
92ae4c86
AR
3783 assert(clientConnection->flags & (COMM_TRANSPARENT | COMM_INTERCEPTION));
3784 request->SetHost(clientConnection->local.toStr(ip, sizeof(ip)));
5c51bffb 3785 request->url.port(clientConnection->local.port());
b3cb9958 3786 request->myportname = port->name;
87f237a9 3787
379e8c1c 3788 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, NULL);
92ae4c86 3789 acl_checklist->src_addr = clientConnection->remote;
b3cb9958
AR
3790 acl_checklist->my_addr = port->s;
3791 acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, this);
379e8c1c
AR
3792 return;
3793 } else {
b3cb9958 3794 SSL_CTX *sslContext = port->staticSslContext.get();
36048c42 3795 httpsEstablish(this, sslContext);
379e8c1c 3796 }
1f7c9178 3797}
3798
95d2589c 3799void
24438ec5 3800ConnStateData::sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply)
ae7ff0b8 3801{
95d2589c
CT
3802 ConnStateData * state_data = (ConnStateData *)(data);
3803 state_data->sslCrtdHandleReply(reply);
3804}
ae7ff0b8 3805
95d2589c 3806void
24438ec5 3807ConnStateData::sslCrtdHandleReply(const Helper::Reply &reply)
95d2589c 3808{
b418d9c8
CT
3809 if (!isOpen()) {
3810 debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
3811 return;
3812 }
3813
2428ce02 3814 if (reply.result == Helper::BrokenHelper) {
5955f162
AJ
3815 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
3816 } else if (!reply.other().hasContent()) {
3817 debugs(1, DBG_IMPORTANT, HERE << "\"ssl_crtd\" helper returned <NULL> reply.");
95d2589c 3818 } else {
ff2d7d92 3819 Ssl::CrtdMessage reply_message(Ssl::CrtdMessage::REPLY);
0272dd08 3820 if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
fb2178bb 3821 debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslConnectHostOrIp << " is incorrect");
95d2589c 3822 } else {
2428ce02 3823 if (reply.result != Helper::Okay) {
fb2178bb 3824 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
95d2589c 3825 } else {
fb2178bb 3826 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
a9c2dd2f 3827 if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
d620ae0e
CT
3828 doPeekAndSpliceStep();
3829 SSL *ssl = fd_table[clientConnection->fd].ssl;
3830 bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
3831 if (!ret)
e4f14091 3832 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
d620ae0e
CT
3833 } else {
3834 SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port);
3835 getSslContextDone(ctx, true);
3836 }
95d2589c
CT
3837 return;
3838 }
3839 }
3840 }
3841 getSslContextDone(NULL);
3842}
ae7ff0b8 3843
06997a38 3844void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties)
fb2178bb 3845{
69f69080 3846 certProperties.commonName = sslCommonName_.isEmpty() ? sslConnectHostOrIp.termedBuf() : sslCommonName_.c_str();
fb2178bb
CT
3847
3848 // fake certificate adaptation requires bump-server-first mode
caf3666d
AR
3849 if (!sslServerBump) {
3850 assert(port->signingCert.get());
3851 certProperties.signWithX509.resetAndLock(port->signingCert.get());
3852 if (port->signPkey.get())
3853 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3854 certProperties.signAlgorithm = Ssl::algSignTrusted;
fb2178bb 3855 return;
caf3666d 3856 }
fb2178bb 3857
7a957a93
AR
3858 // In case of an error while connecting to the secure server, use a fake
3859 // trusted certificate, with no mimicked fields and no adaptation
3860 // algorithms. There is nothing we can mimic so we want to minimize the
3861 // number of warnings the user will have to see to get to the error page.
fd4624d7
CT
3862 assert(sslServerBump->entry);
3863 if (sslServerBump->entry->isEmpty()) {
3864 if (X509 *mimicCert = sslServerBump->serverCert.get())
59a49556
CT
3865 certProperties.mimicCert.resetAndLock(mimicCert);
3866
b248c2a3 3867 ACLFilledChecklist checklist(NULL, sslServerBump->request.getRaw(),
59a49556 3868 clientConnection != NULL ? clientConnection->rfc931 : dash_str);
7a957a93 3869 checklist.sslErrors = cbdataReference(sslServerBump->sslErrors);
59a49556
CT
3870
3871 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != NULL; ca = ca->next) {
7a957a93 3872 // If the algorithm already set, then ignore it.
a06042fa 3873 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
87f237a9
A
3874 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
3875 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
a06042fa
CT
3876 continue;
3877
59a49556
CT
3878 if (ca->aclList && checklist.fastCheck(ca->aclList) == ACCESS_ALLOWED) {
3879 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
3880 const char *param = ca->param;
87f237a9 3881
7a957a93
AR
3882 // For parameterless CN adaptation, use hostname from the
3883 // CONNECT request.
a06042fa 3884 if (ca->alg == Ssl::algSetCommonName) {
59a49556
CT
3885 if (!param)
3886 param = sslConnectHostOrIp.termedBuf();
3887 certProperties.commonName = param;
3888 certProperties.setCommonName = true;
87f237a9 3889 } else if (ca->alg == Ssl::algSetValidAfter)
59a49556 3890 certProperties.setValidAfter = true;
87f237a9 3891 else if (ca->alg == Ssl::algSetValidBefore)
59a49556
CT
3892 certProperties.setValidBefore = true;
3893
87f237a9 3894 debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
8f9720ce 3895 alg << " param: " << (param ? param : "-"));
aebe6888 3896 }
fb2178bb 3897 }
aebe6888 3898
59a49556
CT
3899 certProperties.signAlgorithm = Ssl::algSignEnd;
3900 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
3901 if (sg->aclList && checklist.fastCheck(sg->aclList) == ACCESS_ALLOWED) {
3902 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
3903 break;
3904 }
aebe6888 3905 }
fd4624d7 3906 } else {// if (!sslServerBump->entry->isEmpty())
59a49556
CT
3907 // Use trusted certificate for a Squid-generated error
3908 // or the user would have to add a security exception
3909 // just to see the error page. We will close the connection
3910 // so that the trust is not extended to non-Squid content.
3911 certProperties.signAlgorithm = Ssl::algSignTrusted;
aebe6888
CT
3912 }
3913
10d914f6 3914 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
aebe6888
CT
3915
3916 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
f4e4d4d6 3917 assert(port->untrustedSigningCert.get());
95588170
CT
3918 certProperties.signWithX509.resetAndLock(port->untrustedSigningCert.get());
3919 certProperties.signWithPkey.resetAndLock(port->untrustedSignPkey.get());
87f237a9 3920 } else {
f4e4d4d6
CT
3921 assert(port->signingCert.get());
3922 certProperties.signWithX509.resetAndLock(port->signingCert.get());
aebe6888
CT
3923
3924 if (port->signPkey.get())
3925 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3926 }
3927 signAlgorithm = certProperties.signAlgorithm;
3c26b00a
CT
3928
3929 certProperties.signHash = Ssl::DefaultSignHash;
fb2178bb
CT
3930}
3931
1ce2822d 3932void
95d2589c
CT
3933ConnStateData::getSslContextStart()
3934{
129fe2a1
CT
3935 assert(areAllContextsForThisConnection());
3936 freeAllContexts();
3937 /* careful: freeAllContexts() above frees request, host, etc. */
3938
fb2178bb 3939 if (port->generateHostCertificates) {
aebe6888 3940 Ssl::CertificateProperties certProperties;
06997a38
CT
3941 buildSslCertGenerationParams(certProperties);
3942 sslBumpCertKey = certProperties.dbKey().c_str();
b38b26cb 3943 assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
fb2178bb 3944
d620ae0e 3945 // Disable caching for bumpPeekAndSplice mode
a9c2dd2f 3946 if (!(sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare))) {
e4f14091 3947 debugs(33, 5, "Finding SSL certificate for " << sslBumpCertKey << " in cache");
31855516 3948 Ssl::LocalContextStorage * ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
d620ae0e 3949 SSL_CTX * dynCtx = NULL;
31855516 3950 Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache ? ssl_ctx_cache->get(sslBumpCertKey.termedBuf()) : NULL;
d620ae0e 3951 if (cachedCtx && (dynCtx = cachedCtx->get())) {
e4f14091 3952 debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " found in cache");
d620ae0e 3953 if (Ssl::verifySslCertificate(dynCtx, certProperties)) {
e4f14091 3954 debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is valid");
d620ae0e
CT
3955 getSslContextDone(dynCtx);
3956 return;
3957 } else {
e4f14091 3958 debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
31855516
CT
3959 if (ssl_ctx_cache)
3960 ssl_ctx_cache->del(sslBumpCertKey.termedBuf());
d620ae0e 3961 }
95d2589c 3962 } else {
e4f14091 3963 debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
95d2589c 3964 }
95d2589c
CT
3965 }
3966
b5faa519 3967#if USE_SSL_CRTD
00fc192d 3968 try {
87f237a9 3969 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
ff2d7d92 3970 Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
87f237a9
A
3971 request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
3972 request_message.composeRequest(certProperties);
3973 debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
3974 Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
3975 return;
3976 } catch (const std::exception &e) {
00fc192d
AR
3977 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
3978 "request for " << certProperties.commonName <<
3979 " certificate: " << e.what() << "; will now block to " <<
3980 "generate that certificate.");
3981 // fall through to do blocking in-process generation.
3982 }
3983#endif // USE_SSL_CRTD
3984
aebe6888 3985 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
a9c2dd2f 3986 if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
d620ae0e
CT
3987 doPeekAndSpliceStep();
3988 SSL *ssl = fd_table[clientConnection->fd].ssl;
3989 if (!Ssl::configureSSL(ssl, certProperties, *port))
e4f14091 3990 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
d620ae0e
CT
3991 } else {
3992 SSL_CTX *dynCtx = Ssl::generateSslContext(certProperties, *port);
3993 getSslContextDone(dynCtx, true);
3994 }
1ce2822d 3995 return;
95d2589c 3996 }
1ce2822d 3997 getSslContextDone(NULL);
95d2589c
CT
3998}
3999
1ce2822d 4000void
95d2589c
CT
4001ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew)
4002{
4003 // Try to add generated ssl context to storage.
4004 if (port->generateHostCertificates && isNew) {
a594dbfa 4005
a411d213
CT
4006 if (signAlgorithm == Ssl::algSignTrusted) {
4007 // Add signing certificate to the certificates chain
4008 X509 *cert = port->signingCert.get();
4009 if (SSL_CTX_add_extra_chain_cert(sslContext, cert)) {
4010 // increase the certificate lock
4011 CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
4012 } else {
4013 const int ssl_error = ERR_get_error();
4014 debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
4015 }
aebe6888 4016 Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
a411d213 4017 }
aebe6888 4018 //else it is self-signed or untrusted do not attrach any certificate
a594dbfa 4019
9873e378 4020 Ssl::LocalContextStorage *ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
b38b26cb 4021 assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
fb2178bb 4022 if (sslContext) {
9873e378 4023 if (!ssl_ctx_cache || !ssl_ctx_cache->add(sslBumpCertKey.termedBuf(), new Ssl::SSL_CTX_Pointer(sslContext))) {
95d2589c 4024 // If it is not in storage delete after using. Else storage deleted it.
73c36fd9 4025 fd_table[clientConnection->fd].dynamicSslContext = sslContext;
95d2589c
CT
4026 }
4027 } else {
fb2178bb 4028 debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslConnectHostOrIp);
95d2589c
CT
4029 }
4030 }
4031
4032 // If generated ssl context = NULL, try to use static ssl context.
4033 if (!sslContext) {
4034 if (!port->staticSslContext) {
e0236918 4035 debugs(83, DBG_IMPORTANT, "Closing SSL " << clientConnection->remote << " as lacking SSL context");
73c36fd9 4036 clientConnection->close();
1ce2822d 4037 return;
95d2589c
CT
4038 } else {
4039 debugs(33, 5, HERE << "Using static ssl context.");
4040 sslContext = port->staticSslContext.get();
4041 }
4042 }
ae7ff0b8 4043
a9b1d8c6 4044 if (!httpsCreate(clientConnection, sslContext))
1ce2822d 4045 return;
ae7ff0b8 4046
4e67d484
CT
4047 // bumped intercepted conns should already have Config.Timeout.request set
4048 // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
4049 // to make sure the connection does not get stuck on non-SSL clients.
4050 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4051 AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
3899de60 4052 this, ConnStateData::requestTimeout);
4e67d484 4053 commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
ae7ff0b8 4054
a3c6762c 4055 // Disable the client read handler until CachePeer selection is complete
73c36fd9
AJ
4056 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
4057 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
ae7ff0b8 4058 switchedToHttps_ = true;
ae7ff0b8 4059}
4060
1ce2822d 4061void
caf3666d 4062ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode)
95d2589c
CT
4063{
4064 assert(!switchedToHttps_);
4065
5c51bffb
AJ
4066 sslConnectHostOrIp = request->url.host();
4067 resetSslCommonName(request->url.host());
95d2589c 4068
83d4cd15
CT
4069 // We are going to read new request
4070 flags.readMore = true;
73c36fd9 4071 debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
95d2589c 4072
4599cded
AJ
4073 // keep version major.minor details the same.
4074 // but we are now performing the HTTPS handshake traffic
4075 transferProtocol.protocol = AnyP::PROTO_HTTPS;
4076
2bd84e5f
CT
4077 // If sslServerBump is set, then we have decided to deny CONNECT
4078 // and now want to switch to SSL to send the error to the client
4079 // without even peeking at the origin server certificate.
caf3666d 4080 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
e857372a 4081 request->flags.sslPeek = true;
65ba7f04 4082 sslServerBump = new Ssl::ServerBump(request);
fd4624d7 4083
d7ce0bcd 4084 // will call httpsPeeked() with certificate and connection, eventually
b248c2a3 4085 FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
d7ce0bcd 4086 return;
e1f72a8b 4087 } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
d620ae0e 4088 request->flags.sslPeek = true;
5d65362c 4089 sslServerBump = new Ssl::ServerBump(request, NULL, bumpServerMode);
d620ae0e 4090 startPeekAndSplice();
e1f72a8b 4091 return;
d7ce0bcd
AR
4092 }
4093
fb2178bb 4094 // otherwise, use sslConnectHostOrIp
d7ce0bcd
AR
4095 getSslContextStart();
4096}
4097
3248e962
CT
4098bool
4099ConnStateData::spliceOnError(const err_type err)
4100{
4101 if (Config.accessList.on_unsupported_protocol) {
4102 assert(serverBump());
4103 ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, serverBump()->request.getRaw(), NULL);
4104 checklist.requestErrorType = err;
4105 checklist.conn(this);
4106 allow_t answer = checklist.fastCheck();
4107 if (answer == ACCESS_ALLOWED && answer.kind == 1) {
4108 splice();
4109 return true;
4110 }
4111 }
4112 return false;
4113}
4114
d620ae0e
CT
4115/** negotiate an SSL connection */
4116static void
4117clientPeekAndSpliceSSL(int fd, void *data)
4118{
4119 ConnStateData *conn = (ConnStateData *)data;
4120 SSL *ssl = fd_table[fd].ssl;
4121
8693472e 4122 debugs(83, 5, "Start peek and splice on FD " << fd);
d620ae0e 4123
3248e962
CT
4124 int ret = 0;
4125 if ((ret = Squid_SSL_accept(conn, clientPeekAndSpliceSSL)) < 0)
d620ae0e
CT
4126 debugs(83, 2, "SSL_accept failed.");
4127
4128 BIO *b = SSL_get_rbio(ssl);
4129 assert(b);
4130 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
3248e962
CT
4131 if (ret < 0) {
4132 const err_type err = bio->noSslClient() ? ERR_PROTOCOL_UNKNOWN : ERR_SECURE_ACCEPT_FAIL;
4133 if (!conn->spliceOnError(err))
4134 conn->clientConnection->close();
4135 return;
44352c16 4136 }
3248e962
CT
4137
4138 if (bio->rBufData().contentSize() > 0)
4139 conn->receivedFirstByte();
4140
d620ae0e 4141 if (bio->gotHello()) {
cedca6e7
CT
4142 if (conn->serverBump()) {
4143 Ssl::Bio::sslFeatures const &features = bio->getFeatures();
69f69080 4144 if (!features.serverName.isEmpty()) {
8693472e 4145 conn->serverBump()->clientSni = features.serverName;
69f69080
CT
4146 conn->resetSslCommonName(features.serverName.c_str());
4147 }
cedca6e7
CT
4148 }
4149
e4f14091 4150 debugs(83, 5, "I got hello. Start forwarding the request!!! ");
d620ae0e
CT
4151 Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
4152 Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
4153 conn->startPeekAndSpliceDone();
4154 return;
4155 }
4156}
4157
4158void ConnStateData::startPeekAndSplice()
4159{
4160 // will call httpsPeeked() with certificate and connection, eventually
4161 SSL_CTX *unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
4162 fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
4163
4164 if (!httpsCreate(clientConnection, unConfiguredCTX))
4165 return;
4166
4167 // commSetConnTimeout() was called for this request before we switched.
3248e962
CT
4168 // Fix timeout to request_start_timeout
4169 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4170 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
4171 TimeoutDialer, this, ConnStateData::requestTimeout);
4172 commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
4173 // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
4174 // a bumbed "connect" request on non transparent port.
4175 receivedFirstByte_ = false;
d620ae0e
CT
4176
4177 // Disable the client read handler until CachePeer selection is complete
4178 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
4179 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientPeekAndSpliceSSL, this, 0);
4180 switchedToHttps_ = true;
4181
4182 SSL *ssl = fd_table[clientConnection->fd].ssl;
4183 BIO *b = SSL_get_rbio(ssl);
4184 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4185 bio->hold(true);
4186}
4187
5d65362c
CT
4188void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
4189{
4190 ConnStateData *connState = (ConnStateData *) data;
4191
4192 // if the connection is closed or closing, just return.
4193 if (!connState->isOpen())
4194 return;
4195
e4f14091 4196 debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
a9c2dd2f
CT
4197 assert(connState->serverBump());
4198 Ssl::BumpMode bumpAction;
4199 if (answer == ACCESS_ALLOWED) {
4200 if (answer.kind == Ssl::bumpNone)
4201 bumpAction = Ssl::bumpSplice;
4202 else if (answer.kind == Ssl::bumpClientFirst || answer.kind == Ssl::bumpServerFirst)
4203 bumpAction = Ssl::bumpBump;
4204 else
4205 bumpAction = (Ssl::BumpMode)answer.kind;
4206 } else
4207 bumpAction = Ssl::bumpSplice;
4208
4209 connState->serverBump()->act.step2 = bumpAction;
4210 connState->sslBumpMode = bumpAction;
4211
4212 if (bumpAction == Ssl::bumpTerminate) {
b54a7c5a 4213 connState->clientConnection->close();
a9c2dd2f
CT
4214 } else if (bumpAction != Ssl::bumpSplice) {
4215 connState->startPeekAndSpliceDone();
3248e962
CT
4216 } else
4217 connState->splice();
4218}
5d65362c 4219
3248e962
CT
4220void
4221ConnStateData::splice()
4222{
4223 //Normally we can splice here, because we just got client hello message
4224 SSL *ssl = fd_table[clientConnection->fd].ssl;
4225 BIO *b = SSL_get_rbio(ssl);
4226 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4227 MemBuf const &rbuf = bio->rBufData();
4228 debugs(83,5, "Bio for " << clientConnection << " read " << rbuf.contentSize() << " helo bytes");
4229 // Do splice:
4230 fd_table[clientConnection->fd].read_method = &default_read_method;
4231 fd_table[clientConnection->fd].write_method = &default_write_method;
4232
4233 if (transparent()) {
4234 // set the current protocol to something sensible (was "HTTPS" for the bumping process)
4235 // we are sending a faked-up HTTP/1.1 message wrapper, so go with that.
4236 transferProtocol = Http::ProtocolVersion();
4237 // fake a CONNECT request to force connState to tunnel
4238 static char ip[MAX_IPSTRLEN];
4239 clientConnection->local.toUrl(ip, sizeof(ip));
4240 in.buf.assign("CONNECT ").append(ip).append(" HTTP/1.1\r\nHost: ").append(ip).append("\r\n\r\n").append(rbuf.content(), rbuf.contentSize());
4241 bool ret = handleReadData();
4242 if (ret)
4243 ret = clientParseRequests();
4244
4245 if (!ret) {
4246 debugs(33, 2, "Failed to start fake CONNECT request for ssl spliced connection: " << clientConnection);
4247 clientConnection->close();
5d65362c 4248 }
3248e962
CT
4249 } else {
4250 // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
4251
4252 // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
4253 transferProtocol = Http::ProtocolVersion();
4254 // in.buf still has the "CONNECT ..." request data, reset it to SSL hello message
4255 in.buf.append(rbuf.content(), rbuf.contentSize());
4256 ClientSocketContext::Pointer context = getCurrentContext();
4257 ClientHttpRequest *http = context->http;
4258 tunnelStart(http, &http->out.size, &http->al->http.code, http->al);
5d65362c
CT
4259 }
4260}
4261
d620ae0e
CT
4262void
4263ConnStateData::startPeekAndSpliceDone()
4264{
5d65362c
CT
4265 // This is the Step2 of the SSL bumping
4266 assert(sslServerBump);
4267 if (sslServerBump->step == Ssl::bumpStep1) {
4268 sslServerBump->step = Ssl::bumpStep2;
4269 // Run a accessList check to check if want to splice or continue bumping
4270
4271 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, sslServerBump->request.getRaw(), NULL);
4272 //acl_checklist->src_addr = params.conn->remote;
4273 //acl_checklist->my_addr = s->s;
4274 acl_checklist->nonBlockingCheck(httpsSslBumpStep2AccessCheckDone, this);
4275 return;
4276 }
4277
31855516 4278 FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
d620ae0e
CT
4279}
4280
4281void
4282ConnStateData::doPeekAndSpliceStep()
4283{
4284 SSL *ssl = fd_table[clientConnection->fd].ssl;
4285 BIO *b = SSL_get_rbio(ssl);
4286 assert(b);
4287 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4288
e4f14091 4289 debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Currrent state:" << SSL_state_string_long(ssl));
d620ae0e
CT
4290 bio->hold(false);
4291
4292 Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, clientNegotiateSSL, this, 0);
4293 switchedToHttps_ = true;
4294}
4295
d7ce0bcd
AR
4296void
4297ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
4298{
fd4624d7 4299 Must(sslServerBump != NULL);
819c207f 4300
061bbdec 4301 if (Comm::IsConnOpen(serverConnection)) {
061bbdec
CT
4302 pinConnection(serverConnection, NULL, NULL, false);
4303
fb2178bb 4304 debugs(33, 5, HERE << "bumped HTTPS server: " << sslConnectHostOrIp);
59a49556 4305 } else {
13858f50 4306 debugs(33, 5, HERE << "Error while bumping: " << sslConnectHostOrIp);
129fe2a1
CT
4307
4308 // copy error detail from bump-server-first request to CONNECT request
4309 if (currentobject != NULL && currentobject->http != NULL && currentobject->http->request)
4310 currentobject->http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
59a49556 4311 }
061bbdec 4312
1ce2822d 4313 getSslContextStart();
95d2589c
CT
4314}
4315
cb4f4424 4316#endif /* USE_OPENSSL */
1f7c9178 4317
00516be1
AR
4318/// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
4319static bool
73c36fd9 4320OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
00516be1 4321{
73c36fd9 4322 if (!Comm::IsConnOpen(c)) {
00516be1
AR
4323 Must(NHttpSockets > 0); // we tried to open some
4324 --NHttpSockets; // there will be fewer sockets than planned
4325 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
4326
4327 if (!NHttpSockets) // we could not open any listen sockets at all
cbff89ba 4328 fatalf("Unable to open %s",FdNote(portType));
00516be1
AR
4329
4330 return false;
4331 }
4332 return true;
4333}
4334
4335/// find any unused HttpSockets[] slot and store fd there or return false
4336static bool
e0d28505 4337AddOpenedHttpSocket(const Comm::ConnectionPointer &conn)
00516be1
AR
4338{
4339 bool found = false;
95dc7ff4 4340 for (int i = 0; i < NHttpSockets && !found; ++i) {
00516be1 4341 if ((found = HttpSockets[i] < 0))
e0d28505 4342 HttpSockets[i] = conn->fd;
00516be1
AR
4343 }
4344 return found;
4345}
15df8349 4346
d193a436 4347static void
15df8349 4348clientHttpConnectionsOpen(void)
4349{
fa720bfb 4350 for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
65d448bc 4351 if (MAXTCPLISTENPORTS == NHttpSockets) {
e0236918
FC
4352 debugs(1, DBG_IMPORTANT, "WARNING: You have too many 'http_port' lines.");
4353 debugs(1, DBG_IMPORTANT, " The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
62e76326 4354 continue;
4355 }
4356
cb4f4424 4357#if USE_OPENSSL
6a25a046 4358 if (s->flags.tunnelSslBumping && !Config.accessList.ssl_bump) {
1ca54a54 4359 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s);
6a25a046 4360 s->flags.tunnelSslBumping = false;
3d61b18e
AJ
4361 }
4362
6a25a046 4363 if (s->flags.tunnelSslBumping &&
2ee34428
A
4364 !s->staticSslContext &&
4365 !s->generateHostCertificates) {
859741ed 4366 debugs(1, DBG_IMPORTANT, "Will not bump SSL at http_port " << s->s << " due to SSL initialization failure.");
6a25a046 4367 s->flags.tunnelSslBumping = false;
ae7ff0b8 4368 }
6a25a046 4369 if (s->flags.tunnelSslBumping) {
95d2589c
CT
4370 // Create ssl_ctx cache for this port.
4371 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
4372 }
ae7ff0b8 4373#endif
4374
e0d28505 4375 // Fill out a Comm::Connection which IPC will open as a listener for us
8bbb16e3 4376 // then pass back when active so we can start a TcpAcceptor subscription.
e0d28505
AJ
4377 s->listenConn = new Comm::Connection;
4378 s->listenConn->local = s->s;
6a25a046 4379 s->listenConn->flags = COMM_NONBLOCKING | (s->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) | (s->flags.natIntercept ? COMM_INTERCEPTION : 0);
c303f6e3 4380
cbff89ba
AJ
4381 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTP
4382 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
fa720bfb 4383 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpAccept", CommAcceptCbPtrFun(httpAccept, CommAcceptCbParams(NULL)));
cbff89ba 4384 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
62e76326 4385
cbff89ba 4386 AsyncCall::Pointer listenCall = asyncCall(33,2, "clientListenerConnectionOpened",
8bbb16e3
AJ
4387 ListeningStartedDialer(&clientListenerConnectionOpened, s, Ipc::fdnHttpSocket, sub));
4388 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpSocket, listenCall);
62e76326 4389
a38ec4b1
FC
4390 HttpSockets[NHttpSockets] = -1; // set in clientListenerConnectionOpened
4391 ++NHttpSockets;
15df8349 4392 }
d193a436 4393}
4394
cb4f4424 4395#if USE_OPENSSL
d193a436 4396static void
4397clientHttpsConnectionsOpen(void)
4398{
fa720bfb 4399 for (AnyP::PortCfgPointer s = HttpsPortList; s != NULL; s = s->next) {
65d448bc 4400 if (MAXTCPLISTENPORTS == NHttpSockets) {
e0236918
FC
4401 debugs(1, DBG_IMPORTANT, "Ignoring 'https_port' lines exceeding the limit.");
4402 debugs(1, DBG_IMPORTANT, "The limit is " << MAXTCPLISTENPORTS << " HTTPS ports.");
62e76326 4403 continue;
4404 }
4405
95d2589c 4406 if (!s->staticSslContext) {
e0236918 4407 debugs(1, DBG_IMPORTANT, "Ignoring https_port " << s->s <<
ae7ff0b8 4408 " due to SSL initialization failure.");
4409 continue;
f9ad0106 4410 }
4411
d7ce0bcd 4412 // TODO: merge with similar code in clientHttpConnectionsOpen()
6a25a046 4413 if (s->flags.tunnelSslBumping && !Config.accessList.ssl_bump) {
1ca54a54 4414 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s);
6a25a046 4415 s->flags.tunnelSslBumping = false;
d7ce0bcd
AR
4416 }
4417
6a25a046 4418 if (s->flags.tunnelSslBumping && !s->staticSslContext && !s->generateHostCertificates) {
4a9d561b 4419 debugs(1, DBG_IMPORTANT, "Will not bump SSL at https_port " << s->s << " due to SSL initialization failure.");
6a25a046 4420 s->flags.tunnelSslBumping = false;
d7ce0bcd
AR
4421 }
4422
6a25a046 4423 if (s->flags.tunnelSslBumping) {
d7ce0bcd
AR
4424 // Create ssl_ctx cache for this port.
4425 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
4426 }
4427
e0d28505 4428 // Fill out a Comm::Connection which IPC will open as a listener for us
859741ed
AJ
4429 s->listenConn = new Comm::Connection;
4430 s->listenConn->local = s->s;
6a25a046
FC
4431 s->listenConn->flags = COMM_NONBLOCKING | (s->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
4432 (s->flags.natIntercept ? COMM_INTERCEPTION : 0);
62e76326 4433
cbff89ba
AJ
4434 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTPS
4435 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
fa720bfb 4436 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpsAccept", CommAcceptCbPtrFun(httpsAccept, CommAcceptCbParams(NULL)));
cbff89ba 4437 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
62e76326 4438
cbff89ba 4439 AsyncCall::Pointer listenCall = asyncCall(33, 2, "clientListenerConnectionOpened",
8bbb16e3 4440 ListeningStartedDialer(&clientListenerConnectionOpened,
f53969cc 4441 s, Ipc::fdnHttpsSocket, sub));
8bbb16e3 4442 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpsSocket, listenCall);
a38ec4b1
FC
4443 HttpSockets[NHttpSockets] = -1;
4444 ++NHttpSockets;
cbff89ba 4445 }
d193a436 4446}
d193a436 4447#endif
4448
92ae4c86 4449void
27c841f6
AR
4450clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT<CommAcceptCbPtrFun> > &subCall, const Ipc::FdNoteId fdNote)
4451{
92ae4c86
AR
4452 // Fill out a Comm::Connection which IPC will open as a listener for us
4453 port->listenConn = new Comm::Connection;
4454 port->listenConn->local = port->s;
e7ce227f
AR
4455 port->listenConn->flags =
4456 COMM_NONBLOCKING |
4457 (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
4458 (port->flags.natIntercept ? COMM_INTERCEPTION : 0);
92ae4c86
AR
4459
4460 // route new connections to subCall
4461 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4462 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
e7ce227f
AR
4463 AsyncCall::Pointer listenCall =
4464 asyncCall(33, 2, "clientListenerConnectionOpened",
4465 ListeningStartedDialer(&clientListenerConnectionOpened,
4466 port, fdNote, sub));
92ae4c86 4467 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, listenCall);
434a79b0 4468
92ae4c86
AR
4469 assert(NHttpSockets < MAXTCPLISTENPORTS);
4470 HttpSockets[NHttpSockets] = -1;
4471 ++NHttpSockets;
434a79b0
DK
4472}
4473
e0d28505 4474/// process clientHttpConnectionsOpen result
00516be1 4475static void
fa720bfb 4476clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub)
00516be1 4477{
fa720bfb
AJ
4478 Must(s != NULL);
4479
8bbb16e3 4480 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
00516be1 4481 return;
62e76326 4482
e0d28505 4483 Must(Comm::IsConnOpen(s->listenConn));
62e76326 4484
8bbb16e3 4485 // TCP: setup a job to handle accept() with subscribed handler
fa720bfb 4486 AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
8bbb16e3 4487
e0236918 4488 debugs(1, DBG_IMPORTANT, "Accepting " <<
6a25a046 4489 (s->flags.natIntercept ? "NAT intercepted " : "") <<
0d901ef4 4490 (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
6a25a046
FC
4491 (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
4492 (s->flags.accelSurrogate ? "reverse-proxy " : "")
8bbb16e3
AJ
4493 << FdNote(portTypeNote) << " connections at "
4494 << s->listenConn);
62e76326 4495
e0d28505 4496 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
d193a436 4497}
4498
d193a436 4499void
4500clientOpenListenSockets(void)
4501{
4502 clientHttpConnectionsOpen();
cb4f4424 4503#if USE_OPENSSL
d193a436 4504 clientHttpsConnectionsOpen();
1f7c9178 4505#endif
92ae4c86 4506 Ftp::StartListening();
62e76326 4507
15df8349 4508 if (NHttpSockets < 1)
e7ce227f 4509 fatal("No HTTP, HTTPS, or FTP ports configured");
15df8349 4510}
edce4d98 4511
c0fbae16 4512void
e7ce227f 4513clientConnectionsClose()
c0fbae16 4514{
fa720bfb 4515 for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
00406b24 4516 if (s->listenConn != NULL) {
e0236918 4517 debugs(1, DBG_IMPORTANT, "Closing HTTP port " << s->listenConn->local);
00406b24
AJ
4518 s->listenConn->close();
4519 s->listenConn = NULL;
04f55905
AJ
4520 }
4521 }
62e76326 4522
cb4f4424 4523#if USE_OPENSSL
fa720bfb 4524 for (AnyP::PortCfgPointer s = HttpsPortList; s != NULL; s = s->next) {
00406b24 4525 if (s->listenConn != NULL) {
e0236918 4526 debugs(1, DBG_IMPORTANT, "Closing HTTPS port " << s->listenConn->local);
00406b24
AJ
4527 s->listenConn->close();
4528 s->listenConn = NULL;
62e76326 4529 }
c0fbae16 4530 }
04f55905
AJ
4531#endif
4532
92ae4c86 4533 Ftp::StopListening();
434a79b0 4534
04f55905 4535 // TODO see if we can drop HttpSockets array entirely */
95dc7ff4 4536 for (int i = 0; i < NHttpSockets; ++i) {
04f55905
AJ
4537 HttpSockets[i] = -1;
4538 }
62e76326 4539
c0fbae16 4540 NHttpSockets = 0;
4541}
f66a9ef4 4542
4543int
190154cf 4544varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
f66a9ef4 4545{
4546 const char *vary = request->vary_headers;
a9925b40 4547 int has_vary = entry->getReply()->header.has(HDR_VARY);
f66a9ef4 4548#if X_ACCELERATOR_VARY
62e76326 4549
edce4d98 4550 has_vary |=
a9925b40 4551 entry->getReply()->header.has(HDR_X_ACCELERATOR_VARY);
f66a9ef4 4552#endif
62e76326 4553
f66a9ef4 4554 if (!has_vary || !entry->mem_obj->vary_headers) {
62e76326 4555 if (vary) {
4556 /* Oops... something odd is going on here.. */
e0236918 4557 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
c877c0bc 4558 entry->mem_obj->urlXXX() << "' '" << vary << "'");
62e76326 4559 safe_free(request->vary_headers);
4560 return VARY_CANCEL;
4561 }
4562
4563 if (!has_vary) {
4564 /* This is not a varying object */
4565 return VARY_NONE;
4566 }
4567
4568 /* virtual "vary" object found. Calculate the vary key and
4569 * continue the search
4570 */
4571 vary = httpMakeVaryMark(request, entry->getReply());
4572
4573 if (vary) {
4574 request->vary_headers = xstrdup(vary);
4575 return VARY_OTHER;
4576 } else {
4577 /* Ouch.. we cannot handle this kind of variance */
4578 /* XXX This cannot really happen, but just to be complete */
4579 return VARY_CANCEL;
4580 }
f66a9ef4 4581 } else {
62e76326 4582 if (!vary) {
4583 vary = httpMakeVaryMark(request, entry->getReply());
4584
4585 if (vary)
4586 request->vary_headers = xstrdup(vary);
4587 }
4588
4589 if (!vary) {
4590 /* Ouch.. we cannot handle this kind of variance */
4591 /* XXX This cannot really happen, but just to be complete */
4592 return VARY_CANCEL;
4593 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
4594 return VARY_MATCH;
4595 } else {
4596 /* Oops.. we have already been here and still haven't
4597 * found the requested variant. Bail out
4598 */
e0236918 4599 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
c877c0bc 4600 entry->mem_obj->urlXXX() << "' '" << vary << "'");
62e76326 4601 return VARY_CANCEL;
4602 }
f66a9ef4 4603 }
4604}
28d4805a 4605
c0941a6a 4606ACLFilledChecklist *
59a1efb2 4607clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
28d4805a 4608{
1cf238db 4609 ConnStateData * conn = http->getConn();
c0941a6a 4610 ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request,
73c36fd9 4611 cbdataReferenceValid(conn) && conn != NULL && conn->clientConnection != NULL ? conn->clientConnection->rfc931 : dash_str);
d4806c91 4612 ch->al = http->al;
28d4805a 4613 /*
4614 * hack for ident ACL. It needs to get full addresses, and a place to store
4615 * the ident result on persistent connections...
4616 */
4617 /* connection oriented auth also needs these two lines for it's operation. */
28d4805a 4618 return ch;
4619}
a46d2c0e 4620
a46d2c0e 4621bool
4622ConnStateData::transparent() const
4623{
40d34a62 4624 return clientConnection != NULL && (clientConnection->flags & (COMM_TRANSPARENT|COMM_INTERCEPTION));
a46d2c0e 4625}
4626
4627bool
4628ConnStateData::reading() const
4629{
f84dd7eb 4630 return reader != NULL;
a46d2c0e 4631}
4632
4633void
f84dd7eb 4634ConnStateData::stopReading()
a46d2c0e 4635{
f84dd7eb 4636 if (reading()) {
7e66d5e2 4637 Comm::ReadCancel(clientConnection->fd, reader);
f84dd7eb
AR
4638 reader = NULL;
4639 }
a46d2c0e 4640}
4641
5f8252d2 4642BodyPipe::Pointer
3e62bd58 4643ConnStateData::expectRequestBody(int64_t size)
5f8252d2 4644{
4645 bodyPipe = new BodyPipe(this);
39cb8c41
AR
4646 if (size >= 0)
4647 bodyPipe->setBodySize(size);
4648 else
4649 startDechunkingRequest();
5f8252d2 4650 return bodyPipe;
4651}
4652
39cb8c41
AR
4653int64_t
4654ConnStateData::mayNeedToReadMoreBody() const
4655{
4656 if (!bodyPipe)
4657 return 0; // request without a body or read/produced all body bytes
4658
4659 if (!bodyPipe->bodySizeKnown())
4660 return -1; // probably need to read more, but we cannot be sure
4661
4662 const int64_t needToProduce = bodyPipe->unproducedSize();
e7287625 4663 const int64_t haveAvailable = static_cast<int64_t>(in.buf.length());
39cb8c41
AR
4664
4665 if (needToProduce <= haveAvailable)
4666 return 0; // we have read what we need (but are waiting for pipe space)
4667
4668 return needToProduce - haveAvailable;
4669}
4670
55e44db9 4671void
cf6eb29e 4672ConnStateData::stopReceiving(const char *error)
55e44db9 4673{
cf6eb29e
CT
4674 debugs(33, 4, HERE << "receiving error (" << clientConnection << "): " << error <<
4675 "; old sending error: " <<
4676 (stoppedSending() ? stoppedSending_ : "none"));
5f8252d2 4677
cf6eb29e
CT
4678 if (const char *oldError = stoppedReceiving()) {
4679 debugs(33, 3, HERE << "already stopped receiving: " << oldError);
4680 return; // nothing has changed as far as this connection is concerned
4681 }
5f8252d2 4682
cf6eb29e 4683 stoppedReceiving_ = error;
5f8252d2 4684
cf6eb29e
CT
4685 if (const char *sendError = stoppedSending()) {
4686 debugs(33, 3, HERE << "closing because also stopped sending: " << sendError);
4687 clientConnection->close();
4688 }
55e44db9 4689}
4690
eb44b2d7 4691void
e29ccb57
A
4692ConnStateData::expectNoForwarding()
4693{
eb44b2d7
CT
4694 if (bodyPipe != NULL) {
4695 debugs(33, 4, HERE << "no consumer for virgin body " << bodyPipe->status());
4696 bodyPipe->expectNoConsumption();
4697 }
4698}
4699
39cb8c41 4700/// initialize dechunking state
3ff65596 4701void
39cb8c41 4702ConnStateData::startDechunkingRequest()
3ff65596 4703{
39cb8c41
AR
4704 Must(bodyPipe != NULL);
4705 debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
3ff65596 4706 assert(!in.bodyParser);
db1720f8 4707 in.bodyParser = new Http1::TeChunkedParser;
3ff65596
AR
4708}
4709
39cb8c41 4710/// put parsed content into input buffer and clean up
3ff65596 4711void
39cb8c41 4712ConnStateData::finishDechunkingRequest(bool withSuccess)
3ff65596 4713{
39cb8c41 4714 debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
3ff65596 4715
39cb8c41
AR
4716 if (bodyPipe != NULL) {
4717 debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
4718 BodyPipe::Pointer myPipe = bodyPipe;
4719 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
4720 Must(!bodyPipe); // we rely on it being nil after we are done with body
4721 if (withSuccess) {
4722 Must(myPipe->bodySizeKnown());
4723 ClientSocketContext::Pointer context = getCurrentContext();
4724 if (context != NULL && context->http && context->http->request)
4725 context->http->request->setContentLength(myPipe->bodySize());
4726 }
3ff65596 4727 }
3ff65596 4728
39cb8c41
AR
4729 delete in.bodyParser;
4730 in.bodyParser = NULL;
3ff65596
AR
4731}
4732
e7287625 4733ConnStateData::In::In() :
f53969cc
SM
4734 bodyParser(NULL),
4735 buf()
a46d2c0e 4736{}
4737
4738ConnStateData::In::~In()
4739{
39cb8c41 4740 delete bodyParser; // TODO: pool
a46d2c0e 4741}
d67acb4e 4742
655daa06
AR
4743void
4744ConnStateData::sendControlMsg(HttpControlMsg msg)
4745{
eedd4182
AR
4746 if (!isOpen()) {
4747 debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
655daa06
AR
4748 return;
4749 }
4750
eedd4182
AR
4751 ClientSocketContext::Pointer context = getCurrentContext();
4752 if (context != NULL) {
4753 context->writeControlMsg(msg); // will call msg.cbSuccess
655daa06
AR
4754 return;
4755 }
4756
4757 debugs(33, 3, HERE << " closing due to missing context for 1xx");
73c36fd9 4758 clientConnection->close();
655daa06
AR
4759}
4760
d7ce0bcd 4761/// Our close handler called by Comm when the pinned connection is closed
d67acb4e
AJ
4762void
4763ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
4764{
7a957a93
AR
4765 // FwdState might repin a failed connection sooner than this close
4766 // callback is called for the failed connection.
693cb033
CT
4767 assert(pinning.serverConnection == io.conn);
4768 pinning.closeHandler = NULL; // Comm unregisters handlers before calling
4769 const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
b54a7c5a 4770 pinning.serverConnection->noteClosure();
89b1d7a2 4771 unpinConnection(false);
f8e4867b 4772
7ac40923 4773 if (sawZeroReply && clientConnection != NULL) {
693cb033
CT
4774 debugs(33, 3, "Closing client connection on pinned zero reply.");
4775 clientConnection->close();
85563fd9 4776 }
f8e4867b 4777
d67acb4e
AJ
4778}
4779
b1cf2350 4780void
f8e4867b 4781ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth, bool monitor)
9e008dda 4782{
e7ce227f 4783 if (!Comm::IsConnOpen(pinning.serverConnection) ||
27c841f6 4784 pinning.serverConnection->fd != pinServer->fd)
89b1d7a2 4785 pinNewConnection(pinServer, request, aPeer, auth);
d67acb4e 4786
f8e4867b
AR
4787 if (monitor)
4788 startPinnedConnectionMonitoring();
89b1d7a2 4789}
9e008dda 4790
89b1d7a2
AR
4791void
4792ConnStateData::pinNewConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth)
4793{
4794 unpinConnection(true); // closes pinned connection, if any, and resets fields
9e008dda 4795
73c36fd9 4796 pinning.serverConnection = pinServer;
d7ce0bcd 4797
85563fd9
AR
4798 debugs(33, 3, HERE << pinning.serverConnection);
4799
89b1d7a2
AR
4800 Must(pinning.serverConnection != NULL);
4801
d7ce0bcd
AR
4802 // when pinning an SSL bumped connection, the request may be NULL
4803 const char *pinnedHost = "[unknown]";
4804 if (request) {
5c51bffb
AJ
4805 pinning.host = xstrdup(request->url.host());
4806 pinning.port = request->url.port();
d7ce0bcd
AR
4807 pinnedHost = pinning.host;
4808 } else {
4dd643d5 4809 pinning.port = pinServer->remote.port();
d7ce0bcd 4810 }
d67acb4e 4811 pinning.pinned = true;
8bcf08e0
FC
4812 if (aPeer)
4813 pinning.peer = cbdataReference(aPeer);
d67acb4e 4814 pinning.auth = auth;
e3a4aecc 4815 char stmp[MAX_IPSTRLEN];
89b1d7a2 4816 char desc[FD_DESC_SZ];
e3a4aecc 4817 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
d7ce0bcd 4818 (auth || !aPeer) ? pinnedHost : aPeer->name,
4dd643d5 4819 clientConnection->remote.toUrl(stmp,MAX_IPSTRLEN),
d7ce0bcd 4820 clientConnection->fd);
73c36fd9 4821 fd_note(pinning.serverConnection->fd, desc);
9e008dda 4822
d67acb4e 4823 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4299f876 4824 pinning.closeHandler = JobCallback(33, 5,
4cb2536f 4825 Dialer, this, ConnStateData::clientPinnedConnectionClosed);
85563fd9
AR
4826 // remember the pinned connection so that cb does not unpin a fresher one
4827 typedef CommCloseCbParams Params;
4828 Params &params = GetCommParams<Params>(pinning.closeHandler);
4829 params.conn = pinning.serverConnection;
73c36fd9 4830 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
7ac40923
AR
4831}
4832
e7ce227f
AR
4833/// [re]start monitoring pinned connection for peer closures so that we can
4834/// propagate them to an _idle_ client pinned to that peer
7ac40923
AR
4835void
4836ConnStateData::startPinnedConnectionMonitoring()
4837{
4838 if (pinning.readHandler != NULL)
4839 return; // already monitoring
4840
4841 typedef CommCbMemFunT<ConnStateData, CommIoCbParams> Dialer;
4842 pinning.readHandler = JobCallback(33, 3,
4843 Dialer, this, ConnStateData::clientPinnedConnectionRead);
7e66d5e2 4844 Comm::Read(pinning.serverConnection, pinning.readHandler);
7ac40923
AR
4845}
4846
4847void
4848ConnStateData::stopPinnedConnectionMonitoring()
4849{
4850 if (pinning.readHandler != NULL) {
7e66d5e2 4851 Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
7ac40923
AR
4852 pinning.readHandler = NULL;
4853 }
4854}
4855
4856/// Our read handler called by Comm when the server either closes an idle pinned connection or
4857/// perhaps unexpectedly sends something on that idle (from Squid p.o.v.) connection.
4858void
4859ConnStateData::clientPinnedConnectionRead(const CommIoCbParams &io)
4860{
4861 pinning.readHandler = NULL; // Comm unregisters handlers before calling
4862
c8407295 4863 if (io.flag == Comm::ERR_CLOSING)
7ac40923
AR
4864 return; // close handler will clean up
4865
4866 // We could use getConcurrentRequestCount(), but this may be faster.
4867 const bool clientIsIdle = !getCurrentContext();
4868
4869 debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
4870 io.size << (clientIsIdle ? " with idle client" : ""));
4871
4872 assert(pinning.serverConnection == io.conn);
4873 pinning.serverConnection->close();
4874
4875 // If we are still sending data to the client, do not close now. When we are done sending,
4876 // ClientSocketContext::keepaliveNextRequest() checks pinning.serverConnection and will close.
4877 // However, if we are idle, then we must close to inform the idle client and minimize races.
4878 if (clientIsIdle && clientConnection != NULL)
4879 clientConnection->close();
d67acb4e
AJ
4880}
4881
e3a4aecc 4882const Comm::ConnectionPointer
a3c6762c 4883ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *aPeer)
d67acb4e 4884{
85563fd9
AR
4885 debugs(33, 7, HERE << pinning.serverConnection);
4886
d67acb4e 4887 bool valid = true;
73c36fd9 4888 if (!Comm::IsConnOpen(pinning.serverConnection))
e3a4aecc 4889 valid = false;
5c51bffb 4890 else if (pinning.auth && pinning.host && request && strcasecmp(pinning.host, request->url.host()) != 0)
9e008dda 4891 valid = false;
5c51bffb 4892 else if (request && pinning.port != request->url.port())
9e008dda 4893 valid = false;
6de3828e 4894 else if (pinning.peer && !cbdataReferenceValid(pinning.peer))
9e008dda 4895 valid = false;
6de3828e 4896 else if (aPeer != pinning.peer)
9e008dda 4897 valid = false;
d67acb4e 4898
9e008dda 4899 if (!valid) {
b1cf2350 4900 /* The pinning info is not safe, remove any pinning info */
89b1d7a2 4901 unpinConnection(true);
d67acb4e
AJ
4902 }
4903
73c36fd9 4904 return pinning.serverConnection;
d67acb4e
AJ
4905}
4906
89b1d7a2
AR
4907Comm::ConnectionPointer
4908ConnStateData::borrowPinnedConnection(HttpRequest *request, const CachePeer *aPeer)
4909{
4910 debugs(33, 7, pinning.serverConnection);
4911 if (validatePinnedConnection(request, aPeer) != NULL)
f8e4867b 4912 stopPinnedConnectionMonitoring();
89b1d7a2
AR
4913
4914 return pinning.serverConnection; // closed if validation failed
4915}
4916
b1cf2350 4917void
89b1d7a2 4918ConnStateData::unpinConnection(const bool andClose)
d67acb4e 4919{
85563fd9
AR
4920 debugs(33, 3, HERE << pinning.serverConnection);
4921
9e008dda
AJ
4922 if (pinning.peer)
4923 cbdataReferenceDone(pinning.peer);
d67acb4e 4924
d7ce0bcd 4925 if (Comm::IsConnOpen(pinning.serverConnection)) {
87f237a9
A
4926 if (pinning.closeHandler != NULL) {
4927 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4928 pinning.closeHandler = NULL;
4929 }
89b1d7a2 4930
f8e4867b 4931 stopPinnedConnectionMonitoring();
89b1d7a2
AR
4932
4933 // close the server side socket if requested
4934 if (andClose)
4935 pinning.serverConnection->close();
4936 pinning.serverConnection = NULL;
d67acb4e 4937 }
d7ce0bcd 4938
d67acb4e 4939 safe_free(pinning.host);
e3a4aecc 4940
693cb033
CT
4941 pinning.zeroReply = false;
4942
e3a4aecc
AJ
4943 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
4944 * connection has gone away */
d67acb4e 4945}
44352c16 4946