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