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