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