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