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