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