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