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