]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
Add comment explaining cause of asserts in stmem.cc
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
3c66d057 1
dd11e0b7 2/*
b631f31c 3 * $Id: client_side.cc,v 1.665 2003/10/21 09:30:09 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? */
528b2c61 1070 else if (httpHeaderHas(&http->request->header, HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
62e76326 1071 range_err = "If-Range match failed";
528b2c61 1072 else if (!http->request->range->canonize(rep))
62e76326 1073 range_err = "canonization failed";
528b2c61 1074 else if (http->request->range->isComplex())
62e76326 1075 range_err = "too complex range header";
62e76326 1076
528b2c61 1077#if 0
62e76326 1078
528b2c61 1079 else if (!logTypeIsATcpHit(http->logType); && http->request->range->offsetLimitExceeded())
62e76326 1080 range_err = "range outside range_offset_limit";
1081
c8be6d7b 1082#endif
528b2c61 1083 /* get rid of our range specs on error */
1084 if (range_err) {
b631f31c 1085 /* XXX We do this here because we need canonisation etc. However, this current
1086 * code will lead to incorrect store offset requests - the store will have the
1087 * offset data, but we won't be requesting it.
1088 * So, we can either re-request, or generate an error
1089 */
62e76326 1090 debug(33, 3) ("clientBuildRangeHeader: will not do ranges: %s.\n", range_err);
00d77d6b 1091 delete http->request->range;
62e76326 1092 http->request->range = NULL;
c8be6d7b 1093 } else {
3cff087f 1094 /* XXX: TODO: Review, this unconditional set may be wrong. - TODO: review. */
1095 httpStatusLineSet(&rep->sline, rep->sline.version,
1096 HTTP_PARTIAL_CONTENT, NULL);
62e76326 1097 const int spec_count = http->request->range->specs.count;
1098 int actual_clen = -1;
1099
1100 debug(33, 3) ("clientBuildRangeHeader: range spec count: %d virgin clen: %d\n",
1101 spec_count, rep->content_length);
1102 assert(spec_count > 0);
1103 /* ETags should not be returned with Partial Content replies? */
1104 httpHeaderDelById(hdr, HDR_ETAG);
1105 /* append appropriate header(s) */
1106
1107 if (spec_count == 1) {
1108 HttpHdrRange::iterator pos = http->request->range->begin();
1109 assert(*pos);
1110 /* append Content-Range */
1111
1112 if (!httpHeaderHas(hdr, HDR_CONTENT_RANGE)) {
1113 /* No content range, so this was a full object we are
1114 * sending parts of.
1115 */
1116 httpHeaderAddContRange(hdr, **pos, rep->content_length);
1117 }
1118
1119 /* set new Content-Length to the actual number of bytes
1120 * transmitted in the message-body */
1121 actual_clen = (*pos)->length;
1122 } else {
1123 /* multipart! */
1124 /* generate boundary string */
1125 http->range_iter.boundary = http->rangeBoundaryStr();
1126 /* delete old Content-Type, add ours */
1127 httpHeaderDelById(hdr, HDR_CONTENT_TYPE);
1128 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
1129 "multipart/byteranges; boundary=\"%s\"",
1130 http->range_iter.boundary.buf());
1131 /* Content-Length is not required in multipart responses
1132 * but it is always nice to have one */
1133 actual_clen = http->mRangeCLen();
2512d159 1134 /* http->out needs to start where we want data at */
1135 http->out.offset = http->range_iter.currentSpec()->offset;
62e76326 1136 }
1137
1138 /* replace Content-Length header */
1139 assert(actual_clen >= 0);
1140
1141 httpHeaderDelById(hdr, HDR_CONTENT_LENGTH);
1142
1143 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, actual_clen);
1144
1145 debug(33, 3) ("clientBuildRangeHeader: actual content length: %d\n", actual_clen);
1146
1147 /* And start the range iter off */
1148 http->range_iter.updateSpec();
c8be6d7b 1149 }
528b2c61 1150}
1151
1152void
1153ClientSocketContext::prepareReply(HttpReply * rep)
1154{
1155 if (http->request->range)
62e76326 1156 buildRangeHeader(rep);
528b2c61 1157}
1158
1159void
1160ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData)
1161{
1162 prepareReply(rep);
1163 /* init mb; put status line and headers if any */
1164 assert (rep);
1165 MemBuf mb = httpReplyPack(rep);
1166 /* Save length of headers for persistent conn checks */
1167 http->out.headers_sz = mb.size;
1168#if HEADERS_LOG
62e76326 1169
528b2c61 1170 headersLog(0, 0, http->request->method, rep);
1171#endif
62e76326 1172
c8be6d7b 1173 if (bodyData.data && bodyData.length) {
62e76326 1174 if (!multipartRangeRequest()) {
2512d159 1175 size_t length = lengthToSend(bodyData.range());
62e76326 1176 noteSentBodyBytes (length);
1177
1178 memBufAppend(&mb, bodyData.data, length);
1179 } else {
2512d159 1180 packRange(bodyData, &mb);
62e76326 1181 }
c8be6d7b 1182 }
62e76326 1183
c8be6d7b 1184 /* write */
528b2c61 1185 comm_old_write_mbuf(fd(), mb, clientWriteComplete, this);
62e76326 1186
c8be6d7b 1187 /* if we don't do it, who will? */
1188}
1189
2246b732 1190/*
edce4d98 1191 * Write a chunk of data to a client socket. If the reply is present, send the reply headers down the wire too,
1192 * and clean them up when finished.
1193 * Pre-condition:
1194 * The request is one backed by a connection, not an internal request.
1195 * data context is not NULL
1196 * There are no more entries in the stream chain.
2246b732 1197 */
edce4d98 1198static void
1199clientSocketRecipient(clientStreamNode * node, clientHttpRequest * http,
62e76326 1200 HttpReply * rep, StoreIOBuffer recievedData)
edce4d98 1201{
1202 int fd;
edce4d98 1203 /* Test preconditions */
1204 assert(node != NULL);
62e76326 1205 /* TODO: handle this rather than asserting
c8be6d7b 1206 * - it should only ever happen if we cause an abort and
edce4d98 1207 * the callback chain loops back to here, so we can simply return.
1208 * However, that itself shouldn't happen, so it stays as an assert for now.
1209 */
1210 assert(cbdataReferenceValid(node));
edce4d98 1211 assert(node->node.next == NULL);
0655fa4d 1212 ClientSocketContext::Pointer context = dynamic_cast<ClientSocketContext *>(node->data.getRaw());
1213 assert(context.getRaw() != NULL);
98242069 1214 assert(connIsUsable(http->getConn()));
1215 fd = http->getConn()->fd;
528b2c61 1216 /* TODO: check offset is what we asked for */
62e76326 1217
98242069 1218 if (context != http->getConn()->getCurrentContext()) {
62e76326 1219 context->deferRecipientForLater(node, rep, recievedData);
1220 return;
edce4d98 1221 }
62e76326 1222
c8be6d7b 1223 if (responseFinishedOrFailed(rep, recievedData)) {
0655fa4d 1224 context->writeComplete(fd, NULL, 0, COMM_OK);
62e76326 1225 return;
edce4d98 1226 }
62e76326 1227
0655fa4d 1228 if (!context->startOfOutput())
62e76326 1229 context->sendBody(rep, recievedData);
7684c4b1 1230 else {
1231 http->al.reply = rep;
62e76326 1232 context->sendStartOfMessage(rep, recievedData);
7684c4b1 1233 }
edce4d98 1234}
1235
62e76326 1236/* Called when a downstream node is no longer interested in
edce4d98 1237 * our data. As we are a terminal node, this means on aborts
1238 * only
1239 */
1240void
1241clientSocketDetach(clientStreamNode * node, clientHttpRequest * http)
1242{
edce4d98 1243 /* Test preconditions */
1244 assert(node != NULL);
62e76326 1245 /* TODO: handle this rather than asserting
c8be6d7b 1246 * - it should only ever happen if we cause an abort and
edce4d98 1247 * the callback chain loops back to here, so we can simply return.
1248 * However, that itself shouldn't happen, so it stays as an assert for now.
1249 */
1250 assert(cbdataReferenceValid(node));
1251 /* Set null by ContextFree */
edce4d98 1252 assert(node->node.next == NULL);
0655fa4d 1253 /* this is the assert discussed above */
e4a67a80 1254 assert(NULL == dynamic_cast<ClientSocketContext *>(node->data.getRaw()));
edce4d98 1255 /* We are only called when the client socket shutsdown.
1256 * Tell the prev pipeline member we're finished
1257 */
1258 clientStreamDetach(node, http);
7a2f978b 1259}
1260
f4f278b5 1261static void
25f651c1 1262clientWriteBodyComplete(int fd, char *buf, size_t size, comm_err_t errflag, int xerrno, void *data)
f4f278b5 1263{
c8be6d7b 1264 clientWriteComplete(fd, NULL, size, errflag, data);
1265}
1266
1267void
a46d2c0e 1268ConnStateData::readNextRequest()
c8be6d7b 1269{
a46d2c0e 1270 debug(33, 5) ("ConnStateData::readNextRequest: FD %d reading next req\n", fd);
1271 fd_note(fd, "Waiting for next request");
f4f278b5 1272 /*
c8be6d7b 1273 * Set the timeout BEFORE calling clientReadRequest().
1274 */
a46d2c0e 1275 commSetTimeout(fd, Config.Timeout.persistent_request,
1276 requestTimeout, this);
1277 readSomeData();
c4b7a5a9 1278 /* Please don't do anything with the FD past here! */
c8be6d7b 1279}
1280
1281void
a2ac85d9 1282ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData::Pointer & conn)
c8be6d7b 1283{
528b2c61 1284 debug(33, 2) ("ClientSocketContextPushDeferredIfNeeded: FD %d Sending next\n",
62e76326 1285 conn->fd);
c8be6d7b 1286 /* If the client stream is waiting on a socket write to occur, then */
62e76326 1287
c8be6d7b 1288 if (deferredRequest->flags.deferred) {
62e76326 1289 /* NO data is allowed to have been sent */
1290 assert(deferredRequest->http->out.size == 0);
1291 clientSocketRecipient(deferredRequest->deferredparams.node,
1292 deferredRequest->http,
1293 deferredRequest->deferredparams.rep,
1294 deferredRequest->deferredparams.queuedBuffer);
c8be6d7b 1295 }
62e76326 1296
c8be6d7b 1297 /* otherwise, the request is still active in a callbacksomewhere,
1298 * and we are done
f4f278b5 1299 */
f4f278b5 1300}
1301
0655fa4d 1302void
1303ClientSocketContext::keepaliveNextRequest()
1a92a1e2 1304{
a2ac85d9 1305 ConnStateData::Pointer conn = http->getConn();
bd4e6ec8 1306
0655fa4d 1307 debug(33, 3) ("ClientSocketContext::keepaliveNextRequest: FD %d\n", conn->fd);
1308 connIsFinished();
1309
1310 ClientSocketContext::Pointer deferredRequest;
62e76326 1311
0655fa4d 1312 if ((deferredRequest = conn->getCurrentContext()).getRaw() == NULL)
a46d2c0e 1313 conn->readNextRequest();
c8be6d7b 1314 else
62e76326 1315 ClientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
1a92a1e2 1316}
1317
c8be6d7b 1318void
1319clientUpdateSocketStats(log_type logType, size_t size)
1320{
1321 if (size == 0)
62e76326 1322 return;
1323
c8be6d7b 1324 kb_incr(&statCounter.client_http.kbytes_out, size);
62e76326 1325
c8be6d7b 1326 if (logTypeIsATcpHit(logType))
62e76326 1327 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
c8be6d7b 1328}
1329
528b2c61 1330/* returns true if there is still data available to pack more ranges
1331 * increments iterator "i"
1332 * used by clientPackMoreRanges */
1333bool
1334ClientSocketContext::canPackMoreRanges() const
1335{
1336 /* first update "i" if needed */
62e76326 1337
528b2c61 1338 if (!http->range_iter.debt()) {
62e76326 1339 debug (33,5)("ClientSocketContext::canPackMoreRanges: At end of current range spec for fd %d\n",fd());
1340
1341 if (http->range_iter.pos.incrementable())
1342 ++http->range_iter.pos;
1343
1344 http->range_iter.updateSpec();
528b2c61 1345 }
62e76326 1346
528b2c61 1347 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
1348 /* paranoid sync condition */
1349 /* continue condition: need_more_data */
1350 debug (33,5)("ClientSocketContext::canPackMoreRanges: returning %d\n", http->range_iter.currentSpec() ? true : false);
1351 return http->range_iter.currentSpec() ? true : false;
1352}
1353
1354off_t
1355ClientSocketContext::getNextRangeOffset() const
1356{
1357 if (http->request->range) {
62e76326 1358 /* offset in range specs does not count the prefix of an http msg */
3d882db7 1359 debug (33,5) ("ClientSocketContext::getNextRangeOffset: http offset %lu\n", (long unsigned)http->out.offset);
62e76326 1360 /* check: reply was parsed and range iterator was initialized */
1361 assert(http->range_iter.valid);
1362 /* filter out data according to range specs */
1363 assert (canPackMoreRanges());
1364 {
1365 off_t start; /* offset of still missing data */
1366 assert(http->range_iter.currentSpec());
1367 start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
1368 debug(33, 3) ("clientPackMoreRanges: in: offset: %ld\n",
1369 (long int) http->out.offset);
1370 debug(33, 3) ("clientPackMoreRanges: out: start: %ld spec[%ld]: [%ld, %ld), len: %ld debt: %ld\n",
1371 (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());
1372
1373 if (http->range_iter.currentSpec()->length != -1)
1374 assert(http->out.offset <= start); /* we did not miss it */
1375
1376 return start;
1377 }
1378
528b2c61 1379 }
1380
1381 return http->out.offset;
1382}
1383
c8be6d7b 1384void
528b2c61 1385ClientSocketContext::pullData()
c8be6d7b 1386{
528b2c61 1387 debug (33,5)("ClientSocketContext::pullData: FD %d attempting to pull upstream data\n", fd());
c8be6d7b 1388 /* More data will be coming from the stream. */
528b2c61 1389 StoreIOBuffer readBuffer;
1390 /* XXX: Next requested byte in the range sequence */
1391 /* XXX: length = getmaximumrangelenfgth */
1392 readBuffer.offset = getNextRangeOffset();
c8be6d7b 1393 readBuffer.length = HTTP_REQBUF_SZ;
528b2c61 1394 readBuffer.data = reqbuf;
1395 /* we may note we have reached the end of the wanted ranges */
1396 clientStreamRead(getTail(), http, readBuffer);
1397}
1398
62e76326 1399clientStream_status_t
528b2c61 1400ClientSocketContext::socketState()
1401{
1402 switch (clientStreamStatus(getTail(), http)) {
62e76326 1403
1404 case STREAM_NONE:
528b2c61 1405 /* check for range support ending */
62e76326 1406
528b2c61 1407 if (http->request->range) {
62e76326 1408 /* check: reply was parsed and range iterator was initialized */
1409 assert(http->range_iter.valid);
1410 /* filter out data according to range specs */
1411
1412 if (!canPackMoreRanges()) {
1413 debug (33,5)("ClientSocketContext::socketState: Range request has hit end of returnable range sequence on fd %d\n", fd());
1414
1415 if (http->request->flags.proxy_keepalive)
1416 return STREAM_COMPLETE;
1417 else
1418 return STREAM_UNPLANNED_COMPLETE;
1419 }
1420 }
1421
1422 return STREAM_NONE;
1423
1424 case STREAM_COMPLETE:
528b2c61 1425 return STREAM_COMPLETE;
62e76326 1426
1427 case STREAM_UNPLANNED_COMPLETE:
1428 return STREAM_UNPLANNED_COMPLETE;
1429
1430 case STREAM_FAILED:
1431 return STREAM_FAILED;
528b2c61 1432 }
62e76326 1433
528b2c61 1434 fatal ("unreachable code\n");
1435 return STREAM_NONE;
c8be6d7b 1436}
edce4d98 1437
1438/* A write has just completed to the client, or we have just realised there is
1439 * no more data to send.
1440 */
e6ccf245 1441void
1442clientWriteComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
7a2f978b 1443{
528b2c61 1444 ClientSocketContext *context = (ClientSocketContext *)data;
0655fa4d 1445 context->writeComplete (fd, bufnotused, size, errflag);
1446}
1447
55e44db9 1448void
1449ClientSocketContext::doClose()
1450{
1451 comm_close(fd());
1452}
1453
1454void
1455ClientSocketContext::initiateClose()
1456{
1457 if (!http || !http->getConn().getRaw()) {
1458 doClose();
1459 return;
1460 }
1461
1462 if (http->getConn()->body.size_left > 0) {
1463 debug(33, 5) ("ClientSocketContext::initiateClose: closing, but first we need to read the rest of the request\n");
1464 /* XXX We assumes the reply does fit in the TCP transmit window.
1465 * If not the connection may stall while sending the reply
1466 * (before reaching here) if the client does not try to read the
1467 * response while sending the request body. As of yet we have
1468 * not received any complaints indicating this may be an issue.
1469 */
1470 http->getConn()->closing(true);
1471 clientAbortBody(http->request);
1472 return;
1473 }
1474
1475 doClose();
1476}
1477
0655fa4d 1478void
1479ClientSocketContext::writeComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag)
1480{
86a2f789 1481 StoreEntry *entry = http->storeEntry();
7a2f978b 1482 http->out.size += size;
c8be6d7b 1483 assert(fd > -1);
e4049756 1484 debugs(33, 5, "clientWriteComplete: FD " << fd << ", sz " << size <<
1485 ", err " << errflag << ", off " << http->out.size << ", len " <<
1486 entry ? objectLen(entry) : 0);
c8be6d7b 1487 clientUpdateSocketStats(http->logType, size);
55e44db9 1488 assert (this->fd() == fd);
62e76326 1489
c8be6d7b 1490 if (errflag || clientHttpRequestStatus(fd, http)) {
62e76326 1491 debug (33,5)("clientWriteComplete: FD %d, closing connection due to failure, or true requeststatus\n", fd);
55e44db9 1492 initiateClose();
62e76326 1493 /* Do we leak here ? */
1494 return;
edce4d98 1495 }
62e76326 1496
0655fa4d 1497 switch (socketState()) {
62e76326 1498
edce4d98 1499 case STREAM_NONE:
0655fa4d 1500 pullData();
62e76326 1501 break;
1502
edce4d98 1503 case STREAM_COMPLETE:
62e76326 1504 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
0655fa4d 1505 keepaliveNextRequest();
62e76326 1506 return;
1507
edce4d98 1508 case STREAM_UNPLANNED_COMPLETE:
62e76326 1509 /* fallthrough */
1510
edce4d98 1511 case STREAM_FAILED:
55e44db9 1512 initiateClose();
62e76326 1513 return;
1514
edce4d98 1515 default:
62e76326 1516 fatal("Hit unreachable code in clientWriteComplete\n");
7a2f978b 1517 }
1518}
1519
e6ccf245 1520extern "C" CSR clientGetMoreData;
1521extern "C" CSS clientReplyStatus;
1522extern "C" CSD clientReplyDetach;
edce4d98 1523
528b2c61 1524static ClientSocketContext *
a2ac85d9 1525parseHttpRequestAbort(ConnStateData::Pointer & conn, const char *uri)
038eb4ed 1526{
edce4d98 1527 clientHttpRequest *http;
528b2c61 1528 ClientSocketContext *context;
1529 StoreIOBuffer tempBuffer;
1530 http = new ClientHttpRequest;
98242069 1531 http->setConn(conn);
c8be6d7b 1532 http->req_sz = conn->in.notYetUsed;
edce4d98 1533 http->uri = xstrdup(uri);
c4b7a5a9 1534 setLogUri (http, uri);
528b2c61 1535 context = ClientSocketContextNew(http);
c8be6d7b 1536 tempBuffer.data = context->reqbuf;
1537 tempBuffer.length = HTTP_REQBUF_SZ;
edce4d98 1538 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 1539 clientReplyStatus, new clientReplyContext(http), clientSocketRecipient,
62e76326 1540 clientSocketDetach, context, tempBuffer);
edce4d98 1541 dlinkAdd(http, &http->active, &ClientActiveRequests);
1542 return context;
038eb4ed 1543}
1544
528b2c61 1545ClientSocketContext *
a2ac85d9 1546clientParseRequestMethod(char *inbuf, method_t * method_p, ConnStateData::Pointer & conn)
c8be6d7b 1547{
1548 char *mstr = NULL;
62e76326 1549
edce4d98 1550 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
62e76326 1551 debug(33, 1) ("clientParseRequestMethod: Can't get request method\n");
1552 return parseHttpRequestAbort(conn, "error:invalid-request");
7ddc902f 1553 }
62e76326 1554
edce4d98 1555 *method_p = urlParseMethod(mstr);
62e76326 1556
edce4d98 1557 if (*method_p == METHOD_NONE) {
62e76326 1558 debug(33, 1) ("clientParseRequestMethod: Unsupported method '%s'\n", mstr);
1559 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
50ddd7a4 1560 }
62e76326 1561
c8be6d7b 1562 debug(33, 5) ("clientParseRequestMethod: Method is '%s'\n", mstr);
1563 return NULL;
1564}
1565
1566char *
1567skipLeadingSpace(char *aString)
1568{
1569 char *result = aString;
62e76326 1570
c8be6d7b 1571 while (xisspace(*aString))
62e76326 1572 ++aString;
1573
c8be6d7b 1574 return result;
1575}
1576
3f38a55e 1577static char *
c8be6d7b 1578findTrailingHTTPVersion(char *uriAndHTTPVersion)
1579{
3f38a55e 1580 char *token;
62e76326 1581
3f38a55e 1582 for (token = strchr(uriAndHTTPVersion, '\n'); token > uriAndHTTPVersion; token--) {
62e76326 1583 if (*token == '\n' || *token == '\r')
1584 continue;
1585
1586 if (xisspace(*token)) {
1587 if (strncasecmp(token + 1, "HTTP/", 5) == 0)
1588 return token + 1;
1589 else
1590 break;
1591 }
c8be6d7b 1592 }
62e76326 1593
3f38a55e 1594 return NULL;
c8be6d7b 1595}
1596
3f38a55e 1597#if UNUSED_CODE
c8be6d7b 1598void
1599trimTrailingSpaces(char *aString, size_t len)
1600{
1601 char *endPointer = aString + len;
62e76326 1602
c8be6d7b 1603 while (endPointer > aString && xisspace(*endPointer))
62e76326 1604 *(endPointer--) = '\0';
c8be6d7b 1605}
62e76326 1606
3f38a55e 1607#endif
50ddd7a4 1608
3f38a55e 1609static ClientSocketContext *
450e0c10 1610parseURIandHTTPVersion(char **url_p, HttpVersion * http_ver_p,
a2ac85d9 1611 ConnStateData::Pointer & conn, char *http_version_str)
c8be6d7b 1612{
1613 char *url;
3f38a55e 1614 char *t;
1615 /* look for URL (strtok initiated by clientParseRequestMethod) */
62e76326 1616
edce4d98 1617 if ((url = strtok(NULL, "\n")) == NULL) {
62e76326 1618 debug(33, 1) ("parseHttpRequest: Missing URL\n");
1619 return parseHttpRequestAbort(conn, "error:missing-url");
7a2f978b 1620 }
62e76326 1621
c8be6d7b 1622 url = skipLeadingSpace(url);
c8be6d7b 1623
3f38a55e 1624 if (!*url || (http_version_str && http_version_str <= url+1)) {
62e76326 1625 debug(33, 1) ("parseHttpRequest: Missing URL\n");
1626 return parseHttpRequestAbort(conn, "error:missing-url");
3f38a55e 1627 }
62e76326 1628
3f38a55e 1629 /* Terminate URL just before HTTP version (or at end of line) */
1630 if (http_version_str)
62e76326 1631 http_version_str[-1] = '\0';
3f38a55e 1632 else {
62e76326 1633 t = url + strlen(url) - 1;
1634
1635 while (t > url && *t == '\r')
1636 *t-- = '\0';
3f38a55e 1637 }
62e76326 1638
edce4d98 1639 debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url);
1640 *url_p = url;
62e76326 1641
3f38a55e 1642 if (http_version_str) {
62e76326 1643 if (sscanf(http_version_str + 5, "%d.%d", &http_ver_p->major,
1644 &http_ver_p->minor) != 2) {
1645 debug(33, 3) ("parseHttpRequest: Invalid HTTP identifier.\n");
1646 return parseHttpRequestAbort(conn, "error:invalid-http-ident");
1647 }
1648
1649 debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n",
1650 http_ver_p->major, http_ver_p->minor);
3f38a55e 1651 } else {
450e0c10 1652 *http_ver_p = HttpVersion(0,9); /* wild guess */
6d38ef86 1653 }
62e76326 1654
c8be6d7b 1655 return NULL;
1656}
1657
1658/* Utility function to perform part of request parsing */
528b2c61 1659static ClientSocketContext *
a2ac85d9 1660clientParseHttpRequestLine(char *reqline, ConnStateData::Pointer &conn,
450e0c10 1661 method_t * method_p, char **url_p, HttpVersion * http_ver_p, char * http_version_str)
c8be6d7b 1662{
528b2c61 1663 ClientSocketContext *result = NULL;
3f38a55e 1664 /* XXX: This sequence relies on strtok() */
62e76326 1665
3f38a55e 1666 if ((result = clientParseRequestMethod(reqline, method_p, conn))
62e76326 1667 || (result = parseURIandHTTPVersion(url_p, http_ver_p, conn, http_version_str)))
1668 return result;
7a2f978b 1669
edce4d98 1670 return NULL;
99edd1c3 1671}
1672
c8be6d7b 1673void
c4b7a5a9 1674setLogUri(clientHttpRequest * http, char const *uri)
c8be6d7b 1675{
a46d0227 1676 safe_free(http->log_uri);
62e76326 1677
c8be6d7b 1678 if (!stringHasCntl(uri))
62e76326 1679 http->log_uri = xstrndup(uri, MAX_URL);
c8be6d7b 1680 else
62e76326 1681 http->log_uri = xstrndup(rfc1738_escape_unescaped(uri), MAX_URL);
c8be6d7b 1682}
1683
3f38a55e 1684static void
a2ac85d9 1685prepareAcceleratedURL(ConnStateData::Pointer & conn, clientHttpRequest *http, char *url, const char *req_hdr)
62e76326 1686{
3f38a55e 1687 int vhost = conn->port->vhost;
1688 int vport = conn->port->vport;
1689 char *host;
c8be6d7b 1690
3f38a55e 1691 http->flags.accel = 1;
c8be6d7b 1692
3f38a55e 1693 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
c8be6d7b 1694
3f38a55e 1695 if (*url != '/') {
62e76326 1696 if (conn->port->vhost)
1697 return; /* already in good shape */
1698
1699 /* else we need to ignore the host name */
1700 url = strstr(url, "//");
1701
3f38a55e 1702#if SHOULD_REJECT_UNKNOWN_URLS
62e76326 1703
1704 if (!url)
1705 return parseHttpRequestAbort(conn, "error:invalid-request");
1706
c8be6d7b 1707#endif
62e76326 1708
1709 if (url)
1710 url = strchr(url + 2, '/');
1711
1712 if (!url)
1713 url = (char *) "/";
3f38a55e 1714 }
1715
1716 if (internalCheck(url)) {
62e76326 1717 /* prepend our name & port */
1718 http->uri = xstrdup(internalLocalUri(NULL, url));
1719 http->flags.internal = 1;
3f38a55e 1720 } else if (vhost && (host = mime_get_header(req_hdr, "Host")) != NULL) {
62e76326 1721 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1722 strlen(host);
1723 http->uri = (char *)xcalloc(url_sz, 1);
1724 snprintf(http->uri, url_sz, "%s://%s%s",
1725 conn->port->protocol, host, url);
1726 debug(33, 5) ("ACCEL VHOST REWRITE: '%s'\n", http->uri);
3f38a55e 1727 } else if (conn->port->defaultsite) {
62e76326 1728 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1729 strlen(conn->port->defaultsite);
1730 http->uri = (char *)xcalloc(url_sz, 1);
1731 snprintf(http->uri, url_sz, "%s://%s%s",
1732 conn->port->protocol, conn->port->defaultsite, url);
1733 debug(33, 5) ("ACCEL DEFAULTSITE REWRITE: '%s'\n", http->uri);
3f38a55e 1734 } else if (vport == -1) {
62e76326 1735 /* Put the local socket IP address as the hostname. */
1736 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1737 http->uri = (char *)xcalloc(url_sz, 1);
1738 snprintf(http->uri, url_sz, "%s://%s:%d%s",
98242069 1739 http->getConn()->port->protocol,
1740 inet_ntoa(http->getConn()->me.sin_addr),
1741 ntohs(http->getConn()->me.sin_port), url);
62e76326 1742 debug(33, 5) ("ACCEL VPORT REWRITE: '%s'\n", http->uri);
3f38a55e 1743 } else if (vport > 0) {
62e76326 1744 /* Put the local socket IP address as the hostname, but static port */
1745 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1746 http->uri = (char *)xcalloc(url_sz, 1);
1747 snprintf(http->uri, url_sz, "%s://%s:%d%s",
98242069 1748 http->getConn()->port->protocol,
1749 inet_ntoa(http->getConn()->me.sin_addr),
62e76326 1750 vport, url);
1751 debug(33, 5) ("ACCEL VPORT REWRITE: '%s'\n", http->uri);
3f38a55e 1752 }
1753}
1754
1755static void
a2ac85d9 1756prepareTransparentURL(ConnStateData::Pointer & conn, clientHttpRequest *http, char *url, const char *req_hdr)
62e76326 1757{
3f38a55e 1758 char *host;
1759
d048c262 1760 http->flags.transparent = 1;
3f38a55e 1761
1762 if (*url != '/')
62e76326 1763 return; /* already in good shape */
3f38a55e 1764
1765 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1766
1767 if (internalCheck(url)) {
62e76326 1768 /* prepend our name & port */
1769 http->uri = xstrdup(internalLocalUri(NULL, url));
1770 http->flags.internal = 1;
3f38a55e 1771 } else if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
62e76326 1772 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1773 strlen(host);
1774 http->uri = (char *)xcalloc(url_sz, 1);
1775 snprintf(http->uri, url_sz, "%s://%s%s",
1776 conn->port->protocol, host, url);
1777 debug(33, 5) ("TRANSPARENT HOST REWRITE: '%s'\n", http->uri);
c8be6d7b 1778 } else {
62e76326 1779 /* Put the local socket IP address as the hostname. */
1780 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1781 http->uri = (char *)xcalloc(url_sz, 1);
1782 snprintf(http->uri, url_sz, "%s://%s:%d%s",
98242069 1783 http->getConn()->port->protocol,
1784 inet_ntoa(http->getConn()->me.sin_addr),
1785 ntohs(http->getConn()->me.sin_port), url);
62e76326 1786 debug(33, 5) ("TRANSPARENT REWRITE: '%s'\n", http->uri);
c8be6d7b 1787 }
c8be6d7b 1788}
1789
7a2f978b 1790/*
1791 * parseHttpRequest()
1792 *
1793 * Returns
c4b7a5a9 1794 * NULL on incomplete requests
528b2c61 1795 * a ClientSocketContext structure on success or failure.
c4b7a5a9 1796 * Sets result->flags.parsed_ok to 0 if failed to parse the request.
1797 * Sets result->flags.parsed_ok to 1 if we have a good request.
7a2f978b 1798 */
528b2c61 1799static ClientSocketContext *
a2ac85d9 1800parseHttpRequest(ConnStateData::Pointer & conn, method_t * method_p,
62e76326 1801 char **prefix_p, size_t * req_line_sz_p)
7a2f978b 1802{
1803 char *inbuf = NULL;
7a2f978b 1804 char *url = NULL;
1805 char *req_hdr = NULL;
3f38a55e 1806 char *t;
450e0c10 1807 HttpVersion http_ver;
2334c194 1808 char *end;
7a2f978b 1809 size_t header_sz; /* size of headers, not including first line */
ea285003 1810 size_t prefix_sz; /* size of whole request (req-line + headers) */
c68e9c6b 1811 size_t req_sz;
edce4d98 1812 clientHttpRequest *http;
528b2c61 1813 ClientSocketContext *result;
1814 StoreIOBuffer tempBuffer;
3f38a55e 1815 char *http_version;
7a2f978b 1816
6792f0d3 1817 /* pre-set these values to make aborting simpler */
1818 *prefix_p = NULL;
1819 *method_p = METHOD_NONE;
6792f0d3 1820
3f38a55e 1821 /* Read the HTTP message. HTTP/0.9 is detected by the absence of a HTTP signature */
62e76326 1822
3f38a55e 1823 if ((t = (char *)memchr(conn->in.buf, '\n', conn->in.notYetUsed)) == NULL) {
62e76326 1824 debug(33, 5) ("Incomplete request, waiting for end of request line\n");
1825 return NULL;
7a2f978b 1826 }
62e76326 1827
3f38a55e 1828 *req_line_sz_p = t - conn->in.buf + 1;
1829 http_version = findTrailingHTTPVersion(conn->in.buf);
62e76326 1830
3f38a55e 1831 if (http_version) {
62e76326 1832 if ((req_sz = headersEnd(conn->in.buf, conn->in.notYetUsed)) == 0) {
1833 debug(33, 5) ("Incomplete request, waiting for end of headers\n");
1834 return NULL;
1835 }
3f38a55e 1836 } else {
62e76326 1837 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
1838 req_sz = t - conn->in.buf + 1; /* HTTP/0.9 requests */
3f38a55e 1839 }
1840
c8be6d7b 1841 assert(req_sz <= conn->in.notYetUsed);
c68e9c6b 1842 /* Use memcpy, not strdup! */
e6ccf245 1843 inbuf = (char *)xmalloc(req_sz + 1);
c68e9c6b 1844 xmemcpy(inbuf, conn->in.buf, req_sz);
1845 *(inbuf + req_sz) = '\0';
3f38a55e 1846 /* and adjust http_version to point into the new copy */
62e76326 1847
3f38a55e 1848 if (http_version)
62e76326 1849 http_version = inbuf + (http_version - conn->in.buf);
3f38a55e 1850
1851 /* Barf on NULL characters in the headers */
1852 if (strlen(inbuf) != req_sz) {
62e76326 1853 debug(33, 1) ("parseHttpRequest: Requestheader contains NULL characters\n");
3f38a55e 1854#if TRY_TO_IGNORE_THIS
62e76326 1855
1856 return parseHttpRequestAbort(conn, "error:invalid-request");
3f38a55e 1857#endif
62e76326 1858
3f38a55e 1859 }
7a2f978b 1860
edce4d98 1861 /* Is there a legitimate first line to the headers ? */
3f38a55e 1862 if ((result = clientParseHttpRequestLine(inbuf, conn, method_p, &url,
62e76326 1863 &http_ver, http_version))) {
1864 /* something wrong, abort */
1865 xfree(inbuf);
1866 return result;
99edd1c3 1867 }
62e76326 1868
c68e9c6b 1869 /*
1870 * Process headers after request line
c8be6d7b 1871 * TODO: Use httpRequestParse here.
c68e9c6b 1872 */
3f38a55e 1873 req_hdr = inbuf + *req_line_sz_p;
62e76326 1874
3f38a55e 1875 header_sz = req_sz - *req_line_sz_p;
62e76326 1876
754b9ce9 1877 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
62e76326 1878
2334c194 1879 end = req_hdr + header_sz;
62e76326 1880
04990e2d 1881 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
99edd1c3 1882
99edd1c3 1883 prefix_sz = end - inbuf;
62e76326 1884
ea285003 1885 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
62e76326 1886 (int) prefix_sz, (int) *req_line_sz_p);
1887
c8be6d7b 1888 assert(prefix_sz <= conn->in.notYetUsed);
7a2f978b 1889
1890 /* Ok, all headers are received */
528b2c61 1891 http = new ClientHttpRequest;
62e76326 1892
7a2f978b 1893 http->http_ver = http_ver;
62e76326 1894
98242069 1895 http->setConn(conn);
62e76326 1896
99edd1c3 1897 http->req_sz = prefix_sz;
62e76326 1898
528b2c61 1899 result = ClientSocketContextNew(http);
62e76326 1900
c8be6d7b 1901 tempBuffer.data = result->reqbuf;
62e76326 1902
c8be6d7b 1903 tempBuffer.length = HTTP_REQBUF_SZ;
62e76326 1904
0655fa4d 1905 ClientStreamData newServer = new clientReplyContext(http);
1906
1907 ClientStreamData newClient = result;
1908
edce4d98 1909 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 1910 clientReplyStatus, newServer, clientSocketRecipient,
1911 clientSocketDetach, newClient, tempBuffer);
62e76326 1912
e6ccf245 1913 *prefix_p = (char *)xmalloc(prefix_sz + 1);
62e76326 1914
99edd1c3 1915 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
62e76326 1916
99edd1c3 1917 *(*prefix_p + prefix_sz) = '\0';
62e76326 1918
0f1bc304 1919 dlinkAdd(http, &http->active, &ClientActiveRequests);
7a2f978b 1920
edce4d98 1921 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n",
62e76326 1922 (*prefix_p) + *req_line_sz_p);
3f38a55e 1923
ba9ebd0a 1924#if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
62e76326 1925
7a2f978b 1926 if ((t = strchr(url, '#'))) /* remove HTML anchors */
62e76326 1927 *t = '\0';
1928
ba9ebd0a 1929#endif
7a2f978b 1930
3f38a55e 1931 /* Rewrite the URL in transparent or accelerator mode */
a46d2c0e 1932 if (conn->transparent()) {
62e76326 1933 prepareTransparentURL(conn, http, url, req_hdr);
3f38a55e 1934 } else if (conn->port->accel) {
62e76326 1935 prepareAcceleratedURL(conn, http, url, req_hdr);
2f2749d7 1936 } else if (internalCheck(url)) {
1937 /* prepend our name & port */
1938 http->uri = xstrdup(internalLocalUri(NULL, url));
1939 http->flags.internal = 1;
1940 http->flags.accel = 1;
3f38a55e 1941 }
1942
1943 if (!http->uri) {
62e76326 1944 /* No special rewrites have been applied above, use the
1945 * requested url. may be rewritten later, so make extra room */
1946 int url_sz = strlen(url) + Config.appendDomainLen + 5;
1947 http->uri = (char *)xcalloc(url_sz, 1);
1948 strcpy(http->uri, url);
3f38a55e 1949 }
62e76326 1950
c8be6d7b 1951 setLogUri(http, http->uri);
93f9abd0 1952 debug(33, 5) ("parseHttpRequest: Complete request received\n");
7a2f978b 1953 xfree(inbuf);
c4b7a5a9 1954 result->flags.parsed_ok = 1;
c8be6d7b 1955 return result;
3f38a55e 1956
7a2f978b 1957}
1958
c8be6d7b 1959int
a46d2c0e 1960ConnStateData::getAvailableBufferLength() const
c8be6d7b 1961{
a46d2c0e 1962 return in.allocatedSize - in.notYetUsed;
c8be6d7b 1963}
1964
1965void
a46d2c0e 1966ConnStateData::makeSpaceAvailable()
c8be6d7b 1967{
a46d2c0e 1968 if (getAvailableBufferLength() < 2) {
1969 in.buf = (char *)memReallocBuf(in.buf, in.allocatedSize * 2, &in.allocatedSize);
62e76326 1970 debug(33, 2) ("growing request buffer: notYetUsed=%ld size=%ld\n",
a46d2c0e 1971 (long) in.notYetUsed, (long) in.allocatedSize);
c8be6d7b 1972 }
1973}
1974
1975void
0655fa4d 1976ConnStateData::addContextToQueue(ClientSocketContext * context)
c8be6d7b 1977{
0655fa4d 1978 ClientSocketContext::Pointer *S;
62e76326 1979
0655fa4d 1980 for (S = (ClientSocketContext::Pointer *) & currentobject; S->getRaw();
62e76326 1981 S = &(*S)->next)
1982
1983 ;
c8be6d7b 1984 *S = context;
62e76326 1985
0655fa4d 1986 ++nrequests;
c8be6d7b 1987}
1988
1989int
0655fa4d 1990ConnStateData::getConcurrentRequestCount() const
c8be6d7b 1991{
1992 int result = 0;
0655fa4d 1993 ClientSocketContext::Pointer *T;
62e76326 1994
0655fa4d 1995 for (T = (ClientSocketContext::Pointer *) &currentobject;
1996 T->getRaw(); T = &(*T)->next, ++result)
62e76326 1997
1998 ;
c8be6d7b 1999 return result;
2000}
2001
2002int
a2ac85d9 2003connReadWasError(ConnStateData::Pointer & conn, comm_err_t flag, int size, int xerrno)
c8be6d7b 2004{
c4b7a5a9 2005 if (flag != COMM_OK) {
62e76326 2006 debug(50, 2) ("connReadWasError: FD %d: got flag %d\n", conn->fd, flag);
2007 return 1;
c4b7a5a9 2008 }
62e76326 2009
c8be6d7b 2010 if (size < 0) {
f3400a93 2011 if (!ignoreErrno(xerrno)) {
2012 debug(50, 2) ("connReadWasError: FD %d: %s\n", conn->fd, xstrerr(xerrno));
62e76326 2013 return 1;
2014 } else if (conn->in.notYetUsed == 0) {
2015 debug(50, 2) ("connReadWasError: FD %d: no data to process (%s)\n",
f3400a93 2016 conn->fd, xstrerr(xerrno));
62e76326 2017 }
c8be6d7b 2018 }
62e76326 2019
c8be6d7b 2020 return 0;
2021}
2022
2023int
a2ac85d9 2024connFinishedWithConn(ConnStateData::Pointer & conn, int size)
c8be6d7b 2025{
2026 if (size == 0) {
0655fa4d 2027 if (conn->getConcurrentRequestCount() == 0 && conn->in.notYetUsed == 0) {
62e76326 2028 /* no current or pending requests */
2029 debug(33, 4) ("connFinishedWithConn: FD %d closed\n", conn->fd);
2030 return 1;
2031 } else if (!Config.onoff.half_closed_clients) {
2032 /* admin doesn't want to support half-closed client sockets */
2033 debug(33, 3) ("connFinishedWithConn: FD %d aborted (half_closed_clients disabled)\n", conn->fd);
2034 return 1;
2035 }
c8be6d7b 2036 }
62e76326 2037
c8be6d7b 2038 return 0;
2039}
2040
2041void
a2ac85d9 2042connNoteUseOfBuffer(ConnStateData::Pointer & conn, size_t byteCount)
c8be6d7b 2043{
2044 assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
2045 conn->in.notYetUsed -= byteCount;
e6ccf245 2046 debug(33, 5) ("conn->in.notYetUsed = %u\n", (unsigned) conn->in.notYetUsed);
c8be6d7b 2047 /*
2048 * If there is still data that will be used,
2049 * move it to the beginning.
2050 */
62e76326 2051
c8be6d7b 2052 if (conn->in.notYetUsed > 0)
62e76326 2053 xmemmove(conn->in.buf, conn->in.buf + byteCount,
2054 conn->in.notYetUsed);
c8be6d7b 2055}
2056
2057int
a2ac85d9 2058connKeepReadingIncompleteRequest(ConnStateData::Pointer & conn)
c8be6d7b 2059{
2060 return conn->in.notYetUsed >= Config.maxRequestHeaderSize ? 0 : 1;
2061}
2062
2063void
a2ac85d9 2064connCancelIncompleteRequests(ConnStateData::Pointer & conn)
c8be6d7b 2065{
528b2c61 2066 ClientSocketContext *context = parseHttpRequestAbort(conn, "error:request-too-large");
2067 clientStreamNode *node = context->getClientReplyContext();
c8be6d7b 2068 assert(!connKeepReadingIncompleteRequest(conn));
e6ccf245 2069 debug(33, 1) ("Request header is too large (%u bytes)\n",
62e76326 2070 (unsigned) conn->in.notYetUsed);
c8be6d7b 2071 debug(33, 1) ("Config 'request_header_max_size'= %ld bytes.\n",
62e76326 2072 (long int) Config.maxRequestHeaderSize);
0655fa4d 2073 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2074 assert (repContext);
2075 repContext->setReplyToError(ERR_TOO_BIG,
2076 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
2077 &conn->peer.sin_addr, NULL, NULL, NULL);
2078 context->registerWithConn();
528b2c61 2079 context->pullData();
c8be6d7b 2080}
2081
7a2f978b 2082static void
a2ac85d9 2083clientMaybeReadData(ConnStateData::Pointer &conn, int do_next_read)
7a2f978b 2084{
c4b7a5a9 2085 if (do_next_read) {
62e76326 2086 conn->flags.readMoreRequests = 1;
a46d2c0e 2087 conn->readSomeData();
c4b7a5a9 2088 }
2089}
2090
2091static void
a2ac85d9 2092clientAfterReadingRequests(int fd, ConnStateData::Pointer &conn, int do_next_read)
c4b7a5a9 2093{
2094 fde *F = &fd_table[fd];
2095
2096 /* Check if a half-closed connection was aborted in the middle */
62e76326 2097
c4b7a5a9 2098 if (F->flags.socket_eof) {
62e76326 2099 if (conn->in.notYetUsed != conn->body.size_left) {
2100 /* != 0 when no request body */
2101 /* Partial request received. Abort client connection! */
98242069 2102 debug(33, 3) ("clientAfterReadingRequests: FD %d aborted, partial request\n", fd);
62e76326 2103 comm_close(fd);
2104 return;
2105 }
c4b7a5a9 2106 }
2107
2108 clientMaybeReadData (conn, do_next_read);
2109}
2110
c4b7a5a9 2111static void
a2ac85d9 2112clientProcessRequest(ConnStateData::Pointer &conn, ClientSocketContext *context, method_t method, char *prefix, size_t req_line_sz)
c4b7a5a9 2113{
2114 clientHttpRequest *http = context->http;
190154cf 2115 HttpRequest *request = NULL;
c4b7a5a9 2116 /* We have an initial client stream in place should it be needed */
2117 /* setup our private context */
2118 connNoteUseOfBuffer(conn, http->req_sz);
2119
0655fa4d 2120 context->registerWithConn();
c4b7a5a9 2121
2122 if (context->flags.parsed_ok == 0) {
62e76326 2123 clientStreamNode *node = context->getClientReplyContext();
50c09fc4 2124 debug(33, 1) ("clientProcessRequest: Invalid Request\n");
0655fa4d 2125 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2126 assert (repContext);
2127 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, NULL,
2128 &conn->peer.sin_addr, NULL, conn->in.buf, NULL);
62e76326 2129 assert(context->http->out.offset == 0);
2130 context->pullData();
2131 conn->flags.readMoreRequests = 0;
2132 return;
c4b7a5a9 2133 }
2134
2135 if ((request = urlParse(method, http->uri)) == NULL) {
62e76326 2136 clientStreamNode *node = context->getClientReplyContext();
2137 debug(33, 5) ("Invalid URL: %s\n", http->uri);
0655fa4d 2138 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2139 assert (repContext);
2140 repContext->setReplyToError(
2141 ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri,
2142 &conn->peer.sin_addr, NULL, NULL, NULL);
62e76326 2143 assert(context->http->out.offset == 0);
2144 context->pullData();
2145 conn->flags.readMoreRequests = 0;
2146 return;
2147 }
c4b7a5a9 2148
528b2c61 2149 /* compile headers */
2150 /* we should skip request line! */
2151 if (!httpRequestParseHeader(request, prefix + req_line_sz))
62e76326 2152 if (http->http_ver.major >= 1)
2153 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
2154 http->uri, prefix);
2155
528b2c61 2156 /* continue anyway? */
c4b7a5a9 2157
2158 request->flags.accelerated = http->flags.accel;
62e76326 2159
d048c262 2160 request->flags.transparent = http->flags.transparent;
2161
c4b7a5a9 2162 if (!http->flags.internal) {
62e76326 2163 if (internalCheck(request->urlpath.buf())) {
2164 if (internalHostnameIs(request->host) &&
2165 request->port == getMyPort()) {
2166 http->flags.internal = 1;
2167 } else if (internalStaticCheck(request->urlpath.buf())) {
2168 xstrncpy(request->host, internalHostname(),
2169 SQUIDHOSTNAMELEN);
2170 request->port = getMyPort();
2171 http->flags.internal = 1;
2172 }
2173 }
c4b7a5a9 2174 }
2175
c4b7a5a9 2176 request->flags.internal = http->flags.internal;
2177 setLogUri (http, urlCanonicalClean(request));
2178 request->client_addr = conn->peer.sin_addr;
47b0c1fa 2179 request->client_port = ntohs(conn->peer.sin_port);
c4b7a5a9 2180 request->my_addr = conn->me.sin_addr;
2181 request->my_port = ntohs(conn->me.sin_port);
2182 request->http_ver = http->http_ver;
62e76326 2183
c4b7a5a9 2184 if (!urlCheckRequest(request) ||
62e76326 2185 httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) {
2186 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2187 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2188 assert (repContext);
2189 repContext->setReplyToError(ERR_UNSUP_REQ,
2190 HTTP_NOT_IMPLEMENTED, request->method, NULL,
2191 &conn->peer.sin_addr, request, NULL, NULL);
62e76326 2192 assert(context->http->out.offset == 0);
2193 context->pullData();
2194 conn->flags.readMoreRequests = 0;
2195 return;
c4b7a5a9 2196 }
2197
2198
2199 if (!clientIsContentLengthValid(request)) {
62e76326 2200 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2201 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2202 assert (repContext);
2203 repContext->setReplyToError(ERR_INVALID_REQ,
2204 HTTP_LENGTH_REQUIRED, request->method, NULL,
2205 &conn->peer.sin_addr, request, NULL, NULL);
62e76326 2206 assert(context->http->out.offset == 0);
2207 context->pullData();
2208 conn->flags.readMoreRequests = 0;
2209 return;
c4b7a5a9 2210 }
2211
2212 http->request = requestLink(request);
2213 clientSetKeepaliveFlag(http);
2214 /* Do we expect a request-body? */
62e76326 2215
c4b7a5a9 2216 if (request->content_length > 0) {
62e76326 2217 conn->body.size_left = request->content_length;
2218 request->body_connection = conn;
2219 /* Is it too large? */
2220
2221 if (!clientIsRequestBodyValid(request->content_length) ||
2222 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
2223 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2224 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2225 assert (repContext);
2226 repContext->setReplyToError(ERR_TOO_BIG,
2227 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
2228 &conn->peer.sin_addr, http->request, NULL, NULL);
62e76326 2229 assert(context->http->out.offset == 0);
2230 context->pullData();
2231 conn->flags.readMoreRequests = 0;
2232 return;
2233 }
2234
2235 context->mayUseConnection(true);
c4b7a5a9 2236 }
2237
2238 /* If this is a CONNECT, don't schedule a read - ssl.c will handle it */
2239 if (http->request->method == METHOD_CONNECT)
62e76326 2240 context->mayUseConnection(true);
2241
c4b7a5a9 2242 clientAccessCheck(http);
2243}
2244
2245static void
a2ac85d9 2246connStripBufferWhitespace (ConnStateData::Pointer &conn)
c4b7a5a9 2247{
2248 while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
62e76326 2249 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
2250 --conn->in.notYetUsed;
c4b7a5a9 2251 }
2252}
2253
2254static int
a2ac85d9 2255connOkToAddRequest(ConnStateData::Pointer &conn)
c4b7a5a9 2256{
0655fa4d 2257 int result = conn->getConcurrentRequestCount() < (Config.onoff.pipeline_prefetch ? 2 : 1);
62e76326 2258
c4b7a5a9 2259 if (!result) {
50c09fc4 2260 debug(33, 3) ("connOkToAddRequest: FD %d max concurrent requests reached\n",
62e76326 2261 conn->fd);
50c09fc4 2262 debug(33, 5) ("connOkToAddRequest: FD %d defering new request until one is done\n",
62e76326 2263 conn->fd);
c4b7a5a9 2264 }
62e76326 2265
c4b7a5a9 2266 return result;
2267}
2268
c4b7a5a9 2269static void
2270clientReadRequest(int fd, char *buf, size_t size, comm_err_t flag, int xerrno,
62e76326 2271 void *data)
c4b7a5a9 2272{
a2ac85d9 2273 ConnStateData::Pointer conn ((ConnStateData *)data);
a46d2c0e 2274 conn->reading(false);
7a2f978b 2275 method_t method;
08e70b8f 2276 char *prefix = NULL;
528b2c61 2277 ClientSocketContext *context;
a46d2c0e 2278 bool do_next_read = 1; /* the default _is_ to read data! - adrian */
c4b7a5a9 2279
2280 assert (fd == conn->fd);
2281
2282 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
62e76326 2283
c4b7a5a9 2284 if (flag == COMM_ERR_CLOSING) {
2285 return;
2286 }
62e76326 2287
44db45e8 2288 /*
2289 * Don't reset the timeout value here. The timeout value will be
2290 * set to Config.Timeout.request by httpAccept() and
2291 * clientWriteComplete(), and should apply to the request as a
2292 * whole, not individual read() calls. Plus, it breaks our
2293 * lame half-close detection
2294 */
f3400a93 2295 if (connReadWasError(conn, flag, size, xerrno)) {
62e76326 2296 comm_close(fd);
2297 return;
7a2f978b 2298 }
c4b7a5a9 2299
2300 if (flag == COMM_OK) {
62e76326 2301 if (size > 0) {
2302 kb_incr(&statCounter.client_http.kbytes_in, size);
2303 conn->in.notYetUsed += size;
d048c262 2304 conn->in.buf[conn->in.notYetUsed] = '\0'; /* Terminate the string */
62e76326 2305 } else if (size == 0) {
2306 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
2307
2308 if (connFinishedWithConn(conn, size)) {
2309 comm_close(fd);
2310 return;
2311 }
2312
2313 /* It might be half-closed, we can't tell */
2314 fd_table[fd].flags.socket_eof = 1;
2315
a46d2c0e 2316 commMarkHalfClosed(fd);
2317
2318 do_next_read = 0;
62e76326 2319
2320 fd_note(fd, "half-closed");
2321
2322 /* There is one more close check at the end, to detect aborted
2323 * (partial) requests. At this point we can't tell if the request
2324 * is partial.
2325 */
2326 /* Continue to process previously read data */
2327 }
c4b7a5a9 2328 }
2329
2330
94439e4e 2331 /* Process request body if any */
50c09fc4 2332 if (conn->in.notYetUsed > 0 && conn->body.callback != NULL) {
2333 ClientBody body(conn);
2334 body.process();
2335 }
62e76326 2336
94439e4e 2337 /* Process next request */
0655fa4d 2338 if (conn->getConcurrentRequestCount() == 0)
62e76326 2339 fd_note(conn->fd, "Reading next request");
c8be6d7b 2340
a46d2c0e 2341 /* XXX: if we read *exactly* two requests, and the client sends no more,
2342 * if pipelined requests are off, we will *never* parse and insert the
2343 * second. the corner condition is due to the parsing being tied to the
2344 * read, not the presence of data in the buffer.
2345 */
c8be6d7b 2346 while (conn->in.notYetUsed > 0 && conn->body.size_left == 0) {
62e76326 2347 size_t req_line_sz;
2348 connStripBufferWhitespace (conn);
2349
2350 if (conn->in.notYetUsed == 0) {
2351 clientAfterReadingRequests(fd, conn, do_next_read);
2352 return;
2353 }
2354
2355 /* Limit the number of concurrent requests to 2 */
2356 if (!connOkToAddRequest(conn)) {
62e76326 2357 return;
2358 }
2359
2360 /* Should not be needed anymore */
2361 /* Terminate the string */
2362 conn->in.buf[conn->in.notYetUsed] = '\0';
2363
2364 /* Process request */
2365 context = parseHttpRequest(conn,
2366 &method, &prefix, &req_line_sz);
2367
2368 /* partial or incomplete request */
2369 if (!context) {
2370 safe_free(prefix);
2371
2372 if (!connKeepReadingIncompleteRequest(conn))
2373 connCancelIncompleteRequests(conn);
2374
2375 break; /* conn->in.notYetUsed > 0 && conn->body.size_left == 0 */
2376 }
2377
2378 /* status -1 or 1 */
2379 if (context) {
2380 commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout,
2381 context->http);
2382
2383 clientProcessRequest(conn, context, method, prefix, req_line_sz);
2384
2385 safe_free(prefix);
2386
2387 if (context->mayUseConnection()) {
2388 debug (33, 3) ("clientReadRequest: Not reading, as this request may need the connection\n");
2389 do_next_read = 0;
2390 break;
2391 }
2392
53ef4763 2393 if (!conn->flags.readMoreRequests) {
2394 conn->flags.readMoreRequests = 1;
62e76326 2395 break;
2396 }
2397
2398 continue; /* while offset > 0 && body.size_left == 0 */
2399 }
94439e4e 2400 } /* while offset > 0 && conn->body.size_left == 0 */
62e76326 2401
c4b7a5a9 2402 clientAfterReadingRequests(fd, conn, do_next_read);
94439e4e 2403}
2404
2405/* file_read like function, for reading body content */
2406void
190154cf 2407clientReadBody(HttpRequest * request, char *buf, size_t size, CBCB * callback,
62e76326 2408 void *cbdata)
94439e4e 2409{
a2ac85d9 2410 ConnStateData::Pointer conn = request->body_connection;
62e76326 2411
a2ac85d9 2412 if (conn.getRaw() == NULL) {
62e76326 2413 debug(33, 5) ("clientReadBody: no body to read, request=%p\n", request);
2414 callback(buf, 0, cbdata); /* Signal end of body */
2415 return;
7a2f978b 2416 }
62e76326 2417
c8be6d7b 2418 debug(33, 2) ("clientReadBody: start fd=%d body_size=%lu in.notYetUsed=%ld cb=%p req=%p\n",
62e76326 2419 conn->fd, (unsigned long int) conn->body.size_left,
2420 (unsigned long int) conn->in.notYetUsed, callback, request);
94439e4e 2421 conn->body.callback = callback;
7ec0ff5c 2422 conn->body.cbdata = cbdataReference(cbdata);
94439e4e 2423 conn->body.buf = buf;
2424 conn->body.bufsize = size;
2425 conn->body.request = requestLink(request);
50c09fc4 2426 ClientBody body (conn);
2427 body.process();
94439e4e 2428}
2429
a2ac85d9 2430ClientBody::ClientBody(ConnStateData::Pointer &aConn) : conn(aConn), buf (NULL), callback(NULL), request(NULL)
50c09fc4 2431{}
2432
2433void
2434ClientBody::preProcessing()
94439e4e 2435{
50c09fc4 2436 callback = conn->body.callback;
2437 request = conn->body.request;
94439e4e 2438 /* Note: request is null while eating "aborted" transfers */
50c09fc4 2439 debug(33, 2) ("clientBody::process: start fd=%d body_size=%lu in.notYetUsed=%lu cb=%p req=%p\n",
62e76326 2440 conn->fd, (unsigned long int) conn->body.size_left,
2441 (unsigned long int) conn->in.notYetUsed, callback, request);
50c09fc4 2442}
2443
2444/* Called by clientReadRequest to process body content */
2445void
2446ClientBody::process()
2447{
2448 preProcessing();
62e76326 2449
50c09fc4 2450 if (conn->in.notYetUsed)
2451 processBuffer();
2452 else
2453 conn->readSomeData();
2454}
62e76326 2455
50c09fc4 2456void
2457ClientBody::processBuffer()
2458{
2459 /* Some sanity checks... */
2460 assert(conn->body.size_left > 0);
2461 assert(conn->in.notYetUsed > 0);
2462 assert(callback != NULL);
2463 buf = conn->body.buf;
2464 assert(buf != NULL);
2465 /* How much do we have to process? */
2466 size_t size = conn->in.notYetUsed;
62e76326 2467
50c09fc4 2468 if (size > conn->body.size_left) /* only process the body part */
2469 size = conn->body.size_left;
62e76326 2470
50c09fc4 2471 if (size > conn->body.bufsize) /* don't copy more than requested */
2472 size = conn->body.bufsize;
62e76326 2473
50c09fc4 2474 xmemcpy(buf, conn->in.buf, size);
62e76326 2475
50c09fc4 2476 conn->body.size_left -= size;
62e76326 2477
50c09fc4 2478 /* Move any remaining data */
2479 conn->in.notYetUsed -= size;
62e76326 2480
50c09fc4 2481 if (conn->in.notYetUsed > 0)
2482 xmemmove(conn->in.buf, conn->in.buf + size, conn->in.notYetUsed);
62e76326 2483
50c09fc4 2484 /* Remove request link if this is the last part of the body, as
2485 * clientReadRequest automatically continues to process next request */
2486 if (conn->body.size_left <= 0 && request != NULL)
2487 request->body_connection = NULL;
62e76326 2488
50c09fc4 2489 /* Remove clientReadBody arguments (the call is completed) */
2490 conn->body.request = NULL;
62e76326 2491
50c09fc4 2492 conn->body.callback = NULL;
62e76326 2493
50c09fc4 2494 conn->body.buf = NULL;
62e76326 2495
50c09fc4 2496 conn->body.bufsize = 0;
2497
2498 /* Remember that we have touched the body, not restartable */
2499 if (request != NULL) {
2500 request->flags.body_sent = 1;
2501 conn->body.request = NULL;
2502 }
62e76326 2503
50c09fc4 2504 /* Invoke callback function */
7ec0ff5c 2505 void *cbdata;
62e76326 2506
7ec0ff5c 2507 if (cbdataReferenceValidDone(conn->body.cbdata, &cbdata))
2508 callback(buf, size, cbdata);
62e76326 2509
50c09fc4 2510 if (request != NULL) {
2511 requestUnlink(request); /* Linked in clientReadBody */
28ee8ce5 2512 }
50c09fc4 2513
2514 debug(33, 2) ("ClientBody::process: end fd=%d size=%lu body_size=%lu in.notYetUsed=%lu cb=%p req=%p\n",
2515 conn->fd, (unsigned long int)size, (unsigned long int) conn->body.size_left,
2516 (unsigned long) conn->in.notYetUsed, callback, request);
94439e4e 2517}
2518
2519/* A dummy handler that throws away a request-body */
2d72d4fd 2520static void
e6ccf245 2521clientReadBodyAbortHandler(char *buf, ssize_t size, void *data)
94439e4e 2522{
c8be6d7b 2523 static char bodyAbortBuf[SQUID_TCP_SO_RCVBUF];
94439e4e 2524 ConnStateData *conn = (ConnStateData *) data;
e6ccf245 2525 debug(33, 2) ("clientReadBodyAbortHandler: fd=%d body_size=%lu in.notYetUsed=%lu\n",
62e76326 2526 conn->fd, (unsigned long int) conn->body.size_left,
2527 (unsigned long) conn->in.notYetUsed);
2528
94439e4e 2529 if (size != 0 && conn->body.size_left != 0) {
62e76326 2530 debug(33, 3) ("clientReadBodyAbortHandler: fd=%d shedule next read\n",
2531 conn->fd);
2532 conn->body.callback = clientReadBodyAbortHandler;
2533 conn->body.buf = bodyAbortBuf;
2534 conn->body.bufsize = sizeof(bodyAbortBuf);
7ec0ff5c 2535 conn->body.cbdata = cbdataReference(data);
94439e4e 2536 }
55e44db9 2537
2538 if (conn->closing())
2539 comm_close(conn->fd);
94439e4e 2540}
2541
2542/* Abort a body request */
2543int
190154cf 2544clientAbortBody(HttpRequest * request)
94439e4e 2545{
a2ac85d9 2546 ConnStateData::Pointer conn = request->body_connection;
94439e4e 2547 char *buf;
2548 CBCB *callback;
94439e4e 2549 request->body_connection = NULL;
62e76326 2550
a2ac85d9 2551 if (conn.getRaw() == NULL || conn->body.size_left <= 0)
62e76326 2552 return 0; /* No body to abort */
2553
94439e4e 2554 if (conn->body.callback != NULL) {
62e76326 2555 buf = conn->body.buf;
2556 callback = conn->body.callback;
62e76326 2557 assert(request == conn->body.request);
2558 conn->body.buf = NULL;
2559 conn->body.callback = NULL;
62e76326 2560 conn->body.request = NULL;
7ec0ff5c 2561 void *cbdata;
2562
2563 if (cbdataReferenceValidDone(conn->body.cbdata, &cbdata))
2564 callback(buf, -1, cbdata); /* Signal abort to clientReadBody caller */
2565
1bb6a805 2566 conn->body.cbdata = NULL;
2567
62e76326 2568 requestUnlink(request);
94439e4e 2569 }
62e76326 2570
a2ac85d9 2571 clientReadBodyAbortHandler(NULL, -1, conn.getRaw()); /* Install abort handler */
50c09fc4 2572 /* ClientBody::process() */
94439e4e 2573 return 1; /* Aborted */
7a2f978b 2574}
2575
2576/* general lifetime handler for HTTP requests */
2577static void
2578requestTimeout(int fd, void *data)
2579{
ad63ceea 2580#if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
7a2f978b 2581 ConnStateData *conn = data;
badd8ff0 2582 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
62e76326 2583
dec5db5d 2584 if (fd_table[fd].wstate) {
2585 /* FIXME: If this code is reinstated, check the conn counters,
2586 * not the fd table state
2587 */
62e76326 2588 /*
2589 * Some data has been sent to the client, just close the FD
2590 */
2591 comm_close(fd);
7a2f978b 2592 } else if (conn->nrequests) {
62e76326 2593 /*
2594 * assume its a persistent connection; just close it
2595 */
2596 comm_close(fd);
7a2f978b 2597 } else {
62e76326 2598 /*
2599 * Generate an error
2600 */
2601 clientHttpRequest **H;
2602 clientStreamNode *node;
2603 clientHttpRequest *http =
2604 parseHttpRequestAbort(conn, "error:Connection%20lifetime%20expired");
2605 node = http->client_stream.tail->prev->data;
0655fa4d 2606 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2607 assert (repContext);
2608 repContext->setReplyToError(ERR_LIFETIME_EXP,
2609 HTTP_REQUEST_TIMEOUT, METHOD_NONE, "N/A", &conn->peer.sin_addr,
2610 NULL, NULL, NULL);
62e76326 2611 /* No requests can be outstanded */
2612 assert(conn->chr == NULL);
2613 /* add to the client request queue */
2614
2615 for (H = &conn->chr; *H; H = &(*H)->next)
2616
2617 ;
2618 *H = http;
2619
2620 clientStreamRead(http->client_stream.tail->data, http, 0,
2621 HTTP_REQBUF_SZ, context->reqbuf);
2622
2623 /*
2624 * if we don't close() here, we still need a timeout handler!
2625 */
2626 commSetTimeout(fd, 30, requestTimeout, conn);
2627
2628 /*
2629 * Aha, but we don't want a read handler!
2630 */
2631 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
7a2f978b 2632 }
62e76326 2633
af57a2e3 2634#else
2635 /*
62e76326 2636 * Just close the connection to not confuse browsers
2637 * using persistent connections. Some browsers opens
2638 * an connection and then does not use it until much
2639 * later (presumeably because the request triggering
2640 * the open has already been completed on another
2641 * connection)
2642 */
ad63ceea 2643 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
62e76326 2644
af57a2e3 2645 comm_close(fd);
62e76326 2646
af57a2e3 2647#endif
7a2f978b 2648}
2649
b5c39993 2650static void
2651clientLifetimeTimeout(int fd, void *data)
2652{
e6ccf245 2653 clientHttpRequest *http = (clientHttpRequest *)data;
edce4d98 2654 debug(33,
62e76326 2655 1) ("WARNING: Closing client %s connection due to lifetime timeout\n",
a2ac85d9 2656 inet_ntoa(http->getConn()->peer.sin_addr));
b5c39993 2657 debug(33, 1) ("\t%s\n", http->uri);
2658 comm_close(fd);
2659}
2660
a46d2c0e 2661static bool
2662okToAccept()
7a2f978b 2663{
3d6629c6 2664 static time_t last_warn = 0;
62e76326 2665
3d6629c6 2666 if (fdNFree() >= RESERVED_FD)
a46d2c0e 2667 return true;
62e76326 2668
3d6629c6 2669 if (last_warn + 15 < squid_curtime) {
62e76326 2670 debug(33, 0) ("WARNING! Your cache is running out of filedescriptors\n");
2671 last_warn = squid_curtime;
3d6629c6 2672 }
62e76326 2673
a46d2c0e 2674 return false;
7a2f978b 2675}
2676
c8be6d7b 2677ConnStateData *
62e76326 2678
3f38a55e 2679connStateCreate(struct sockaddr_in *peer, struct sockaddr_in *me, int fd, http_port_list *port)
c8be6d7b 2680{
a46d2c0e 2681 ConnStateData *result = new ConnStateData;
c4b7a5a9 2682 result->peer = *peer;
2683 result->log_addr = peer->sin_addr;
c8be6d7b 2684 result->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
c4b7a5a9 2685 result->me = *me;
c8be6d7b 2686 result->fd = fd;
e6ccf245 2687 result->in.buf = (char *)memAllocBuf(CLIENT_REQ_BUF_SZ, &result->in.allocatedSize);
3f38a55e 2688 result->port = cbdataReference(port);
62e76326 2689
2690 if (port->transparent)
2691 {
2692
2693 struct sockaddr_in dst;
2694
2695 if (clientNatLookup(fd, *me, *peer, &dst) == 0) {
2696 result->me = dst; /* XXX This should be moved to another field */
a46d2c0e 2697 result->transparent(true);
62e76326 2698 }
3f38a55e 2699 }
62e76326 2700
c4b7a5a9 2701 result->flags.readMoreRequests = 1;
c8be6d7b 2702 return result;
2703}
2704
bf8e5903 2705/* Handle a new connection on HTTP socket. */
7a2f978b 2706void
ee0989f2 2707httpAccept(int sock, int newfd, ConnectionDetail *details,
62e76326 2708 comm_err_t flag, int xerrno, void *data)
7a2f978b 2709{
3f38a55e 2710 http_port_list *s = (http_port_list *)data;
7a2f978b 2711 ConnStateData *connState = NULL;
02d1422b 2712
2713 if (flag == COMM_ERR_CLOSING) {
2714 return;
2715 }
2716
a46d2c0e 2717 if (!okToAccept())
2718 AcceptLimiter::Instance().defer (sock, httpAccept, data);
2719 else
2720 /* kick off another one for later */
2721 comm_accept(sock, httpAccept, data);
c4b7a5a9 2722
62e76326 2723 if (flag != COMM_OK) {
62e76326 2724 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
f3400a93 2725 sock, xstrerr(xerrno));
62e76326 2726 return;
2727 }
2728
2729 debug(33, 4) ("httpAccept: FD %d: accepted\n", newfd);
2730 fd_note(newfd, "client http connect");
2731 connState = connStateCreate(&details->peer, &details->me, newfd, s);
62e76326 2732 comm_add_close_handler(newfd, connStateFree, connState);
2733
2734 if (Config.onoff.log_fqdn)
2735 fqdncache_gethostbyaddr(details->peer.sin_addr, FQDN_LOOKUP_IF_MISS);
2736
2737 commSetTimeout(newfd, Config.Timeout.request, requestTimeout, connState);
2738
3898f57f 2739#if USE_IDENT
62e76326 2740
2741 ACLChecklist identChecklist;
2742
2743 identChecklist.src_addr = details->peer.sin_addr;
2744
2745 identChecklist.my_addr = details->me.sin_addr;
2746
2747 identChecklist.my_port = ntohs(details->me.sin_port);
2748
b448c119 2749 identChecklist.accessList = Config.accessList.identLookup;
2750
2751 if (identChecklist.fastCheck())
62e76326 2752 identStart(&details->me, &details->peer, clientIdentDone, connState);
2753
b448c119 2754 identChecklist.accessList = NULL;
2755
3898f57f 2756#endif
62e76326 2757
a46d2c0e 2758 connState->readSomeData();
62e76326 2759
2760 clientdbEstablished(details->peer.sin_addr, 1);
2761
2762 incoming_sockets_accepted++;
7a2f978b 2763}
2764
1f7c9178 2765#if USE_SSL
2766
2767/* negotiate an SSL connection */
2768static void
2769clientNegotiateSSL(int fd, void *data)
2770{
e6ccf245 2771 ConnStateData *conn = (ConnStateData *)data;
1f7c9178 2772 X509 *client_cert;
a7ad6e4e 2773 SSL *ssl = fd_table[fd].ssl;
1f7c9178 2774 int ret;
2775
a7ad6e4e 2776 if ((ret = SSL_accept(ssl)) <= 0) {
62e76326 2777 int ssl_error = SSL_get_error(ssl, ret);
2778
2779 switch (ssl_error) {
2780
2781 case SSL_ERROR_WANT_READ:
2782 commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
2783 return;
2784
2785 case SSL_ERROR_WANT_WRITE:
2786 commSetSelect(fd, COMM_SELECT_WRITE, clientNegotiateSSL, conn, 0);
2787 return;
2788
2789 default:
2790 debug(81, 1) ("clientNegotiateSSL: Error negotiating SSL connection on FD %d: %s (%d/%d)\n",
2791 fd, ERR_error_string(ERR_get_error(), NULL), ssl_error, ret);
2792 comm_close(fd);
2793 return;
2794 }
2795
2796 /* NOTREACHED */
1f7c9178 2797 }
62e76326 2798
27d8545c 2799 debug(83, 5) ("clientNegotiateSSL: FD %d negotiated cipher %s\n", fd,
62e76326 2800 SSL_get_cipher(fd_table[fd].ssl));
1f7c9178 2801
2802 client_cert = SSL_get_peer_certificate(fd_table[fd].ssl);
62e76326 2803
1f7c9178 2804 if (client_cert != NULL) {
62e76326 2805 debug(83, 5) ("clientNegotiateSSL: FD %d client certificate: subject: %s\n",
2806 fd, X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
1f7c9178 2807
62e76326 2808 debug(83, 5) ("clientNegotiateSSL: FD %d client certificate: issuer: %s\n",
2809 fd, X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
1f7c9178 2810
62e76326 2811 X509_free(client_cert);
1f7c9178 2812 } else {
62e76326 2813 debug(83, 5) ("clientNegotiateSSL: FD %d has no certificate.\n", fd);
1f7c9178 2814 }
2815
a46d2c0e 2816 conn->readSomeData();
1f7c9178 2817}
2818
2819/* handle a new HTTPS connection */
2820static void
15595aab 2821httpsAccept(int sock, int newfd, ConnectionDetail *details,
62e76326 2822 comm_err_t flag, int xerrno, void *data)
1f7c9178 2823{
3f38a55e 2824 https_port_list *s = (https_port_list *)data;
2825 SSL_CTX *sslContext = s->sslContext;
1f7c9178 2826 ConnStateData *connState = NULL;
1f7c9178 2827 SSL *ssl;
2828 int ssl_error;
c4b7a5a9 2829
02d1422b 2830 if (flag == COMM_ERR_CLOSING) {
2831 return;
2832 }
2833
a46d2c0e 2834 if (!okToAccept())
2835 AcceptLimiter::Instance().defer (sock, httpsAccept, data);
2836 else
2837 /* kick off another one for later */
2838 comm_accept(sock, httpsAccept, data);
2839
c4b7a5a9 2840 if (flag != COMM_OK) {
62e76326 2841 errno = xerrno;
2842 debug(50, 1) ("httpsAccept: FD %d: accept failure: %s\n",
f3400a93 2843 sock, xstrerr(xerrno));
62e76326 2844 return;
c4b7a5a9 2845 }
62e76326 2846
c4b7a5a9 2847 if ((ssl = SSL_new(sslContext)) == NULL) {
62e76326 2848 ssl_error = ERR_get_error();
2849 debug(83, 1) ("httpsAccept: Error allocating handle: %s\n",
2850 ERR_error_string(ssl_error, NULL));
2851 return;
c4b7a5a9 2852 }
2853
2854 SSL_set_fd(ssl, newfd);
2855 fd_table[newfd].ssl = ssl;
2856 fd_table[newfd].read_method = &ssl_read_method;
2857 fd_table[newfd].write_method = &ssl_write_method;
2858 debug(50, 5) ("httpsAccept: FD %d accepted, starting SSL negotiation.\n", newfd);
3f38a55e 2859 fd_note(newfd, "client https connect");
62e76326 2860
3f38a55e 2861 connState = connStateCreate(&details->peer, &details->me, newfd, (http_port_list *)s);
2862 connState->port = (http_port_list *)cbdataReference(s);
c4b7a5a9 2863 comm_add_close_handler(newfd, connStateFree, connState);
62e76326 2864
c4b7a5a9 2865 if (Config.onoff.log_fqdn)
62e76326 2866 fqdncache_gethostbyaddr(details->peer.sin_addr, FQDN_LOOKUP_IF_MISS);
2867
c4b7a5a9 2868 commSetTimeout(newfd, Config.Timeout.request, requestTimeout, connState);
62e76326 2869
1f7c9178 2870#if USE_IDENT
62e76326 2871
8000a965 2872 ACLChecklist identChecklist;
62e76326 2873
15595aab 2874 identChecklist.src_addr = details->peer.sin_addr;
62e76326 2875
15595aab 2876 identChecklist.my_addr = details->me.sin_addr;
62e76326 2877
15595aab 2878 identChecklist.my_port = ntohs(details->me.sin_port);
62e76326 2879
010af6e6 2880 identChecklist.accessList = Config.accessList.identLookup;
2881
2882 if (identChecklist.fastCheck())
62e76326 2883 identStart(&details->me, &details->peer, clientIdentDone, connState);
2884
010af6e6 2885 identChecklist.accessList = NULL;
2886
1f7c9178 2887#endif
62e76326 2888
c4b7a5a9 2889 commSetSelect(newfd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
62e76326 2890
15595aab 2891 clientdbEstablished(details->peer.sin_addr, 1);
62e76326 2892
3f38a55e 2893 incoming_sockets_accepted++;
1f7c9178 2894}
2895
2896#endif /* USE_SSL */
2897
15df8349 2898
d193a436 2899static void
15df8349 2900clientHttpConnectionsOpen(void)
2901{
3f38a55e 2902 http_port_list *s;
15df8349 2903 int fd;
62e76326 2904
7e3ce7b9 2905 for (s = Config.Sockaddr.http; s; s = s->next) {
62e76326 2906 if (MAXHTTPPORTS == NHttpSockets) {
2907 debug(1, 1) ("WARNING: You have too many 'http_port' lines.\n");
2908 debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
2909 continue;
2910 }
2911
2912 enter_suid();
2913 fd = comm_open(SOCK_STREAM,
bdb741f4 2914 IPPROTO_TCP,
62e76326 2915 s->s.sin_addr,
2916 ntohs(s->s.sin_port), COMM_NONBLOCKING, "HTTP Socket");
2917 leave_suid();
2918
2919 if (fd < 0)
2920 continue;
2921
2922 comm_listen(fd);
2923
2924 comm_accept(fd, httpAccept, s);
2925
2315dd40 2926 debug(1, 1) ("Accepting %s HTTP connections at %s, port %d, FD %d.\n",
2927 s->transparent ? "transparently proxied" :
2928 s->accel ? "accelerated" :
24450a8f 2929 "",
62e76326 2930 inet_ntoa(s->s.sin_addr), (int) ntohs(s->s.sin_port), fd);
2931
2932 HttpSockets[NHttpSockets++] = fd;
15df8349 2933 }
d193a436 2934}
2935
2936#if USE_SSL
2937static void
2938clientHttpsConnectionsOpen(void)
2939{
2940 https_port_list *s;
d193a436 2941 int fd;
62e76326 2942
3f38a55e 2943 for (s = Config.Sockaddr.https; s; s = (https_port_list *)s->http.next) {
62e76326 2944 if (MAXHTTPPORTS == NHttpSockets) {
2945 debug(1, 1) ("WARNING: You have too many 'https_port' lines.\n");
2946 debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
2947 continue;
2948 }
2949
2950 enter_suid();
2951 fd = comm_open(SOCK_STREAM,
bdb741f4 2952 IPPROTO_TCP,
62e76326 2953 s->http.s.sin_addr,
2954 ntohs(s->http.s.sin_port), COMM_NONBLOCKING, "HTTPS Socket");
2955 leave_suid();
2956
2957 if (fd < 0)
2958 continue;
2959
2960 comm_listen(fd);
2961
2962 comm_accept(fd, httpsAccept, s);
2963
62e76326 2964 debug(1, 1) ("Accepting HTTPS connections at %s, port %d, FD %d.\n",
2965 inet_ntoa(s->http.s.sin_addr), (int) ntohs(s->http.s.sin_port), fd);
2966
2967 HttpSockets[NHttpSockets++] = fd;
1f7c9178 2968 }
d193a436 2969}
2970
2971#endif
2972
2973void
2974clientOpenListenSockets(void)
2975{
2976 clientHttpConnectionsOpen();
2977#if USE_SSL
62e76326 2978
d193a436 2979 clientHttpsConnectionsOpen();
1f7c9178 2980#endif
62e76326 2981
15df8349 2982 if (NHttpSockets < 1)
62e76326 2983 fatal("Cannot open HTTP Port");
15df8349 2984}
edce4d98 2985
c0fbae16 2986void
2987clientHttpConnectionsClose(void)
2988{
2989 int i;
62e76326 2990
c0fbae16 2991 for (i = 0; i < NHttpSockets; i++) {
62e76326 2992 if (HttpSockets[i] >= 0) {
2993 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets[i]);
2994 comm_close(HttpSockets[i]);
2995 HttpSockets[i] = -1;
2996 }
c0fbae16 2997 }
62e76326 2998
c0fbae16 2999 NHttpSockets = 0;
3000}
f66a9ef4 3001
3002int
190154cf 3003varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
f66a9ef4 3004{
3005 const char *vary = request->vary_headers;
528b2c61 3006 int has_vary = httpHeaderHas(&entry->getReply()->header, HDR_VARY);
f66a9ef4 3007#if X_ACCELERATOR_VARY
62e76326 3008
edce4d98 3009 has_vary |=
62e76326 3010 httpHeaderHas(&entry->getReply()->header, HDR_X_ACCELERATOR_VARY);
f66a9ef4 3011#endif
62e76326 3012
f66a9ef4 3013 if (!has_vary || !entry->mem_obj->vary_headers) {
62e76326 3014 if (vary) {
3015 /* Oops... something odd is going on here.. */
3016 debug(33,
3017 1)
3018 ("varyEvaluateMatch: Oops. Not a Vary object on second attempt, '%s' '%s'\n",
3019 entry->mem_obj->url, vary);
3020 safe_free(request->vary_headers);
3021 return VARY_CANCEL;
3022 }
3023
3024 if (!has_vary) {
3025 /* This is not a varying object */
3026 return VARY_NONE;
3027 }
3028
3029 /* virtual "vary" object found. Calculate the vary key and
3030 * continue the search
3031 */
3032 vary = httpMakeVaryMark(request, entry->getReply());
3033
3034 if (vary) {
3035 request->vary_headers = xstrdup(vary);
3036 return VARY_OTHER;
3037 } else {
3038 /* Ouch.. we cannot handle this kind of variance */
3039 /* XXX This cannot really happen, but just to be complete */
3040 return VARY_CANCEL;
3041 }
f66a9ef4 3042 } else {
62e76326 3043 if (!vary) {
3044 vary = httpMakeVaryMark(request, entry->getReply());
3045
3046 if (vary)
3047 request->vary_headers = xstrdup(vary);
3048 }
3049
3050 if (!vary) {
3051 /* Ouch.. we cannot handle this kind of variance */
3052 /* XXX This cannot really happen, but just to be complete */
3053 return VARY_CANCEL;
3054 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
3055 return VARY_MATCH;
3056 } else {
3057 /* Oops.. we have already been here and still haven't
3058 * found the requested variant. Bail out
3059 */
3060 debug(33, 1) ("varyEvaluateMatch: Oops. Not a Vary match on second attempt, '%s' '%s'\n",
3061 entry->mem_obj->url, vary);
3062 return VARY_CANCEL;
3063 }
f66a9ef4 3064 }
3065}
28d4805a 3066
4fb35c3c 3067ACLChecklist *
98242069 3068clientAclChecklistCreate(const acl_access * acl, clientHttpRequest * http)
28d4805a 3069{
4fb35c3c 3070 ACLChecklist *ch;
a2ac85d9 3071 ConnStateData::Pointer conn = http->getConn();
3072 ch = aclChecklistCreate(acl, http->request, conn.getRaw() != NULL ? conn->rfc931 : dash_str);
28d4805a 3073
3074 /*
3075 * hack for ident ACL. It needs to get full addresses, and a place to store
3076 * the ident result on persistent connections...
3077 */
3078 /* connection oriented auth also needs these two lines for it's operation. */
3079 /*
3080 * Internal requests do not have a connection reference, because: A) their
3081 * byte count may be transformed before being applied to an outbound
3082 * connection B) they are internal - any limiting on them should be done on
3083 * the server end.
3084 */
62e76326 3085
a2ac85d9 3086 if (conn.getRaw() != NULL)
3087 ch->conn(conn); /* unreferenced in acl.cc */
28d4805a 3088
3089 return ch;
3090}
a46d2c0e 3091
3092CBDATA_CLASS_INIT(ConnStateData);
3093
3094void *
3095ConnStateData::operator new (size_t)
3096{
3097 CBDATA_INIT_TYPE(ConnStateData);
3098 ConnStateData *result = cbdataAlloc(ConnStateData);
3099 return result;
3100}
3101
3102void
3103ConnStateData::operator delete (void *address)
3104{
3105 ConnStateData *t = static_cast<ConnStateData *>(address);
3106 cbdataFree(t);
3107}
3108
55e44db9 3109ConnStateData::ConnStateData() : transparent_ (false), reading_ (false), closing_ (false)
b65351fa 3110{
3111 openReference = this;
3112}
a46d2c0e 3113
3114bool
3115ConnStateData::transparent() const
3116{
3117 return transparent_;
3118}
3119
3120void
3121ConnStateData::transparent(bool const anInt)
3122{
3123 transparent_ = anInt;
3124}
3125
3126bool
3127ConnStateData::reading() const
3128{
3129 return reading_;
3130}
3131
3132void
3133ConnStateData::reading(bool const newBool)
3134{
3135 assert (reading() != newBool);
3136 reading_ = newBool;
3137}
3138
55e44db9 3139bool
3140ConnStateData::closing() const
3141{
3142 return closing_;
3143}
3144
3145void
3146ConnStateData::closing(bool const newBool)
3147{
3148 assert (closing() != newBool);
3149 closing_ = newBool;
3150}
3151
a46d2c0e 3152char *
3153ConnStateData::In::addressToReadInto() const
3154{
3155 return buf + notYetUsed;
3156}
3157
3158ConnStateData::In::In() : buf (NULL), notYetUsed (0), allocatedSize (0)
3159{}
3160
3161ConnStateData::In::~In()
3162{
3163 if (allocatedSize)
3164 memFreeBuf(allocatedSize, buf);
3165}