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