]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
Partial bug #2964 fix: writer stuck in a "waiting for preview answer" state.
[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
1176 return etagIsEqual(&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);
1264 /* ETags should not be returned with Partial Content replies? */
a9925b40 1265 hdr->delById(HDR_ETAG);
62e76326 1266 /* append appropriate header(s) */
1267
1268 if (spec_count == 1) {
fedd1531 1269 if (!replyMatchRequest) {
1270 hdr->delById(HDR_CONTENT_RANGE);
1271 hdr->putContRange(rep->content_range);
1272 actual_clen = rep->content_length;
1273 //http->range_iter.pos = rep->content_range->spec.begin();
1274 (*http->range_iter.pos)->offset = rep->content_range->spec.offset;
1275 (*http->range_iter.pos)->length = rep->content_range->spec.length;
1276
1277 } else {
1278 HttpHdrRange::iterator pos = http->request->range->begin();
1279 assert(*pos);
1280 /* append Content-Range */
1281
1282 if (!hdr->has(HDR_CONTENT_RANGE)) {
1283 /* No content range, so this was a full object we are
1284 * sending parts of.
1285 */
1286 httpHeaderAddContRange(hdr, **pos, rep->content_length);
1287 }
1288
1289 /* set new Content-Length to the actual number of bytes
1290 * transmitted in the message-body */
1291 actual_clen = (*pos)->length;
62e76326 1292 }
62e76326 1293 } else {
1294 /* multipart! */
1295 /* generate boundary string */
1296 http->range_iter.boundary = http->rangeBoundaryStr();
1297 /* delete old Content-Type, add ours */
a9925b40 1298 hdr->delById(HDR_CONTENT_TYPE);
62e76326 1299 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
826a1fed
FC
1300 "multipart/byteranges; boundary=\"" SQUIDSTRINGPH "\"",
1301 SQUIDSTRINGPRINT(http->range_iter.boundary));
62e76326 1302 /* Content-Length is not required in multipart responses
1303 * but it is always nice to have one */
1304 actual_clen = http->mRangeCLen();
2512d159 1305 /* http->out needs to start where we want data at */
1306 http->out.offset = http->range_iter.currentSpec()->offset;
62e76326 1307 }
1308
1309 /* replace Content-Length header */
1310 assert(actual_clen >= 0);
1311
a9925b40 1312 hdr->delById(HDR_CONTENT_LENGTH);
62e76326 1313
47f6e231 1314 hdr->putInt64(HDR_CONTENT_LENGTH, actual_clen);
62e76326 1315
bf8fe701 1316 debugs(33, 3, "clientBuildRangeHeader: actual content length: " << actual_clen);
62e76326 1317
1318 /* And start the range iter off */
1319 http->range_iter.updateSpec();
c8be6d7b 1320 }
528b2c61 1321}
1322
1323void
1324ClientSocketContext::prepareReply(HttpReply * rep)
1325{
fedd1531 1326 reply = rep;
1327
528b2c61 1328 if (http->request->range)
62e76326 1329 buildRangeHeader(rep);
528b2c61 1330}
1331
1332void
1333ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData)
1334{
1335 prepareReply(rep);
528b2c61 1336 assert (rep);
06a5ae20 1337 MemBuf *mb = rep->pack();
528b2c61 1338 /* Save length of headers for persistent conn checks */
032785bf 1339 http->out.headers_sz = mb->contentSize();
528b2c61 1340#if HEADERS_LOG
62e76326 1341
528b2c61 1342 headersLog(0, 0, http->request->method, rep);
1343#endif
62e76326 1344
c8be6d7b 1345 if (bodyData.data && bodyData.length) {
4ad60609
AR
1346 if (multipartRangeRequest())
1347 packRange(bodyData, mb);
1348 else if (http->request->flags.chunked_reply) {
1349 packChunk(bodyData, *mb);
1350 } else {
2512d159 1351 size_t length = lengthToSend(bodyData.range());
62e76326 1352 noteSentBodyBytes (length);
1353
2fe7eff9 1354 mb->append(bodyData.data, length);
62e76326 1355 }
c8be6d7b 1356 }
62e76326 1357
c8be6d7b 1358 /* write */
425802c8 1359 debugs(33,7, HERE << "sendStartOfMessage schedules clientWriteComplete");
9e008dda
AJ
1360 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
1361 CommIoCbPtrFun(clientWriteComplete, this));
1cf238db 1362 comm_write_mbuf(fd(), mb, call);
62e76326 1363
032785bf 1364 delete mb;
c8be6d7b 1365}
1366
63be0a78 1367/**
7dc5f514 1368 * Write a chunk of data to a client socket. If the reply is present,
1369 * send the reply headers down the wire too, and clean them up when
1370 * finished.
9e008dda 1371 * Pre-condition:
edce4d98 1372 * The request is one backed by a connection, not an internal request.
1373 * data context is not NULL
1374 * There are no more entries in the stream chain.
2246b732 1375 */
edce4d98 1376static void
59a1efb2 1377clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http,
2324cda2 1378 HttpReply * rep, StoreIOBuffer receivedData)
edce4d98 1379{
1380 int fd;
edce4d98 1381 /* Test preconditions */
1382 assert(node != NULL);
559da936 1383 PROF_start(clientSocketRecipient);
62e76326 1384 /* TODO: handle this rather than asserting
9e008dda
AJ
1385 * - it should only ever happen if we cause an abort and
1386 * the callback chain loops back to here, so we can simply return.
1387 * However, that itself shouldn't happen, so it stays as an assert for now.
edce4d98 1388 */
1389 assert(cbdataReferenceValid(node));
edce4d98 1390 assert(node->node.next == NULL);
0655fa4d 1391 ClientSocketContext::Pointer context = dynamic_cast<ClientSocketContext *>(node->data.getRaw());
94a396a3 1392 assert(context != NULL);
98242069 1393 assert(connIsUsable(http->getConn()));
1394 fd = http->getConn()->fd;
528b2c61 1395 /* TODO: check offset is what we asked for */
62e76326 1396
98242069 1397 if (context != http->getConn()->getCurrentContext()) {
2324cda2 1398 context->deferRecipientForLater(node, rep, receivedData);
559da936 1399 PROF_stop(clientSocketRecipient);
62e76326 1400 return;
edce4d98 1401 }
62e76326 1402
4ad60609
AR
1403 // After sending Transfer-Encoding: chunked (at least), always send
1404 // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
1405 const bool mustSendLastChunk = http->request->flags.chunked_reply &&
4cb2536f 1406 !http->request->flags.stream_error && !context->startOfOutput();
4ad60609 1407 if (responseFinishedOrFailed(rep, receivedData) && !mustSendLastChunk) {
0655fa4d 1408 context->writeComplete(fd, NULL, 0, COMM_OK);
559da936 1409 PROF_stop(clientSocketRecipient);
62e76326 1410 return;
edce4d98 1411 }
62e76326 1412
0655fa4d 1413 if (!context->startOfOutput())
2324cda2 1414 context->sendBody(rep, receivedData);
7684c4b1 1415 else {
a4785954 1416 assert(rep);
90a8964c 1417 http->al.reply = HTTPMSGLOCK(rep);
2324cda2 1418 context->sendStartOfMessage(rep, receivedData);
7684c4b1 1419 }
fc68f6b1 1420
559da936 1421 PROF_stop(clientSocketRecipient);
edce4d98 1422}
1423
63be0a78 1424/**
1425 * Called when a downstream node is no longer interested in
edce4d98 1426 * our data. As we are a terminal node, this means on aborts
1427 * only
1428 */
1429void
59a1efb2 1430clientSocketDetach(clientStreamNode * node, ClientHttpRequest * http)
edce4d98 1431{
edce4d98 1432 /* Test preconditions */
1433 assert(node != NULL);
62e76326 1434 /* TODO: handle this rather than asserting
9e008dda
AJ
1435 * - it should only ever happen if we cause an abort and
1436 * the callback chain loops back to here, so we can simply return.
edce4d98 1437 * However, that itself shouldn't happen, so it stays as an assert for now.
1438 */
1439 assert(cbdataReferenceValid(node));
1440 /* Set null by ContextFree */
edce4d98 1441 assert(node->node.next == NULL);
0655fa4d 1442 /* this is the assert discussed above */
e4a67a80 1443 assert(NULL == dynamic_cast<ClientSocketContext *>(node->data.getRaw()));
edce4d98 1444 /* We are only called when the client socket shutsdown.
1445 * Tell the prev pipeline member we're finished
1446 */
1447 clientStreamDetach(node, http);
7a2f978b 1448}
1449
f4f278b5 1450static void
25f651c1 1451clientWriteBodyComplete(int fd, char *buf, size_t size, comm_err_t errflag, int xerrno, void *data)
f4f278b5 1452{
425802c8 1453 debugs(33,7, HERE << "clientWriteBodyComplete schedules clientWriteComplete");
2b663917 1454 clientWriteComplete(fd, NULL, size, errflag, xerrno, data);
c8be6d7b 1455}
1456
1457void
a46d2c0e 1458ConnStateData::readNextRequest()
c8be6d7b 1459{
bf8fe701 1460 debugs(33, 5, "ConnStateData::readNextRequest: FD " << fd << " reading next req");
1461
a46d2c0e 1462 fd_note(fd, "Waiting for next request");
63be0a78 1463 /**
c8be6d7b 1464 * Set the timeout BEFORE calling clientReadRequest().
1465 */
1cf238db 1466 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876 1467 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
4cb2536f 1468 TimeoutDialer, this, ConnStateData::requestTimeout);
1cf238db 1469 commSetTimeout(fd, Config.Timeout.persistent_request, timeoutCall);
1470
a46d2c0e 1471 readSomeData();
63be0a78 1472 /** Please don't do anything with the FD past here! */
c8be6d7b 1473}
1474
09d3938c 1475static void
1cf238db 1476ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn)
c8be6d7b 1477{
bf8fe701 1478 debugs(33, 2, "ClientSocketContextPushDeferredIfNeeded: FD " << conn->fd << " Sending next");
1479
63be0a78 1480 /** If the client stream is waiting on a socket write to occur, then */
62e76326 1481
c8be6d7b 1482 if (deferredRequest->flags.deferred) {
63be0a78 1483 /** NO data is allowed to have been sent. */
62e76326 1484 assert(deferredRequest->http->out.size == 0);
63be0a78 1485 /** defer now. */
62e76326 1486 clientSocketRecipient(deferredRequest->deferredparams.node,
1487 deferredRequest->http,
1488 deferredRequest->deferredparams.rep,
1489 deferredRequest->deferredparams.queuedBuffer);
c8be6d7b 1490 }
62e76326 1491
63be0a78 1492 /** otherwise, the request is still active in a callbacksomewhere,
c8be6d7b 1493 * and we are done
f4f278b5 1494 */
f4f278b5 1495}
1496
0655fa4d 1497void
1498ClientSocketContext::keepaliveNextRequest()
1a92a1e2 1499{
1cf238db 1500 ConnStateData * conn = http->getConn();
f900210a 1501 bool do_next_read = false;
bd4e6ec8 1502
bf8fe701 1503 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: FD " << conn->fd);
0655fa4d 1504 connIsFinished();
1505
d67acb4e
AJ
1506 if (conn->pinning.pinned && conn->pinning.fd == -1) {
1507 debugs(33, 2, "clientKeepaliveNextRequest: FD " << conn->fd << " Connection was pinned but server side gone. Terminating client connection");
1508 comm_close(conn->fd);
1509 return;
1510 }
1511
63be0a78 1512 /** \par
f900210a 1513 * Attempt to parse a request from the request buffer.
1514 * If we've been fed a pipelined request it may already
1515 * be in our read buffer.
1516 *
63be0a78 1517 \par
f900210a 1518 * This needs to fall through - if we're unlucky and parse the _last_ request
1519 * from our read buffer we may never re-register for another client read.
1520 */
1521
1522 if (clientParseRequest(conn, do_next_read)) {
bf8fe701 1523 debugs(33, 3, "clientSocketContext::keepaliveNextRequest: FD " << conn->fd << ": parsed next request from buffer");
f900210a 1524 }
1525
63be0a78 1526 /** \par
f900210a 1527 * Either we need to kick-start another read or, if we have
1528 * a half-closed connection, kill it after the last request.
1529 * This saves waiting for half-closed connections to finished being
1530 * half-closed _AND_ then, sometimes, spending "Timeout" time in
1531 * the keepalive "Waiting for next request" state.
1532 */
1533 if (commIsHalfClosed(conn->fd) && (conn->getConcurrentRequestCount() == 0)) {
bf8fe701 1534 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: half-closed client with no pending requests, closing");
f900210a 1535 comm_close(conn->fd);
1536 return;
1537 }
1538
0655fa4d 1539 ClientSocketContext::Pointer deferredRequest;
62e76326 1540
63be0a78 1541 /** \par
f900210a 1542 * At this point we either have a parsed request (which we've
1543 * kicked off the processing for) or not. If we have a deferred
1544 * request (parsed but deferred for pipeling processing reasons)
1545 * then look at processing it. If not, simply kickstart
1546 * another read.
1547 */
1548
1549 if ((deferredRequest = conn->getCurrentContext()).getRaw()) {
bf8fe701 1550 debugs(33, 3, "ClientSocketContext:: FD " << conn->fd << ": calling PushDeferredIfNeeded");
62e76326 1551 ClientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
f900210a 1552 } else {
bf8fe701 1553 debugs(33, 3, "ClientSocketContext:: FD " << conn->fd << ": calling conn->readNextRequest()");
f900210a 1554 conn->readNextRequest();
1555 }
1a92a1e2 1556}
1557
c8be6d7b 1558void
1559clientUpdateSocketStats(log_type logType, size_t size)
1560{
1561 if (size == 0)
62e76326 1562 return;
1563
c8be6d7b 1564 kb_incr(&statCounter.client_http.kbytes_out, size);
62e76326 1565
c8be6d7b 1566 if (logTypeIsATcpHit(logType))
62e76326 1567 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
c8be6d7b 1568}
1569
63be0a78 1570/**
528b2c61 1571 * increments iterator "i"
63be0a78 1572 * used by clientPackMoreRanges
1573 *
1574 \retval true there is still data available to pack more ranges
9e008dda 1575 \retval false
63be0a78 1576 */
528b2c61 1577bool
1578ClientSocketContext::canPackMoreRanges() const
1579{
63be0a78 1580 /** first update iterator "i" if needed */
62e76326 1581
528b2c61 1582 if (!http->range_iter.debt()) {
63be0a78 1583 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: At end of current range spec for FD " << fd());
62e76326 1584
1585 if (http->range_iter.pos.incrementable())
1586 ++http->range_iter.pos;
1587
1588 http->range_iter.updateSpec();
528b2c61 1589 }
62e76326 1590
528b2c61 1591 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
63be0a78 1592
528b2c61 1593 /* paranoid sync condition */
1594 /* continue condition: need_more_data */
bf8fe701 1595 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: returning " << (http->range_iter.currentSpec() ? true : false));
528b2c61 1596 return http->range_iter.currentSpec() ? true : false;
1597}
1598
47f6e231 1599int64_t
528b2c61 1600ClientSocketContext::getNextRangeOffset() const
1601{
1602 if (http->request->range) {
62e76326 1603 /* offset in range specs does not count the prefix of an http msg */
47f6e231 1604 debugs (33, 5, "ClientSocketContext::getNextRangeOffset: http offset " << http->out.offset);
62e76326 1605 /* check: reply was parsed and range iterator was initialized */
1606 assert(http->range_iter.valid);
1607 /* filter out data according to range specs */
1608 assert (canPackMoreRanges());
1609 {
47f6e231 1610 int64_t start; /* offset of still missing data */
62e76326 1611 assert(http->range_iter.currentSpec());
1612 start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
47f6e231 1613 debugs(33, 3, "clientPackMoreRanges: in: offset: " << http->out.offset);
1614 debugs(33, 3, "clientPackMoreRanges: out:"
9e008dda
AJ
1615 " start: " << start <<
1616 " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
1617 " [" << http->range_iter.currentSpec()->offset <<
1618 ", " << http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length << "),"
1619 " len: " << http->range_iter.currentSpec()->length <<
1620 " debt: " << http->range_iter.debt());
62e76326 1621 if (http->range_iter.currentSpec()->length != -1)
1622 assert(http->out.offset <= start); /* we did not miss it */
1623
1624 return start;
1625 }
1626
fedd1531 1627 } else if (reply && reply->content_range) {
1628 /* request does not have ranges, but reply does */
63be0a78 1629 /** \todo FIXME: should use range_iter_pos on reply, as soon as reply->content_range
1630 * becomes HttpHdrRange rather than HttpHdrRangeSpec.
1631 */
fedd1531 1632 return http->out.offset + reply->content_range->spec.offset;
528b2c61 1633 }
1634
1635 return http->out.offset;
1636}
1637
c8be6d7b 1638void
528b2c61 1639ClientSocketContext::pullData()
c8be6d7b 1640{
ec69c304 1641 debugs(33, 5, "ClientSocketContext::pullData: FD " << fd() <<
9e008dda 1642 " attempting to pull upstream data");
bf8fe701 1643
c8be6d7b 1644 /* More data will be coming from the stream. */
528b2c61 1645 StoreIOBuffer readBuffer;
1646 /* XXX: Next requested byte in the range sequence */
1647 /* XXX: length = getmaximumrangelenfgth */
1648 readBuffer.offset = getNextRangeOffset();
c8be6d7b 1649 readBuffer.length = HTTP_REQBUF_SZ;
528b2c61 1650 readBuffer.data = reqbuf;
1651 /* we may note we have reached the end of the wanted ranges */
1652 clientStreamRead(getTail(), http, readBuffer);
1653}
1654
62e76326 1655clientStream_status_t
528b2c61 1656ClientSocketContext::socketState()
1657{
1658 switch (clientStreamStatus(getTail(), http)) {
62e76326 1659
1660 case STREAM_NONE:
528b2c61 1661 /* check for range support ending */
62e76326 1662
528b2c61 1663 if (http->request->range) {
62e76326 1664 /* check: reply was parsed and range iterator was initialized */
1665 assert(http->range_iter.valid);
1666 /* filter out data according to range specs */
1667
1668 if (!canPackMoreRanges()) {
920ba08d 1669 debugs(33, 5, HERE << "Range request at end of returnable " <<
9e008dda 1670 "range sequence on FD " << fd());
62e76326 1671
1672 if (http->request->flags.proxy_keepalive)
1673 return STREAM_COMPLETE;
1674 else
1675 return STREAM_UNPLANNED_COMPLETE;
1676 }
fedd1531 1677 } else if (reply && reply->content_range) {
425802c8 1678 /* reply has content-range, but Squid is not managing ranges */
1679 const int64_t &bytesSent = http->out.offset;
1680 const int64_t &bytesExpected = reply->content_range->spec.length;
fedd1531 1681
425802c8 1682 debugs(33, 7, HERE << "body bytes sent vs. expected: " <<
9e008dda
AJ
1683 bytesSent << " ? " << bytesExpected << " (+" <<
1684 reply->content_range->spec.offset << ")");
425802c8 1685
1686 // did we get at least what we expected, based on range specs?
1687
65058d64
CT
1688 if (bytesSent == bytesExpected) { // got everything
1689 if (http->request->flags.proxy_keepalive)
1690 return STREAM_COMPLETE;
1691 else
1692 return STREAM_UNPLANNED_COMPLETE;
1693 }
425802c8 1694
1695 // The logic below is not clear: If we got more than we
1696 // expected why would persistency matter? Should not this
1697 // always be an error?
1698 if (bytesSent > bytesExpected) { // got extra
fedd1531 1699 if (http->request->flags.proxy_keepalive)
1700 return STREAM_COMPLETE;
1701 else
1702 return STREAM_UNPLANNED_COMPLETE;
1703 }
425802c8 1704
1705 // did not get enough yet, expecting more
62e76326 1706 }
1707
1708 return STREAM_NONE;
1709
1710 case STREAM_COMPLETE:
528b2c61 1711 return STREAM_COMPLETE;
62e76326 1712
1713 case STREAM_UNPLANNED_COMPLETE:
1714 return STREAM_UNPLANNED_COMPLETE;
1715
1716 case STREAM_FAILED:
1717 return STREAM_FAILED;
528b2c61 1718 }
62e76326 1719
528b2c61 1720 fatal ("unreachable code\n");
1721 return STREAM_NONE;
c8be6d7b 1722}
edce4d98 1723
63be0a78 1724/**
1725 * A write has just completed to the client, or we have just realised there is
edce4d98 1726 * no more data to send.
1727 */
e6ccf245 1728void
2b663917 1729clientWriteComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
7a2f978b 1730{
528b2c61 1731 ClientSocketContext *context = (ClientSocketContext *)data;
0655fa4d 1732 context->writeComplete (fd, bufnotused, size, errflag);
1733}
1734
f692498b
AJ
1735/// remembers the abnormal connection termination for logging purposes
1736void
1737ClientSocketContext::noteIoError(const int xerrno)
1738{
1739 if (http) {
1740 if (xerrno == ETIMEDOUT)
1741 http->al.http.timedout = true;
1742 else // even if xerrno is zero (which means read abort/eof)
1743 http->al.http.aborted = true;
1744 }
1745}
1746
1747
55e44db9 1748void
1749ClientSocketContext::doClose()
1750{
1751 comm_close(fd());
1752}
1753
6e1d409c
AR
1754/** Called to initiate (and possibly complete) closing of the context.
1755 * The underlying socket may be already closed */
55e44db9 1756void
5f8252d2 1757ClientSocketContext::initiateClose(const char *reason)
55e44db9 1758{
5f8252d2 1759 debugs(33, 5, HERE << "initiateClose: closing for " << reason);
fc68f6b1 1760
3b299123 1761 if (http != NULL) {
1cf238db 1762 ConnStateData * conn = http->getConn();
3b299123 1763
1764 if (conn != NULL) {
39cb8c41 1765 if (const int64_t expecting = conn->mayNeedToReadMoreBody()) {
5f8252d2 1766 debugs(33, 5, HERE << "ClientSocketContext::initiateClose: " <<
1767 "closing, but first " << conn << " needs to read " <<
1768 expecting << " request body bytes with " <<
1769 conn->in.notYetUsed << " notYetUsed");
1770
1771 if (conn->closing()) {
1772 debugs(33, 2, HERE << "avoiding double-closing " << conn);
1773 return;
1774 }
fc68f6b1 1775
3b299123 1776 /*
1777 * XXX We assume the reply fits in the TCP transmit
1778 * window. If not the connection may stall while sending
1779 * the reply (before reaching here) if the client does not
1780 * try to read the response while sending the request body.
1781 * As of yet we have not received any complaints indicating
1782 * this may be an issue.
55e44db9 1783 */
5f8252d2 1784 conn->startClosing(reason);
fc68f6b1 1785
3b299123 1786 return;
1787 }
1788 }
55e44db9 1789 }
1790
1791 doClose();
1792}
1793
0655fa4d 1794void
e053c141 1795ClientSocketContext::writeComplete(int aFileDescriptor, char *bufnotused, size_t size, comm_err_t errflag)
0655fa4d 1796{
86a2f789 1797 StoreEntry *entry = http->storeEntry();
7a2f978b 1798 http->out.size += size;
e053c141
FC
1799 assert(aFileDescriptor > -1);
1800 debugs(33, 5, "clientWriteComplete: FD " << aFileDescriptor << ", sz " << size <<
e4049756 1801 ", err " << errflag << ", off " << http->out.size << ", len " <<
707fdc47 1802 entry ? entry->objectLen() : 0);
c8be6d7b 1803 clientUpdateSocketStats(http->logType, size);
e053c141 1804 assert (this->fd() == aFileDescriptor);
62e76326 1805
5f8252d2 1806 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
fc68f6b1 1807
5f8252d2 1808 if (errflag == COMM_ERR_CLOSING)
1809 return;
1810
e053c141 1811 if (errflag || clientHttpRequestStatus(aFileDescriptor, http)) {
5f8252d2 1812 initiateClose("failure or true request status");
62e76326 1813 /* Do we leak here ? */
1814 return;
edce4d98 1815 }
62e76326 1816
0655fa4d 1817 switch (socketState()) {
62e76326 1818
edce4d98 1819 case STREAM_NONE:
0655fa4d 1820 pullData();
62e76326 1821 break;
1822
edce4d98 1823 case STREAM_COMPLETE:
e053c141 1824 debugs(33, 5, "clientWriteComplete: FD " << aFileDescriptor << " Keeping Alive");
0655fa4d 1825 keepaliveNextRequest();
62e76326 1826 return;
1827
edce4d98 1828 case STREAM_UNPLANNED_COMPLETE:
6e1d409c
AR
1829 initiateClose("STREAM_UNPLANNED_COMPLETE");
1830 return;
62e76326 1831
edce4d98 1832 case STREAM_FAILED:
6e1d409c 1833 initiateClose("STREAM_FAILED");
62e76326 1834 return;
1835
edce4d98 1836 default:
62e76326 1837 fatal("Hit unreachable code in clientWriteComplete\n");
7a2f978b 1838 }
1839}
1840
e6ccf245 1841extern "C" CSR clientGetMoreData;
1842extern "C" CSS clientReplyStatus;
1843extern "C" CSD clientReplyDetach;
edce4d98 1844
528b2c61 1845static ClientSocketContext *
1cf238db 1846parseHttpRequestAbort(ConnStateData * conn, const char *uri)
038eb4ed 1847{
59a1efb2 1848 ClientHttpRequest *http;
528b2c61 1849 ClientSocketContext *context;
1850 StoreIOBuffer tempBuffer;
a0355e95 1851 http = new ClientHttpRequest(conn);
c8be6d7b 1852 http->req_sz = conn->in.notYetUsed;
edce4d98 1853 http->uri = xstrdup(uri);
c4b7a5a9 1854 setLogUri (http, uri);
528b2c61 1855 context = ClientSocketContextNew(http);
c8be6d7b 1856 tempBuffer.data = context->reqbuf;
1857 tempBuffer.length = HTTP_REQBUF_SZ;
edce4d98 1858 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 1859 clientReplyStatus, new clientReplyContext(http), clientSocketRecipient,
62e76326 1860 clientSocketDetach, context, tempBuffer);
edce4d98 1861 return context;
038eb4ed 1862}
1863
c8be6d7b 1864char *
1865skipLeadingSpace(char *aString)
1866{
1867 char *result = aString;
62e76326 1868
c8be6d7b 1869 while (xisspace(*aString))
62e76326 1870 ++aString;
1871
c8be6d7b 1872 return result;
1873}
1874
63be0a78 1875/**
d4a04ed5 1876 * 'end' defaults to NULL for backwards compatibility
1877 * remove default value if we ever get rid of NULL-terminated
1878 * request buffers.
1879 */
1880const char *
1881findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
c8be6d7b 1882{
d4a04ed5 1883 if (NULL == end) {
1884 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1885 assert(end);
1886 }
62e76326 1887
d4a04ed5 1888 for (; end > uriAndHTTPVersion; end--) {
1889 if (*end == '\n' || *end == '\r')
62e76326 1890 continue;
1891
d4a04ed5 1892 if (xisspace(*end)) {
1893 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1894 return end + 1;
62e76326 1895 else
1896 break;
1897 }
c8be6d7b 1898 }
62e76326 1899
3f38a55e 1900 return NULL;
c8be6d7b 1901}
1902
c8be6d7b 1903void
59a1efb2 1904setLogUri(ClientHttpRequest * http, char const *uri)
c8be6d7b 1905{
a46d0227 1906 safe_free(http->log_uri);
62e76326 1907
c8be6d7b 1908 if (!stringHasCntl(uri))
62e76326 1909 http->log_uri = xstrndup(uri, MAX_URL);
c8be6d7b 1910 else
62e76326 1911 http->log_uri = xstrndup(rfc1738_escape_unescaped(uri), MAX_URL);
c8be6d7b 1912}
1913
3f38a55e 1914static void
1cf238db 1915prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
62e76326 1916{
3f38a55e 1917 int vhost = conn->port->vhost;
1918 int vport = conn->port->vport;
1919 char *host;
cc192b50 1920 char ntoabuf[MAX_IPSTRLEN];
c8be6d7b 1921
3f38a55e 1922 http->flags.accel = 1;
c8be6d7b 1923
3f38a55e 1924 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
c8be6d7b 1925
34399323 1926 if (strncasecmp(url, "cache_object://", 15) == 0)
1927 return; /* already in good shape */
1928
3f38a55e 1929 if (*url != '/') {
62e76326 1930 if (conn->port->vhost)
1931 return; /* already in good shape */
1932
1933 /* else we need to ignore the host name */
1934 url = strstr(url, "//");
1935
3f38a55e 1936#if SHOULD_REJECT_UNKNOWN_URLS
62e76326 1937
719c7e0a
AJ
1938 if (!url) {
1939 hp->request_parse_status = HTTP_BAD_REQUEST;
62e76326 1940 return parseHttpRequestAbort(conn, "error:invalid-request");
719c7e0a 1941 }
c8be6d7b 1942#endif
62e76326 1943
1944 if (url)
1945 url = strchr(url + 2, '/');
1946
1947 if (!url)
1948 url = (char *) "/";
3f38a55e 1949 }
1950
1951 if (internalCheck(url)) {
62e76326 1952 /* prepend our name & port */
1953 http->uri = xstrdup(internalLocalUri(NULL, url));
ae7ff0b8 1954 return;
1955 }
1956
1957 const bool switchedToHttps = conn->switchedToHttps();
1958 const bool tryHostHeader = vhost || switchedToHttps;
1959 if (tryHostHeader && (host = mime_get_header(req_hdr, "Host")) != NULL) {
62e76326 1960 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1961 strlen(host);
1962 http->uri = (char *)xcalloc(url_sz, 1);
ae7ff0b8 1963 const char *protocol = switchedToHttps ?
9e008dda 1964 "https" : conn->port->protocol;
ae7ff0b8 1965 snprintf(http->uri, url_sz, "%s://%s%s", protocol, host, url);
bf8fe701 1966 debugs(33, 5, "ACCEL VHOST REWRITE: '" << http->uri << "'");
3f38a55e 1967 } else if (conn->port->defaultsite) {
62e76326 1968 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1969 strlen(conn->port->defaultsite);
1970 http->uri = (char *)xcalloc(url_sz, 1);
1971 snprintf(http->uri, url_sz, "%s://%s%s",
1972 conn->port->protocol, conn->port->defaultsite, url);
bf8fe701 1973 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: '" << http->uri <<"'");
3f38a55e 1974 } else if (vport == -1) {
62e76326 1975 /* Put the local socket IP address as the hostname. */
1976 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1977 http->uri = (char *)xcalloc(url_sz, 1);
1978 snprintf(http->uri, url_sz, "%s://%s:%d%s",
98242069 1979 http->getConn()->port->protocol,
cc192b50 1980 http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN),
1981 http->getConn()->me.GetPort(), url);
bf8fe701 1982 debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
3f38a55e 1983 } else if (vport > 0) {
62e76326 1984 /* Put the local socket IP address as the hostname, but static port */
1985 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1986 http->uri = (char *)xcalloc(url_sz, 1);
1987 snprintf(http->uri, url_sz, "%s://%s:%d%s",
98242069 1988 http->getConn()->port->protocol,
cc192b50 1989 http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN),
62e76326 1990 vport, url);
bf8fe701 1991 debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
3f38a55e 1992 }
1993}
1994
1995static void
1cf238db 1996prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
62e76326 1997{
3f38a55e 1998 char *host;
cc192b50 1999 char ntoabuf[MAX_IPSTRLEN];
3f38a55e 2000
3f38a55e 2001 if (*url != '/')
62e76326 2002 return; /* already in good shape */
3f38a55e 2003
2004 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
2005
f024c970 2006 if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
62e76326 2007 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
2008 strlen(host);
2009 http->uri = (char *)xcalloc(url_sz, 1);
2010 snprintf(http->uri, url_sz, "%s://%s%s",
2011 conn->port->protocol, host, url);
bf8fe701 2012 debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
c8be6d7b 2013 } else {
62e76326 2014 /* Put the local socket IP address as the hostname. */
2015 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
2016 http->uri = (char *)xcalloc(url_sz, 1);
2017 snprintf(http->uri, url_sz, "%s://%s:%d%s",
98242069 2018 http->getConn()->port->protocol,
cc192b50 2019 http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN),
2020 http->getConn()->me.GetPort(), url);
bf8fe701 2021 debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
c8be6d7b 2022 }
c8be6d7b 2023}
2024
63be0a78 2025/**
7a2f978b 2026 * parseHttpRequest()
9e008dda 2027 *
7a2f978b 2028 * Returns
c4b7a5a9 2029 * NULL on incomplete requests
528b2c61 2030 * a ClientSocketContext structure on success or failure.
c4b7a5a9 2031 * Sets result->flags.parsed_ok to 0 if failed to parse the request.
2032 * Sets result->flags.parsed_ok to 1 if we have a good request.
7a2f978b 2033 */
528b2c61 2034static ClientSocketContext *
1cf238db 2035parseHttpRequest(ConnStateData *conn, HttpParser *hp, HttpRequestMethod * method_p, HttpVersion *http_ver)
7a2f978b 2036{
7a2f978b 2037 char *req_hdr = NULL;
2334c194 2038 char *end;
c68e9c6b 2039 size_t req_sz;
59a1efb2 2040 ClientHttpRequest *http;
528b2c61 2041 ClientSocketContext *result;
2042 StoreIOBuffer tempBuffer;
84cc2635 2043 int r;
7a2f978b 2044
6792f0d3 2045 /* pre-set these values to make aborting simpler */
6792f0d3 2046 *method_p = METHOD_NONE;
6792f0d3 2047
7ed7d3da
AJ
2048 /* NP: don't be tempted to move this down or remove again.
2049 * It's the only DDoS protection old-String has against long URL */
2050 if ( hp->bufsiz <= 0) {
2051 debugs(33, 5, "Incomplete request, waiting for end of request line");
2052 return NULL;
e1381638 2053 } else if ( (size_t)hp->bufsiz >= Config.maxRequestHeaderSize && headersEnd(hp->buf, Config.maxRequestHeaderSize) == 0) {
7ed7d3da 2054 debugs(33, 5, "parseHttpRequest: Too large request");
719c7e0a 2055 hp->request_parse_status = HTTP_HEADER_TOO_LARGE;
7ed7d3da
AJ
2056 return parseHttpRequestAbort(conn, "error:request-too-large");
2057 }
2058
84cc2635 2059 /* Attempt to parse the first line; this'll define the method, url, version and header begin */
2060 r = HttpParserParseReqLine(hp);
fc68f6b1 2061
84cc2635 2062 if (r == 0) {
bf8fe701 2063 debugs(33, 5, "Incomplete request, waiting for end of request line");
fc68f6b1 2064 return NULL;
7a2f978b 2065 }
fc68f6b1 2066
84cc2635 2067 if (r == -1) {
2068 return parseHttpRequestAbort(conn, "error:invalid-request");
2069 }
fc68f6b1 2070
84cc2635 2071 /* Request line is valid here .. */
2072 *http_ver = HttpVersion(hp->v_maj, hp->v_min);
62e76326 2073
52512f28 2074 /* This call scans the entire request, not just the headers */
84cc2635 2075 if (hp->v_maj > 0) {
a5baffba 2076 if ((req_sz = headersEnd(hp->buf, hp->bufsiz)) == 0) {
bf8fe701 2077 debugs(33, 5, "Incomplete request, waiting for end of headers");
62e76326 2078 return NULL;
2079 }
3f38a55e 2080 } else {
bf8fe701 2081 debugs(33, 3, "parseHttpRequest: Missing HTTP identifier");
84cc2635 2082 req_sz = HttpParserReqSz(hp);
3f38a55e 2083 }
2084
bb790702 2085 /* We know the whole request is in hp->buf now */
52512f28 2086
a5baffba 2087 assert(req_sz <= (size_t) hp->bufsiz);
fc68f6b1 2088
a5baffba 2089 /* Will the following be true with HTTP/0.9 requests? probably not .. */
2090 /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */
2091 assert(req_sz > 0);
fc68f6b1 2092
a5baffba 2093 hp->hdr_end = req_sz - 1;
fc68f6b1 2094
a5baffba 2095 hp->hdr_start = hp->req_end + 1;
3f38a55e 2096
5b648f60 2097 /* Enforce max_request_size */
5b648f60 2098 if (req_sz >= Config.maxRequestHeaderSize) {
bf8fe701 2099 debugs(33, 5, "parseHttpRequest: Too large request");
719c7e0a 2100 hp->request_parse_status = HTTP_HEADER_TOO_LARGE;
5b648f60 2101 return parseHttpRequestAbort(conn, "error:request-too-large");
2102 }
2103
84cc2635 2104 /* Set method_p */
60745f24 2105 *method_p = HttpRequestMethod(&hp->buf[hp->m_start], &hp->buf[hp->m_end]+1);
fc68f6b1 2106
adf29627
AJ
2107 /* deny CONNECT via accelerated ports */
2108 if (*method_p == METHOD_CONNECT && conn && conn->port && conn->port->accel) {
2109 debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << conn->port->protocol << " Accelerator port " << conn->port->s.GetPort() );
2110 /* XXX need a way to say "this many character length string" */
2111 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->buf);
719c7e0a 2112 hp->request_parse_status = HTTP_METHOD_NOT_ALLOWED;
adf29627
AJ
2113 return parseHttpRequestAbort(conn, "error:method-not-allowed");
2114 }
2115
84cc2635 2116 if (*method_p == METHOD_NONE) {
fc68f6b1 2117 /* XXX need a way to say "this many character length string" */
bf8fe701 2118 debugs(33, 1, "clientParseRequestMethod: Unsupported method in request '" << hp->buf << "'");
719c7e0a 2119 hp->request_parse_status = HTTP_METHOD_NOT_ALLOWED;
84cc2635 2120 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
3f38a55e 2121 }
7a2f978b 2122
c68e9c6b 2123 /*
2124 * Process headers after request line
c8be6d7b 2125 * TODO: Use httpRequestParse here.
c68e9c6b 2126 */
84cc2635 2127 /* XXX this code should be modified to take a const char * later! */
2128 req_hdr = (char *) hp->buf + hp->req_end + 1;
fc68f6b1 2129
bf8fe701 2130 debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr << "}");
fc68f6b1 2131
52512f28 2132 end = (char *) hp->buf + hp->hdr_end;
fc68f6b1 2133
bf8fe701 2134 debugs(33, 3, "parseHttpRequest: end = {" << end << "}");
99edd1c3 2135
bf8fe701 2136 debugs(33, 3, "parseHttpRequest: prefix_sz = " <<
2137 (int) HttpParserRequestLen(hp) << ", req_line_sz = " <<
2138 HttpParserReqSz(hp));
62e76326 2139
7a2f978b 2140 /* Ok, all headers are received */
a0355e95 2141 http = new ClientHttpRequest(conn);
62e76326 2142
a5baffba 2143 http->req_sz = HttpParserRequestLen(hp);
528b2c61 2144 result = ClientSocketContextNew(http);
c8be6d7b 2145 tempBuffer.data = result->reqbuf;
2146 tempBuffer.length = HTTP_REQBUF_SZ;
62e76326 2147
0655fa4d 2148 ClientStreamData newServer = new clientReplyContext(http);
0655fa4d 2149 ClientStreamData newClient = result;
edce4d98 2150 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 2151 clientReplyStatus, newServer, clientSocketRecipient,
2152 clientSocketDetach, newClient, tempBuffer);
62e76326 2153
bf8fe701 2154 debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp->buf) + hp->hdr_start);
3f38a55e 2155
3ff65596
AR
2156 /* set url */
2157 /*
2158 * XXX this should eventually not use a malloc'ed buffer; the transformation code
2159 * below needs to be modified to not expect a mutable nul-terminated string.
2160 */
2161 char *url = (char *)xmalloc(hp->u_end - hp->u_start + 16);
2162
2163 memcpy(url, hp->buf + hp->u_start, hp->u_end - hp->u_start + 1);
2164
2165 url[hp->u_end - hp->u_start + 1] = '\0';
2166
ba9ebd0a 2167#if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
62e76326 2168
7a2f978b 2169 if ((t = strchr(url, '#'))) /* remove HTML anchors */
62e76326 2170 *t = '\0';
2171
ba9ebd0a 2172#endif
7a2f978b 2173
3f38a55e 2174 /* Rewrite the URL in transparent or accelerator mode */
89272111
AJ
2175 /* NP: there are several cases to traverse here:
2176 * - standard mode (forward proxy)
2177 * - transparent mode (TPROXY)
2178 * - transparent mode with failures
2179 * - intercept mode (NAT)
2180 * - intercept mode with failures
2181 * - accelerator mode (reverse proxy)
2182 * - internal URL
2183 * - mixed combos of the above with internal URL
2184 */
a46d2c0e 2185 if (conn->transparent()) {
89272111 2186 /* intercept or transparent mode, properly working with no failures */
891de1fe
AJ
2187 http->flags.intercepted = conn->port->intercepted;
2188 http->flags.spoof_client_ip = conn->port->spoof_client_ip;
89272111
AJ
2189 prepareTransparentURL(conn, http, url, req_hdr);
2190
2191 } else if (conn->port->intercepted || conn->port->spoof_client_ip) {
2192 /* transparent or intercept mode with failures */
62e76326 2193 prepareTransparentURL(conn, http, url, req_hdr);
89272111 2194
ae7ff0b8 2195 } else if (conn->port->accel || conn->switchedToHttps()) {
89272111 2196 /* accelerator mode */
62e76326 2197 prepareAcceleratedURL(conn, http, url, req_hdr);
89272111 2198
2f2749d7 2199 } else if (internalCheck(url)) {
89272111 2200 /* internal URL mode */
2f2749d7 2201 /* prepend our name & port */
2202 http->uri = xstrdup(internalLocalUri(NULL, url));
2f2749d7 2203 http->flags.accel = 1;
3f38a55e 2204 }
2205
2206 if (!http->uri) {
62e76326 2207 /* No special rewrites have been applied above, use the
2208 * requested url. may be rewritten later, so make extra room */
2209 int url_sz = strlen(url) + Config.appendDomainLen + 5;
2210 http->uri = (char *)xcalloc(url_sz, 1);
2211 strcpy(http->uri, url);
3f38a55e 2212 }
62e76326 2213
c8be6d7b 2214 setLogUri(http, http->uri);
bf8fe701 2215 debugs(33, 5, "parseHttpRequest: Complete request received");
c4b7a5a9 2216 result->flags.parsed_ok = 1;
84cc2635 2217 xfree(url);
c8be6d7b 2218 return result;
7a2f978b 2219}
2220
c8be6d7b 2221int
a46d2c0e 2222ConnStateData::getAvailableBufferLength() const
c8be6d7b 2223{
1a419b96 2224 int result = in.allocatedSize - in.notYetUsed - 1;
2225 assert (result >= 0);
2226 return result;
c8be6d7b 2227}
2228
2229void
a46d2c0e 2230ConnStateData::makeSpaceAvailable()
c8be6d7b 2231{
a46d2c0e 2232 if (getAvailableBufferLength() < 2) {
2233 in.buf = (char *)memReallocBuf(in.buf, in.allocatedSize * 2, &in.allocatedSize);
4a7a3d56 2234 debugs(33, 2, "growing request buffer: notYetUsed=" << in.notYetUsed << " size=" << in.allocatedSize);
c8be6d7b 2235 }
2236}
2237
2238void
0655fa4d 2239ConnStateData::addContextToQueue(ClientSocketContext * context)
c8be6d7b 2240{
0655fa4d 2241 ClientSocketContext::Pointer *S;
62e76326 2242
0655fa4d 2243 for (S = (ClientSocketContext::Pointer *) & currentobject; S->getRaw();
3d0ac046 2244 S = &(*S)->next);
c8be6d7b 2245 *S = context;
62e76326 2246
0655fa4d 2247 ++nrequests;
c8be6d7b 2248}
2249
2250int
0655fa4d 2251ConnStateData::getConcurrentRequestCount() const
c8be6d7b 2252{
2253 int result = 0;
0655fa4d 2254 ClientSocketContext::Pointer *T;
62e76326 2255
0655fa4d 2256 for (T = (ClientSocketContext::Pointer *) &currentobject;
3d0ac046 2257 T->getRaw(); T = &(*T)->next, ++result);
c8be6d7b 2258 return result;
2259}
2260
2261int
1cf238db 2262ConnStateData::connReadWasError(comm_err_t flag, int size, int xerrno)
c8be6d7b 2263{
c4b7a5a9 2264 if (flag != COMM_OK) {
1cf238db 2265 debugs(33, 2, "connReadWasError: FD " << fd << ": got flag " << flag);
62e76326 2266 return 1;
c4b7a5a9 2267 }
62e76326 2268
c8be6d7b 2269 if (size < 0) {
f3400a93 2270 if (!ignoreErrno(xerrno)) {
1cf238db 2271 debugs(33, 2, "connReadWasError: FD " << fd << ": " << xstrerr(xerrno));
62e76326 2272 return 1;
1cf238db 2273 } else if (in.notYetUsed == 0) {
2274 debugs(33, 2, "connReadWasError: FD " << fd << ": no data to process (" << xstrerr(xerrno) << ")");
62e76326 2275 }
c8be6d7b 2276 }
62e76326 2277
c8be6d7b 2278 return 0;
2279}
2280
2281int
1cf238db 2282ConnStateData::connFinishedWithConn(int size)
c8be6d7b 2283{
2284 if (size == 0) {
1cf238db 2285 if (getConcurrentRequestCount() == 0 && in.notYetUsed == 0) {
62e76326 2286 /* no current or pending requests */
1cf238db 2287 debugs(33, 4, "connFinishedWithConn: FD " << fd << " closed");
62e76326 2288 return 1;
2289 } else if (!Config.onoff.half_closed_clients) {
2290 /* admin doesn't want to support half-closed client sockets */
1cf238db 2291 debugs(33, 3, "connFinishedWithConn: FD " << fd << " aborted (half_closed_clients disabled)");
f692498b 2292 notifyAllContexts(0); // no specific error implies abort
62e76326 2293 return 1;
2294 }
c8be6d7b 2295 }
62e76326 2296
c8be6d7b 2297 return 0;
2298}
2299
2300void
3b299123 2301connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
c8be6d7b 2302{
2303 assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
2304 conn->in.notYetUsed -= byteCount;
d5fa7219 2305 debugs(33, 5, HERE << "conn->in.notYetUsed = " << conn->in.notYetUsed);
c8be6d7b 2306 /*
9e008dda 2307 * If there is still data that will be used,
c8be6d7b 2308 * move it to the beginning.
2309 */
62e76326 2310
c8be6d7b 2311 if (conn->in.notYetUsed > 0)
62e76326 2312 xmemmove(conn->in.buf, conn->in.buf + byteCount,
2313 conn->in.notYetUsed);
c8be6d7b 2314}
2315
39cb8c41
AR
2316/// respond with ERR_TOO_BIG if request header exceeds request_header_max_size
2317void
2318ConnStateData::checkHeaderLimits()
c8be6d7b 2319{
39cb8c41
AR
2320 if (in.notYetUsed < Config.maxRequestHeaderSize)
2321 return; // can accumulte more header data
3ff65596 2322
39cb8c41 2323 debugs(33, 3, "Request header is too large (" << in.notYetUsed << " > " <<
de48b288 2324 Config.maxRequestHeaderSize << " bytes)");
c8be6d7b 2325
39cb8c41 2326 ClientSocketContext *context = parseHttpRequestAbort(this, "error:request-too-large");
528b2c61 2327 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2328 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2329 assert (repContext);
2330 repContext->setReplyToError(ERR_TOO_BIG,
39cb8c41
AR
2331 HTTP_BAD_REQUEST, METHOD_NONE, NULL,
2332 peer, NULL, NULL, NULL);
0655fa4d 2333 context->registerWithConn();
528b2c61 2334 context->pullData();
c8be6d7b 2335}
2336
1cf238db 2337void
2338ConnStateData::clientMaybeReadData(int do_next_read)
7a2f978b 2339{
c4b7a5a9 2340 if (do_next_read) {
1cf238db 2341 flags.readMoreRequests = true;
2342 readSomeData();
c4b7a5a9 2343 }
2344}
2345
1cf238db 2346void
2347ConnStateData::clientAfterReadingRequests(int do_next_read)
c4b7a5a9 2348{
39cb8c41
AR
2349 // Were we expecting to read more request body from half-closed connection?
2350 if (mayNeedToReadMoreBody() && commIsHalfClosed(fd)) {
2351 debugs(33, 3, HERE << "truncated body: closing half-closed FD " << fd);
2352 comm_close(fd);
2353 return;
c4b7a5a9 2354 }
2355
1cf238db 2356 clientMaybeReadData (do_next_read);
c4b7a5a9 2357}
2358
c4b7a5a9 2359static void
1cf238db 2360clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, HttpVersion http_ver)
c4b7a5a9 2361{
59a1efb2 2362 ClientHttpRequest *http = context->http;
190154cf 2363 HttpRequest *request = NULL;
5f8252d2 2364 bool notedUseOfBuffer = false;
39cb8c41 2365 bool chunked = false;
e18b8316 2366 bool mustReplyToOptions = false;
3ff65596 2367 bool unsupportedTe = false;
39cb8c41 2368 bool expectBody = false;
5f8252d2 2369
c4b7a5a9 2370 /* We have an initial client stream in place should it be needed */
2371 /* setup our private context */
0655fa4d 2372 context->registerWithConn();
c4b7a5a9 2373
2374 if (context->flags.parsed_ok == 0) {
62e76326 2375 clientStreamNode *node = context->getClientReplyContext();
bf8fe701 2376 debugs(33, 1, "clientProcessRequest: Invalid Request");
0655fa4d 2377 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2378 assert (repContext);
e212e5e3 2379 switch (hp->request_parse_status) {
719c7e0a 2380 case HTTP_HEADER_TOO_LARGE:
b5bd8515 2381 repContext->setReplyToError(ERR_TOO_BIG, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
719c7e0a
AJ
2382 break;
2383 case HTTP_METHOD_NOT_ALLOWED:
2384 repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_METHOD_NOT_ALLOWED, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
2385 break;
2386 default:
2387 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
2388 }
62e76326 2389 assert(context->http->out.offset == 0);
2390 context->pullData();
48962ba8 2391 conn->flags.readMoreRequests = false;
fc68f6b1 2392 goto finish;
c4b7a5a9 2393 }
2394
c21ad0f5 2395 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
62e76326 2396 clientStreamNode *node = context->getClientReplyContext();
bf8fe701 2397 debugs(33, 5, "Invalid URL: " << http->uri);
0655fa4d 2398 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2399 assert (repContext);
cc192b50 2400 repContext->setReplyToError(ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, NULL, NULL);
62e76326 2401 assert(context->http->out.offset == 0);
2402 context->pullData();
48962ba8 2403 conn->flags.readMoreRequests = false;
fc68f6b1 2404 goto finish;
62e76326 2405 }
c4b7a5a9 2406
e57c1885
AJ
2407 /* RFC 2616 section 10.5.6 : handle unsupported HTTP versions cleanly. */
2408 /* We currently only accept 0.9, 1.0, 1.1 */
2409 if ( (http_ver.major == 0 && http_ver.minor != 9) ||
af6a12ee
AJ
2410 (http_ver.major == 1 && http_ver.minor > 1 ) ||
2411 (http_ver.major > 1) ) {
e57c1885
AJ
2412
2413 clientStreamNode *node = context->getClientReplyContext();
2414 debugs(33, 5, "Unsupported HTTP version discovered. :\n" << HttpParserHdrBuf(hp));
2415 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2416 assert (repContext);
2417 repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, HTTP_HTTP_VERSION_NOT_SUPPORTED, method, http->uri, conn->peer, NULL, HttpParserHdrBuf(hp), NULL);
2418 assert(context->http->out.offset == 0);
2419 context->pullData();
2420 conn->flags.readMoreRequests = false;
2421 goto finish;
2422 }
2423
528b2c61 2424 /* compile headers */
2425 /* we should skip request line! */
666f514b 2426 /* XXX should actually know the damned buffer size here */
38e82382 2427 if (http_ver.major >= 1 && !request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) {
47ac2ebe 2428 clientStreamNode *node = context->getClientReplyContext();
bf8fe701 2429 debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp));
47ac2ebe 2430 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2431 assert (repContext);
38e82382 2432 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, NULL, NULL);
47ac2ebe 2433 assert(context->http->out.offset == 0);
2434 context->pullData();
48962ba8 2435 conn->flags.readMoreRequests = false;
fc68f6b1 2436 goto finish;
47ac2ebe 2437 }
c4b7a5a9 2438
2439 request->flags.accelerated = http->flags.accel;
432bc83c 2440 request->flags.ignore_cc = conn->port->ignore_cc;
7f7bdd96 2441 request->flags.no_direct = request->flags.accelerated ? !conn->port->allow_direct : 0;
2ad20b4f
AJ
2442
2443 /** \par
2444 * If transparent or interception mode is working clone the transparent and interception flags
2445 * from the port settings to the request.
2446 */
b7ac5457 2447 if (Ip::Interceptor.InterceptActive()) {
2ad20b4f
AJ
2448 request->flags.intercepted = http->flags.intercepted;
2449 }
b7ac5457 2450 if (Ip::Interceptor.TransparentActive()) {
f165d2fb 2451 request->flags.spoof_client_ip = conn->port->spoof_client_ip;
2ad20b4f 2452 }
fc68f6b1 2453
5b4117d8 2454 if (internalCheck(request->urlpath.termedBuf())) {
cc192b50 2455 if (internalHostnameIs(request->GetHost()) &&
f024c970 2456 request->port == getMyPort()) {
2457 http->flags.internal = 1;
9c175897 2458 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.termedBuf())) {
cc192b50 2459 request->SetHost(internalHostname());
f024c970 2460 request->port = getMyPort();
2461 http->flags.internal = 1;
62e76326 2462 }
f024c970 2463 }
e72a0ec0 2464
f024c970 2465 if (http->flags.internal) {
2466 request->protocol = PROTO_HTTP;
2467 request->login[0] = '\0';
c4b7a5a9 2468 }
2469
c4b7a5a9 2470 request->flags.internal = http->flags.internal;
2471 setLogUri (http, urlCanonicalClean(request));
cc192b50 2472 request->client_addr = conn->peer;
a98c2da5
AJ
2473#if USE_SQUID_EUI
2474 request->client_eui48 = conn->peer_eui48;
2475 request->client_eui64 = conn->peer_eui64;
2476#endif
3d674977
AJ
2477#if FOLLOW_X_FORWARDED_FOR
2478 request->indirect_client_addr = conn->peer;
2479#endif /* FOLLOW_X_FORWARDED_FOR */
cc192b50 2480 request->my_addr = conn->me;
8ae66e43 2481 request->http_ver = http_ver;
62e76326 2482
39cb8c41
AR
2483 if (request->header.chunked()) {
2484 chunked = true;
2485 } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
2486 const String te = request->header.getList(HDR_TRANSFER_ENCODING);
2487 // HTTP/1.1 requires chunking to be the last encoding if there is one
2488 unsupportedTe = te.size() && te != "identity";
2489 } // else implied identity coding
de48b288 2490
e18b8316
AR
2491 if (method == METHOD_TRACE || method == METHOD_OPTIONS)
2492 request->max_forwards = request->header.getInt64(HDR_MAX_FORWARDS);
2493
2494 mustReplyToOptions = (method == METHOD_OPTIONS) && (request->max_forwards == 0);
e18b8316 2495 if (!urlCheckRequest(request) || mustReplyToOptions || unsupportedTe) {
62e76326 2496 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2497 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2498 assert (repContext);
2499 repContext->setReplyToError(ERR_UNSUP_REQ,
2500 HTTP_NOT_IMPLEMENTED, request->method, NULL,
cc192b50 2501 conn->peer, request, NULL, NULL);
62e76326 2502 assert(context->http->out.offset == 0);
2503 context->pullData();
48962ba8 2504 conn->flags.readMoreRequests = false;
fc68f6b1 2505 goto finish;
c4b7a5a9 2506 }
2507
2508
39cb8c41 2509 if (!chunked && !clientIsContentLengthValid(request)) {
62e76326 2510 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2511 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2512 assert (repContext);
2513 repContext->setReplyToError(ERR_INVALID_REQ,
2514 HTTP_LENGTH_REQUIRED, request->method, NULL,
cc192b50 2515 conn->peer, request, NULL, NULL);
62e76326 2516 assert(context->http->out.offset == 0);
2517 context->pullData();
48962ba8 2518 conn->flags.readMoreRequests = false;
fc68f6b1 2519 goto finish;
c4b7a5a9 2520 }
2521
52b601ff 2522 if (request->header.has(HDR_EXPECT)) {
655daa06
AR
2523 const String expect = request->header.getList(HDR_EXPECT);
2524 const bool supportedExpect = (expect.caseCmp("100-continue") == 0);
2525 if (!supportedExpect) {
52b601ff
AJ
2526 clientStreamNode *node = context->getClientReplyContext();
2527 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2528 assert (repContext);
2529 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_EXPECTATION_FAILED, request->method, http->uri, conn->peer, request, NULL, NULL);
2530 assert(context->http->out.offset == 0);
2531 context->pullData();
2532 goto finish;
2533 }
2534 }
2535
6dd9f4bd 2536 http->request = HTTPMSGLOCK(request);
c4b7a5a9 2537 clientSetKeepaliveFlag(http);
62e76326 2538
b66e0e86 2539 /* If this is a CONNECT, don't schedule a read - ssl.c will handle it */
2540 if (http->request->method == METHOD_CONNECT)
2541 context->mayUseConnection(true);
fc68f6b1 2542
b66e0e86 2543 /* Do we expect a request-body? */
39cb8c41
AR
2544 expectBody = chunked || request->content_length > 0;
2545 if (!context->mayUseConnection() && expectBody) {
2546 request->body_pipe = conn->expectRequestBody(
de48b288 2547 chunked ? -1 : request->content_length);
5f8252d2 2548
2549 // consume header early so that body pipe gets just the body
1cf238db 2550 connNoteUseOfBuffer(conn, http->req_sz);
5f8252d2 2551 notedUseOfBuffer = true;
2552
62e76326 2553 /* Is it too large? */
39cb8c41 2554 if (!chunked && // if chunked, we will check as we accumulate
de48b288 2555 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
62e76326 2556 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2557 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2558 assert (repContext);
2559 repContext->setReplyToError(ERR_TOO_BIG,
2560 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
cc192b50 2561 conn->peer, http->request, NULL, NULL);
62e76326 2562 assert(context->http->out.offset == 0);
2563 context->pullData();
5f8252d2 2564 goto finish;
62e76326 2565 }
2566
39cb8c41
AR
2567 // We may stop producing, comm_close, and/or call setReplyToError()
2568 // below, so quit on errors to avoid http->doCallouts()
2569 if (!conn->handleRequestBodyData())
de48b288 2570 goto finish;
39cb8c41 2571
10b06767
AJ
2572 if (!request->body_pipe->productionEnded())
2573 conn->readSomeData();
2574
2575 context->mayUseConnection(!request->body_pipe->productionEnded());
c4b7a5a9 2576 }
2577
de31d06f 2578 http->calloutContext = new ClientRequestContext(http);
2579
2580 http->doCallouts();
9e008dda 2581
4c29340e 2582finish:
5f8252d2 2583 if (!notedUseOfBuffer)
1cf238db 2584 connNoteUseOfBuffer(conn, http->req_sz);
52c2c8a8 2585
2586 /*
2587 * DPW 2007-05-18
2588 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
2589 * to here because calling comm_reset_close() causes http to
2590 * be freed and the above connNoteUseOfBuffer() would hit an
2591 * assertion, not to mention that we were accessing freed memory.
2592 */
2593 if (http->request->flags.resetTCP() && conn->fd > -1) {
9e008dda
AJ
2594 debugs(33, 3, HERE << "Sending TCP RST on FD " << conn->fd);
2595 conn->flags.readMoreRequests = false;
2596 comm_reset_close(conn->fd);
2597 return;
52c2c8a8 2598 }
c4b7a5a9 2599}
2600
2601static void
1cf238db 2602connStripBufferWhitespace (ConnStateData * conn)
c4b7a5a9 2603{
2604 while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
62e76326 2605 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
2606 --conn->in.notYetUsed;
c4b7a5a9 2607 }
2608}
2609
2610static int
1cf238db 2611connOkToAddRequest(ConnStateData * conn)
c4b7a5a9 2612{
0655fa4d 2613 int result = conn->getConcurrentRequestCount() < (Config.onoff.pipeline_prefetch ? 2 : 1);
62e76326 2614
c4b7a5a9 2615 if (!result) {
bf8fe701 2616 debugs(33, 3, "connOkToAddRequest: FD " << conn->fd <<
2617 " max concurrent requests reached");
2618 debugs(33, 5, "connOkToAddRequest: FD " << conn->fd <<
2619 " defering new request until one is done");
c4b7a5a9 2620 }
62e76326 2621
c4b7a5a9 2622 return result;
2623}
2624
63be0a78 2625/**
f900210a 2626 * Attempt to parse one or more requests from the input buffer.
2627 * If a request is successfully parsed, even if the next request
2628 * is only partially parsed, it will return TRUE.
2629 * do_next_read is updated to indicate whether a read should be
2630 * scheduled.
2631 */
2632static bool
1cf238db 2633clientParseRequest(ConnStateData * conn, bool &do_next_read)
f900210a 2634{
60745f24 2635 HttpRequestMethod method;
f900210a 2636 ClientSocketContext *context;
2637 bool parsed_req = false;
8ae66e43 2638 HttpVersion http_ver;
a5baffba 2639 HttpParser hp;
f900210a 2640
bf8fe701 2641 debugs(33, 5, "clientParseRequest: FD " << conn->fd << ": attempting to parse");
f900210a 2642
39cb8c41
AR
2643 // Loop while we have read bytes that are not needed for producing the body
2644 // On errors, bodyPipe may become nil, but readMoreRequests will be cleared
2645 while (conn->in.notYetUsed > 0 && !conn->bodyPipe &&
de48b288 2646 conn->flags.readMoreRequests) {
f900210a 2647 connStripBufferWhitespace (conn);
2648
fc68f6b1 2649 /* Don't try to parse if the buffer is empty */
2650
2651 if (conn->in.notYetUsed == 0)
2652 break;
4681057e 2653
f900210a 2654 /* Limit the number of concurrent requests to 2 */
2655
2656 if (!connOkToAddRequest(conn)) {
2657 break;
2658 }
2659
2660 /* Should not be needed anymore */
2661 /* Terminate the string */
2662 conn->in.buf[conn->in.notYetUsed] = '\0';
2663
fc68f6b1 2664 /* Begin the parsing */
2665 HttpParserInit(&hp, conn->in.buf, conn->in.notYetUsed);
a5baffba 2666
f900210a 2667 /* Process request */
fc68f6b1 2668 PROF_start(parseHttpRequest);
2669
a5baffba 2670 context = parseHttpRequest(conn, &hp, &method, &http_ver);
fc68f6b1 2671
2672 PROF_stop(parseHttpRequest);
f900210a 2673
2674 /* partial or incomplete request */
2675 if (!context) {
39cb8c41
AR
2676 // TODO: why parseHttpRequest can just return parseHttpRequestAbort
2677 // (which becomes context) but checkHeaderLimits cannot?
2678 conn->checkHeaderLimits();
f900210a 2679 break;
2680 }
2681
2682 /* status -1 or 1 */
2683 if (context) {
bf8fe701 2684 debugs(33, 5, "clientParseRequest: FD " << conn->fd << ": parsed a request");
f900210a 2685 commSetTimeout(conn->fd, Config.Timeout.lifetime, clientLifetimeTimeout,
2686 context->http);
2687
a5baffba 2688 clientProcessRequest(conn, &hp, context, method, http_ver);
f900210a 2689
f900210a 2690 parsed_req = true;
2691
2692 if (context->mayUseConnection()) {
bf8fe701 2693 debugs(33, 3, "clientParseRequest: Not reading, as this request may need the connection");
f900210a 2694 do_next_read = 0;
2695 break;
2696 }
f900210a 2697 }
39cb8c41 2698 }
fc68f6b1 2699
a5baffba 2700 /* XXX where to 'finish' the parsing pass? */
f900210a 2701
2702 return parsed_req;
2703}
2704
1cf238db 2705void
2706ConnStateData::clientReadRequest(const CommIoCbParams &io)
c4b7a5a9 2707{
1cf238db 2708 debugs(33,5,HERE << "clientReadRequest FD " << io.fd << " size " << io.size);
f84dd7eb
AR
2709 Must(reading());
2710 reader = NULL;
a46d2c0e 2711 bool do_next_read = 1; /* the default _is_ to read data! - adrian */
c4b7a5a9 2712
1cf238db 2713 assert (io.fd == fd);
c4b7a5a9 2714
2715 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
62e76326 2716
1cf238db 2717 if (io.flag == COMM_ERR_CLOSING) {
cc192b50 2718 debugs(33,5, HERE << " FD " << fd << " closing Bailout.");
c4b7a5a9 2719 return;
2720 }
62e76326 2721
44db45e8 2722 /*
2723 * Don't reset the timeout value here. The timeout value will be
2724 * set to Config.Timeout.request by httpAccept() and
2725 * clientWriteComplete(), and should apply to the request as a
2726 * whole, not individual read() calls. Plus, it breaks our
2727 * lame half-close detection
2728 */
1cf238db 2729 if (connReadWasError(io.flag, io.size, io.xerrno)) {
f692498b 2730 notifyAllContexts(io.xerrno);
62e76326 2731 comm_close(fd);
2732 return;
7a2f978b 2733 }
c4b7a5a9 2734
1cf238db 2735 if (io.flag == COMM_OK) {
2736 if (io.size > 0) {
2737 kb_incr(&statCounter.client_http.kbytes_in, io.size);
3b299123 2738
39cb8c41
AR
2739 // may comm_close or setReplyToError
2740 if (!handleReadData(io.buf, io.size))
9e008dda 2741 return;
a31a78fb 2742
1cf238db 2743 } else if (io.size == 0) {
bf8fe701 2744 debugs(33, 5, "clientReadRequest: FD " << fd << " closed?");
62e76326 2745
1cf238db 2746 if (connFinishedWithConn(io.size)) {
62e76326 2747 comm_close(fd);
2748 return;
2749 }
2750
2751 /* It might be half-closed, we can't tell */
2752 fd_table[fd].flags.socket_eof = 1;
2753
a46d2c0e 2754 commMarkHalfClosed(fd);
2755
2756 do_next_read = 0;
62e76326 2757
2758 fd_note(fd, "half-closed");
2759
2760 /* There is one more close check at the end, to detect aborted
2761 * (partial) requests. At this point we can't tell if the request
2762 * is partial.
2763 */
2764 /* Continue to process previously read data */
2765 }
c4b7a5a9 2766 }
2767
94439e4e 2768 /* Process next request */
1cf238db 2769 if (getConcurrentRequestCount() == 0)
2770 fd_note(fd, "Reading next request");
c8be6d7b 2771
1cf238db 2772 if (! clientParseRequest(this, do_next_read)) {
9e008dda
AJ
2773 if (!isOpen())
2774 return;
f900210a 2775 /*
2776 * If the client here is half closed and we failed
2777 * to parse a request, close the connection.
2778 * The above check with connFinishedWithConn() only
2779 * succeeds _if_ the buffer is empty which it won't
2780 * be if we have an incomplete request.
6e1d409c 2781 * XXX: This duplicates ClientSocketContext::keepaliveNextRequest
f900210a 2782 */
1cf238db 2783 if (getConcurrentRequestCount() == 0 && commIsHalfClosed(fd)) {
bf8fe701 2784 debugs(33, 5, "clientReadRequest: FD " << fd << ": half-closed connection, no completed request parsed, connection closing.");
f900210a 2785 comm_close(fd);
ee6f0213 2786 return;
62e76326 2787 }
f900210a 2788 }
ee6f0213 2789
1cf238db 2790 if (!isOpen())
2e216b1d 2791 return;
2792
1cf238db 2793 clientAfterReadingRequests(do_next_read);
94439e4e 2794}
2795
63be0a78 2796/**
2797 * called when new request data has been read from the socket
39cb8c41
AR
2798 *
2799 * \retval false called comm_close or setReplyToError (the caller should bail)
2800 * \retval true we did not call comm_close or setReplyToError
63be0a78 2801 */
39cb8c41 2802bool
5f8252d2 2803ConnStateData::handleReadData(char *buf, size_t size)
94439e4e 2804{
5f8252d2 2805 char *current_buf = in.addressToReadInto();
62e76326 2806
5f8252d2 2807 if (buf != current_buf)
2808 xmemmove(current_buf, buf, size);
3b299123 2809
5f8252d2 2810 in.notYetUsed += size;
fc68f6b1 2811
5f8252d2 2812 in.buf[in.notYetUsed] = '\0'; /* Terminate the string */
3b299123 2813
5f8252d2 2814 // if we are reading a body, stuff data into the body pipe
2815 if (bodyPipe != NULL)
39cb8c41
AR
2816 return handleRequestBodyData();
2817 return true;
94439e4e 2818}
2819
63be0a78 2820/**
bb790702 2821 * called when new request body data has been buffered in in.buf
63be0a78 2822 * may close the connection if we were closing and piped everything out
39cb8c41
AR
2823 *
2824 * \retval false called comm_close or setReplyToError (the caller should bail)
2825 * \retval true we did not call comm_close or setReplyToError
63be0a78 2826 */
39cb8c41 2827bool
5f8252d2 2828ConnStateData::handleRequestBodyData()
94439e4e 2829{
5f8252d2 2830 assert(bodyPipe != NULL);
2831
e1381638 2832 size_t putSize = 0;
3ff65596 2833
39cb8c41
AR
2834 if (in.bodyParser) { // chunked encoding
2835 if (const err_type error = handleChunkedRequestBody(putSize)) {
2836 abortChunkedRequestBody(error);
2837 return false;
3ff65596 2838 }
39cb8c41 2839 } else { // identity encoding
3ff65596
AR
2840 debugs(33,5, HERE << "handling plain request body for FD " << fd);
2841 putSize = bodyPipe->putMoreData(in.buf, in.notYetUsed);
2842 if (!bodyPipe->mayNeedMoreData()) {
2843 // BodyPipe will clear us automagically when we produced everything
2844 bodyPipe = NULL;
2845 }
2846 }
2847
2848 if (putSize > 0)
2849 connNoteUseOfBuffer(this, putSize);
5f8252d2 2850
3ff65596 2851 if (!bodyPipe) {
5f8252d2 2852 debugs(33,5, HERE << "produced entire request body for FD " << fd);
62e76326 2853
5f8252d2 2854 if (closing()) {
2855 /* we've finished reading like good clients,
2856 * now do the close that initiateClose initiated.
5f8252d2 2857 */
2858 comm_close(fd);
39cb8c41
AR
2859 return false;
2860 }
2861 }
2862
2863 return true;
2864}
2865
2866/// parses available chunked encoded body bytes, checks size, returns errors
2867err_type
2868ConnStateData::handleChunkedRequestBody(size_t &putSize)
2869{
2870 debugs(33,7, HERE << "chunked from FD " << fd << ": " << in.notYetUsed);
2871
2872 try { // the parser will throw on errors
2873
2874 if (!in.notYetUsed) // nothing to do (MemBuf::init requires this check)
2875 return ERR_NONE;
2876
2877 MemBuf raw; // ChunkedCodingParser only works with MemBufs
2878 // add one because MemBuf will assert if it cannot 0-terminate
2879 raw.init(in.notYetUsed, in.notYetUsed+1);
2880 raw.append(in.buf, in.notYetUsed);
2881
2882 const mb_size_t wasContentSize = raw.contentSize();
2883 BodyPipeCheckout bpc(*bodyPipe);
2884 const bool parsed = in.bodyParser->parse(&raw, &bpc.buf);
2885 bpc.checkIn();
2886 putSize = wasContentSize - raw.contentSize();
2887
2888 // dechunk then check: the size limit applies to _dechunked_ content
2889 if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
2890 return ERR_TOO_BIG;
2891
2892 if (parsed) {
2893 finishDechunkingRequest(true);
2894 Must(!bodyPipe);
2895 return ERR_NONE; // nil bodyPipe implies body end for the caller
5f8252d2 2896 }
39cb8c41
AR
2897
2898 // if chunk parser needs data, then the body pipe must need it too
2899 Must(!in.bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
2900
2901 // if parser needs more space and we can consume nothing, we will stall
2902 Must(!in.bodyParser->needsMoreSpace() || bodyPipe->buf().hasContent());
2903 } catch (...) { // TODO: be more specific
2904 debugs(33, 3, HERE << "malformed chunks" << bodyPipe->status());
2905 return ERR_INVALID_REQ;
94439e4e 2906 }
39cb8c41
AR
2907
2908 debugs(33, 7, HERE << "need more chunked data" << *bodyPipe->status());
2909 return ERR_NONE;
2910}
2911
2912/// quit on errors related to chunked request body handling
2913void
2914ConnStateData::abortChunkedRequestBody(const err_type error)
2915{
2916 finishDechunkingRequest(false);
2917
2918 // XXX: The code below works if we fail during initial request parsing,
2919 // but if we fail when the server-side works already, the server may send
2920 // us its response too, causing various assertions. How to prevent that?
2921#if WE_KNOW_HOW_TO_SEND_ERRORS
2922 ClientSocketContext::Pointer context = getCurrentContext();
2923 if (context != NULL && !context->http->out.offset) { // output nothing yet
2924 clientStreamNode *node = context->getClientReplyContext();
2925 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
2926 assert(repContext);
2927 const http_status scode = (error == ERR_TOO_BIG) ?
de48b288 2928 HTTP_REQUEST_ENTITY_TOO_LARGE : HTTP_BAD_REQUEST;
39cb8c41
AR
2929 repContext->setReplyToError(error, scode,
2930 repContext->http->request->method,
2931 repContext->http->uri,
2932 peer,
2933 repContext->http->request,
2934 in.buf, NULL);
2935 context->pullData();
2936 } else {
2937 // close or otherwise we may get stuck as nobody will notice the error?
de48b288 2938 comm_reset_close(fd);
39cb8c41
AR
2939 }
2940#else
2941 debugs(33, 3, HERE << "aborting chunked request without error " << error);
2942 comm_reset_close(fd);
2943#endif
2944 flags.readMoreRequests = false;
5f8252d2 2945}
55e44db9 2946
5f8252d2 2947void
1cf238db 2948ConnStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer )
5f8252d2 2949{
2950 handleRequestBodyData();
2951}
2952
2953void
1cf238db 2954ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer )
5f8252d2 2955{
2956 if (!closing())
2957 startClosing("body consumer aborted");
94439e4e 2958}
2959
63be0a78 2960/** general lifetime handler for HTTP requests */
1cf238db 2961void
2962ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
7a2f978b 2963{
ad63ceea 2964#if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
1cf238db 2965 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
62e76326 2966
1cf238db 2967 if (COMMIO_FD_WRITECB(io.fd)->active) {
dec5db5d 2968 /* FIXME: If this code is reinstated, check the conn counters,
2969 * not the fd table state
2970 */
62e76326 2971 /*
2972 * Some data has been sent to the client, just close the FD
2973 */
1cf238db 2974 comm_close(io.fd);
2975 } else if (nrequests) {
62e76326 2976 /*
2977 * assume its a persistent connection; just close it
2978 */
1cf238db 2979 comm_close(io.fd);
7a2f978b 2980 } else {
62e76326 2981 /*
2982 * Generate an error
2983 */
59a1efb2 2984 ClientHttpRequest **H;
62e76326 2985 clientStreamNode *node;
719c7e0a 2986 ClientHttpRequest *http = parseHttpRequestAbort(this, "error:Connection%20lifetime%20expired");
62e76326 2987 node = http->client_stream.tail->prev->data;
0655fa4d 2988 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2989 assert (repContext);
2990 repContext->setReplyToError(ERR_LIFETIME_EXP,
1cf238db 2991 HTTP_REQUEST_TIMEOUT, METHOD_NONE, "N/A", &peer.sin_addr,
0655fa4d 2992 NULL, NULL, NULL);
62e76326 2993 /* No requests can be outstanded */
1cf238db 2994 assert(chr == NULL);
62e76326 2995 /* add to the client request queue */
2996
3d0ac046 2997 for (H = &chr; *H; H = &(*H)->next);
62e76326 2998 *H = http;
2999
3000 clientStreamRead(http->client_stream.tail->data, http, 0,
3001 HTTP_REQBUF_SZ, context->reqbuf);
3002
3003 /*
3004 * if we don't close() here, we still need a timeout handler!
3005 */
9e008dda 3006 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876
AR
3007 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3008 TimeoutDialer, this, ConnStateData::requestTimeout);
9e008dda 3009 commSetTimeout(io.fd, 30, timeoutCall);
62e76326 3010
3011 /*
3012 * Aha, but we don't want a read handler!
3013 */
1cf238db 3014 commSetSelect(io.fd, COMM_SELECT_READ, NULL, NULL, 0);
7a2f978b 3015 }
62e76326 3016
af57a2e3 3017#else
3018 /*
62e76326 3019 * Just close the connection to not confuse browsers
3020 * using persistent connections. Some browsers opens
3021 * an connection and then does not use it until much
3022 * later (presumeably because the request triggering
3023 * the open has already been completed on another
3024 * connection)
3025 */
1cf238db 3026 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
62e76326 3027
1cf238db 3028 comm_close(io.fd);
62e76326 3029
af57a2e3 3030#endif
7a2f978b 3031}
3032
b5c39993 3033static void
3034clientLifetimeTimeout(int fd, void *data)
3035{
59a1efb2 3036 ClientHttpRequest *http = (ClientHttpRequest *)data;
f692498b 3037 debugs(33, 1, "WARNING: Closing client " << " connection due to lifetime timeout");
bf8fe701 3038 debugs(33, 1, "\t" << http->uri);
f692498b 3039 http->al.http.timedout = true;
b5c39993 3040 comm_close(fd);
3041}
3042
c8be6d7b 3043ConnStateData *
b7ac5457 3044connStateCreate(const Ip::Address &peer, const Ip::Address &me, int fd, http_port_list *port)
c8be6d7b 3045{
a46d2c0e 3046 ConnStateData *result = new ConnStateData;
2ad20b4f 3047
cc192b50 3048 result->peer = peer;
3049 result->log_addr = peer;
055421ee 3050 result->log_addr.ApplyMask(Config.Addrs.client_netmask);
cc192b50 3051 result->me = me;
c8be6d7b 3052 result->fd = fd;
e6ccf245 3053 result->in.buf = (char *)memAllocBuf(CLIENT_REQ_BUF_SZ, &result->in.allocatedSize);
3f38a55e 3054 result->port = cbdataReference(port);
62e76326 3055
9e008dda 3056 if (port->intercepted || port->spoof_client_ip) {
b7ac5457 3057 Ip::Address client, dst;
62e76326 3058
b7ac5457 3059 if (Ip::Interceptor.NatLookup(fd, me, peer, client, dst) == 0) {
482a5c64
AJ
3060 result->me = client;
3061 result->peer = dst;
a46d2c0e 3062 result->transparent(true);
62e76326 3063 }
3f38a55e 3064 }
62e76326 3065
5529ca8a 3066 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
9e008dda 3067 (result->transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
5529ca8a 3068#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
3069 int i = IP_PMTUDISC_DONT;
3070 setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof i);
3071
3072#else
3073
3074 static int reported = 0;
3075
3076 if (!reported) {
bf8fe701 3077 debugs(33, 1, "Notice: httpd_accel_no_pmtu_disc not supported on your platform");
5529ca8a 3078 reported = 1;
3079 }
3080
3081#endif
3082
3083 }
3084
48962ba8 3085 result->flags.readMoreRequests = true;
c8be6d7b 3086 return result;
3087}
3088
63be0a78 3089/** Handle a new connection on HTTP socket. */
7a2f978b 3090void
ee0989f2 3091httpAccept(int sock, int newfd, ConnectionDetail *details,
62e76326 3092 comm_err_t flag, int xerrno, void *data)
7a2f978b 3093{
3f38a55e 3094 http_port_list *s = (http_port_list *)data;
7a2f978b 3095 ConnStateData *connState = NULL;
02d1422b 3096
62e76326 3097 if (flag != COMM_OK) {
bf8fe701 3098 debugs(33, 1, "httpAccept: FD " << sock << ": accept failure: " << xstrerr(xerrno));
62e76326 3099 return;
3100 }
3101
bf8fe701 3102 debugs(33, 4, "httpAccept: FD " << newfd << ": accepted");
62e76326 3103 fd_note(newfd, "client http connect");
1cf238db 3104 connState = connStateCreate(&details->peer, &details->me, newfd, s);
3105
3106 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4299f876 3107 AsyncCall::Pointer call = JobCallback(33, 5,
4cb2536f 3108 Dialer, connState, ConnStateData::connStateClosed);
1cf238db 3109 comm_add_close_handler(newfd, call);
62e76326 3110
3111 if (Config.onoff.log_fqdn)
cc192b50 3112 fqdncache_gethostbyaddr(details->peer, FQDN_LOOKUP_IF_MISS);
62e76326 3113
9e008dda 3114 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876 3115 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
4cb2536f 3116 TimeoutDialer, connState, ConnStateData::requestTimeout);
9e008dda 3117 commSetTimeout(newfd, Config.Timeout.read, timeoutCall);
62e76326 3118
3898f57f 3119#if USE_IDENT
4daaf3cb
AJ
3120 if (Ident::TheConfig.identLookup) {
3121 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
b50e327b
AJ
3122 identChecklist.src_addr = details->peer;
3123 identChecklist.my_addr = details->me;
b50e327b 3124 if (identChecklist.fastCheck())
4daaf3cb 3125 Ident::Start(details->me, details->peer, clientIdentDone, connState);
b50e327b 3126 }
3898f57f 3127#endif
62e76326 3128
a98c2da5
AJ
3129#if USE_SQUID_EUI
3130 if (Eui::TheConfig.euiLookup) {
3131 if (details->peer.IsIPv4()) {
3132 connState->peer_eui48.lookup(details->peer);
05320519 3133 } else if (details->peer.IsIPv6()) {
a98c2da5
AJ
3134 connState->peer_eui64.lookup(details->peer);
3135 }
3136 }
3137#endif
3138
b2130d58 3139 if (s->tcp_keepalive.enabled) {
9e008dda 3140 commSetTcpKeepalive(newfd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
b2130d58 3141 }
3142
a46d2c0e 3143 connState->readSomeData();
62e76326 3144
cc192b50 3145 clientdbEstablished(details->peer, 1);
62e76326 3146
3147 incoming_sockets_accepted++;
7a2f978b 3148}
3149
1f7c9178 3150#if USE_SSL
3151
63be0a78 3152/** Create SSL connection structure and update fd_table */
ae7ff0b8 3153static SSL *
3154httpsCreate(int newfd, ConnectionDetail *details, SSL_CTX *sslContext)
3155{
3156 SSL *ssl = SSL_new(sslContext);
3157
3158 if (!ssl) {
3159 const int ssl_error = ERR_get_error();
3160 debugs(83, 1, "httpsAccept: Error allocating handle: " << ERR_error_string(ssl_error, NULL) );
3161 comm_close(newfd);
3162 return NULL;
3163 }
3164
3165 SSL_set_fd(ssl, newfd);
3166 fd_table[newfd].ssl = ssl;
3167 fd_table[newfd].read_method = &ssl_read_method;
3168 fd_table[newfd].write_method = &ssl_write_method;
3169
3170 debugs(33, 5, "httpsCreate: will negotate SSL on FD " << newfd);
3171 fd_note(newfd, "client https start");
3172
3173 return ssl;
3174}
3175
63be0a78 3176/** negotiate an SSL connection */
1f7c9178 3177static void
3178clientNegotiateSSL(int fd, void *data)
3179{
e6ccf245 3180 ConnStateData *conn = (ConnStateData *)data;
1f7c9178 3181 X509 *client_cert;
a7ad6e4e 3182 SSL *ssl = fd_table[fd].ssl;
1f7c9178 3183 int ret;
3184
a7ad6e4e 3185 if ((ret = SSL_accept(ssl)) <= 0) {
62e76326 3186 int ssl_error = SSL_get_error(ssl, ret);
3187
3188 switch (ssl_error) {
3189
3190 case SSL_ERROR_WANT_READ:
3191 commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
3192 return;
3193
3194 case SSL_ERROR_WANT_WRITE:
3195 commSetSelect(fd, COMM_SELECT_WRITE, clientNegotiateSSL, conn, 0);
3196 return;
3197
6de9e64b 3198 case SSL_ERROR_SYSCALL:
3199
3200 if (ret == 0) {
bf8fe701 3201 debugs(83, 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Aborted by client");
6de9e64b 3202 comm_close(fd);
3203 return;
3204 } else {
3205 int hard = 1;
3206
3207 if (errno == ECONNRESET)
3208 hard = 0;
3209
9e008dda 3210 debugs(83, hard ? 1 : 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
bf8fe701 3211 fd << ": " << strerror(errno) << " (" << errno << ")");
6de9e64b 3212
3213 comm_close(fd);
3214
3215 return;
3216 }
3217
3218 case SSL_ERROR_ZERO_RETURN:
bf8fe701 3219 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Closed by client");
6de9e64b 3220 comm_close(fd);
3221 return;
3222
62e76326 3223 default:
9e008dda
AJ
3224 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
3225 fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
3226 " (" << ssl_error << "/" << ret << ")");
62e76326 3227 comm_close(fd);
3228 return;
3229 }
3230
3231 /* NOTREACHED */
1f7c9178 3232 }
62e76326 3233
6de9e64b 3234 if (SSL_session_reused(ssl)) {
bf8fe701 3235 debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
3236 " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
6de9e64b 3237 } else {
3238 if (do_debug(83, 4)) {
3239 /* Write out the SSL session details.. actually the call below, but
3240 * OpenSSL headers do strange typecasts confusing GCC.. */
3241 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
afdd443f 3242#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
9f3de01a 3243 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 3244
0fd2205b 3245#elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2930f303 3246
0fd2205b 3247 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
3248 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
3249 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
3250 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
3251 * Because there are two possible usable cast, if you get an error here, try the other
3252 * commented line. */
3253
3254 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
3255 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
2930f303 3256
0e33d58c 3257#else
3258
bf8fe701 3259 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
0fd2205b 3260
0e33d58c 3261#endif
6de9e64b 3262 /* Note: This does not automatically fflush the log file.. */
3263 }
3264
bf8fe701 3265 debugs(83, 2, "clientNegotiateSSL: New session " <<
3266 SSL_get_session(ssl) << " on FD " << fd << " (" <<
3267 fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
3268 ")");
6de9e64b 3269 }
3270
bf8fe701 3271 debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
3272 SSL_get_cipher(ssl));
1f7c9178 3273
6de9e64b 3274 client_cert = SSL_get_peer_certificate(ssl);
62e76326 3275
1f7c9178 3276 if (client_cert != NULL) {
bf8fe701 3277 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3278 " client certificate: subject: " <<
3279 X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
3280
3281 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3282 " client certificate: issuer: " <<
3283 X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
1f7c9178 3284
1f7c9178 3285
62e76326 3286 X509_free(client_cert);
1f7c9178 3287 } else {
bf8fe701 3288 debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
3289 " has no certificate.");
1f7c9178 3290 }
3291
a46d2c0e 3292 conn->readSomeData();
1f7c9178 3293}
3294
63be0a78 3295/** handle a new HTTPS connection */
1f7c9178 3296static void
15595aab 3297httpsAccept(int sock, int newfd, ConnectionDetail *details,
62e76326 3298 comm_err_t flag, int xerrno, void *data)
1f7c9178 3299{
3f38a55e 3300 https_port_list *s = (https_port_list *)data;
3301 SSL_CTX *sslContext = s->sslContext;
c4b7a5a9 3302
3303 if (flag != COMM_OK) {
62e76326 3304 errno = xerrno;
bf8fe701 3305 debugs(33, 1, "httpsAccept: FD " << sock << ": accept failure: " << xstrerr(xerrno));
62e76326 3306 return;
c4b7a5a9 3307 }
62e76326 3308
ae7ff0b8 3309 SSL *ssl = NULL;
3310 if (!(ssl = httpsCreate(newfd, details, sslContext)))
62e76326 3311 return;
94c3d66f 3312
1cf238db 3313 debugs(33, 5, "httpsAccept: FD " << newfd << " accepted, starting SSL negotiation.");
3314 fd_note(newfd, "client https connect");
ae7ff0b8 3315 ConnStateData *connState = connStateCreate(details->peer, details->me,
9e008dda 3316 newfd, &s->http);
1cf238db 3317 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4299f876 3318 AsyncCall::Pointer call = JobCallback(33, 5,
4cb2536f 3319 Dialer, connState, ConnStateData::connStateClosed);
1cf238db 3320 comm_add_close_handler(newfd, call);
62e76326 3321
c4b7a5a9 3322 if (Config.onoff.log_fqdn)
cc192b50 3323 fqdncache_gethostbyaddr(details->peer, FQDN_LOOKUP_IF_MISS);
62e76326 3324
9e008dda 3325 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876
AR
3326 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3327 TimeoutDialer, connState, ConnStateData::requestTimeout);
9e008dda 3328 commSetTimeout(newfd, Config.Timeout.request, timeoutCall);
62e76326 3329
1f7c9178 3330#if USE_IDENT
4daaf3cb
AJ
3331 if (Ident::TheConfig.identLookup) {
3332 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
b50e327b
AJ
3333 identChecklist.src_addr = details->peer;
3334 identChecklist.my_addr = details->me;
b50e327b 3335 if (identChecklist.fastCheck())
4daaf3cb 3336 Ident::Start(details->me, details->peer, clientIdentDone, connState);
b50e327b 3337 }
1f7c9178 3338#endif
62e76326 3339
b2130d58 3340 if (s->http.tcp_keepalive.enabled) {
9e008dda 3341 commSetTcpKeepalive(newfd, s->http.tcp_keepalive.idle, s->http.tcp_keepalive.interval, s->http.tcp_keepalive.timeout);
b2130d58 3342 }
3343
c4b7a5a9 3344 commSetSelect(newfd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
62e76326 3345
cc192b50 3346 clientdbEstablished(details->peer, 1);
62e76326 3347
3f38a55e 3348 incoming_sockets_accepted++;
1f7c9178 3349}
3350
ae7ff0b8 3351bool
3352ConnStateData::switchToHttps()
3353{
3354 assert(!switchedToHttps_);
3355
3356 //HTTPMSGLOCK(currentobject->http->request);
3357 assert(areAllContextsForThisConnection());
3358 freeAllContexts();
3359 //currentobject->connIsFinished();
3360
3361 debugs(33, 5, HERE << "converting FD " << fd << " to SSL");
3362
3363 // fake a ConnectionDetail object; XXX: make ConnState a ConnectionDetail?
3364 ConnectionDetail detail;
3365 detail.me = me;
3366 detail.peer = peer;
3367
3368 SSL_CTX *sslContext = port->sslContext;
3369 SSL *ssl = NULL;
3370 if (!(ssl = httpsCreate(fd, &detail, sslContext)))
3371 return false;
3372
3373 // commSetTimeout() was called for this request before we switched.
3374
3375 // Disable the client read handler until peer selection is complete
3376 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
3377
3378 commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
3379
3380 switchedToHttps_ = true;
3381 return true;
3382}
3383
1f7c9178 3384#endif /* USE_SSL */
3385
00516be1
AR
3386/// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
3387static bool
3388OpenedHttpSocket(int fd, const char *msgIfFail)
3389{
3390 if (fd < 0) {
3391 Must(NHttpSockets > 0); // we tried to open some
3392 --NHttpSockets; // there will be fewer sockets than planned
3393 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3394
3395 if (!NHttpSockets) // we could not open any listen sockets at all
3396 fatal(msgIfFail);
3397
3398 return false;
3399 }
3400 return true;
3401}
3402
3403/// find any unused HttpSockets[] slot and store fd there or return false
3404static bool
3405AddOpenedHttpSocket(int fd)
3406{
3407 bool found = false;
3408 for (int i = 0; i < NHttpSockets && !found; i++) {
3409 if ((found = HttpSockets[i] < 0))
3410 HttpSockets[i] = fd;
3411 }
3412 return found;
3413}
15df8349 3414
d193a436 3415static void
15df8349 3416clientHttpConnectionsOpen(void)
3417{
cc192b50 3418 http_port_list *s = NULL;
ae7ff0b8 3419#if USE_SSL
3420 int bumpCount = 0; // counts http_ports with sslBump option
3421#endif
62e76326 3422
7e3ce7b9 3423 for (s = Config.Sockaddr.http; s; s = s->next) {
62e76326 3424 if (MAXHTTPPORTS == NHttpSockets) {
bf8fe701 3425 debugs(1, 1, "WARNING: You have too many 'http_port' lines.");
3426 debugs(1, 1, " The limit is " << MAXHTTPPORTS);
62e76326 3427 continue;
3428 }
3429
ae7ff0b8 3430#if USE_SSL
3431 if (s->sslBump && s->sslContext == NULL) {
3432 debugs(1, 1, "Will not bump SSL at http_port " <<
3433 s->http.s << " due to SSL initialization failure.");
3434 s->sslBump = 0;
3435 }
3436 if (s->sslBump)
3437 ++bumpCount;
3438#endif
3439
04f55905
AJ
3440 /* AYJ: 2009-12-27: bit bumpy. new ListenStateData(...) should be doing all the Comm:: stuff ... */
3441
fe090a86 3442 const int openFlags = COMM_NONBLOCKING |
5667a628 3443 (s->spoof_client_ip ? COMM_TRANSPARENT : 0);
c303f6e3 3444
fe090a86 3445 AsyncCall::Pointer callback = asyncCall(33,2,
5667a628
AR
3446 "clientHttpConnectionOpened",
3447 ListeningStartedDialer(&clientHttpConnectionOpened, s));
fe090a86 3448 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->s, openFlags,
5667a628 3449 Ipc::fdnHttpSocket, callback);
62e76326 3450
fe090a86 3451 HttpSockets[NHttpSockets++] = -1; // set in clientHttpConnectionOpened
15df8349 3452 }
ae7ff0b8 3453
3454#if USE_SSL
3455 if (bumpCount && !Config.accessList.ssl_bump)
3456 debugs(33, 1, "WARNING: http_port(s) with SslBump found, but no " <<
9e008dda
AJ
3457 std::endl << "\tssl_bump ACL configured. No requests will be " <<
3458 "bumped.");
ae7ff0b8 3459#endif
d193a436 3460}
3461
00516be1 3462/// process clientHttpConnectionsOpen result
fe090a86
AR
3463static void
3464clientHttpConnectionOpened(int fd, int, http_port_list *s)
3465{
00516be1 3466 if (!OpenedHttpSocket(fd, "Cannot open HTTP Port"))
fe090a86 3467 return;
fe090a86
AR
3468
3469 Must(s);
62e76326 3470
5667a628
AR
3471 AsyncCall::Pointer call = commCbCall(5,5, "SomeCommAcceptHandler(httpAccept)",
3472 CommAcceptCbPtrFun(httpAccept, s));
62e76326 3473
5667a628 3474 s->listener = new Comm::ListenStateData(fd, call, true);
62e76326 3475
5667a628
AR
3476 debugs(1, 1, "Accepting " <<
3477 (s->intercepted ? " intercepted" : "") <<
3478 (s->spoof_client_ip ? " spoofing" : "") <<
3479 (s->sslBump ? " bumpy" : "") <<
3480 (s->accel ? " accelerated" : "")
3481 << " HTTP connections at " << s->s
3482 << ", FD " << fd << "." );
62e76326 3483
00516be1 3484 Must(AddOpenedHttpSocket(fd)); // otherwise, we have received a fd we did not ask for
d193a436 3485}
3486
3487#if USE_SSL
3488static void
3489clientHttpsConnectionsOpen(void)
3490{
3491 https_port_list *s;
62e76326 3492
3f38a55e 3493 for (s = Config.Sockaddr.https; s; s = (https_port_list *)s->http.next) {
62e76326 3494 if (MAXHTTPPORTS == NHttpSockets) {
ae7ff0b8 3495 debugs(1, 1, "Ignoring 'https_port' lines exceeding the limit.");
3496 debugs(1, 1, "The limit is " << MAXHTTPPORTS << " HTTPS ports.");
62e76326 3497 continue;
3498 }
3499
f9ad0106 3500 if (s->sslContext == NULL) {
ae7ff0b8 3501 debugs(1, 1, "Ignoring https_port " << s->http.s <<
3502 " due to SSL initialization failure.");
3503 continue;
f9ad0106 3504 }
3505
00516be1 3506 AsyncCall::Pointer call = asyncCall(33, 2, "clientHttpsConnectionOpened",
5667a628 3507 ListeningStartedDialer(&clientHttpsConnectionOpened, &s->http));
62e76326 3508
00516be1 3509 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->http.s, COMM_NONBLOCKING,
5667a628 3510 Ipc::fdnHttpsSocket, call);
62e76326 3511
00516be1
AR
3512 HttpSockets[NHttpSockets++] = -1;
3513 }
3514}
62e76326 3515
00516be1
AR
3516/// process clientHttpsConnectionsOpen result
3517static void
3518clientHttpsConnectionOpened(int fd, int, http_port_list *s)
3519{
3520 if (!OpenedHttpSocket(fd, "Cannot open HTTPS Port"))
3521 return;
62e76326 3522
00516be1 3523 Must(s);
62e76326 3524
5667a628
AR
3525 AsyncCall::Pointer call = commCbCall(5,5, "SomeCommAcceptHandler(httpsAccept)",
3526 CommAcceptCbPtrFun(httpsAccept, s));
62e76326 3527
5667a628 3528 s->listener = new Comm::ListenStateData(fd, call, true);
62e76326 3529
5667a628 3530 debugs(1, 1, "Accepting HTTPS connections at " << s->s << ", FD " << fd << ".");
62e76326 3531
00516be1 3532 Must(AddOpenedHttpSocket(fd)); // otherwise, we have received a fd we did not ask for
d193a436 3533}
3534
3535#endif
3536
3537void
3538clientOpenListenSockets(void)
3539{
3540 clientHttpConnectionsOpen();
3541#if USE_SSL
3542 clientHttpsConnectionsOpen();
1f7c9178 3543#endif
62e76326 3544
15df8349 3545 if (NHttpSockets < 1)
598c41da 3546 fatal("No HTTP or HTTPS ports configured");
15df8349 3547}
edce4d98 3548
c0fbae16 3549void
3550clientHttpConnectionsClose(void)
3551{
04f55905
AJ
3552 for (http_port_list *s = Config.Sockaddr.http; s; s = s->next) {
3553 if (s->listener) {
3554 debugs(1, 1, "FD " << s->listener->fd << " Closing HTTP connection");
3555 delete s->listener;
3556 s->listener = NULL;
3557 }
3558 }
62e76326 3559
04f55905
AJ
3560#if USE_SSL
3561 for (http_port_list *s = Config.Sockaddr.https; s; s = s->next) {
3562 if (s->listener) {
3563 debugs(1, 1, "FD " << s->listener->fd << " Closing HTTPS connection");
3564 delete s->listener;
3565 s->listener = NULL;
62e76326 3566 }
c0fbae16 3567 }
04f55905
AJ
3568#endif
3569
3570 // TODO see if we can drop HttpSockets array entirely */
3571 for (int i = 0; i < NHttpSockets; i++) {
3572 HttpSockets[i] = -1;
3573 }
62e76326 3574
c0fbae16 3575 NHttpSockets = 0;
3576}
f66a9ef4 3577
3578int
190154cf 3579varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
f66a9ef4 3580{
3581 const char *vary = request->vary_headers;
a9925b40 3582 int has_vary = entry->getReply()->header.has(HDR_VARY);
f66a9ef4 3583#if X_ACCELERATOR_VARY
62e76326 3584
edce4d98 3585 has_vary |=
a9925b40 3586 entry->getReply()->header.has(HDR_X_ACCELERATOR_VARY);
f66a9ef4 3587#endif
62e76326 3588
f66a9ef4 3589 if (!has_vary || !entry->mem_obj->vary_headers) {
62e76326 3590 if (vary) {
3591 /* Oops... something odd is going on here.. */
9e008dda
AJ
3592 debugs(33, 1, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3593 entry->mem_obj->url << "' '" << vary << "'");
62e76326 3594 safe_free(request->vary_headers);
3595 return VARY_CANCEL;
3596 }
3597
3598 if (!has_vary) {
3599 /* This is not a varying object */
3600 return VARY_NONE;
3601 }
3602
3603 /* virtual "vary" object found. Calculate the vary key and
3604 * continue the search
3605 */
3606 vary = httpMakeVaryMark(request, entry->getReply());
3607
3608 if (vary) {
3609 request->vary_headers = xstrdup(vary);
3610 return VARY_OTHER;
3611 } else {
3612 /* Ouch.. we cannot handle this kind of variance */
3613 /* XXX This cannot really happen, but just to be complete */
3614 return VARY_CANCEL;
3615 }
f66a9ef4 3616 } else {
62e76326 3617 if (!vary) {
3618 vary = httpMakeVaryMark(request, entry->getReply());
3619
3620 if (vary)
3621 request->vary_headers = xstrdup(vary);
3622 }
3623
3624 if (!vary) {
3625 /* Ouch.. we cannot handle this kind of variance */
3626 /* XXX This cannot really happen, but just to be complete */
3627 return VARY_CANCEL;
3628 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
3629 return VARY_MATCH;
3630 } else {
3631 /* Oops.. we have already been here and still haven't
3632 * found the requested variant. Bail out
3633 */
bf8fe701 3634 debugs(33, 1, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
9e008dda 3635 entry->mem_obj->url << "' '" << vary << "'");
62e76326 3636 return VARY_CANCEL;
3637 }
f66a9ef4 3638 }
3639}
28d4805a 3640
c0941a6a 3641ACLFilledChecklist *
59a1efb2 3642clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
28d4805a 3643{
1cf238db 3644 ConnStateData * conn = http->getConn();
c0941a6a 3645 ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request,
af6a12ee 3646 cbdataReferenceValid(conn) && conn != NULL ? conn->rfc931 : dash_str);
28d4805a 3647
3648 /*
3649 * hack for ident ACL. It needs to get full addresses, and a place to store
3650 * the ident result on persistent connections...
3651 */
3652 /* connection oriented auth also needs these two lines for it's operation. */
3653 /*
3654 * Internal requests do not have a connection reference, because: A) their
3655 * byte count may be transformed before being applied to an outbound
3656 * connection B) they are internal - any limiting on them should be done on
3657 * the server end.
3658 */
62e76326 3659
1cf238db 3660 if (conn != NULL)
c0941a6a 3661 ch->conn(conn); /* unreferenced in FilledCheckList.cc */
28d4805a 3662
3663 return ch;
3664}
a46d2c0e 3665
3666CBDATA_CLASS_INIT(ConnStateData);
3667
f84dd7eb 3668ConnStateData::ConnStateData() :AsyncJob("ConnStateData"), transparent_ (false), closing_ (false)
b65351fa 3669{
d67acb4e
AJ
3670 pinning.fd = -1;
3671 pinning.pinned = false;
3672 pinning.auth = false;
b65351fa 3673}
a46d2c0e 3674
3675bool
3676ConnStateData::transparent() const
3677{
3678 return transparent_;
3679}
3680
3681void
3682ConnStateData::transparent(bool const anInt)
3683{
3684 transparent_ = anInt;
3685}
3686
3687bool
3688ConnStateData::reading() const
3689{
f84dd7eb 3690 return reader != NULL;
a46d2c0e 3691}
3692
3693void
f84dd7eb 3694ConnStateData::stopReading()
a46d2c0e 3695{
f84dd7eb
AR
3696 if (reading()) {
3697 comm_read_cancel(fd, reader);
3698 reader = NULL;
3699 }
a46d2c0e 3700}
3701
5f8252d2 3702
3703BodyPipe::Pointer
3e62bd58 3704ConnStateData::expectRequestBody(int64_t size)
5f8252d2 3705{
3706 bodyPipe = new BodyPipe(this);
39cb8c41
AR
3707 if (size >= 0)
3708 bodyPipe->setBodySize(size);
3709 else
3710 startDechunkingRequest();
5f8252d2 3711 return bodyPipe;
3712}
3713
39cb8c41
AR
3714int64_t
3715ConnStateData::mayNeedToReadMoreBody() const
3716{
3717 if (!bodyPipe)
3718 return 0; // request without a body or read/produced all body bytes
3719
3720 if (!bodyPipe->bodySizeKnown())
3721 return -1; // probably need to read more, but we cannot be sure
3722
3723 const int64_t needToProduce = bodyPipe->unproducedSize();
3724 const int64_t haveAvailable = static_cast<int64_t>(in.notYetUsed);
3725
3726 if (needToProduce <= haveAvailable)
3727 return 0; // we have read what we need (but are waiting for pipe space)
3728
3729 return needToProduce - haveAvailable;
3730}
3731
55e44db9 3732bool
3733ConnStateData::closing() const
3734{
3735 return closing_;
3736}
3737
63be0a78 3738/**
3739 * Called by ClientSocketContext to give the connection a chance to read
3740 * the entire body before closing the socket.
3741 */
55e44db9 3742void
5f8252d2 3743ConnStateData::startClosing(const char *reason)
55e44db9 3744{
5f8252d2 3745 debugs(33, 5, HERE << "startClosing " << this << " for " << reason);
3746 assert(!closing());
3747 closing_ = true;
3748
3749 assert(bodyPipe != NULL);
5f8252d2 3750
3751 // We do not have to abort the body pipeline because we are going to
3752 // read the entire body anyway.
3753 // Perhaps an ICAP server wants to log the complete request.
3754
3755 // If a consumer abort have caused this closing, we may get stuck
3756 // as nobody is consuming our data. Allow auto-consumption.
3757 bodyPipe->enableAutoConsumption();
55e44db9 3758}
3759
39cb8c41 3760/// initialize dechunking state
3ff65596 3761void
39cb8c41 3762ConnStateData::startDechunkingRequest()
3ff65596 3763{
39cb8c41
AR
3764 Must(bodyPipe != NULL);
3765 debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
3ff65596
AR
3766 assert(!in.bodyParser);
3767 in.bodyParser = new ChunkedCodingParser;
3ff65596
AR
3768}
3769
39cb8c41 3770/// put parsed content into input buffer and clean up
3ff65596 3771void
39cb8c41 3772ConnStateData::finishDechunkingRequest(bool withSuccess)
3ff65596 3773{
39cb8c41 3774 debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
3ff65596 3775
39cb8c41
AR
3776 if (bodyPipe != NULL) {
3777 debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
3778 BodyPipe::Pointer myPipe = bodyPipe;
3779 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
3780 Must(!bodyPipe); // we rely on it being nil after we are done with body
3781 if (withSuccess) {
3782 Must(myPipe->bodySizeKnown());
3783 ClientSocketContext::Pointer context = getCurrentContext();
3784 if (context != NULL && context->http && context->http->request)
3785 context->http->request->setContentLength(myPipe->bodySize());
3786 }
3ff65596 3787 }
3ff65596 3788
39cb8c41
AR
3789 delete in.bodyParser;
3790 in.bodyParser = NULL;
3ff65596
AR
3791}
3792
a46d2c0e 3793char *
3794ConnStateData::In::addressToReadInto() const
3795{
3796 return buf + notYetUsed;
3797}
3798
3ff65596 3799ConnStateData::In::In() : bodyParser(NULL),
39cb8c41 3800 buf (NULL), notYetUsed (0), allocatedSize (0)
a46d2c0e 3801{}
3802
3803ConnStateData::In::~In()
3804{
3805 if (allocatedSize)
3806 memFreeBuf(allocatedSize, buf);
39cb8c41 3807 delete bodyParser; // TODO: pool
a46d2c0e 3808}
d67acb4e 3809
655daa06
AR
3810void
3811ConnStateData::sendControlMsg(HttpControlMsg msg)
3812{
3813 ClientSocketContext::Pointer context = getCurrentContext();
3814 if (context != NULL) {
3815 context->writeControlMsg(msg); // will call msg.cbSuccess
3816 return;
3817 }
3818
3819 if (!isOpen()) {
3820 debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
3821 return;
3822 }
3823
3824 debugs(33, 3, HERE << " closing due to missing context for 1xx");
3825 comm_close(fd);
3826}
3827
d67acb4e
AJ
3828/* This is a comm call normally scheduled by comm_close() */
3829void
3830ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
3831{
3832 pinning.fd = -1;
3833 if (pinning.peer) {
9e008dda 3834 cbdataReferenceDone(pinning.peer);
d67acb4e
AJ
3835 }
3836 safe_free(pinning.host);
3837 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
3838 * connection has gone away */
3839}
3840
8bcf08e0 3841void ConnStateData::pinConnection(int pinning_fd, HttpRequest *request, struct peer *aPeer, bool auth)
9e008dda 3842{
d67acb4e
AJ
3843 fde *f;
3844 char desc[FD_DESC_SZ];
3845
3846 if (pinning.fd == pinning_fd)
9e008dda 3847 return;
d67acb4e 3848 else if (pinning.fd != -1)
9e008dda
AJ
3849 comm_close(pinning.fd);
3850
3851 if (pinning.host)
3852 safe_free(pinning.host);
3853
d67acb4e
AJ
3854 pinning.fd = pinning_fd;
3855 pinning.host = xstrdup(request->GetHost());
3856 pinning.port = request->port;
3857 pinning.pinned = true;
3858 if (pinning.peer)
9e008dda 3859 cbdataReferenceDone(pinning.peer);
8bcf08e0
FC
3860 if (aPeer)
3861 pinning.peer = cbdataReference(aPeer);
d67acb4e
AJ
3862 pinning.auth = auth;
3863 f = &fd_table[fd];
3864 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s:%d (%d)",
8bcf08e0 3865 (auth || !aPeer) ? request->GetHost() : aPeer->name, f->ipaddr, (int) f->remote_port, fd);
d67acb4e 3866 fd_note(pinning_fd, desc);
9e008dda 3867
d67acb4e 3868 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4299f876 3869 pinning.closeHandler = JobCallback(33, 5,
4cb2536f 3870 Dialer, this, ConnStateData::clientPinnedConnectionClosed);
d67acb4e
AJ
3871 comm_add_close_handler(pinning_fd, pinning.closeHandler);
3872
3873}
3874
8bcf08e0 3875int ConnStateData::validatePinnedConnection(HttpRequest *request, const struct peer *aPeer)
d67acb4e
AJ
3876{
3877 bool valid = true;
3878 if (pinning.fd < 0)
9e008dda
AJ
3879 return -1;
3880
d67acb4e 3881 if (pinning.auth && request && strcasecmp(pinning.host, request->GetHost()) != 0) {
9e008dda 3882 valid = false;
d67acb4e 3883 }
9e008dda
AJ
3884 if (request && pinning.port != request->port) {
3885 valid = false;
d67acb4e 3886 }
9e008dda
AJ
3887 if (pinning.peer && !cbdataReferenceValid(pinning.peer)) {
3888 valid = false;
d67acb4e 3889 }
8bcf08e0 3890 if (aPeer != pinning.peer) {
9e008dda 3891 valid = false;
d67acb4e
AJ
3892 }
3893
9e008dda
AJ
3894 if (!valid) {
3895 int pinning_fd=pinning.fd;
3896 /* The pinning info is not safe, remove any pinning info*/
3897 unpinConnection();
d67acb4e 3898
9e008dda
AJ
3899 /* also close the server side socket, we should not use it for invalid/unauthenticated
3900 requests...
3901 */
3902 comm_close(pinning_fd);
3903 return -1;
d67acb4e
AJ
3904 }
3905
3906 return pinning.fd;
3907}
3908
3909void ConnStateData::unpinConnection()
3910{
9e008dda
AJ
3911 if (pinning.peer)
3912 cbdataReferenceDone(pinning.peer);
d67acb4e 3913
9e008dda
AJ
3914 if (pinning.closeHandler != NULL) {
3915 comm_remove_close_handler(pinning.fd, pinning.closeHandler);
3916 pinning.closeHandler = NULL;
d67acb4e
AJ
3917 }
3918 pinning.fd = -1;
3919 safe_free(pinning.host);
3920}