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