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