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