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