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