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