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