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