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