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