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