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