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