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