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