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