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