]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
Moved some http.cc-related prototypes to http.h
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
dd11e0b7 1/*
262a0e14 2 * $Id$
dd11e0b7 3 *
4 * DEBUG: section 33 Client-side Routines
5 * AUTHOR: Duane Wessels
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 8 * ----------------------------------------------------------
dd11e0b7 9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
dd11e0b7 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
9e008dda 23 *
dd11e0b7 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
9e008dda 28 *
dd11e0b7 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
dd11e0b7 33 */
f88bb09c 34
63be0a78 35/**
36 \defgroup ClientSide Client-Side Logics
37 *
d85b8894 38 \section cserrors Errors and client side
edce4d98 39 *
63be0a78 40 \par Problem the first:
41 * the store entry is no longer authoritative on the
edce4d98 42 * reply status. EBITTEST (E_ABORT) is no longer a valid test outside
43 * of client_side_reply.c.
44 * Problem the second: resources are wasted if we delay in cleaning up.
45 * Problem the third we can't depend on a connection close to clean up.
9e008dda 46 *
63be0a78 47 \par Nice thing the first:
9e008dda 48 * Any step in the stream can callback with data
edce4d98 49 * representing an error.
50 * Nice thing the second: once you stop requesting reads from upstream,
51 * upstream can be stopped too.
52 *
63be0a78 53 \par Solution #1:
54 * Error has a callback mechanism to hand over a membuf
9e008dda 55 * with the error content. The failing node pushes that back as the
edce4d98 56 * reply. Can this be generalised to reduce duplicate efforts?
57 * A: Possibly. For now, only one location uses this.
58 * How to deal with pre-stream errors?
59 * Tell client_side_reply that we *want* an error page before any
60 * stream calls occur. Then we simply read as normal.
63be0a78 61 *
62 *
d85b8894 63 \section pconn_logic Persistent connection logic:
63be0a78 64 *
65 \par
66 * requests (httpClientRequest structs) get added to the connection
67 * list, with the current one being chr
9e008dda 68 *
63be0a78 69 \par
70 * The request is *immediately* kicked off, and data flows through
71 * to clientSocketRecipient.
9e008dda 72 *
63be0a78 73 \par
74 * If the data that arrives at clientSocketRecipient is not for the current
75 * request, clientSocketRecipient simply returns, without requesting more
76 * data, or sending it.
77 *
78 \par
9e008dda
AJ
79 * ClientKeepAliveNextRequest will then detect the presence of data in
80 * the next ClientHttpRequest, and will send it, restablishing the
63be0a78 81 * data flow.
edce4d98 82 */
83
582c2af2 84#include "squid.h"
04f55905 85#include "acl/FilledChecklist.h"
65d448bc 86#include "anyp/PortCfg.h"
00406b24 87#include "base/Subscription.h"
d841c88d 88#include "base/TextException.h"
04f55905 89#include "ChunkedCodingParser.h"
95e6d864 90#include "client_db.h"
04f55905
AJ
91#include "client_side_reply.h"
92#include "client_side_request.h"
582c2af2 93#include "client_side.h"
04f55905 94#include "ClientRequestContext.h"
c8be6d7b 95#include "clientStream.h"
c4b7a5a9 96#include "comm.h"
cfd66529 97#include "comm/Connection.h"
d841c88d 98#include "comm/Loops.h"
cbff89ba 99#include "comm/TcpAcceptor.h"
582c2af2
FC
100#include "comm/Write.h"
101#include "CommCalls.h"
8eb0a7ee 102#include "errorpage.h"
056b24a1 103#include "eui/Config.h"
c4ad1349 104#include "fd.h"
04f55905 105#include "fde.h"
fd4624d7 106#include "forward.h"
95e6d864 107#include "fqdncache.h"
5c0c642e 108#include "http.h"
25b6a907 109#include "HttpHdrContRange.h"
528b2c61 110#include "HttpReply.h"
111#include "HttpRequest.h"
4daaf3cb
AJ
112#include "ident/Config.h"
113#include "ident/Ident.h"
308e60be 114#include "internal.h"
cbff89ba 115#include "ipc/FdNotes.h"
fe090a86 116#include "ipc/StartListening.h"
1c7ae5ff 117#include "log/access_log.h"
8a89c28f 118#include "Mem.h"
0eb49b6d 119#include "MemBuf.h"
04f55905 120#include "MemObject.h"
582c2af2
FC
121#include "profiler/Profiler.h"
122#include "protos.h"
1fa9b1a7 123#include "rfc1738.h"
582c2af2 124#include "SquidTime.h"
e1656dc4 125#include "StatCounters.h"
00a7574e 126#include "StatHist.h"
582c2af2
FC
127#include "Store.h"
128#include "TimeOrTag.h"
4e540555 129#include "tools.h"
b1bd952a 130#include "URL.h"
582c2af2
FC
131
132#if USE_AUTH
133#include "auth/UserRequest.h"
134#endif
135#if USE_DELAY_POOLS
136#include "ClientInfo.h"
137#endif
95d2589c
CT
138#if USE_SSL
139#include "ssl/context_storage.h"
140#include "ssl/helper.h"
fd4624d7 141#include "ssl/ServerBump.h"
4db984be 142#include "ssl/support.h"
95d2589c
CT
143#include "ssl/gadgets.h"
144#endif
145#if USE_SSL_CRTD
146#include "ssl/crtd_message.h"
147#include "ssl/certificate_db.h"
148#endif
149
582c2af2
FC
150#if HAVE_LIMITS_H
151#include <limits.h>
152#endif
153#if HAVE_MATH_H
154#include <math.h>
155#endif
95d2589c
CT
156#if HAVE_LIMITS
157#include <limits>
158#endif
159
5492ad1d 160#if LINGERING_CLOSE
161#define comm_close comm_lingering_close
162#endif
163
cbff89ba 164/// dials clientListenerConnectionOpened call
fe090a86
AR
165class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
166{
167public:
65d448bc
AJ
168 typedef void (*Handler)(AnyP::PortCfg *portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
169 ListeningStartedDialer(Handler aHandler, AnyP::PortCfg *aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
8bbb16e3 170 handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}
fe090a86 171
5667a628
AR
172 virtual void print(std::ostream &os) const {
173 startPrint(os) <<
8bbb16e3 174 ", " << FdNote(portTypeNote) << " port=" << (void*)portCfg << ')';
5667a628 175 }
fe090a86
AR
176
177 virtual bool canDial(AsyncCall &) const { return true; }
8bbb16e3 178 virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }
fe090a86
AR
179
180public:
181 Handler handler;
182
183private:
65d448bc 184 AnyP::PortCfg *portCfg; ///< from Config.Sockaddr.http
cbff89ba 185 Ipc::FdNoteId portTypeNote; ///< Type of IPC socket being opened
cbff89ba 186 Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
fe090a86
AR
187};
188
65d448bc 189static void clientListenerConnectionOpened(AnyP::PortCfg *s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);
fe090a86 190
edce4d98 191/* our socket-related context */
62e76326 192
62e76326 193
0655fa4d 194CBDATA_CLASS_INIT(ClientSocketContext);
62e76326 195
0655fa4d 196void *
197ClientSocketContext::operator new (size_t byteCount)
198{
199 /* derived classes with different sizes must implement their own new */
200 assert (byteCount == sizeof (ClientSocketContext));
201 CBDATA_INIT_TYPE(ClientSocketContext);
202 return cbdataAlloc(ClientSocketContext);
203}
62e76326 204
0655fa4d 205void
206ClientSocketContext::operator delete (void *address)
207{
208 cbdataFree (address);
209}
7a2f978b 210
edce4d98 211/* Local functions */
528b2c61 212/* ClientSocketContext */
be364179 213static ClientSocketContext *ClientSocketContextNew(const Comm::ConnectionPointer &clientConn, ClientHttpRequest *);
edce4d98 214/* other */
2b663917 215static IOCB clientWriteComplete;
216static IOCB clientWriteBodyComplete;
e0d28505 217static IOACB httpAccept;
40d34a62 218#if USE_SSL
e0d28505 219static IOACB httpsAccept;
40d34a62 220#endif
8d77a37c 221static CTCB clientLifetimeTimeout;
719c7e0a 222static ClientSocketContext *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
1cf238db 223static ClientSocketContext *parseHttpRequest(ConnStateData *, HttpParser *, HttpRequestMethod *, HttpVersion *);
3898f57f 224#if USE_IDENT
05832ae1 225static IDCB clientIdentDone;
3898f57f 226#endif
edce4d98 227static CSCB clientSocketRecipient;
228static CSD clientSocketDetach;
59a1efb2 229static void clientSetKeepaliveFlag(ClientHttpRequest *);
190154cf 230static int clientIsContentLengthValid(HttpRequest * r);
47f6e231 231static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
50c09fc4 232
c8be6d7b 233static void clientUpdateStatHistCounters(log_type logType, int svc_time);
234static void clientUpdateStatCounters(log_type logType);
235static void clientUpdateHierCounters(HierarchyLogEntry *);
528b2c61 236static bool clientPingHasFinished(ping_data const *aPing);
41ebd397 237void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);
e4a67a80 238#ifndef PURIFY
5c336a3b 239static bool connIsUsable(ConnStateData * conn);
e4a67a80 240#endif
2324cda2 241static int responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer const &receivedData);
1cf238db 242static void ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn);
c8be6d7b 243static void clientUpdateSocketStats(log_type logType, size_t size);
244
84cc2635 245char *skipLeadingSpace(char *aString);
3b299123 246static void connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount);
62e76326 247
65d448bc 248static ConnStateData *connStateCreate(const Comm::ConnectionPointer &client, AnyP::PortCfg *port);
ae7ff0b8 249
c8be6d7b 250
251clientStreamNode *
528b2c61 252ClientSocketContext::getTail() const
c8be6d7b 253{
0655fa4d 254 if (http->client_stream.tail)
255 return (clientStreamNode *)http->client_stream.tail->data;
256
257 return NULL;
c8be6d7b 258}
259
260clientStreamNode *
528b2c61 261ClientSocketContext::getClientReplyContext() const
c8be6d7b 262{
528b2c61 263 return (clientStreamNode *)http->client_stream.tail->prev->data;
c8be6d7b 264}
265
63be0a78 266/**
c4b7a5a9 267 * This routine should be called to grow the inbuf and then
268 * call comm_read().
269 */
270void
a46d2c0e 271ConnStateData::readSomeData()
c4b7a5a9 272{
a46d2c0e 273 if (reading())
62e76326 274 return;
275
73c36fd9 276 debugs(33, 4, HERE << clientConnection << ": reading request...");
62e76326 277
90737510 278 if (!maybeMakeSpaceAvailable())
1368d115 279 return;
62e76326 280
1cf238db 281 typedef CommCbMemFunT<ConnStateData, CommIoCbParams> Dialer;
d1c7f781 282 reader = JobCallback(33, 5, Dialer, this, ConnStateData::clientReadRequest);
73c36fd9 283 comm_read(clientConnection, in.addressToReadInto(), getAvailableBufferLength(), reader);
c4b7a5a9 284}
62e76326 285
c4b7a5a9 286
c8be6d7b 287void
1cf238db 288ClientSocketContext::removeFromConnectionList(ConnStateData * conn)
c8be6d7b 289{
0655fa4d 290 ClientSocketContext::Pointer *tempContextPointer;
1cf238db 291 assert(conn != NULL && cbdataReferenceValid(conn));
94a396a3 292 assert(conn->getCurrentContext() != NULL);
c8be6d7b 293 /* Unlink us from the connection request list */
0655fa4d 294 tempContextPointer = & conn->currentobject;
62e76326 295
0655fa4d 296 while (tempContextPointer->getRaw()) {
62e76326 297 if (*tempContextPointer == this)
298 break;
299
300 tempContextPointer = &(*tempContextPointer)->next;
c8be6d7b 301 }
62e76326 302
0655fa4d 303 assert(tempContextPointer->getRaw() != NULL);
528b2c61 304 *tempContextPointer = next;
305 next = NULL;
c8be6d7b 306}
ea6f43cd 307
0655fa4d 308ClientSocketContext::~ClientSocketContext()
f88bb09c 309{
0655fa4d 310 clientStreamNode *node = getTail();
311
312 if (node) {
313 ClientSocketContext *streamContext = dynamic_cast<ClientSocketContext *> (node->data.getRaw());
314
315 if (streamContext) {
316 /* We are *always* the tail - prevent recursive free */
317 assert(this == streamContext);
318 node->data = NULL;
319 }
320 }
321
322 if (connRegistered_)
323 deRegisterWithConn();
324
325 httpRequestFree(http);
326
edce4d98 327 /* clean up connection links to us */
c53577d9 328 assert(this != next.getRaw());
0655fa4d 329}
62e76326 330
0655fa4d 331void
332ClientSocketContext::registerWithConn()
333{
334 assert (!connRegistered_);
335 assert (http);
94a396a3 336 assert (http->getConn() != NULL);
0655fa4d 337 connRegistered_ = true;
98242069 338 http->getConn()->addContextToQueue(this);
0655fa4d 339}
340
341void
342ClientSocketContext::deRegisterWithConn()
343{
344 assert (connRegistered_);
98242069 345 removeFromConnectionList(http->getConn());
0655fa4d 346 connRegistered_ = false;
347}
348
349void
350ClientSocketContext::connIsFinished()
351{
352 assert (http);
94a396a3 353 assert (http->getConn() != NULL);
0655fa4d 354 deRegisterWithConn();
355 /* we can't handle any more stream data - detach */
356 clientStreamDetach(getTail(), http);
357}
358
fedd1531 359ClientSocketContext::ClientSocketContext() : http(NULL), reply(NULL), next(NULL),
0655fa4d 360 writtenToSocket(0),
361 mayUseConnection_ (false),
362 connRegistered_ (false)
363{
364 memset (reqbuf, '\0', sizeof (reqbuf));
365 flags.deferred = 0;
366 flags.parsed_ok = 0;
367 deferredparams.node = NULL;
368 deferredparams.rep = NULL;
f88bb09c 369}
370
528b2c61 371ClientSocketContext *
be364179 372ClientSocketContextNew(const Comm::ConnectionPointer &client, ClientHttpRequest * http)
f88bb09c 373{
528b2c61 374 ClientSocketContext *newContext;
edce4d98 375 assert(http != NULL);
0655fa4d 376 newContext = new ClientSocketContext;
c8be6d7b 377 newContext->http = http;
be364179 378 newContext->clientConnection = client;
c8be6d7b 379 return newContext;
4d55827a 380}
381
655daa06
AR
382void
383ClientSocketContext::writeControlMsg(HttpControlMsg &msg)
384{
385 HttpReply *rep = msg.reply;
386 Must(rep);
387
388 // apply selected clientReplyContext::buildReplyHeader() mods
389 // it is not clear what headers are required for control messages
390 rep->header.removeHopByHopEntries();
391 rep->header.putStr(HDR_CONNECTION, "keep-alive");
392 httpHdrMangleList(&rep->header, http->request, ROR_REPLY);
393
394 // remember the callback
395 cbControlMsgSent = msg.cbSuccess;
396
397 MemBuf *mb = rep->pack();
398
1ce34ddd
AJ
399 debugs(11, 2, "HTTP Client " << clientConnection);
400 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
401
655daa06
AR
402 AsyncCall::Pointer call = commCbCall(33, 5, "ClientSocketContext::wroteControlMsg",
403 CommIoCbPtrFun(&WroteControlMsg, this));
73c36fd9 404 Comm::Write(clientConnection, mb, call);
655daa06
AR
405
406 delete mb;
407}
408
409/// called when we wrote the 1xx response
410void
e0d28505 411ClientSocketContext::wroteControlMsg(const Comm::ConnectionPointer &conn, char *, size_t, comm_err_t errflag, int xerrno)
655daa06
AR
412{
413 if (errflag == COMM_ERR_CLOSING)
414 return;
415
416 if (errflag == COMM_OK) {
417 ScheduleCallHere(cbControlMsgSent);
418 return;
419 }
420
421 debugs(33, 3, HERE << "1xx writing failed: " << xstrerr(xerrno));
422 // no error notification: see HttpControlMsg.h for rationale and
423 // note that some errors are detected elsewhere (e.g., close handler)
424
425 // close on 1xx errors to be conservative and to simplify the code
426 // (if we do not close, we must notify the source of a failure!)
80463bb4 427 conn->close();
655daa06
AR
428}
429
430/// wroteControlMsg() wrapper: ClientSocketContext is not an AsyncJob
431void
e0d28505 432ClientSocketContext::WroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
655daa06
AR
433{
434 ClientSocketContext *context = static_cast<ClientSocketContext*>(data);
e0d28505 435 context->wroteControlMsg(conn, bufnotused, size, errflag, xerrno);
655daa06
AR
436}
437
edce4d98 438#if USE_IDENT
4d55827a 439static void
edce4d98 440clientIdentDone(const char *ident, void *data)
4d55827a 441{
cc59d02a 442 ConnStateData *conn = (ConnStateData *)data;
73c36fd9 443 xstrncpy(conn->clientConnection->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
e81957b7 444}
447e176b 445#endif
7a2f978b 446
c8be6d7b 447void
448clientUpdateStatCounters(log_type logType)
a7c05555 449{
e4f1fdae 450 ++statCounter.client_http.requests;
62e76326 451
c8be6d7b 452 if (logTypeIsATcpHit(logType))
e4f1fdae 453 ++statCounter.client_http.hits;
62e76326 454
c8be6d7b 455 if (logType == LOG_TCP_HIT)
e4f1fdae 456 ++statCounter.client_http.disk_hits;
c8be6d7b 457 else if (logType == LOG_TCP_MEM_HIT)
e4f1fdae 458 ++statCounter.client_http.mem_hits;
c8be6d7b 459}
460
461void
462clientUpdateStatHistCounters(log_type logType, int svc_time)
463{
e8baef82 464 statCounter.client_http.allSvcTime.count(svc_time);
63be0a78 465 /**
ee1679df 466 * The idea here is not to be complete, but to get service times
467 * for only well-defined types. For example, we don't include
1d7ab0f4 468 * LOG_TCP_REFRESH_FAIL because its not really a cache hit
ee1679df 469 * (we *tried* to validate it, but failed).
470 */
62e76326 471
c8be6d7b 472 switch (logType) {
62e76326 473
1d7ab0f4 474 case LOG_TCP_REFRESH_UNMODIFIED:
e8baef82 475 statCounter.client_http.nearHitSvcTime.count(svc_time);
62e76326 476 break;
477
ee1679df 478 case LOG_TCP_IMS_HIT:
e8baef82 479 statCounter.client_http.nearMissSvcTime.count(svc_time);
62e76326 480 break;
481
ee1679df 482 case LOG_TCP_HIT:
62e76326 483
ee1679df 484 case LOG_TCP_MEM_HIT:
62e76326 485
b540e168 486 case LOG_TCP_OFFLINE_HIT:
e8baef82 487 statCounter.client_http.hitSvcTime.count(svc_time);
62e76326 488 break;
489
ee1679df 490 case LOG_TCP_MISS:
62e76326 491
ee1679df 492 case LOG_TCP_CLIENT_REFRESH_MISS:
e8baef82 493 statCounter.client_http.missSvcTime.count(svc_time);
62e76326 494 break;
495
ee1679df 496 default:
62e76326 497 /* make compiler warnings go away */
498 break;
ee1679df 499 }
c8be6d7b 500}
501
528b2c61 502bool
c8be6d7b 503clientPingHasFinished(ping_data const *aPing)
504{
505 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
62e76326 506 return true;
507
528b2c61 508 return false;
c8be6d7b 509}
510
511void
512clientUpdateHierCounters(HierarchyLogEntry * someEntry)
513{
514 ping_data *i;
62e76326 515
c8547a11 516 switch (someEntry->code) {
517#if USE_CACHE_DIGESTS
62e76326 518
c8547a11 519 case CD_PARENT_HIT:
62e76326 520
a196e1e4 521 case CD_SIBLING_HIT:
95dc7ff4 522 ++ statCounter.cd.times_used;
62e76326 523 break;
c8547a11 524#endif
62e76326 525
c8547a11 526 case SIBLING_HIT:
62e76326 527
c8547a11 528 case PARENT_HIT:
62e76326 529
c8547a11 530 case FIRST_PARENT_MISS:
62e76326 531
c8547a11 532 case CLOSEST_PARENT_MISS:
95dc7ff4 533 ++ statCounter.icp.times_used;
62e76326 534 i = &someEntry->ping;
535
536 if (clientPingHasFinished(i))
e8baef82 537 statCounter.icp.querySvcTime.count(tvSubUsec(i->start, i->stop));
62e76326 538
539 if (i->timeout)
95dc7ff4 540 ++ statCounter.icp.query_timeouts;
62e76326 541
542 break;
543
c8547a11 544 case CLOSEST_PARENT:
62e76326 545
c8547a11 546 case CLOSEST_DIRECT:
95dc7ff4 547 ++ statCounter.netdb.times_used;
62e76326 548
549 break;
550
69c95dd3 551 default:
62e76326 552 break;
17b6e784 553 }
a7c05555 554}
555
528b2c61 556void
557ClientHttpRequest::updateCounters()
c8be6d7b 558{
528b2c61 559 clientUpdateStatCounters(logType);
62e76326 560
528b2c61 561 if (request->errType != ERR_NONE)
95dc7ff4 562 ++ statCounter.client_http.errors;
62e76326 563
528b2c61 564 clientUpdateStatHistCounters(logType,
1cf238db 565 tvSubMsec(start_time, current_time));
62e76326 566
528b2c61 567 clientUpdateHierCounters(&request->hier);
c8be6d7b 568}
569
edce4d98 570void
41ebd397 571prepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry::Pointer &aLogEntry)
7a2f978b 572{
c8be6d7b 573 assert(request);
41ebd397 574 assert(aLogEntry != NULL);
7684c4b1 575
576 if (Config.onoff.log_mime_hdrs) {
577 Packer p;
578 MemBuf mb;
2fe7eff9 579 mb.init();
7684c4b1 580 packerToMemInit(&p, &mb);
a9925b40 581 request->header.packInto(&p);
105d1937
A
582 //This is the request after adaptation or redirection
583 aLogEntry->headers.adapted_request = xstrdup(mb.buf);
584
585 // the virgin request is saved to aLogEntry->request
586 if (aLogEntry->request) {
587 packerClean(&p);
588 mb.reset();
589 packerToMemInit(&p, &mb);
590 aLogEntry->request->header.packInto(&p);
591 aLogEntry->headers.request = xstrdup(mb.buf);
592 }
3ff65596 593
5038f9d8
AR
594#if USE_ADAPTATION
595 const Adaptation::History::Pointer ah = request->adaptLogHistory();
596 if (ah != NULL) {
597 packerClean(&p);
598 mb.reset();
599 packerToMemInit(&p, &mb);
600 ah->lastMeta.packInto(&p);
99690f32 601 aLogEntry->adapt.last_meta = xstrdup(mb.buf);
5038f9d8 602 }
3ff65596
AR
603#endif
604
7684c4b1 605 packerClean(&p);
2fe7eff9 606 mb.clean();
7684c4b1 607 }
608
3ff65596 609#if ICAP_CLIENT
5038f9d8 610 const Adaptation::Icap::History::Pointer ih = request->icapHistory();
3ff65596
AR
611 if (ih != NULL)
612 aLogEntry->icap.processingTime = ih->processingTime();
613#endif
614
c8be6d7b 615 aLogEntry->http.method = request->method;
616 aLogEntry->http.version = request->http_ver;
c8be6d7b 617 aLogEntry->hier = request->hier;
3ff65596
AR
618 if (request->content_length > 0) // negative when no body or unknown length
619 aLogEntry->cache.requestSize += request->content_length;
5b4117d8 620 aLogEntry->cache.extuser = request->extacl_user.termedBuf();
abb929f0 621
2f1431ea 622#if USE_AUTH
a33a428a 623 if (request->auth_user_request != NULL) {
f5691f9c 624 if (request->auth_user_request->username())
a33a428a 625 aLogEntry->cache.authuser = xstrdup(request->auth_user_request->username());
7a2f978b 626 }
2f1431ea 627#endif
64b66b76 628
a119c6ad
AR
629 // Adapted request, if any, inherits and then collects all the stats, but
630 // the virgin request gets logged instead; copy the stats to log them.
631 // TODO: avoid losses by keeping these stats in a shared history object?
64b66b76 632 if (aLogEntry->request) {
a119c6ad 633 aLogEntry->request->dnsWait = request->dnsWait;
64b66b76
CT
634 aLogEntry->request->errType = request->errType;
635 aLogEntry->request->errDetail = request->errDetail;
636 }
c8be6d7b 637}
638
639void
528b2c61 640ClientHttpRequest::logRequest()
641{
f5f9e44c 642 if (!out.size && !logType)
6fd1086a
AR
643 debugs(33, 5, HERE << "logging half-baked transaction: " << log_uri);
644
41ebd397
CT
645 al->icp.opcode = ICP_INVALID;
646 al->url = log_uri;
647 debugs(33, 9, "clientLogRequest: al.url='" << al->url << "'");
62e76326 648
41ebd397
CT
649 if (al->reply) {
650 al->http.code = al->reply->sline.status;
651 al->http.content_type = al->reply->content_type.termedBuf();
6fd1086a 652 } else if (loggingEntry() && loggingEntry()->mem_obj) {
41ebd397
CT
653 al->http.code = loggingEntry()->mem_obj->getReply()->sline.status;
654 al->http.content_type = loggingEntry()->mem_obj->getReply()->content_type.termedBuf();
6fd1086a 655 }
5f8252d2 656
41ebd397 657 debugs(33, 9, "clientLogRequest: http.code='" << al->http.code << "'");
90a8964c 658
6fd1086a 659 if (loggingEntry() && loggingEntry()->mem_obj)
41ebd397 660 al->cache.objectSize = loggingEntry()->contentLen();
cc192b50 661
41ebd397 662 al->cache.caddr.SetNoAddr();
90a8964c 663
28417506 664 if (getConn() != NULL) {
41ebd397
CT
665 al->cache.caddr = getConn()->log_addr;
666 al->cache.port = cbdataReference(getConn()->port);
28417506 667 }
1a86db31 668
41ebd397
CT
669 al->cache.requestSize = req_sz;
670 al->cache.requestHeadersSize = req_sz;
90a8964c 671
41ebd397
CT
672 al->cache.replySize = out.size;
673 al->cache.replyHeadersSize = out.headers_sz;
90a8964c 674
41ebd397 675 al->cache.highOffset = out.offset;
90a8964c 676
41ebd397 677 al->cache.code = logType;
62e76326 678
41ebd397 679 al->cache.msec = tvSubMsec(start_time, current_time);
62e76326 680
6fd1086a 681 if (request)
41ebd397 682 prepareLogWithRequestDetails(request, al);
62e76326 683
1b76e6c1 684 if (getConn() != NULL && getConn()->clientConnection != NULL && getConn()->clientConnection->rfc931[0])
41ebd397 685 al->cache.rfc931 = getConn()->clientConnection->rfc931;
62e76326 686
2ea1afad 687#if USE_SSL && 0
62e76326 688
6fd1086a
AR
689 /* This is broken. Fails if the connection has been closed. Needs
690 * to snarf the ssl details some place earlier..
691 */
692 if (getConn() != NULL)
41ebd397 693 al->cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl);
62e76326 694
a7ad6e4e 695#endif
62e76326 696
6fd1086a 697 ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.log, this);
62e76326 698
41ebd397
CT
699 if (al->reply)
700 checklist->reply = HTTPMSGLOCK(al->reply);
62e76326 701
2efeb0b7 702 if (!Config.accessList.log || checklist->fastCheck() == ACCESS_ALLOWED) {
6fd1086a 703 if (request)
41ebd397
CT
704 al->adapted_request = HTTPMSGLOCK(request);
705 accessLogLog(al, checklist);
6fd1086a 706 updateCounters();
7684c4b1 707
1b76e6c1
AJ
708 if (getConn() != NULL && getConn()->clientConnection != NULL)
709 clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size);
7a2f978b 710 }
2a6ab636 711
6fd1086a 712 delete checklist;
c8be6d7b 713}
714
715void
528b2c61 716ClientHttpRequest::freeResources()
c8be6d7b 717{
528b2c61 718 safe_free(uri);
719 safe_free(log_uri);
720 safe_free(redirect.location);
30abd221 721 range_iter.boundary.clean();
6dd9f4bd 722 HTTPMSGUNLOCK(request);
62e76326 723
528b2c61 724 if (client_stream.tail)
62e76326 725 clientStreamAbort((clientStreamNode *)client_stream.tail->data, this);
c8be6d7b 726}
727
728void
729httpRequestFree(void *data)
730{
59a1efb2 731 ClientHttpRequest *http = (ClientHttpRequest *)data;
c8be6d7b 732 assert(http != NULL);
528b2c61 733 delete http;
7a2f978b 734}
735
a46d2c0e 736bool
737ConnStateData::areAllContextsForThisConnection() const
c8be6d7b 738{
a46d2c0e 739 assert(this != NULL);
0655fa4d 740 ClientSocketContext::Pointer context = getCurrentContext();
62e76326 741
0655fa4d 742 while (context.getRaw()) {
98242069 743 if (context->http->getConn() != this)
a46d2c0e 744 return false;
62e76326 745
746 context = context->next;
c8be6d7b 747 }
62e76326 748
a46d2c0e 749 return true;
c8be6d7b 750}
751
752void
a46d2c0e 753ConnStateData::freeAllContexts()
c8be6d7b 754{
0655fa4d 755 ClientSocketContext::Pointer context;
62e76326 756
c53577d9 757 while ((context = getCurrentContext()).getRaw() != NULL) {
0655fa4d 758 assert(getCurrentContext() !=
759 getCurrentContext()->next);
760 context->connIsFinished();
761 assert (context != currentobject);
c8be6d7b 762 }
763}
764
f692498b
AJ
765/// propagates abort event to all contexts
766void
767ConnStateData::notifyAllContexts(int xerrno)
768{
769 typedef ClientSocketContext::Pointer CSCP;
770 for (CSCP c = getCurrentContext(); c.getRaw(); c = c->next)
771 c->noteIoError(xerrno);
772}
773
7a2f978b 774/* This is a handler normally called by comm_close() */
1cf238db 775void ConnStateData::connStateClosed(const CommCloseCbParams &io)
7a2f978b 776{
6e1d409c 777 deleteThis("ConnStateData::connStateClosed");
a46d2c0e 778}
62e76326 779
6e1d409c 780// cleans up before destructor is called
a2ac85d9 781void
6e1d409c 782ConnStateData::swanSong()
a46d2c0e 783{
73c36fd9 784 debugs(33, 2, HERE << clientConnection);
f35961af 785 flags.readMore = false;
73c36fd9 786 clientdbEstablished(clientConnection->remote, -1); /* decrement */
a46d2c0e 787 assert(areAllContextsForThisConnection());
788 freeAllContexts();
2f1431ea 789#if USE_AUTH
6bf4f823 790 if (auth_user_request != NULL) {
6e1d409c 791 debugs(33, 4, "ConnStateData::swanSong: freeing auth_user_request '" << auth_user_request << "' (this is '" << this << "')");
f5691f9c 792 auth_user_request->onConnectionClose(this);
6bf4f823 793 }
2f1431ea 794#endif
6e1d409c 795
73c36fd9
AJ
796 if (Comm::IsConnOpen(pinning.serverConnection))
797 pinning.serverConnection->close();
798 pinning.serverConnection = NULL;
e3a4aecc 799
73c36fd9
AJ
800 if (Comm::IsConnOpen(clientConnection))
801 clientConnection->close();
802 clientConnection = NULL;
d67acb4e 803
6e1d409c
AR
804 BodyProducer::swanSong();
805 flags.swanSang = true;
a2ac85d9 806}
807
808bool
809ConnStateData::isOpen() const
810{
10b06767 811 return cbdataReferenceValid(this) && // XXX: checking "this" in a method
73c36fd9
AJ
812 Comm::IsConnOpen(clientConnection) &&
813 !fd_table[clientConnection->fd].closing();
a2ac85d9 814}
815
816ConnStateData::~ConnStateData()
817{
818 assert(this != NULL);
73c36fd9 819 debugs(33, 3, HERE << clientConnection);
a2ac85d9 820
821 if (isOpen())
e0236918 822 debugs(33, DBG_IMPORTANT, "BUG: ConnStateData did not close " << clientConnection);
6e1d409c
AR
823
824 if (!flags.swanSang)
e0236918 825 debugs(33, DBG_IMPORTANT, "BUG: ConnStateData was not destroyed properly; " << clientConnection);
62e76326 826
e0db3481 827 cbdataReferenceDone(port);
83fdd41a 828
279152e7
AJ
829 if (bodyPipe != NULL)
830 stopProducingFor(bodyPipe, false);
87f237a9 831
fd4624d7
CT
832#if USE_SSL
833 delete sslServerBump;
8a7fe008 834#endif
7a2f978b 835}
836
63be0a78 837/**
c68e9c6b 838 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
839 * This is the client-side persistent connection flag. We need
840 * to set this relatively early in the request processing
841 * to handle hacks for broken servers and clients.
842 */
843static void
59a1efb2 844clientSetKeepaliveFlag(ClientHttpRequest * http)
c68e9c6b 845{
190154cf 846 HttpRequest *request = http->request;
f6329bc3 847
bf8fe701 848 debugs(33, 3, "clientSetKeepaliveFlag: http_ver = " <<
849 request->http_ver.major << "." << request->http_ver.minor);
850 debugs(33, 3, "clientSetKeepaliveFlag: method = " <<
60745f24 851 RequestMethodStr(request->method));
62e76326 852
4a1acc56
AJ
853 // TODO: move to HttpRequest::hdrCacheInit, just like HttpReply.
854 request->flags.proxy_keepalive = request->persistent() ? 1 : 0;
c68e9c6b 855}
856
31be8b80 857static int
190154cf 858clientIsContentLengthValid(HttpRequest * r)
31be8b80 859{
914b89a2 860 switch (r->method.id()) {
62e76326 861
ffc128c4 862 case METHOD_GET:
62e76326 863
ffc128c4 864 case METHOD_HEAD:
62e76326 865 /* We do not want to see a request entity on GET/HEAD requests */
866 return (r->content_length <= 0 || Config.onoff.request_entities);
867
ffc128c4 868 default:
62e76326 869 /* For other types of requests we don't care */
870 return 1;
ffc128c4 871 }
62e76326 872
ffc128c4 873 /* NOT REACHED */
31be8b80 874}
875
c8be6d7b 876int
47f6e231 877clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength)
efd900cb 878{
c8be6d7b 879 if (Config.maxRequestBodySize &&
62e76326 880 bodyLength > Config.maxRequestBodySize)
881 return 1; /* too large */
882
efd900cb 883 return 0;
884}
885
e4a67a80 886#ifndef PURIFY
5c336a3b 887bool
1cf238db 888connIsUsable(ConnStateData * conn)
c8be6d7b 889{
73c36fd9 890 if (conn == NULL || !cbdataReferenceValid(conn) || !Comm::IsConnOpen(conn->clientConnection))
5c336a3b 891 return false;
62e76326 892
5c336a3b 893 return true;
c8be6d7b 894}
895
e4a67a80 896#endif
897
39cb8c41 898// careful: the "current" context may be gone if we wrote an early response
0655fa4d 899ClientSocketContext::Pointer
900ConnStateData::getCurrentContext() const
c8be6d7b 901{
0655fa4d 902 assert(this);
903 return currentobject;
c8be6d7b 904}
905
906void
2324cda2 907ClientSocketContext::deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData)
528b2c61 908{
bf8fe701 909 debugs(33, 2, "clientSocketRecipient: Deferring request " << http->uri);
528b2c61 910 assert(flags.deferred == 0);
911 flags.deferred = 1;
912 deferredparams.node = node;
913 deferredparams.rep = rep;
2324cda2 914 deferredparams.queuedBuffer = receivedData;
c8be6d7b 915 return;
916}
917
918int
2324cda2 919responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer const & receivedData)
c8be6d7b 920{
2324cda2 921 if (rep == NULL && receivedData.data == NULL && receivedData.length == 0)
62e76326 922 return 1;
923
c8be6d7b 924 return 0;
925}
926
0655fa4d 927bool
928ClientSocketContext::startOfOutput() const
c8be6d7b 929{
0655fa4d 930 return http->out.size == 0;
c8be6d7b 931}
932
528b2c61 933size_t
47f6e231 934ClientSocketContext::lengthToSend(Range<int64_t> const &available)
528b2c61 935{
47f6e231 936 /*the size of available range can always fit in a size_t type*/
937 size_t maximum = (size_t)available.size();
2512d159 938
528b2c61 939 if (!http->request->range)
62e76326 940 return maximum;
941
528b2c61 942 assert (canPackMoreRanges());
62e76326 943
528b2c61 944 if (http->range_iter.debt() == -1)
62e76326 945 return maximum;
946
528b2c61 947 assert (http->range_iter.debt() > 0);
62e76326 948
2512d159 949 /* TODO this + the last line could be a range intersection calculation */
47f6e231 950 if (available.start < http->range_iter.currentSpec()->offset)
2512d159 951 return 0;
952
d85c3078 953 return min(http->range_iter.debt(), (int64_t)maximum);
528b2c61 954}
955
c8be6d7b 956void
528b2c61 957ClientSocketContext::noteSentBodyBytes(size_t bytes)
958{
959 http->out.offset += bytes;
62e76326 960
528b2c61 961 if (!http->request->range)
62e76326 962 return;
963
528b2c61 964 if (http->range_iter.debt() != -1) {
62e76326 965 http->range_iter.debt(http->range_iter.debt() - bytes);
966 assert (http->range_iter.debt() >= 0);
528b2c61 967 }
62e76326 968
dd272b8e 969 /* debt() always stops at -1, below that is a bug */
970 assert (http->range_iter.debt() >= -1);
528b2c61 971}
62e76326 972
528b2c61 973bool
974ClientHttpRequest::multipartRangeRequest() const
975{
976 return request->multipartRangeRequest();
977}
978
979bool
980ClientSocketContext::multipartRangeRequest() const
981{
982 return http->multipartRangeRequest();
983}
984
985void
986ClientSocketContext::sendBody(HttpReply * rep, StoreIOBuffer bodyData)
c8be6d7b 987{
988 assert(rep == NULL);
528b2c61 989
4ad60609 990 if (!multipartRangeRequest() && !http->request->flags.chunked_reply) {
2512d159 991 size_t length = lengthToSend(bodyData.range());
62e76326 992 noteSentBodyBytes (length);
9e008dda
AJ
993 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteBodyComplete",
994 CommIoCbPtrFun(clientWriteBodyComplete, this));
73c36fd9 995 Comm::Write(clientConnection, bodyData.data, length, call, NULL);
62e76326 996 return;
528b2c61 997 }
998
999 MemBuf mb;
2fe7eff9 1000 mb.init();
4ad60609
AR
1001 if (multipartRangeRequest())
1002 packRange(bodyData, &mb);
1003 else
1004 packChunk(bodyData, mb);
2512d159 1005
9e008dda 1006 if (mb.contentSize()) {
2512d159 1007 /* write */
9e008dda
AJ
1008 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
1009 CommIoCbPtrFun(clientWriteComplete, this));
73c36fd9 1010 Comm::Write(clientConnection, &mb, call);
1cf238db 1011 } else
73c36fd9 1012 writeComplete(clientConnection, NULL, 0, COMM_OK);
c8be6d7b 1013}
1014
4ad60609
AR
1015/**
1016 * Packs bodyData into mb using chunked encoding. Packs the last-chunk
1017 * if bodyData is empty.
1018 */
1019void
1020ClientSocketContext::packChunk(const StoreIOBuffer &bodyData, MemBuf &mb)
1021{
1022 const uint64_t length =
1023 static_cast<uint64_t>(lengthToSend(bodyData.range()));
1024 noteSentBodyBytes(length);
1025
c91ca3ce 1026 mb.Printf("%" PRIX64 "\r\n", length);
4ad60609
AR
1027 mb.append(bodyData.data, length);
1028 mb.Printf("\r\n");
1029}
1030
63be0a78 1031/** put terminating boundary for multiparts */
528b2c61 1032static void
30abd221 1033clientPackTermBound(String boundary, MemBuf * mb)
528b2c61 1034{
826a1fed 1035 mb->Printf("\r\n--" SQUIDSTRINGPH "--\r\n", SQUIDSTRINGPRINT(boundary));
4a7a3d56 1036 debugs(33, 6, "clientPackTermBound: buf offset: " << mb->size);
528b2c61 1037}
1038
63be0a78 1039/** appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
528b2c61 1040static void
30abd221 1041clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
528b2c61 1042{
75faaa7a 1043 HttpHeader hdr(hoReply);
528b2c61 1044 Packer p;
1045 assert(rep);
1046 assert(spec);
1047
1048 /* put boundary */
5b4117d8 1049 debugs(33, 5, "clientPackRangeHdr: appending boundary: " << boundary);
528b2c61 1050 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
826a1fed 1051 mb->Printf("\r\n--" SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(boundary));
528b2c61 1052
1053 /* stuff the header with required entries and pack it */
62e76326 1054
a9925b40 1055 if (rep->header.has(HDR_CONTENT_TYPE))
1056 hdr.putStr(HDR_CONTENT_TYPE, rep->header.getStr(HDR_CONTENT_TYPE));
62e76326 1057
528b2c61 1058 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
62e76326 1059
528b2c61 1060 packerToMemInit(&p, mb);
62e76326 1061
a9925b40 1062 hdr.packInto(&p);
62e76326 1063
528b2c61 1064 packerClean(&p);
62e76326 1065
519e0948 1066 hdr.clean();
528b2c61 1067
1068 /* append <crlf> (we packed a header, not a reply) */
2fe7eff9 1069 mb->Printf("\r\n");
528b2c61 1070}
1071
63be0a78 1072/**
bb790702 1073 * extracts a "range" from *buf and appends them to mb, updating
528b2c61 1074 * all offsets and such.
1075 */
c8be6d7b 1076void
2512d159 1077ClientSocketContext::packRange(StoreIOBuffer const &source, MemBuf * mb)
528b2c61 1078{
1079 HttpHdrRangeIter * i = &http->range_iter;
47f6e231 1080 Range<int64_t> available (source.range());
b65351fa 1081 char const *buf = source.data;
62e76326 1082
2512d159 1083 while (i->currentSpec() && available.size()) {
62e76326 1084 const size_t copy_sz = lengthToSend(available);
62e76326 1085
2512d159 1086 if (copy_sz) {
1087 /*
1088 * intersection of "have" and "need" ranges must not be empty
1089 */
1090 assert(http->out.offset < i->currentSpec()->offset + i->currentSpec()->length);
13c2229a 1091 assert(http->out.offset + (int64_t)available.size() > i->currentSpec()->offset);
2512d159 1092
1093 /*
1094 * put boundary and headers at the beginning of a range in a
1095 * multi-range
1096 */
1097
1098 if (http->multipartRangeRequest() && i->debt() == i->currentSpec()->length) {
1099 assert(http->memObject());
1100 clientPackRangeHdr(
1101 http->memObject()->getReply(), /* original reply */
1102 i->currentSpec(), /* current range */
1103 i->boundary, /* boundary, the same for all */
1104 mb);
1105 }
62e76326 1106
2512d159 1107 /*
1108 * append content
1109 */
4a7a3d56 1110 debugs(33, 3, "clientPackRange: appending " << copy_sz << " bytes");
62e76326 1111
2512d159 1112 noteSentBodyBytes (copy_sz);
62e76326 1113
2fe7eff9 1114 mb->append(buf, copy_sz);
62e76326 1115
2512d159 1116 /*
1117 * update offsets
1118 */
1119 available.start += copy_sz;
62e76326 1120
2512d159 1121 buf += copy_sz;
62e76326 1122
2512d159 1123 }
62e76326 1124
c7329b75 1125 if (!canPackMoreRanges()) {
bf8fe701 1126 debugs(33, 3, "clientPackRange: Returning because !canPackMoreRanges.");
c7329b75 1127
1128 if (i->debt() == 0)
1129 /* put terminating boundary for multiparts */
1130 clientPackTermBound(i->boundary, mb);
62e76326 1131
62e76326 1132 return;
c7329b75 1133 }
62e76326 1134
8bcf08e0 1135 int64_t nextOffset = getNextRangeOffset();
62e76326 1136
8bcf08e0 1137 assert (nextOffset >= http->out.offset);
62e76326 1138
8bcf08e0 1139 int64_t skip = nextOffset - http->out.offset;
62e76326 1140
2512d159 1141 /* adjust for not to be transmitted bytes */
8bcf08e0 1142 http->out.offset = nextOffset;
2512d159 1143
c16f948b 1144 if (available.size() <= (uint64_t)skip)
62e76326 1145 return;
1146
2512d159 1147 available.start += skip;
62e76326 1148
2512d159 1149 buf += skip;
1150
1151 if (copy_sz == 0)
1152 return;
528b2c61 1153 }
1154}
1155
63be0a78 1156/** returns expected content length for multi-range replies
528b2c61 1157 * note: assumes that httpHdrRangeCanonize has already been called
1158 * warning: assumes that HTTP headers for individual ranges at the
1159 * time of the actuall assembly will be exactly the same as
1160 * the headers when clientMRangeCLen() is called */
1161int
1162ClientHttpRequest::mRangeCLen()
c8be6d7b 1163{
47f6e231 1164 int64_t clen = 0;
c8be6d7b 1165 MemBuf mb;
528b2c61 1166
86a2f789 1167 assert(memObject());
528b2c61 1168
2fe7eff9 1169 mb.init();
528b2c61 1170 HttpHdrRange::iterator pos = request->range->begin();
62e76326 1171
528b2c61 1172 while (pos != request->range->end()) {
62e76326 1173 /* account for headers for this range */
2fe7eff9 1174 mb.reset();
86a2f789 1175 clientPackRangeHdr(memObject()->getReply(),
62e76326 1176 *pos, range_iter.boundary, &mb);
1177 clen += mb.size;
528b2c61 1178
62e76326 1179 /* account for range content */
1180 clen += (*pos)->length;
528b2c61 1181
4a7a3d56 1182 debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
62e76326 1183 ++pos;
528b2c61 1184 }
62e76326 1185
528b2c61 1186 /* account for the terminating boundary */
2fe7eff9 1187 mb.reset();
62e76326 1188
528b2c61 1189 clientPackTermBound(range_iter.boundary, &mb);
62e76326 1190
528b2c61 1191 clen += mb.size;
1192
2fe7eff9 1193 mb.clean();
62e76326 1194
528b2c61 1195 return clen;
1196}
1197
63be0a78 1198/**
528b2c61 1199 * returns true if If-Range specs match reply, false otherwise
1200 */
1201static int
59a1efb2 1202clientIfRangeMatch(ClientHttpRequest * http, HttpReply * rep)
528b2c61 1203{
a9925b40 1204 const TimeOrTag spec = http->request->header.getTimeOrTag(HDR_IF_RANGE);
528b2c61 1205 /* check for parsing falure */
62e76326 1206
528b2c61 1207 if (!spec.valid)
62e76326 1208 return 0;
1209
528b2c61 1210 /* got an ETag? */
1211 if (spec.tag.str) {
a9925b40 1212 ETag rep_tag = rep->header.getETag(HDR_ETAG);
bf8fe701 1213 debugs(33, 3, "clientIfRangeMatch: ETags: " << spec.tag.str << " and " <<
1214 (rep_tag.str ? rep_tag.str : "<none>"));
62e76326 1215
1216 if (!rep_tag.str)
1217 return 0; /* entity has no etag to compare with! */
1218
1219 if (spec.tag.weak || rep_tag.weak) {
e0236918 1220 debugs(33, DBG_IMPORTANT, "clientIfRangeMatch: Weak ETags are not allowed in If-Range: " << spec.tag.str << " ? " << rep_tag.str);
62e76326 1221 return 0; /* must use strong validator for sub-range requests */
1222 }
1223
bd76482d 1224 return etagIsStrongEqual(rep_tag, spec.tag);
528b2c61 1225 }
62e76326 1226
528b2c61 1227 /* got modification time? */
1228 if (spec.time >= 0) {
86a2f789 1229 return http->storeEntry()->lastmod <= spec.time;
528b2c61 1230 }
62e76326 1231
528b2c61 1232 assert(0); /* should not happen */
1233 return 0;
1234}
1235
63be0a78 1236/**
1237 * generates a "unique" boundary string for multipart responses
528b2c61 1238 * the caller is responsible for cleaning the string */
30abd221 1239String
528b2c61 1240ClientHttpRequest::rangeBoundaryStr() const
1241{
1242 assert(this);
1243 const char *key;
7dbca7a4
AJ
1244 String b(APP_FULLNAME);
1245 b.append(":",1);
86a2f789 1246 key = storeEntry()->getMD5Text();
528b2c61 1247 b.append(key, strlen(key));
1248 return b;
1249}
1250
63be0a78 1251/** adds appropriate Range headers if needed */
528b2c61 1252void
1253ClientSocketContext::buildRangeHeader(HttpReply * rep)
1254{
1255 HttpHeader *hdr = rep ? &rep->header : 0;
1256 const char *range_err = NULL;
190154cf 1257 HttpRequest *request = http->request;
528b2c61 1258 assert(request->range);
1259 /* check if we still want to do ranges */
62e76326 1260
11e3fa1c
AJ
1261 int64_t roffLimit = request->getRangeOffsetLimit();
1262
528b2c61 1263 if (!rep)
62e76326 1264 range_err = "no [parse-able] reply";
528b2c61 1265 else if ((rep->sline.status != HTTP_OK) && (rep->sline.status != HTTP_PARTIAL_CONTENT))
62e76326 1266 range_err = "wrong status code";
a9925b40 1267 else if (hdr->has(HDR_CONTENT_RANGE))
62e76326 1268 range_err = "origin server does ranges";
528b2c61 1269 else if (rep->content_length < 0)
62e76326 1270 range_err = "unknown length";
86a2f789 1271 else if (rep->content_length != http->memObject()->getReply()->content_length)
62e76326 1272 range_err = "INCONSISTENT length"; /* a bug? */
96cbbe03 1273
1274 /* hits only - upstream peer determines correct behaviour on misses, and client_side_reply determines
9e008dda 1275 * hits candidates
96cbbe03 1276 */
a9925b40 1277 else if (logTypeIsATcpHit(http->logType) && http->request->header.has(HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
62e76326 1278 range_err = "If-Range match failed";
528b2c61 1279 else if (!http->request->range->canonize(rep))
62e76326 1280 range_err = "canonization failed";
528b2c61 1281 else if (http->request->range->isComplex())
62e76326 1282 range_err = "too complex range header";
11e3fa1c 1283 else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded(roffLimit))
62e76326 1284 range_err = "range outside range_offset_limit";
1285
528b2c61 1286 /* get rid of our range specs on error */
1287 if (range_err) {
b631f31c 1288 /* XXX We do this here because we need canonisation etc. However, this current
9e008dda 1289 * code will lead to incorrect store offset requests - the store will have the
b631f31c 1290 * offset data, but we won't be requesting it.
1291 * So, we can either re-request, or generate an error
1292 */
bf8fe701 1293 debugs(33, 3, "clientBuildRangeHeader: will not do ranges: " << range_err << ".");
00d77d6b 1294 delete http->request->range;
62e76326 1295 http->request->range = NULL;
c8be6d7b 1296 } else {
3cff087f 1297 /* XXX: TODO: Review, this unconditional set may be wrong. - TODO: review. */
1298 httpStatusLineSet(&rep->sline, rep->sline.version,
1299 HTTP_PARTIAL_CONTENT, NULL);
fedd1531 1300 // web server responded with a valid, but unexpected range.
1301 // will (try-to) forward as-is.
1302 //TODO: we should cope with multirange request/responses
1303 bool replyMatchRequest = rep->content_range != NULL ?
1304 request->range->contains(rep->content_range->spec) :
1305 true;
62e76326 1306 const int spec_count = http->request->range->specs.count;
47f6e231 1307 int64_t actual_clen = -1;
62e76326 1308
47f6e231 1309 debugs(33, 3, "clientBuildRangeHeader: range spec count: " <<
9e008dda 1310 spec_count << " virgin clen: " << rep->content_length);
62e76326 1311 assert(spec_count > 0);
62e76326 1312 /* append appropriate header(s) */
1313
1314 if (spec_count == 1) {
fedd1531 1315 if (!replyMatchRequest) {
1316 hdr->delById(HDR_CONTENT_RANGE);
1317 hdr->putContRange(rep->content_range);
1318 actual_clen = rep->content_length;
1319 //http->range_iter.pos = rep->content_range->spec.begin();
1320 (*http->range_iter.pos)->offset = rep->content_range->spec.offset;
1321 (*http->range_iter.pos)->length = rep->content_range->spec.length;
1322
1323 } else {
1324 HttpHdrRange::iterator pos = http->request->range->begin();
1325 assert(*pos);
1326 /* append Content-Range */
1327
1328 if (!hdr->has(HDR_CONTENT_RANGE)) {
1329 /* No content range, so this was a full object we are
1330 * sending parts of.
1331 */
1332 httpHeaderAddContRange(hdr, **pos, rep->content_length);
1333 }
1334
1335 /* set new Content-Length to the actual number of bytes
1336 * transmitted in the message-body */
1337 actual_clen = (*pos)->length;
62e76326 1338 }
62e76326 1339 } else {
1340 /* multipart! */
1341 /* generate boundary string */
1342 http->range_iter.boundary = http->rangeBoundaryStr();
1343 /* delete old Content-Type, add ours */
a9925b40 1344 hdr->delById(HDR_CONTENT_TYPE);
62e76326 1345 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
826a1fed
FC
1346 "multipart/byteranges; boundary=\"" SQUIDSTRINGPH "\"",
1347 SQUIDSTRINGPRINT(http->range_iter.boundary));
62e76326 1348 /* Content-Length is not required in multipart responses
1349 * but it is always nice to have one */
1350 actual_clen = http->mRangeCLen();
2512d159 1351 /* http->out needs to start where we want data at */
1352 http->out.offset = http->range_iter.currentSpec()->offset;
62e76326 1353 }
1354
1355 /* replace Content-Length header */
1356 assert(actual_clen >= 0);
1357
a9925b40 1358 hdr->delById(HDR_CONTENT_LENGTH);
62e76326 1359
47f6e231 1360 hdr->putInt64(HDR_CONTENT_LENGTH, actual_clen);
62e76326 1361
bf8fe701 1362 debugs(33, 3, "clientBuildRangeHeader: actual content length: " << actual_clen);
62e76326 1363
1364 /* And start the range iter off */
1365 http->range_iter.updateSpec();
c8be6d7b 1366 }
528b2c61 1367}
1368
1369void
1370ClientSocketContext::prepareReply(HttpReply * rep)
1371{
fedd1531 1372 reply = rep;
1373
528b2c61 1374 if (http->request->range)
62e76326 1375 buildRangeHeader(rep);
528b2c61 1376}
1377
1378void
1379ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData)
1380{
1381 prepareReply(rep);
528b2c61 1382 assert (rep);
06a5ae20 1383 MemBuf *mb = rep->pack();
1ce34ddd
AJ
1384
1385 // dump now, so we dont output any body.
1386 debugs(11, 2, "HTTP Client " << clientConnection);
1387 debugs(11, 2, "HTTP Client REPLY:\n---------\n" << mb->buf << "\n----------");
1388
528b2c61 1389 /* Save length of headers for persistent conn checks */
032785bf 1390 http->out.headers_sz = mb->contentSize();
528b2c61 1391#if HEADERS_LOG
62e76326 1392
528b2c61 1393 headersLog(0, 0, http->request->method, rep);
1394#endif
62e76326 1395
c8be6d7b 1396 if (bodyData.data && bodyData.length) {
4ad60609
AR
1397 if (multipartRangeRequest())
1398 packRange(bodyData, mb);
1399 else if (http->request->flags.chunked_reply) {
1400 packChunk(bodyData, *mb);
1401 } else {
2512d159 1402 size_t length = lengthToSend(bodyData.range());
62e76326 1403 noteSentBodyBytes (length);
1404
2fe7eff9 1405 mb->append(bodyData.data, length);
62e76326 1406 }
c8be6d7b 1407 }
62e76326 1408
c8be6d7b 1409 /* write */
425802c8 1410 debugs(33,7, HERE << "sendStartOfMessage schedules clientWriteComplete");
9e008dda
AJ
1411 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
1412 CommIoCbPtrFun(clientWriteComplete, this));
73c36fd9 1413 Comm::Write(clientConnection, mb, call);
032785bf 1414 delete mb;
c8be6d7b 1415}
1416
63be0a78 1417/**
7dc5f514 1418 * Write a chunk of data to a client socket. If the reply is present,
1419 * send the reply headers down the wire too, and clean them up when
1420 * finished.
9e008dda 1421 * Pre-condition:
edce4d98 1422 * The request is one backed by a connection, not an internal request.
1423 * data context is not NULL
1424 * There are no more entries in the stream chain.
2246b732 1425 */
edce4d98 1426static void
59a1efb2 1427clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http,
2324cda2 1428 HttpReply * rep, StoreIOBuffer receivedData)
edce4d98 1429{
edce4d98 1430 /* Test preconditions */
1431 assert(node != NULL);
559da936 1432 PROF_start(clientSocketRecipient);
62e76326 1433 /* TODO: handle this rather than asserting
9e008dda
AJ
1434 * - it should only ever happen if we cause an abort and
1435 * the callback chain loops back to here, so we can simply return.
1436 * However, that itself shouldn't happen, so it stays as an assert for now.
edce4d98 1437 */
1438 assert(cbdataReferenceValid(node));
edce4d98 1439 assert(node->node.next == NULL);
0655fa4d 1440 ClientSocketContext::Pointer context = dynamic_cast<ClientSocketContext *>(node->data.getRaw());
94a396a3 1441 assert(context != NULL);
98242069 1442 assert(connIsUsable(http->getConn()));
5c336a3b 1443
528b2c61 1444 /* TODO: check offset is what we asked for */
62e76326 1445
98242069 1446 if (context != http->getConn()->getCurrentContext()) {
2324cda2 1447 context->deferRecipientForLater(node, rep, receivedData);
559da936 1448 PROF_stop(clientSocketRecipient);
62e76326 1449 return;
edce4d98 1450 }
62e76326 1451
4ad60609
AR
1452 // After sending Transfer-Encoding: chunked (at least), always send
1453 // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
1454 const bool mustSendLastChunk = http->request->flags.chunked_reply &&
4cb2536f 1455 !http->request->flags.stream_error && !context->startOfOutput();
4ad60609 1456 if (responseFinishedOrFailed(rep, receivedData) && !mustSendLastChunk) {
73c36fd9 1457 context->writeComplete(context->clientConnection, NULL, 0, COMM_OK);
559da936 1458 PROF_stop(clientSocketRecipient);
62e76326 1459 return;
edce4d98 1460 }
62e76326 1461
0655fa4d 1462 if (!context->startOfOutput())
2324cda2 1463 context->sendBody(rep, receivedData);
7684c4b1 1464 else {
a4785954 1465 assert(rep);
41ebd397 1466 http->al->reply = HTTPMSGLOCK(rep);
2324cda2 1467 context->sendStartOfMessage(rep, receivedData);
7684c4b1 1468 }
fc68f6b1 1469
559da936 1470 PROF_stop(clientSocketRecipient);
edce4d98 1471}
1472
63be0a78 1473/**
1474 * Called when a downstream node is no longer interested in
edce4d98 1475 * our data. As we are a terminal node, this means on aborts
1476 * only
1477 */
1478void
59a1efb2 1479clientSocketDetach(clientStreamNode * node, ClientHttpRequest * http)
edce4d98 1480{
edce4d98 1481 /* Test preconditions */
1482 assert(node != NULL);
62e76326 1483 /* TODO: handle this rather than asserting
9e008dda
AJ
1484 * - it should only ever happen if we cause an abort and
1485 * the callback chain loops back to here, so we can simply return.
edce4d98 1486 * However, that itself shouldn't happen, so it stays as an assert for now.
1487 */
1488 assert(cbdataReferenceValid(node));
1489 /* Set null by ContextFree */
edce4d98 1490 assert(node->node.next == NULL);
0655fa4d 1491 /* this is the assert discussed above */
e4a67a80 1492 assert(NULL == dynamic_cast<ClientSocketContext *>(node->data.getRaw()));
edce4d98 1493 /* We are only called when the client socket shutsdown.
1494 * Tell the prev pipeline member we're finished
1495 */
1496 clientStreamDetach(node, http);
7a2f978b 1497}
1498
f4f278b5 1499static void
e0d28505 1500clientWriteBodyComplete(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t errflag, int xerrno, void *data)
f4f278b5 1501{
425802c8 1502 debugs(33,7, HERE << "clientWriteBodyComplete schedules clientWriteComplete");
e0d28505 1503 clientWriteComplete(conn, NULL, size, errflag, xerrno, data);
c8be6d7b 1504}
1505
1506void
a46d2c0e 1507ConnStateData::readNextRequest()
c8be6d7b 1508{
73c36fd9 1509 debugs(33, 5, HERE << clientConnection << " reading next req");
bf8fe701 1510
97b32442 1511 fd_note(clientConnection->fd, "Idle client: Waiting for next request");
63be0a78 1512 /**
c8be6d7b 1513 * Set the timeout BEFORE calling clientReadRequest().
1514 */
1cf238db 1515 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876 1516 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
4cb2536f 1517 TimeoutDialer, this, ConnStateData::requestTimeout);
97b32442 1518 commSetConnTimeout(clientConnection, Config.Timeout.clientIdlePconn, timeoutCall);
1cf238db 1519
a46d2c0e 1520 readSomeData();
63be0a78 1521 /** Please don't do anything with the FD past here! */
c8be6d7b 1522}
1523
09d3938c 1524static void
1cf238db 1525ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn)
c8be6d7b 1526{
73c36fd9 1527 debugs(33, 2, HERE << conn->clientConnection << " Sending next");
bf8fe701 1528
63be0a78 1529 /** If the client stream is waiting on a socket write to occur, then */
62e76326 1530
c8be6d7b 1531 if (deferredRequest->flags.deferred) {
63be0a78 1532 /** NO data is allowed to have been sent. */
62e76326 1533 assert(deferredRequest->http->out.size == 0);
63be0a78 1534 /** defer now. */
62e76326 1535 clientSocketRecipient(deferredRequest->deferredparams.node,
1536 deferredRequest->http,
1537 deferredRequest->deferredparams.rep,
1538 deferredRequest->deferredparams.queuedBuffer);
c8be6d7b 1539 }
62e76326 1540
63be0a78 1541 /** otherwise, the request is still active in a callbacksomewhere,
c8be6d7b 1542 * and we are done
f4f278b5 1543 */
f4f278b5 1544}
1545
cf6eb29e 1546/// called when we have successfully finished writing the response
0655fa4d 1547void
1548ClientSocketContext::keepaliveNextRequest()
1a92a1e2 1549{
1cf238db 1550 ConnStateData * conn = http->getConn();
bd4e6ec8 1551
73c36fd9 1552 debugs(33, 3, HERE << "ConnnStateData(" << conn->clientConnection << "), Context(" << clientConnection << ")");
0655fa4d 1553 connIsFinished();
1554
73c36fd9
AJ
1555 if (conn->pinning.pinned && !Comm::IsConnOpen(conn->pinning.serverConnection)) {
1556 debugs(33, 2, HERE << conn->clientConnection << " Connection was pinned but server side gone. Terminating client connection");
1557 conn->clientConnection->close();
d67acb4e
AJ
1558 return;
1559 }
1560
cf6eb29e
CT
1561 /** \par
1562 * We are done with the response, and we are either still receiving request
1563 * body (early response!) or have already stopped receiving anything.
1564 *
1565 * If we are still receiving, then clientParseRequest() below will fail.
1566 * (XXX: but then we will call readNextRequest() which may succeed and
1567 * execute a smuggled request as we are not done with the current request).
1568 *
1569 * If we stopped because we got everything, then try the next request.
1570 *
1571 * If we stopped receiving because of an error, then close now to avoid
1572 * getting stuck and to prevent accidental request smuggling.
1573 */
1574
1575 if (const char *reason = conn->stoppedReceiving()) {
1576 debugs(33, 3, HERE << "closing for earlier request error: " << reason);
1577 conn->clientConnection->close();
1578 return;
1579 }
1580
63be0a78 1581 /** \par
f900210a 1582 * Attempt to parse a request from the request buffer.
1583 * If we've been fed a pipelined request it may already
1584 * be in our read buffer.
1585 *
63be0a78 1586 \par
f900210a 1587 * This needs to fall through - if we're unlucky and parse the _last_ request
1588 * from our read buffer we may never re-register for another client read.
1589 */
1590
f35961af 1591 if (conn->clientParseRequests()) {
73c36fd9 1592 debugs(33, 3, HERE << conn->clientConnection << ": parsed next request from buffer");
f900210a 1593 }
1594
63be0a78 1595 /** \par
f900210a 1596 * Either we need to kick-start another read or, if we have
1597 * a half-closed connection, kill it after the last request.
1598 * This saves waiting for half-closed connections to finished being
1599 * half-closed _AND_ then, sometimes, spending "Timeout" time in
1600 * the keepalive "Waiting for next request" state.
1601 */
73c36fd9 1602 if (commIsHalfClosed(conn->clientConnection->fd) && (conn->getConcurrentRequestCount() == 0)) {
bf8fe701 1603 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: half-closed client with no pending requests, closing");
73c36fd9 1604 conn->clientConnection->close();
f900210a 1605 return;
1606 }
1607
0655fa4d 1608 ClientSocketContext::Pointer deferredRequest;
62e76326 1609
63be0a78 1610 /** \par
f900210a 1611 * At this point we either have a parsed request (which we've
1612 * kicked off the processing for) or not. If we have a deferred
1613 * request (parsed but deferred for pipeling processing reasons)
1614 * then look at processing it. If not, simply kickstart
1615 * another read.
1616 */
1617
1618 if ((deferredRequest = conn->getCurrentContext()).getRaw()) {
73c36fd9 1619 debugs(33, 3, HERE << conn->clientConnection << ": calling PushDeferredIfNeeded");
62e76326 1620 ClientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
f35961af 1621 } else if (conn->flags.readMore) {
73c36fd9 1622 debugs(33, 3, HERE << conn->clientConnection << ": calling conn->readNextRequest()");
f900210a 1623 conn->readNextRequest();
f35961af
AR
1624 } else {
1625 // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
1b76e6c1 1626 debugs(33, DBG_IMPORTANT, HERE << "abandoning " << conn->clientConnection);
f900210a 1627 }
1a92a1e2 1628}
1629
c8be6d7b 1630void
1631clientUpdateSocketStats(log_type logType, size_t size)
1632{
1633 if (size == 0)
62e76326 1634 return;
1635
c8be6d7b 1636 kb_incr(&statCounter.client_http.kbytes_out, size);
62e76326 1637
c8be6d7b 1638 if (logTypeIsATcpHit(logType))
62e76326 1639 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
c8be6d7b 1640}
1641
63be0a78 1642/**
528b2c61 1643 * increments iterator "i"
63be0a78 1644 * used by clientPackMoreRanges
1645 *
1646 \retval true there is still data available to pack more ranges
9e008dda 1647 \retval false
63be0a78 1648 */
528b2c61 1649bool
1650ClientSocketContext::canPackMoreRanges() const
1651{
63be0a78 1652 /** first update iterator "i" if needed */
62e76326 1653
528b2c61 1654 if (!http->range_iter.debt()) {
73c36fd9 1655 debugs(33, 5, HERE << "At end of current range spec for " << clientConnection);
62e76326 1656
1657 if (http->range_iter.pos.incrementable())
1658 ++http->range_iter.pos;
1659
1660 http->range_iter.updateSpec();
528b2c61 1661 }
62e76326 1662
528b2c61 1663 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
63be0a78 1664
528b2c61 1665 /* paranoid sync condition */
1666 /* continue condition: need_more_data */
bf8fe701 1667 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: returning " << (http->range_iter.currentSpec() ? true : false));
528b2c61 1668 return http->range_iter.currentSpec() ? true : false;
1669}
1670
47f6e231 1671int64_t
528b2c61 1672ClientSocketContext::getNextRangeOffset() const
1673{
1674 if (http->request->range) {
62e76326 1675 /* offset in range specs does not count the prefix of an http msg */
47f6e231 1676 debugs (33, 5, "ClientSocketContext::getNextRangeOffset: http offset " << http->out.offset);
62e76326 1677 /* check: reply was parsed and range iterator was initialized */
1678 assert(http->range_iter.valid);
1679 /* filter out data according to range specs */
1680 assert (canPackMoreRanges());
1681 {
47f6e231 1682 int64_t start; /* offset of still missing data */
62e76326 1683 assert(http->range_iter.currentSpec());
1684 start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
47f6e231 1685 debugs(33, 3, "clientPackMoreRanges: in: offset: " << http->out.offset);
1686 debugs(33, 3, "clientPackMoreRanges: out:"
9e008dda
AJ
1687 " start: " << start <<
1688 " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
1689 " [" << http->range_iter.currentSpec()->offset <<
1690 ", " << http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length << "),"
1691 " len: " << http->range_iter.currentSpec()->length <<
1692 " debt: " << http->range_iter.debt());
62e76326 1693 if (http->range_iter.currentSpec()->length != -1)
1694 assert(http->out.offset <= start); /* we did not miss it */
1695
1696 return start;
1697 }
1698
fedd1531 1699 } else if (reply && reply->content_range) {
1700 /* request does not have ranges, but reply does */
63be0a78 1701 /** \todo FIXME: should use range_iter_pos on reply, as soon as reply->content_range
1702 * becomes HttpHdrRange rather than HttpHdrRangeSpec.
1703 */
fedd1531 1704 return http->out.offset + reply->content_range->spec.offset;
528b2c61 1705 }
1706
1707 return http->out.offset;
1708}
1709
c8be6d7b 1710void
528b2c61 1711ClientSocketContext::pullData()
c8be6d7b 1712{
73c36fd9 1713 debugs(33, 5, HERE << clientConnection << " attempting to pull upstream data");
bf8fe701 1714
c8be6d7b 1715 /* More data will be coming from the stream. */
528b2c61 1716 StoreIOBuffer readBuffer;
1717 /* XXX: Next requested byte in the range sequence */
1718 /* XXX: length = getmaximumrangelenfgth */
1719 readBuffer.offset = getNextRangeOffset();
c8be6d7b 1720 readBuffer.length = HTTP_REQBUF_SZ;
528b2c61 1721 readBuffer.data = reqbuf;
1722 /* we may note we have reached the end of the wanted ranges */
1723 clientStreamRead(getTail(), http, readBuffer);
1724}
1725
62e76326 1726clientStream_status_t
528b2c61 1727ClientSocketContext::socketState()
1728{
1729 switch (clientStreamStatus(getTail(), http)) {
62e76326 1730
1731 case STREAM_NONE:
528b2c61 1732 /* check for range support ending */
62e76326 1733
528b2c61 1734 if (http->request->range) {
62e76326 1735 /* check: reply was parsed and range iterator was initialized */
1736 assert(http->range_iter.valid);
1737 /* filter out data according to range specs */
1738
1739 if (!canPackMoreRanges()) {
920ba08d 1740 debugs(33, 5, HERE << "Range request at end of returnable " <<
73c36fd9 1741 "range sequence on " << clientConnection);
62e76326 1742
1743 if (http->request->flags.proxy_keepalive)
1744 return STREAM_COMPLETE;
1745 else
1746 return STREAM_UNPLANNED_COMPLETE;
1747 }
fedd1531 1748 } else if (reply && reply->content_range) {
425802c8 1749 /* reply has content-range, but Squid is not managing ranges */
1750 const int64_t &bytesSent = http->out.offset;
1751 const int64_t &bytesExpected = reply->content_range->spec.length;
fedd1531 1752
425802c8 1753 debugs(33, 7, HERE << "body bytes sent vs. expected: " <<
9e008dda
AJ
1754 bytesSent << " ? " << bytesExpected << " (+" <<
1755 reply->content_range->spec.offset << ")");
425802c8 1756
1757 // did we get at least what we expected, based on range specs?
1758
65058d64
CT
1759 if (bytesSent == bytesExpected) { // got everything
1760 if (http->request->flags.proxy_keepalive)
1761 return STREAM_COMPLETE;
1762 else
1763 return STREAM_UNPLANNED_COMPLETE;
1764 }
425802c8 1765
1766 // The logic below is not clear: If we got more than we
1767 // expected why would persistency matter? Should not this
1768 // always be an error?
1769 if (bytesSent > bytesExpected) { // got extra
fedd1531 1770 if (http->request->flags.proxy_keepalive)
1771 return STREAM_COMPLETE;
1772 else
1773 return STREAM_UNPLANNED_COMPLETE;
1774 }
425802c8 1775
1776 // did not get enough yet, expecting more
62e76326 1777 }
1778
1779 return STREAM_NONE;
1780
1781 case STREAM_COMPLETE:
528b2c61 1782 return STREAM_COMPLETE;
62e76326 1783
1784 case STREAM_UNPLANNED_COMPLETE:
1785 return STREAM_UNPLANNED_COMPLETE;
1786
1787 case STREAM_FAILED:
1788 return STREAM_FAILED;
528b2c61 1789 }
62e76326 1790
528b2c61 1791 fatal ("unreachable code\n");
1792 return STREAM_NONE;
c8be6d7b 1793}
edce4d98 1794
63be0a78 1795/**
1796 * A write has just completed to the client, or we have just realised there is
edce4d98 1797 * no more data to send.
1798 */
e6ccf245 1799void
e0d28505 1800clientWriteComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
7a2f978b 1801{
528b2c61 1802 ClientSocketContext *context = (ClientSocketContext *)data;
e0d28505 1803 context->writeComplete(conn, bufnotused, size, errflag);
0655fa4d 1804}
1805
f692498b
AJ
1806/// remembers the abnormal connection termination for logging purposes
1807void
1808ClientSocketContext::noteIoError(const int xerrno)
1809{
1810 if (http) {
1811 if (xerrno == ETIMEDOUT)
41ebd397 1812 http->al->http.timedout = true;
f692498b 1813 else // even if xerrno is zero (which means read abort/eof)
41ebd397 1814 http->al->http.aborted = true;
f692498b
AJ
1815 }
1816}
1817
1818
55e44db9 1819void
1820ClientSocketContext::doClose()
1821{
73c36fd9 1822 clientConnection->close();
55e44db9 1823}
1824
cf6eb29e 1825/// called when we encounter a response-related error
55e44db9 1826void
5f8252d2 1827ClientSocketContext::initiateClose(const char *reason)
55e44db9 1828{
cf6eb29e
CT
1829 http->getConn()->stopSending(reason); // closes ASAP
1830}
5f8252d2 1831
cf6eb29e
CT
1832void
1833ConnStateData::stopSending(const char *error)
1834{
1835 debugs(33, 4, HERE << "sending error (" << clientConnection << "): " << error <<
1836 "; old receiving error: " <<
1837 (stoppedReceiving() ? stoppedReceiving_ : "none"));
fc68f6b1 1838
cf6eb29e
CT
1839 if (const char *oldError = stoppedSending()) {
1840 debugs(33, 3, HERE << "already stopped sending: " << oldError);
1841 return; // nothing has changed as far as this connection is concerned
1842 }
1843 stoppedSending_ = error;
fc68f6b1 1844
cf6eb29e
CT
1845 if (!stoppedReceiving()) {
1846 if (const int64_t expecting = mayNeedToReadMoreBody()) {
1847 debugs(33, 5, HERE << "must still read " << expecting <<
1848 " request body bytes with " << in.notYetUsed << " unused");
1849 return; // wait for the request receiver to finish reading
3b299123 1850 }
55e44db9 1851 }
1852
cf6eb29e 1853 clientConnection->close();
55e44db9 1854}
1855
0655fa4d 1856void
e0d28505 1857ClientSocketContext::writeComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag)
0655fa4d 1858{
5ad038e1 1859 const StoreEntry *entry = http->storeEntry();
7a2f978b 1860 http->out.size += size;
e0d28505 1861 debugs(33, 5, HERE << conn << ", sz " << size <<
e4049756 1862 ", err " << errflag << ", off " << http->out.size << ", len " <<
5ad038e1 1863 (entry ? entry->objectLen() : 0));
c8be6d7b 1864 clientUpdateSocketStats(http->logType, size);
62e76326 1865
5f8252d2 1866 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
fc68f6b1 1867
24bf76b5 1868 if (errflag == COMM_ERR_CLOSING || !Comm::IsConnOpen(conn))
5f8252d2 1869 return;
1870
e0d28505 1871 if (errflag || clientHttpRequestStatus(conn->fd, http)) {
5f8252d2 1872 initiateClose("failure or true request status");
62e76326 1873 /* Do we leak here ? */
1874 return;
edce4d98 1875 }
62e76326 1876
0655fa4d 1877 switch (socketState()) {
62e76326 1878
edce4d98 1879 case STREAM_NONE:
0655fa4d 1880 pullData();
62e76326 1881 break;
1882
edce4d98 1883 case STREAM_COMPLETE:
e0d28505 1884 debugs(33, 5, HERE << conn << " Keeping Alive");
0655fa4d 1885 keepaliveNextRequest();
62e76326 1886 return;
1887
edce4d98 1888 case STREAM_UNPLANNED_COMPLETE:
6e1d409c
AR
1889 initiateClose("STREAM_UNPLANNED_COMPLETE");
1890 return;
62e76326 1891
edce4d98 1892 case STREAM_FAILED:
6e1d409c 1893 initiateClose("STREAM_FAILED");
62e76326 1894 return;
1895
edce4d98 1896 default:
62e76326 1897 fatal("Hit unreachable code in clientWriteComplete\n");
7a2f978b 1898 }
1899}
1900
e6ccf245 1901extern "C" CSR clientGetMoreData;
1902extern "C" CSS clientReplyStatus;
1903extern "C" CSD clientReplyDetach;
edce4d98 1904
528b2c61 1905static ClientSocketContext *
be364179 1906parseHttpRequestAbort(ConnStateData * csd, const char *uri)
038eb4ed 1907{
59a1efb2 1908 ClientHttpRequest *http;
528b2c61 1909 ClientSocketContext *context;
1910 StoreIOBuffer tempBuffer;
be364179
AJ
1911 http = new ClientHttpRequest(csd);
1912 http->req_sz = csd->in.notYetUsed;
edce4d98 1913 http->uri = xstrdup(uri);
c4b7a5a9 1914 setLogUri (http, uri);
73c36fd9 1915 context = ClientSocketContextNew(csd->clientConnection, http);
c8be6d7b 1916 tempBuffer.data = context->reqbuf;
1917 tempBuffer.length = HTTP_REQBUF_SZ;
edce4d98 1918 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 1919 clientReplyStatus, new clientReplyContext(http), clientSocketRecipient,
62e76326 1920 clientSocketDetach, context, tempBuffer);
edce4d98 1921 return context;
038eb4ed 1922}
1923
c8be6d7b 1924char *
1925skipLeadingSpace(char *aString)
1926{
1927 char *result = aString;
62e76326 1928
c8be6d7b 1929 while (xisspace(*aString))
62e76326 1930 ++aString;
1931
c8be6d7b 1932 return result;
1933}
1934
63be0a78 1935/**
d4a04ed5 1936 * 'end' defaults to NULL for backwards compatibility
1937 * remove default value if we ever get rid of NULL-terminated
1938 * request buffers.
1939 */
1940const char *
1941findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
c8be6d7b 1942{
d4a04ed5 1943 if (NULL == end) {
1944 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1945 assert(end);
1946 }
62e76326 1947
5e263176 1948 for (; end > uriAndHTTPVersion; --end) {
d4a04ed5 1949 if (*end == '\n' || *end == '\r')
62e76326 1950 continue;
1951
d4a04ed5 1952 if (xisspace(*end)) {
1953 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1954 return end + 1;
62e76326 1955 else
1956 break;
1957 }
c8be6d7b 1958 }
62e76326 1959
3f38a55e 1960 return NULL;
c8be6d7b 1961}
1962
c8be6d7b 1963void
727552f4 1964setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl)
c8be6d7b 1965{
a46d0227 1966 safe_free(http->log_uri);
62e76326 1967
727552f4
CT
1968 if (!cleanUrl)
1969 // The uri is already clean just dump it.
62e76326 1970 http->log_uri = xstrndup(uri, MAX_URL);
727552f4 1971 else {
2bea559d
A
1972 int flags = 0;
1973 switch (Config.uri_whitespace) {
1974 case URI_WHITESPACE_ALLOW:
1975 flags |= RFC1738_ESCAPE_NOSPACE;
1976
1977 case URI_WHITESPACE_ENCODE:
1978 flags |= RFC1738_ESCAPE_UNESCAPED;
1979 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1980 break;
727552f4 1981
2bea559d
A
1982 case URI_WHITESPACE_CHOP: {
1983 flags |= RFC1738_ESCAPE_NOSPACE;
1984 flags |= RFC1738_ESCAPE_UNESCAPED;
1985 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1986 int pos = strcspn(http->log_uri, w_space);
1987 http->log_uri[pos] = '\0';
1988 }
1989 break;
1990
1991 case URI_WHITESPACE_DENY:
1992 case URI_WHITESPACE_STRIP:
1993 default: {
1994 const char *t;
1995 char *tmp_uri = static_cast<char*>(xmalloc(strlen(uri) + 1));
1996 char *q = tmp_uri;
1997 t = uri;
1998 while (*t) {
a38ec4b1
FC
1999 if (!xisspace(*t)) {
2000 *q = *t;
2001 ++q;
2002 }
95dc7ff4 2003 ++t;
727552f4 2004 }
2bea559d
A
2005 *q = '\0';
2006 http->log_uri = xstrndup(rfc1738_escape_unescaped(tmp_uri), MAX_URL);
2007 xfree(tmp_uri);
2008 }
2009 break;
2010 }
727552f4 2011 }
c8be6d7b 2012}
2013
3f38a55e 2014static void
1cf238db 2015prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
62e76326 2016{
3f38a55e 2017 int vhost = conn->port->vhost;
2018 int vport = conn->port->vport;
2019 char *host;
de12d836 2020 char ipbuf[MAX_IPSTRLEN];
c8be6d7b 2021
3f38a55e 2022 http->flags.accel = 1;
c8be6d7b 2023
3f38a55e 2024 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
c8be6d7b 2025
34399323 2026 if (strncasecmp(url, "cache_object://", 15) == 0)
2027 return; /* already in good shape */
2028
3f38a55e 2029 if (*url != '/') {
62e76326 2030 if (conn->port->vhost)
2031 return; /* already in good shape */
2032
2033 /* else we need to ignore the host name */
2034 url = strstr(url, "//");
2035
3f38a55e 2036#if SHOULD_REJECT_UNKNOWN_URLS
62e76326 2037
719c7e0a
AJ
2038 if (!url) {
2039 hp->request_parse_status = HTTP_BAD_REQUEST;
62e76326 2040 return parseHttpRequestAbort(conn, "error:invalid-request");
719c7e0a 2041 }
c8be6d7b 2042#endif
62e76326 2043
2044 if (url)
2045 url = strchr(url + 2, '/');
2046
2047 if (!url)
2048 url = (char *) "/";
3f38a55e 2049 }
2050
5463e4b9
AJ
2051 if (vport < 0)
2052 vport = http->getConn()->clientConnection->local.GetPort();
2053
ae7ff0b8 2054 const bool switchedToHttps = conn->switchedToHttps();
2055 const bool tryHostHeader = vhost || switchedToHttps;
2056 if (tryHostHeader && (host = mime_get_header(req_hdr, "Host")) != NULL) {
5463e4b9
AJ
2057 debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
2058 char thost[256];
2059 if (vport > 0) {
2060 thost[0] = '\0';
2061 char *t = NULL;
2062 if (host[strlen(host)] != ']' && (t = strrchr(host,':')) != NULL) {
2063 strncpy(thost, host, (t-host));
2064 snprintf(thost+(t-host), sizeof(thost)-(t-host), ":%d", vport);
2065 host = thost;
2066 } else if (!t) {
2067 snprintf(thost, sizeof(thost), "%s:%d",host, vport);
2068 host = thost;
2069 }
2070 } // else nothing to alter port-wise.
62e76326 2071 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
2072 strlen(host);
2073 http->uri = (char *)xcalloc(url_sz, 1);
ae7ff0b8 2074 const char *protocol = switchedToHttps ?
9e008dda 2075 "https" : conn->port->protocol;
ae7ff0b8 2076 snprintf(http->uri, url_sz, "%s://%s%s", protocol, host, url);
bf8fe701 2077 debugs(33, 5, "ACCEL VHOST REWRITE: '" << http->uri << "'");
5463e4b9
AJ
2078 } else if (conn->port->defaultsite /* && !vhost */) {
2079 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
62e76326 2080 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
2081 strlen(conn->port->defaultsite);
2082 http->uri = (char *)xcalloc(url_sz, 1);
5463e4b9
AJ
2083 char vportStr[32];
2084 vportStr[0] = '\0';
2085 if (vport > 0) {
2086 snprintf(vportStr, sizeof(vportStr),":%d",vport);
2087 }
2088 snprintf(http->uri, url_sz, "%s://%s%s%s",
2089 conn->port->protocol, conn->port->defaultsite, vportStr, url);
bf8fe701 2090 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: '" << http->uri <<"'");
5463e4b9
AJ
2091 } else if (vport > 0 /* && (!vhost || no Host:) */) {
2092 debugs(33, 5, "ACCEL VPORT REWRITE: http_port IP + vport=" << vport);
2093 /* Put the local socket IP address as the hostname, with whatever vport we found */
62e76326 2094 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
2095 http->uri = (char *)xcalloc(url_sz, 1);
73c36fd9 2096 http->getConn()->clientConnection->local.ToHostname(ipbuf,MAX_IPSTRLEN);
62e76326 2097 snprintf(http->uri, url_sz, "%s://%s:%d%s",
98242069 2098 http->getConn()->port->protocol,
de12d836 2099 ipbuf, vport, url);
bf8fe701 2100 debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
3f38a55e 2101 }
2102}
2103
2104static void
1cf238db 2105prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
62e76326 2106{
3f38a55e 2107 char *host;
de12d836 2108 char ipbuf[MAX_IPSTRLEN];
3f38a55e 2109
3f38a55e 2110 if (*url != '/')
62e76326 2111 return; /* already in good shape */
3f38a55e 2112
2113 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
2114
f024c970 2115 if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
62e76326 2116 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
2117 strlen(host);
2118 http->uri = (char *)xcalloc(url_sz, 1);
60b2e919 2119 snprintf(http->uri, url_sz, "%s://%s%s", conn->port->protocol, host, url);
bf8fe701 2120 debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
c8be6d7b 2121 } else {
62e76326 2122 /* Put the local socket IP address as the hostname. */
2123 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
2124 http->uri = (char *)xcalloc(url_sz, 1);
60b2e919
CT
2125 http->getConn()->clientConnection->local.ToHostname(ipbuf,MAX_IPSTRLEN);
2126 snprintf(http->uri, url_sz, "%s://%s:%d%s",
2127 http->getConn()->port->protocol,
73c36fd9 2128 ipbuf, http->getConn()->clientConnection->local.GetPort(), url);
bf8fe701 2129 debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
c8be6d7b 2130 }
c8be6d7b 2131}
2132
63be0a78 2133/**
7a2f978b 2134 * parseHttpRequest()
9e008dda 2135 *
7a2f978b 2136 * Returns
c4b7a5a9 2137 * NULL on incomplete requests
528b2c61 2138 * a ClientSocketContext structure on success or failure.
c4b7a5a9 2139 * Sets result->flags.parsed_ok to 0 if failed to parse the request.
2140 * Sets result->flags.parsed_ok to 1 if we have a good request.
7a2f978b 2141 */
528b2c61 2142static ClientSocketContext *
be364179 2143parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_p, HttpVersion *http_ver)
7a2f978b 2144{
7a2f978b 2145 char *req_hdr = NULL;
2334c194 2146 char *end;
c68e9c6b 2147 size_t req_sz;
59a1efb2 2148 ClientHttpRequest *http;
528b2c61 2149 ClientSocketContext *result;
2150 StoreIOBuffer tempBuffer;
84cc2635 2151 int r;
7a2f978b 2152
6792f0d3 2153 /* pre-set these values to make aborting simpler */
6792f0d3 2154 *method_p = METHOD_NONE;
6792f0d3 2155
7ed7d3da
AJ
2156 /* NP: don't be tempted to move this down or remove again.
2157 * It's the only DDoS protection old-String has against long URL */
2158 if ( hp->bufsiz <= 0) {
2159 debugs(33, 5, "Incomplete request, waiting for end of request line");
2160 return NULL;
e1381638 2161 } else if ( (size_t)hp->bufsiz >= Config.maxRequestHeaderSize && headersEnd(hp->buf, Config.maxRequestHeaderSize) == 0) {
7ed7d3da 2162 debugs(33, 5, "parseHttpRequest: Too large request");
719c7e0a 2163 hp->request_parse_status = HTTP_HEADER_TOO_LARGE;
be364179 2164 return parseHttpRequestAbort(csd, "error:request-too-large");
7ed7d3da
AJ
2165 }
2166
84cc2635 2167 /* Attempt to parse the first line; this'll define the method, url, version and header begin */
2168 r = HttpParserParseReqLine(hp);
fc68f6b1 2169
84cc2635 2170 if (r == 0) {
bf8fe701 2171 debugs(33, 5, "Incomplete request, waiting for end of request line");
fc68f6b1 2172 return NULL;
7a2f978b 2173 }
fc68f6b1 2174
84cc2635 2175 if (r == -1) {
be364179 2176 return parseHttpRequestAbort(csd, "error:invalid-request");
84cc2635 2177 }
fc68f6b1 2178
84cc2635 2179 /* Request line is valid here .. */
74f478f8 2180 *http_ver = HttpVersion(hp->req.v_maj, hp->req.v_min);
62e76326 2181
52512f28 2182 /* This call scans the entire request, not just the headers */
74f478f8 2183 if (hp->req.v_maj > 0) {
a5baffba 2184 if ((req_sz = headersEnd(hp->buf, hp->bufsiz)) == 0) {
bf8fe701 2185 debugs(33, 5, "Incomplete request, waiting for end of headers");
62e76326 2186 return NULL;
2187 }
3f38a55e 2188 } else {
bf8fe701 2189 debugs(33, 3, "parseHttpRequest: Missing HTTP identifier");
84cc2635 2190 req_sz = HttpParserReqSz(hp);
3f38a55e 2191 }
2192
bb790702 2193 /* We know the whole request is in hp->buf now */
52512f28 2194
a5baffba 2195 assert(req_sz <= (size_t) hp->bufsiz);
fc68f6b1 2196
a5baffba 2197 /* Will the following be true with HTTP/0.9 requests? probably not .. */
2198 /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */
2199 assert(req_sz > 0);
fc68f6b1 2200
a5baffba 2201 hp->hdr_end = req_sz - 1;
fc68f6b1 2202
74f478f8 2203 hp->hdr_start = hp->req.end + 1;
3f38a55e 2204
5b648f60 2205 /* Enforce max_request_size */
5b648f60 2206 if (req_sz >= Config.maxRequestHeaderSize) {
bf8fe701 2207 debugs(33, 5, "parseHttpRequest: Too large request");
719c7e0a 2208 hp->request_parse_status = HTTP_HEADER_TOO_LARGE;
be364179 2209 return parseHttpRequestAbort(csd, "error:request-too-large");
5b648f60 2210 }
2211
84cc2635 2212 /* Set method_p */
74f478f8 2213 *method_p = HttpRequestMethod(&hp->buf[hp->req.m_start], &hp->buf[hp->req.m_end]+1);
fc68f6b1 2214
adf29627 2215 /* deny CONNECT via accelerated ports */
be364179
AJ
2216 if (*method_p == METHOD_CONNECT && csd && csd->port && csd->port->accel) {
2217 debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << csd->port->protocol << " Accelerator port " << csd->port->s.GetPort() );
adf29627
AJ
2218 /* XXX need a way to say "this many character length string" */
2219 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->buf);
719c7e0a 2220 hp->request_parse_status = HTTP_METHOD_NOT_ALLOWED;
be364179 2221 return parseHttpRequestAbort(csd, "error:method-not-allowed");
adf29627
AJ
2222 }
2223
84cc2635 2224 if (*method_p == METHOD_NONE) {
fc68f6b1 2225 /* XXX need a way to say "this many character length string" */
e0236918 2226 debugs(33, DBG_IMPORTANT, "clientParseRequestMethod: Unsupported method in request '" << hp->buf << "'");
719c7e0a 2227 hp->request_parse_status = HTTP_METHOD_NOT_ALLOWED;
be364179 2228 return parseHttpRequestAbort(csd, "error:unsupported-request-method");
3f38a55e 2229 }
7a2f978b 2230
c68e9c6b 2231 /*
2232 * Process headers after request line
c8be6d7b 2233 * TODO: Use httpRequestParse here.
c68e9c6b 2234 */
84cc2635 2235 /* XXX this code should be modified to take a const char * later! */
74f478f8 2236 req_hdr = (char *) hp->buf + hp->req.end + 1;
fc68f6b1 2237
bf8fe701 2238 debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr << "}");
fc68f6b1 2239
52512f28 2240 end = (char *) hp->buf + hp->hdr_end;
fc68f6b1 2241
bf8fe701 2242 debugs(33, 3, "parseHttpRequest: end = {" << end << "}");
99edd1c3 2243
bf8fe701 2244 debugs(33, 3, "parseHttpRequest: prefix_sz = " <<
2245 (int) HttpParserRequestLen(hp) << ", req_line_sz = " <<
2246 HttpParserReqSz(hp));
62e76326 2247
7a2f978b 2248 /* Ok, all headers are received */
be364179 2249 http = new ClientHttpRequest(csd);
62e76326 2250
a5baffba 2251 http->req_sz = HttpParserRequestLen(hp);
73c36fd9 2252 result = ClientSocketContextNew(csd->clientConnection, http);
c8be6d7b 2253 tempBuffer.data = result->reqbuf;
2254 tempBuffer.length = HTTP_REQBUF_SZ;
62e76326 2255
0655fa4d 2256 ClientStreamData newServer = new clientReplyContext(http);
0655fa4d 2257 ClientStreamData newClient = result;
edce4d98 2258 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 2259 clientReplyStatus, newServer, clientSocketRecipient,
2260 clientSocketDetach, newClient, tempBuffer);
62e76326 2261
bf8fe701 2262 debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp->buf) + hp->hdr_start);
3f38a55e 2263
3ff65596
AR
2264 /* set url */
2265 /*
2266 * XXX this should eventually not use a malloc'ed buffer; the transformation code
2267 * below needs to be modified to not expect a mutable nul-terminated string.
2268 */
74f478f8 2269 char *url = (char *)xmalloc(hp->req.u_end - hp->req.u_start + 16);
3ff65596 2270
74f478f8 2271 memcpy(url, hp->buf + hp->req.u_start, hp->req.u_end - hp->req.u_start + 1);
3ff65596 2272
74f478f8 2273 url[hp->req.u_end - hp->req.u_start + 1] = '\0';
3ff65596 2274
ba9ebd0a 2275#if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
62e76326 2276
7a2f978b 2277 if ((t = strchr(url, '#'))) /* remove HTML anchors */
62e76326 2278 *t = '\0';
2279
ba9ebd0a 2280#endif
7a2f978b 2281
5463e4b9 2282 debugs(33,5, HERE << "repare absolute URL from " << (csd->transparent()?"intercept":(csd->port->accel ? "accel":"")));
3f38a55e 2283 /* Rewrite the URL in transparent or accelerator mode */
89272111
AJ
2284 /* NP: there are several cases to traverse here:
2285 * - standard mode (forward proxy)
2286 * - transparent mode (TPROXY)
2287 * - transparent mode with failures
2288 * - intercept mode (NAT)
2289 * - intercept mode with failures
2290 * - accelerator mode (reverse proxy)
2291 * - internal URL
2292 * - mixed combos of the above with internal URL
2293 */
be364179 2294 if (csd->transparent()) {
89272111 2295 /* intercept or transparent mode, properly working with no failures */
be364179 2296 prepareTransparentURL(csd, http, url, req_hdr);
89272111 2297
2f2749d7 2298 } else if (internalCheck(url)) {
89272111 2299 /* internal URL mode */
2f2749d7 2300 /* prepend our name & port */
2301 http->uri = xstrdup(internalLocalUri(NULL, url));
59c59acf
AJ
2302 // We just re-wrote the URL. Must replace the Host: header.
2303 // But have not parsed there yet!! flag for local-only handling.
2304 http->flags.internal = 1;
2305
2306 } else if (csd->port->accel || csd->switchedToHttps()) {
2307 /* accelerator mode */
2308 prepareAcceleratedURL(csd, http, url, req_hdr);
3f38a55e 2309 }
2310
2311 if (!http->uri) {
62e76326 2312 /* No special rewrites have been applied above, use the
2313 * requested url. may be rewritten later, so make extra room */
2314 int url_sz = strlen(url) + Config.appendDomainLen + 5;
2315 http->uri = (char *)xcalloc(url_sz, 1);
2316 strcpy(http->uri, url);
3f38a55e 2317 }
62e76326 2318
bf8fe701 2319 debugs(33, 5, "parseHttpRequest: Complete request received");
1ce34ddd
AJ
2320
2321 // XXX: crop this dump at the end of headers. No need for extras
2322 debugs(11, 2, "HTTP Client " << csd->clientConnection);
2323 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << (hp->buf) + hp->req.m_start << "\n----------");
2324
c4b7a5a9 2325 result->flags.parsed_ok = 1;
84cc2635 2326 xfree(url);
c8be6d7b 2327 return result;
7a2f978b 2328}
2329
c8be6d7b 2330int
a46d2c0e 2331ConnStateData::getAvailableBufferLength() const
c8be6d7b 2332{
f5f9e44c
AR
2333 assert (in.allocatedSize > in.notYetUsed); // allocated more than used
2334 const size_t result = in.allocatedSize - in.notYetUsed - 1;
2335 // huge request_header_max_size may lead to more than INT_MAX unused space
2336 assert (static_cast<ssize_t>(result) <= INT_MAX);
1a419b96 2337 return result;
c8be6d7b 2338}
2339
1368d115
CT
2340bool
2341ConnStateData::maybeMakeSpaceAvailable()
c8be6d7b 2342{
a46d2c0e 2343 if (getAvailableBufferLength() < 2) {
90737510
A
2344 size_t newSize;
2345 if (in.allocatedSize >= Config.maxRequestBufferSize) {
2346 debugs(33, 4, "request buffer full: client_request_buffer_max_size=" << Config.maxRequestBufferSize);
2347 return false;
2348 }
2349 if ((newSize=in.allocatedSize * 2) > Config.maxRequestBufferSize) {
2350 newSize=Config.maxRequestBufferSize;
2351 }
2352 in.buf = (char *)memReallocBuf(in.buf, newSize, &in.allocatedSize);
2353 debugs(33, 2, "growing request buffer: notYetUsed=" << in.notYetUsed << " size=" << in.allocatedSize);
c8be6d7b 2354 }
1368d115 2355 return true;
c8be6d7b 2356}
2357
2358void
0655fa4d 2359ConnStateData::addContextToQueue(ClientSocketContext * context)
c8be6d7b 2360{
0655fa4d 2361 ClientSocketContext::Pointer *S;
62e76326 2362
0655fa4d 2363 for (S = (ClientSocketContext::Pointer *) & currentobject; S->getRaw();
3d0ac046 2364 S = &(*S)->next);
c8be6d7b 2365 *S = context;
62e76326 2366
0655fa4d 2367 ++nrequests;
c8be6d7b 2368}
2369
2370int
0655fa4d 2371ConnStateData::getConcurrentRequestCount() const
c8be6d7b 2372{
2373 int result = 0;
0655fa4d 2374 ClientSocketContext::Pointer *T;
62e76326 2375
0655fa4d 2376 for (T = (ClientSocketContext::Pointer *) &currentobject;
3d0ac046 2377 T->getRaw(); T = &(*T)->next, ++result);
c8be6d7b 2378 return result;
2379}
2380
2381int
1cf238db 2382ConnStateData::connReadWasError(comm_err_t flag, int size, int xerrno)
c8be6d7b 2383{
c4b7a5a9 2384 if (flag != COMM_OK) {
73c36fd9 2385 debugs(33, 2, "connReadWasError: FD " << clientConnection << ": got flag " << flag);
62e76326 2386 return 1;
c4b7a5a9 2387 }
62e76326 2388
c8be6d7b 2389 if (size < 0) {
f3400a93 2390 if (!ignoreErrno(xerrno)) {
73c36fd9 2391 debugs(33, 2, "connReadWasError: FD " << clientConnection << ": " << xstrerr(xerrno));
62e76326 2392 return 1;
1cf238db 2393 } else if (in.notYetUsed == 0) {
73c36fd9 2394 debugs(33, 2, "connReadWasError: FD " << clientConnection << ": no data to process (" << xstrerr(xerrno) << ")");
62e76326 2395 }
c8be6d7b 2396 }
62e76326 2397
c8be6d7b 2398 return 0;
2399}
2400
2401int
1cf238db 2402ConnStateData::connFinishedWithConn(int size)
c8be6d7b 2403{
2404 if (size == 0) {
1cf238db 2405 if (getConcurrentRequestCount() == 0 && in.notYetUsed == 0) {
62e76326 2406 /* no current or pending requests */
73c36fd9 2407 debugs(33, 4, HERE << clientConnection << " closed");
62e76326 2408 return 1;
2409 } else if (!Config.onoff.half_closed_clients) {
2410 /* admin doesn't want to support half-closed client sockets */
73c36fd9 2411 debugs(33, 3, HERE << clientConnection << " aborted (half_closed_clients disabled)");
f692498b 2412 notifyAllContexts(0); // no specific error implies abort
62e76326 2413 return 1;
2414 }
c8be6d7b 2415 }
62e76326 2416
c8be6d7b 2417 return 0;
2418}
2419
2420void
3b299123 2421connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
c8be6d7b 2422{
2423 assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
2424 conn->in.notYetUsed -= byteCount;
d5fa7219 2425 debugs(33, 5, HERE << "conn->in.notYetUsed = " << conn->in.notYetUsed);
c8be6d7b 2426 /*
9e008dda 2427 * If there is still data that will be used,
c8be6d7b 2428 * move it to the beginning.
2429 */
62e76326 2430
c8be6d7b 2431 if (conn->in.notYetUsed > 0)
41d00cd3 2432 memmove(conn->in.buf, conn->in.buf + byteCount, conn->in.notYetUsed);
c8be6d7b 2433}
2434
39cb8c41
AR
2435/// respond with ERR_TOO_BIG if request header exceeds request_header_max_size
2436void
2437ConnStateData::checkHeaderLimits()
c8be6d7b 2438{
39cb8c41
AR
2439 if (in.notYetUsed < Config.maxRequestHeaderSize)
2440 return; // can accumulte more header data
3ff65596 2441
39cb8c41 2442 debugs(33, 3, "Request header is too large (" << in.notYetUsed << " > " <<
de48b288 2443 Config.maxRequestHeaderSize << " bytes)");
c8be6d7b 2444
39cb8c41 2445 ClientSocketContext *context = parseHttpRequestAbort(this, "error:request-too-large");
528b2c61 2446 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2447 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2448 assert (repContext);
2449 repContext->setReplyToError(ERR_TOO_BIG,
39cb8c41 2450 HTTP_BAD_REQUEST, METHOD_NONE, NULL,
73c36fd9 2451 clientConnection->remote, NULL, NULL, NULL);
0655fa4d 2452 context->registerWithConn();
528b2c61 2453 context->pullData();
c8be6d7b 2454}
2455
1cf238db 2456void
f35961af 2457ConnStateData::clientAfterReadingRequests()
c4b7a5a9 2458{
39cb8c41 2459 // Were we expecting to read more request body from half-closed connection?
73c36fd9
AJ
2460 if (mayNeedToReadMoreBody() && commIsHalfClosed(clientConnection->fd)) {
2461 debugs(33, 3, HERE << "truncated body: closing half-closed " << clientConnection);
2462 clientConnection->close();
39cb8c41 2463 return;
c4b7a5a9 2464 }
2465
f35961af
AR
2466 if (flags.readMore)
2467 readSomeData();
c4b7a5a9 2468}
2469
84c77748
AR
2470void
2471ConnStateData::quitAfterError(HttpRequest *request)
2472{
2473 // From HTTP p.o.v., we do not have to close after every error detected
2474 // at the client-side, but many such errors do require closure and the
2475 // client-side code is bad at handling errors so we play it safe.
2476 if (request)
2477 request->flags.proxy_keepalive = 0;
2478 flags.readMore = false;
2479 debugs(33,4, HERE << "Will close after error: " << clientConnection);
2480}
2481
8eb0a7ee
CT
2482#if USE_SSL
2483bool ConnStateData::serveDelayedError(ClientSocketContext *context)
2484{
2485 ClientHttpRequest *http = context->http;
fd4624d7
CT
2486
2487 if (!sslServerBump)
8eb0a7ee
CT
2488 return false;
2489
fd4624d7 2490 assert(sslServerBump->entry);
7a957a93 2491 // Did we create an error entry while processing CONNECT?
fd4624d7 2492 if (!sslServerBump->entry->isEmpty()) {
84c77748
AR
2493 quitAfterError(http->request);
2494
7a957a93
AR
2495 // Get the saved error entry and send it to the client by replacing the
2496 // ClientHttpRequest store entry with it.
8eb0a7ee
CT
2497 clientStreamNode *node = context->getClientReplyContext();
2498 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
7a957a93
AR
2499 assert(repContext);
2500 debugs(33, 5, "Responding with delated error for " << http->uri);
fd4624d7 2501 repContext->setReplyToStoreEntry(sslServerBump->entry);
7a957a93
AR
2502
2503 // save the original request for logging purposes
71cae389
CT
2504 if (!context->http->al->request)
2505 context->http->al->request = HTTPMSGLOCK(http->request);
7a957a93
AR
2506
2507 // Get error details from the fake certificate-peeking request.
129fe2a1 2508 http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
8eb0a7ee 2509 context->pullData();
8eb0a7ee
CT
2510 return true;
2511 }
2512
7a957a93
AR
2513 // In bump-server-first mode, we have not necessarily seen the intended
2514 // server name at certificate-peeking time. Check for domain mismatch now,
2515 // when we can extract the intended name from the bumped HTTP request.
2516 if (sslServerBump->serverCert.get()) {
8eb0a7ee 2517 HttpRequest *request = http->request;
fd4624d7 2518 if (!Ssl::checkX509ServerValidity(sslServerBump->serverCert.get(), request->GetHost())) {
7a957a93
AR
2519 debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
2520 "does not match domainname " << request->GetHost());
8eb0a7ee
CT
2521
2522 ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
7a957a93 2523 check.sslErrors = new Ssl::Errors(SQUID_X509_V_ERR_DOMAIN_MISMATCH);
8eb0a7ee
CT
2524 if (Comm::IsConnOpen(pinning.serverConnection))
2525 check.fd(pinning.serverConnection->fd);
7a957a93
AR
2526 const bool allowDomainMismatch =
2527 check.fastCheck() == ACCESS_ALLOWED;
2528 delete check.sslErrors;
2529 check.sslErrors = NULL;
8eb0a7ee 2530
7d82c5b8 2531 if (!allowDomainMismatch) {
84c77748
AR
2532 quitAfterError(request);
2533
8eb0a7ee
CT
2534 clientStreamNode *node = context->getClientReplyContext();
2535 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2536 assert (repContext);
2537
7a957a93 2538 // Fill the server IP and hostname for error page generation.
fd4624d7
CT
2539 HttpRequest::Pointer const & peekerRequest = sslServerBump->request;
2540 request->hier.note(peekerRequest->hier.tcpServer, request->GetHost());
7a957a93 2541
8eb0a7ee 2542 // Create an error object and fill it
03f00a11 2543 ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
8eb0a7ee 2544 err->src_addr = clientConnection->remote;
7a957a93
AR
2545 Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
2546 SQUID_X509_V_ERR_DOMAIN_MISMATCH,
2547 sslServerBump->serverCert.get(), NULL);
8eb0a7ee 2548 err->detail = errDetail;
7a957a93 2549 // Save the original request for logging purposes.
71cae389
CT
2550 if (!context->http->al->request)
2551 context->http->al->request = HTTPMSGLOCK(request);
8eb0a7ee
CT
2552 repContext->setReplyToError(request->method, err);
2553 assert(context->http->out.offset == 0);
2554 context->pullData();
8eb0a7ee
CT
2555 return true;
2556 }
2557 }
2558 }
2559
2560 return false;
2561}
7a957a93 2562#endif // USE_SSL
8eb0a7ee 2563
c4b7a5a9 2564static void
1cf238db 2565clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, HttpVersion http_ver)
c4b7a5a9 2566{
59a1efb2 2567 ClientHttpRequest *http = context->http;
190154cf 2568 HttpRequest *request = NULL;
5f8252d2 2569 bool notedUseOfBuffer = false;
39cb8c41 2570 bool chunked = false;
e18b8316 2571 bool mustReplyToOptions = false;
3ff65596 2572 bool unsupportedTe = false;
39cb8c41 2573 bool expectBody = false;
5f8252d2 2574
c4b7a5a9 2575 /* We have an initial client stream in place should it be needed */
2576 /* setup our private context */
0655fa4d 2577 context->registerWithConn();
c4b7a5a9 2578
2579 if (context->flags.parsed_ok == 0) {
62e76326 2580 clientStreamNode *node = context->getClientReplyContext();
f4762666 2581 debugs(33, 2, "clientProcessRequest: Invalid Request");
84c77748 2582 conn->quitAfterError(NULL);
727552f4
CT
2583 // setLogUri should called before repContext->setReplyToError
2584 setLogUri(http, http->uri, true);
0655fa4d 2585 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2586 assert (repContext);
e212e5e3 2587 switch (hp->request_parse_status) {
719c7e0a 2588 case HTTP_HEADER_TOO_LARGE:
73c36fd9 2589 repContext->setReplyToError(ERR_TOO_BIG, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL);
719c7e0a
AJ
2590 break;
2591 case HTTP_METHOD_NOT_ALLOWED:
be364179 2592 repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_METHOD_NOT_ALLOWED, method, http->uri,
73c36fd9 2593 conn->clientConnection->remote, NULL, conn->in.buf, NULL);
719c7e0a
AJ
2594 break;
2595 default:
9815f129 2596 repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, method, http->uri,
73c36fd9 2597 conn->clientConnection->remote, NULL, conn->in.buf, NULL);
719c7e0a 2598 }
62e76326 2599 assert(context->http->out.offset == 0);
2600 context->pullData();
fc68f6b1 2601 goto finish;
c4b7a5a9 2602 }
2603
c21ad0f5 2604 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
62e76326 2605 clientStreamNode *node = context->getClientReplyContext();
bf8fe701 2606 debugs(33, 5, "Invalid URL: " << http->uri);
84c77748 2607 conn->quitAfterError(request);
727552f4
CT
2608 // setLogUri should called before repContext->setReplyToError
2609 setLogUri(http, http->uri, true);
0655fa4d 2610 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2611 assert (repContext);
73c36fd9 2612 repContext->setReplyToError(ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
62e76326 2613 assert(context->http->out.offset == 0);
2614 context->pullData();
fc68f6b1 2615 goto finish;
62e76326 2616 }
c4b7a5a9 2617
e57c1885
AJ
2618 /* RFC 2616 section 10.5.6 : handle unsupported HTTP versions cleanly. */
2619 /* We currently only accept 0.9, 1.0, 1.1 */
2620 if ( (http_ver.major == 0 && http_ver.minor != 9) ||
af6a12ee
AJ
2621 (http_ver.major == 1 && http_ver.minor > 1 ) ||
2622 (http_ver.major > 1) ) {
e57c1885
AJ
2623
2624 clientStreamNode *node = context->getClientReplyContext();
2625 debugs(33, 5, "Unsupported HTTP version discovered. :\n" << HttpParserHdrBuf(hp));
84c77748 2626 conn->quitAfterError(request);
727552f4
CT
2627 // setLogUri should called before repContext->setReplyToError
2628 setLogUri(http, http->uri, true);
e57c1885
AJ
2629 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2630 assert (repContext);
be364179 2631 repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, HTTP_HTTP_VERSION_NOT_SUPPORTED, method, http->uri,
73c36fd9 2632 conn->clientConnection->remote, NULL, HttpParserHdrBuf(hp), NULL);
e57c1885
AJ
2633 assert(context->http->out.offset == 0);
2634 context->pullData();
e57c1885
AJ
2635 goto finish;
2636 }
2637
528b2c61 2638 /* compile headers */
2639 /* we should skip request line! */
666f514b 2640 /* XXX should actually know the damned buffer size here */
38e82382 2641 if (http_ver.major >= 1 && !request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) {
47ac2ebe 2642 clientStreamNode *node = context->getClientReplyContext();
bf8fe701 2643 debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp));
84c77748 2644 conn->quitAfterError(request);
727552f4
CT
2645 // setLogUri should called before repContext->setReplyToError
2646 setLogUri(http, http->uri, true);
47ac2ebe 2647 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2648 assert (repContext);
73c36fd9 2649 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
47ac2ebe 2650 assert(context->http->out.offset == 0);
2651 context->pullData();
fc68f6b1 2652 goto finish;
47ac2ebe 2653 }
c4b7a5a9 2654
40d34a62
AJ
2655 request->clientConnectionManager = conn;
2656
c4b7a5a9 2657 request->flags.accelerated = http->flags.accel;
b68415a1 2658 request->flags.sslBumped = conn->switchedToHttps();
85563fd9 2659 request->flags.canRePin = request->flags.sslBumped && conn->pinning.pinned;
432bc83c 2660 request->flags.ignore_cc = conn->port->ignore_cc;
7fb65ee4
AR
2661 // TODO: decouple http->flags.accel from request->flags.sslBumped
2662 request->flags.no_direct = (request->flags.accelerated && !request->flags.sslBumped) ?
87f237a9 2663 !conn->port->allow_direct : 0;
21512911
CT
2664#if USE_AUTH
2665 if (request->flags.sslBumped) {
2666 if (conn->auth_user_request != NULL)
2667 request->auth_user_request = conn->auth_user_request;
2668 }
2669#endif
2ad20b4f
AJ
2670
2671 /** \par
2672 * If transparent or interception mode is working clone the transparent and interception flags
2673 * from the port settings to the request.
2674 */
40d34a62 2675 if (http->clientConnection != NULL) {
304a6180
CT
2676 request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
2677 request->flags.spoof_client_ip = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
2ad20b4f 2678 }
fc68f6b1 2679
5b4117d8 2680 if (internalCheck(request->urlpath.termedBuf())) {
cc192b50 2681 if (internalHostnameIs(request->GetHost()) &&
f024c970 2682 request->port == getMyPort()) {
2683 http->flags.internal = 1;
9c175897 2684 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.termedBuf())) {
cc192b50 2685 request->SetHost(internalHostname());
f024c970 2686 request->port = getMyPort();
2687 http->flags.internal = 1;
62e76326 2688 }
f024c970 2689 }
e72a0ec0 2690
f024c970 2691 if (http->flags.internal) {
0c3d3f65 2692 request->protocol = AnyP::PROTO_HTTP;
f024c970 2693 request->login[0] = '\0';
c4b7a5a9 2694 }
2695
c4b7a5a9 2696 request->flags.internal = http->flags.internal;
2697 setLogUri (http, urlCanonicalClean(request));
73c36fd9 2698 request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
3d674977 2699#if FOLLOW_X_FORWARDED_FOR
40d34a62
AJ
2700 // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
2701 // not a details about teh TCP connection itself
73c36fd9 2702 request->indirect_client_addr = conn->clientConnection->remote;
3d674977 2703#endif /* FOLLOW_X_FORWARDED_FOR */
73c36fd9 2704 request->my_addr = conn->clientConnection->local;
35fb56c9 2705 request->myportname = conn->port->name;
8ae66e43 2706 request->http_ver = http_ver;
62e76326 2707
9174ba3d
AJ
2708 // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
2709 // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
2710 request->clientConnectionManager = conn;
2711
39cb8c41
AR
2712 if (request->header.chunked()) {
2713 chunked = true;
2714 } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
2715 const String te = request->header.getList(HDR_TRANSFER_ENCODING);
2716 // HTTP/1.1 requires chunking to be the last encoding if there is one
2717 unsupportedTe = te.size() && te != "identity";
2718 } // else implied identity coding
de48b288 2719
34ee6e73 2720 mustReplyToOptions = (method == METHOD_OPTIONS) &&
d916dcb8 2721 (request->header.getInt64(HDR_MAX_FORWARDS) == 0);
e18b8316 2722 if (!urlCheckRequest(request) || mustReplyToOptions || unsupportedTe) {
62e76326 2723 clientStreamNode *node = context->getClientReplyContext();
84c77748 2724 conn->quitAfterError(request);
0655fa4d 2725 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2726 assert (repContext);
be364179 2727 repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED, request->method, NULL,
73c36fd9 2728 conn->clientConnection->remote, request, NULL, NULL);
62e76326 2729 assert(context->http->out.offset == 0);
2730 context->pullData();
fc68f6b1 2731 goto finish;
c4b7a5a9 2732 }
2733
2734
39cb8c41 2735 if (!chunked && !clientIsContentLengthValid(request)) {
62e76326 2736 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2737 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2738 assert (repContext);
84c77748 2739 conn->quitAfterError(request);
0655fa4d 2740 repContext->setReplyToError(ERR_INVALID_REQ,
2741 HTTP_LENGTH_REQUIRED, request->method, NULL,
73c36fd9 2742 conn->clientConnection->remote, request, NULL, NULL);
62e76326 2743 assert(context->http->out.offset == 0);
2744 context->pullData();
fc68f6b1 2745 goto finish;
c4b7a5a9 2746 }
2747
52b601ff 2748 if (request->header.has(HDR_EXPECT)) {
655daa06
AR
2749 const String expect = request->header.getList(HDR_EXPECT);
2750 const bool supportedExpect = (expect.caseCmp("100-continue") == 0);
2751 if (!supportedExpect) {
52b601ff
AJ
2752 clientStreamNode *node = context->getClientReplyContext();
2753 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2754 assert (repContext);
84c77748 2755 conn->quitAfterError(request);
be364179 2756 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_EXPECTATION_FAILED, request->method, http->uri,
73c36fd9 2757 conn->clientConnection->remote, request, NULL, NULL);
52b601ff
AJ
2758 assert(context->http->out.offset == 0);
2759 context->pullData();
2760 goto finish;
2761 }
2762 }
2763
6dd9f4bd 2764 http->request = HTTPMSGLOCK(request);
c4b7a5a9 2765 clientSetKeepaliveFlag(http);
62e76326 2766
f35961af
AR
2767 // Let tunneling code be fully responsible for CONNECT requests
2768 if (http->request->method == METHOD_CONNECT) {
b66e0e86 2769 context->mayUseConnection(true);
f35961af
AR
2770 conn->flags.readMore = false;
2771 }
fc68f6b1 2772
061bbdec 2773#if USE_SSL
8eb0a7ee 2774 if (conn->switchedToHttps() && conn->serveDelayedError(context))
061bbdec 2775 goto finish;
061bbdec
CT
2776#endif
2777
b66e0e86 2778 /* Do we expect a request-body? */
39cb8c41
AR
2779 expectBody = chunked || request->content_length > 0;
2780 if (!context->mayUseConnection() && expectBody) {
2781 request->body_pipe = conn->expectRequestBody(
de48b288 2782 chunked ? -1 : request->content_length);
5f8252d2 2783
2784 // consume header early so that body pipe gets just the body
1cf238db 2785 connNoteUseOfBuffer(conn, http->req_sz);
5f8252d2 2786 notedUseOfBuffer = true;
2787
62e76326 2788 /* Is it too large? */
39cb8c41 2789 if (!chunked && // if chunked, we will check as we accumulate
de48b288 2790 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
62e76326 2791 clientStreamNode *node = context->getClientReplyContext();
0655fa4d 2792 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2793 assert (repContext);
84c77748 2794 conn->quitAfterError(request);
0655fa4d 2795 repContext->setReplyToError(ERR_TOO_BIG,
2796 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
73c36fd9 2797 conn->clientConnection->remote, http->request, NULL, NULL);
62e76326 2798 assert(context->http->out.offset == 0);
2799 context->pullData();
5f8252d2 2800 goto finish;
62e76326 2801 }
2802
39cb8c41
AR
2803 // We may stop producing, comm_close, and/or call setReplyToError()
2804 // below, so quit on errors to avoid http->doCallouts()
2805 if (!conn->handleRequestBodyData())
de48b288 2806 goto finish;
39cb8c41 2807
f35961af
AR
2808 if (!request->body_pipe->productionEnded()) {
2809 debugs(33, 5, HERE << "need more request body");
2810 context->mayUseConnection(true);
2811 assert(conn->flags.readMore);
2812 }
c4b7a5a9 2813 }
2814
de31d06f 2815 http->calloutContext = new ClientRequestContext(http);
2816
2817 http->doCallouts();
9e008dda 2818
4c29340e 2819finish:
5f8252d2 2820 if (!notedUseOfBuffer)
1cf238db 2821 connNoteUseOfBuffer(conn, http->req_sz);
52c2c8a8 2822
2823 /*
2824 * DPW 2007-05-18
2825 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
2826 * to here because calling comm_reset_close() causes http to
2827 * be freed and the above connNoteUseOfBuffer() would hit an
2828 * assertion, not to mention that we were accessing freed memory.
2829 */
b8faead9 2830 if (request && request->flags.resetTCP() && Comm::IsConnOpen(conn->clientConnection)) {
73c36fd9 2831 debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
f35961af 2832 conn->flags.readMore = false;
73c36fd9 2833 comm_reset_close(conn->clientConnection);
52c2c8a8 2834 }
c4b7a5a9 2835}
2836
2837static void
1cf238db 2838connStripBufferWhitespace (ConnStateData * conn)
c4b7a5a9 2839{
2840 while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
41d00cd3 2841 memmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
5e263176 2842 -- conn->in.notYetUsed;
c4b7a5a9 2843 }
2844}
2845
2846static int
1cf238db 2847connOkToAddRequest(ConnStateData * conn)
c4b7a5a9 2848{
0655fa4d 2849 int result = conn->getConcurrentRequestCount() < (Config.onoff.pipeline_prefetch ? 2 : 1);
62e76326 2850
c4b7a5a9 2851 if (!result) {
73c36fd9
AJ
2852 debugs(33, 3, HERE << conn->clientConnection << " max concurrent requests reached");
2853 debugs(33, 5, HERE << conn->clientConnection << " defering new request until one is done");
c4b7a5a9 2854 }
62e76326 2855
c4b7a5a9 2856 return result;
2857}
2858
63be0a78 2859/**
f900210a 2860 * Attempt to parse one or more requests from the input buffer.
2861 * If a request is successfully parsed, even if the next request
2862 * is only partially parsed, it will return TRUE.
f900210a 2863 */
4959e21e 2864bool
f35961af 2865ConnStateData::clientParseRequests()
f900210a 2866{
60745f24 2867 HttpRequestMethod method;
f900210a 2868 bool parsed_req = false;
8ae66e43 2869 HttpVersion http_ver;
f900210a 2870
1b76e6c1 2871 debugs(33, 5, HERE << clientConnection << ": attempting to parse");
f900210a 2872
39cb8c41 2873 // Loop while we have read bytes that are not needed for producing the body
f35961af
AR
2874 // On errors, bodyPipe may become nil, but readMore will be cleared
2875 while (in.notYetUsed > 0 && !bodyPipe && flags.readMore) {
4959e21e 2876 connStripBufferWhitespace(this);
f900210a 2877
fc68f6b1 2878 /* Don't try to parse if the buffer is empty */
4959e21e 2879 if (in.notYetUsed == 0)
fc68f6b1 2880 break;
4681057e 2881
f900210a 2882 /* Limit the number of concurrent requests to 2 */
4959e21e 2883 if (!connOkToAddRequest(this)) {
f900210a 2884 break;
2885 }
2886
2887 /* Should not be needed anymore */
2888 /* Terminate the string */
4959e21e 2889 in.buf[in.notYetUsed] = '\0';
f900210a 2890
fc68f6b1 2891 /* Begin the parsing */
fc68f6b1 2892 PROF_start(parseHttpRequest);
4959e21e 2893 HttpParserInit(&parser_, in.buf, in.notYetUsed);
fc68f6b1 2894
4959e21e
AJ
2895 /* Process request */
2896 ClientSocketContext *context = parseHttpRequest(this, &parser_, &method, &http_ver);
fc68f6b1 2897 PROF_stop(parseHttpRequest);
f900210a 2898
2899 /* partial or incomplete request */
2900 if (!context) {
39cb8c41
AR
2901 // TODO: why parseHttpRequest can just return parseHttpRequestAbort
2902 // (which becomes context) but checkHeaderLimits cannot?
4959e21e 2903 checkHeaderLimits();
f900210a 2904 break;
2905 }
2906
2907 /* status -1 or 1 */
2908 if (context) {
1b76e6c1 2909 debugs(33, 5, HERE << clientConnection << ": parsed a request");
8d77a37c 2910 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
dc49061a 2911 CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
1b76e6c1 2912 commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
f900210a 2913
4959e21e 2914 clientProcessRequest(this, &parser_, context, method, http_ver);
f900210a 2915
4959e21e 2916 parsed_req = true; // XXX: do we really need to parse everything right NOW ?
f900210a 2917
2918 if (context->mayUseConnection()) {
f35961af 2919 debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
f900210a 2920 break;
2921 }
f900210a 2922 }
39cb8c41 2923 }
fc68f6b1 2924
a5baffba 2925 /* XXX where to 'finish' the parsing pass? */
f900210a 2926 return parsed_req;
2927}
2928
1cf238db 2929void
2930ConnStateData::clientReadRequest(const CommIoCbParams &io)
c4b7a5a9 2931{
be364179 2932 debugs(33,5,HERE << io.conn << " size " << io.size);
f84dd7eb
AR
2933 Must(reading());
2934 reader = NULL;
c4b7a5a9 2935
c4b7a5a9 2936 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
62e76326 2937
1cf238db 2938 if (io.flag == COMM_ERR_CLOSING) {
be364179 2939 debugs(33,5, HERE << io.conn << " closing Bailout.");
c4b7a5a9 2940 return;
2941 }
62e76326 2942
73c36fd9
AJ
2943 assert(Comm::IsConnOpen(clientConnection));
2944 assert(io.conn->fd == clientConnection->fd);
5c336a3b 2945
44db45e8 2946 /*
2947 * Don't reset the timeout value here. The timeout value will be
2948 * set to Config.Timeout.request by httpAccept() and
2949 * clientWriteComplete(), and should apply to the request as a
2950 * whole, not individual read() calls. Plus, it breaks our
2951 * lame half-close detection
2952 */
1cf238db 2953 if (connReadWasError(io.flag, io.size, io.xerrno)) {
f692498b 2954 notifyAllContexts(io.xerrno);
be364179 2955 io.conn->close();
62e76326 2956 return;
7a2f978b 2957 }
c4b7a5a9 2958
1cf238db 2959 if (io.flag == COMM_OK) {
2960 if (io.size > 0) {
e4f1fdae 2961 kb_incr(&(statCounter.client_http.kbytes_in), io.size);
3b299123 2962
39cb8c41
AR
2963 // may comm_close or setReplyToError
2964 if (!handleReadData(io.buf, io.size))
9e008dda 2965 return;
a31a78fb 2966
1cf238db 2967 } else if (io.size == 0) {
be364179 2968 debugs(33, 5, HERE << io.conn << " closed?");
62e76326 2969
1cf238db 2970 if (connFinishedWithConn(io.size)) {
73c36fd9 2971 clientConnection->close();
62e76326 2972 return;
2973 }
2974
2975 /* It might be half-closed, we can't tell */
be364179 2976 fd_table[io.conn->fd].flags.socket_eof = 1;
62e76326 2977
be364179 2978 commMarkHalfClosed(io.conn->fd);
a46d2c0e 2979
be364179 2980 fd_note(io.conn->fd, "half-closed");
62e76326 2981
2982 /* There is one more close check at the end, to detect aborted
2983 * (partial) requests. At this point we can't tell if the request
2984 * is partial.
2985 */
2986 /* Continue to process previously read data */
2987 }
c4b7a5a9 2988 }
2989
94439e4e 2990 /* Process next request */
1cf238db 2991 if (getConcurrentRequestCount() == 0)
5c336a3b 2992 fd_note(io.fd, "Reading next request");
c8be6d7b 2993
f35961af 2994 if (!clientParseRequests()) {
9e008dda
AJ
2995 if (!isOpen())
2996 return;
f900210a 2997 /*
2998 * If the client here is half closed and we failed
2999 * to parse a request, close the connection.
3000 * The above check with connFinishedWithConn() only
3001 * succeeds _if_ the buffer is empty which it won't
3002 * be if we have an incomplete request.
6e1d409c 3003 * XXX: This duplicates ClientSocketContext::keepaliveNextRequest
f900210a 3004 */
5c336a3b 3005 if (getConcurrentRequestCount() == 0 && commIsHalfClosed(io.fd)) {
be364179 3006 debugs(33, 5, HERE << io.conn << ": half-closed connection, no completed request parsed, connection closing.");
73c36fd9 3007 clientConnection->close();
ee6f0213 3008 return;
62e76326 3009 }
f900210a 3010 }
ee6f0213 3011
1cf238db 3012 if (!isOpen())
2e216b1d 3013 return;
3014
f35961af 3015 clientAfterReadingRequests();
94439e4e 3016}
3017
63be0a78 3018/**
3019 * called when new request data has been read from the socket
39cb8c41
AR
3020 *
3021 * \retval false called comm_close or setReplyToError (the caller should bail)
3022 * \retval true we did not call comm_close or setReplyToError
63be0a78 3023 */
39cb8c41 3024bool
5f8252d2 3025ConnStateData::handleReadData(char *buf, size_t size)
94439e4e 3026{
5f8252d2 3027 char *current_buf = in.addressToReadInto();
62e76326 3028
5f8252d2 3029 if (buf != current_buf)
41d00cd3 3030 memmove(current_buf, buf, size);
3b299123 3031
5f8252d2 3032 in.notYetUsed += size;
fc68f6b1 3033
5f8252d2 3034 in.buf[in.notYetUsed] = '\0'; /* Terminate the string */
3b299123 3035
5f8252d2 3036 // if we are reading a body, stuff data into the body pipe
3037 if (bodyPipe != NULL)
39cb8c41
AR
3038 return handleRequestBodyData();
3039 return true;
94439e4e 3040}
3041
63be0a78 3042/**
bb790702 3043 * called when new request body data has been buffered in in.buf
63be0a78 3044 * may close the connection if we were closing and piped everything out
39cb8c41
AR
3045 *
3046 * \retval false called comm_close or setReplyToError (the caller should bail)
3047 * \retval true we did not call comm_close or setReplyToError
63be0a78 3048 */
39cb8c41 3049bool
5f8252d2 3050ConnStateData::handleRequestBodyData()
94439e4e 3051{
5f8252d2 3052 assert(bodyPipe != NULL);
3053
e1381638 3054 size_t putSize = 0;
3ff65596 3055
39cb8c41
AR
3056 if (in.bodyParser) { // chunked encoding
3057 if (const err_type error = handleChunkedRequestBody(putSize)) {
3058 abortChunkedRequestBody(error);
3059 return false;
3ff65596 3060 }
39cb8c41 3061 } else { // identity encoding
73c36fd9 3062 debugs(33,5, HERE << "handling plain request body for " << clientConnection);
3ff65596
AR
3063 putSize = bodyPipe->putMoreData(in.buf, in.notYetUsed);
3064 if (!bodyPipe->mayNeedMoreData()) {
3065 // BodyPipe will clear us automagically when we produced everything
3066 bodyPipe = NULL;
3067 }
3068 }
3069
3070 if (putSize > 0)
3071 connNoteUseOfBuffer(this, putSize);
5f8252d2 3072
3ff65596 3073 if (!bodyPipe) {
73c36fd9 3074 debugs(33,5, HERE << "produced entire request body for " << clientConnection);
62e76326 3075
cf6eb29e 3076 if (const char *reason = stoppedSending()) {
5f8252d2 3077 /* we've finished reading like good clients,
3078 * now do the close that initiateClose initiated.
5f8252d2 3079 */
cf6eb29e 3080 debugs(33, 3, HERE << "closing for earlier sending error: " << reason);
73c36fd9 3081 clientConnection->close();
39cb8c41
AR
3082 return false;
3083 }
3084 }
3085
3086 return true;
3087}
3088
3089/// parses available chunked encoded body bytes, checks size, returns errors
3090err_type
3091ConnStateData::handleChunkedRequestBody(size_t &putSize)
3092{
73c36fd9 3093 debugs(33,7, HERE << "chunked from " << clientConnection << ": " << in.notYetUsed);
39cb8c41
AR
3094
3095 try { // the parser will throw on errors
3096
3097 if (!in.notYetUsed) // nothing to do (MemBuf::init requires this check)
3098 return ERR_NONE;
3099
3100 MemBuf raw; // ChunkedCodingParser only works with MemBufs
3101 // add one because MemBuf will assert if it cannot 0-terminate
3102 raw.init(in.notYetUsed, in.notYetUsed+1);
3103 raw.append(in.buf, in.notYetUsed);
3104
3105 const mb_size_t wasContentSize = raw.contentSize();
3106 BodyPipeCheckout bpc(*bodyPipe);
3107 const bool parsed = in.bodyParser->parse(&raw, &bpc.buf);
3108 bpc.checkIn();
3109 putSize = wasContentSize - raw.contentSize();
3110
3111 // dechunk then check: the size limit applies to _dechunked_ content
3112 if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
3113 return ERR_TOO_BIG;
3114
3115 if (parsed) {
3116 finishDechunkingRequest(true);
3117 Must(!bodyPipe);
3118 return ERR_NONE; // nil bodyPipe implies body end for the caller
5f8252d2 3119 }
39cb8c41
AR
3120
3121 // if chunk parser needs data, then the body pipe must need it too
3122 Must(!in.bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
3123
3124 // if parser needs more space and we can consume nothing, we will stall
3125 Must(!in.bodyParser->needsMoreSpace() || bodyPipe->buf().hasContent());
3126 } catch (...) { // TODO: be more specific
3127 debugs(33, 3, HERE << "malformed chunks" << bodyPipe->status());
3128 return ERR_INVALID_REQ;
94439e4e 3129 }
39cb8c41
AR
3130
3131 debugs(33, 7, HERE << "need more chunked data" << *bodyPipe->status());
3132 return ERR_NONE;
3133}
3134
3135/// quit on errors related to chunked request body handling
3136void
3137ConnStateData::abortChunkedRequestBody(const err_type error)
3138{
3139 finishDechunkingRequest(false);
3140
3141 // XXX: The code below works if we fail during initial request parsing,
3142 // but if we fail when the server-side works already, the server may send
3143 // us its response too, causing various assertions. How to prevent that?
3144#if WE_KNOW_HOW_TO_SEND_ERRORS
3145 ClientSocketContext::Pointer context = getCurrentContext();
3146 if (context != NULL && !context->http->out.offset) { // output nothing yet
3147 clientStreamNode *node = context->getClientReplyContext();
3148 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
3149 assert(repContext);
3150 const http_status scode = (error == ERR_TOO_BIG) ?
de48b288 3151 HTTP_REQUEST_ENTITY_TOO_LARGE : HTTP_BAD_REQUEST;
39cb8c41
AR
3152 repContext->setReplyToError(error, scode,
3153 repContext->http->request->method,
3154 repContext->http->uri,
3155 peer,
3156 repContext->http->request,
3157 in.buf, NULL);
3158 context->pullData();
3159 } else {
3160 // close or otherwise we may get stuck as nobody will notice the error?
73c36fd9 3161 comm_reset_close(clientConnection);
39cb8c41
AR
3162 }
3163#else
3164 debugs(33, 3, HERE << "aborting chunked request without error " << error);
73c36fd9 3165 comm_reset_close(clientConnection);
39cb8c41 3166#endif
f35961af 3167 flags.readMore = false;
5f8252d2 3168}
55e44db9 3169
5f8252d2 3170void
1cf238db 3171ConnStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer )
5f8252d2 3172{
90737510 3173 if (!handleRequestBodyData())
1368d115
CT
3174 return;
3175
6bec3b16 3176 // too late to read more body
cf6eb29e 3177 if (!isOpen() || stoppedReceiving())
6bec3b16
AR
3178 return;
3179
1368d115 3180 readSomeData();
5f8252d2 3181}
3182
3183void
1cf238db 3184ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer )
5f8252d2 3185{
cf6eb29e
CT
3186 // request reader may get stuck waiting for space if nobody consumes body
3187 if (bodyPipe != NULL)
3188 bodyPipe->enableAutoConsumption();
3189
3190 stopReceiving("virgin request body consumer aborted"); // closes ASAP
94439e4e 3191}
3192
63be0a78 3193/** general lifetime handler for HTTP requests */
1cf238db 3194void
3195ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
7a2f978b 3196{
ad63ceea 3197#if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
1cf238db 3198 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
62e76326 3199
1cf238db 3200 if (COMMIO_FD_WRITECB(io.fd)->active) {
dec5db5d 3201 /* FIXME: If this code is reinstated, check the conn counters,
3202 * not the fd table state
3203 */
62e76326 3204 /*
3205 * Some data has been sent to the client, just close the FD
3206 */
73c36fd9 3207 clientConnection->close();
1cf238db 3208 } else if (nrequests) {
62e76326 3209 /*
3210 * assume its a persistent connection; just close it
3211 */
73c36fd9 3212 clientConnection->close();
7a2f978b 3213 } else {
62e76326 3214 /*
3215 * Generate an error
3216 */
59a1efb2 3217 ClientHttpRequest **H;
62e76326 3218 clientStreamNode *node;
719c7e0a 3219 ClientHttpRequest *http = parseHttpRequestAbort(this, "error:Connection%20lifetime%20expired");
62e76326 3220 node = http->client_stream.tail->prev->data;
0655fa4d 3221 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
3222 assert (repContext);
3223 repContext->setReplyToError(ERR_LIFETIME_EXP,
1cf238db 3224 HTTP_REQUEST_TIMEOUT, METHOD_NONE, "N/A", &peer.sin_addr,
0655fa4d 3225 NULL, NULL, NULL);
62e76326 3226 /* No requests can be outstanded */
1cf238db 3227 assert(chr == NULL);
62e76326 3228 /* add to the client request queue */
3229
3d0ac046 3230 for (H = &chr; *H; H = &(*H)->next);
62e76326 3231 *H = http;
3232
3233 clientStreamRead(http->client_stream.tail->data, http, 0,
3234 HTTP_REQBUF_SZ, context->reqbuf);
3235
3236 /*
3237 * if we don't close() here, we still need a timeout handler!
3238 */
9e008dda 3239 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876
AR
3240 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3241 TimeoutDialer, this, ConnStateData::requestTimeout);
8d77a37c 3242 commSetConnTimeout(io.conn, 30, timeoutCall);
62e76326 3243
3244 /*
3245 * Aha, but we don't want a read handler!
3246 */
d841c88d 3247 Comm::SetSelect(io.fd, COMM_SELECT_READ, NULL, NULL, 0);
7a2f978b 3248 }
62e76326 3249
af57a2e3 3250#else
3251 /*
62e76326 3252 * Just close the connection to not confuse browsers
3253 * using persistent connections. Some browsers opens
3254 * an connection and then does not use it until much
3255 * later (presumeably because the request triggering
3256 * the open has already been completed on another
3257 * connection)
3258 */
1cf238db 3259 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
8d77a37c 3260 io.conn->close();
af57a2e3 3261#endif
7a2f978b 3262}
3263
b5c39993 3264static void
8d77a37c 3265clientLifetimeTimeout(const CommTimeoutCbParams &io)
b5c39993 3266{
8d77a37c 3267 ClientHttpRequest *http = static_cast<ClientHttpRequest *>(io.data);
cb61ec47
AJ
3268 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout");
3269 debugs(33, DBG_IMPORTANT, "\t" << http->uri);
41ebd397 3270 http->al->http.timedout = true;
8d77a37c
AJ
3271 if (Comm::IsConnOpen(io.conn))
3272 io.conn->close();
b5c39993 3273}
3274
c8be6d7b 3275ConnStateData *
65d448bc 3276connStateCreate(const Comm::ConnectionPointer &client, AnyP::PortCfg *port)
c8be6d7b 3277{
a46d2c0e 3278 ConnStateData *result = new ConnStateData;
2ad20b4f 3279
73c36fd9 3280 result->clientConnection = client;
be364179 3281 result->log_addr = client->remote;
055421ee 3282 result->log_addr.ApplyMask(Config.Addrs.client_netmask);
e6ccf245 3283 result->in.buf = (char *)memAllocBuf(CLIENT_REQ_BUF_SZ, &result->in.allocatedSize);
3f38a55e 3284 result->port = cbdataReference(port);
62e76326 3285
5529ca8a 3286 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
9e008dda 3287 (result->transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
5529ca8a 3288#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
3289 int i = IP_PMTUDISC_DONT;
be364179 3290 setsockopt(client->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof i);
5529ca8a 3291#else
5529ca8a 3292 static int reported = 0;
3293
3294 if (!reported) {
e0236918 3295 debugs(33, DBG_IMPORTANT, "Notice: httpd_accel_no_pmtu_disc not supported on your platform");
5529ca8a 3296 reported = 1;
3297 }
89aec9b6
AJ
3298#endif
3299 }
5529ca8a 3300
89aec9b6
AJ
3301 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
3302 AsyncCall::Pointer call = JobCallback(33, 5, Dialer, result, ConnStateData::connStateClosed);
3303 comm_add_close_handler(client->fd, call);
5529ca8a 3304
89aec9b6
AJ
3305 if (Config.onoff.log_fqdn)
3306 fqdncache_gethostbyaddr(client->remote, FQDN_LOOKUP_IF_MISS);
3307
3308#if USE_IDENT
3309 if (Ident::TheConfig.identLookup) {
3310 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
3311 identChecklist.src_addr = client->remote;
3312 identChecklist.my_addr = client->local;
2efeb0b7 3313 if (identChecklist.fastCheck() == ACCESS_ALLOWED)
89aec9b6
AJ
3314 Ident::Start(client, clientIdentDone, result);
3315 }
5529ca8a 3316#endif
3317
89aec9b6
AJ
3318#if USE_SQUID_EUI
3319 if (Eui::TheConfig.euiLookup) {
3320 if (client->remote.IsIPv4()) {
3321 result->clientConnection->remoteEui48.lookup(client->remote);
3322 } else if (client->remote.IsIPv6()) {
3323 result->clientConnection->remoteEui64.lookup(client->remote);
3324 }
5529ca8a 3325 }
89aec9b6
AJ
3326#endif
3327
3328 clientdbEstablished(client->remote, 1);
5529ca8a 3329
f35961af 3330 result->flags.readMore = true;
c8be6d7b 3331 return result;
3332}
3333
63be0a78 3334/** Handle a new connection on HTTP socket. */
7a2f978b 3335void
449f0115 3336httpAccept(const CommAcceptCbParams &params)
7a2f978b 3337{
65d448bc 3338 AnyP::PortCfg *s = static_cast<AnyP::PortCfg *>(params.data);
02d1422b 3339
449f0115 3340 if (params.flag != COMM_OK) {
cbff89ba 3341 // Its possible the call was still queued when the client disconnected
449f0115 3342 debugs(33, 2, "httpAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
62e76326 3343 return;
3344 }
3345
449f0115
AJ
3346 debugs(33, 4, HERE << params.conn << ": accepted");
3347 fd_note(params.conn->fd, "client http connect");
1cf238db 3348
89aec9b6 3349 if (s->tcp_keepalive.enabled) {
449f0115 3350 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
89aec9b6 3351 }
62e76326 3352
95dc7ff4 3353 ++ incoming_sockets_accepted;
89aec9b6
AJ
3354
3355 // Socket is ready, setup the connection manager to start using it
449f0115 3356 ConnStateData *connState = connStateCreate(params.conn, s);
62e76326 3357
9e008dda 3358 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4299f876 3359 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
4cb2536f 3360 TimeoutDialer, connState, ConnStateData::requestTimeout);
6b2a2108 3361 commSetConnTimeout(params.conn, Config.Timeout.request, timeoutCall);
b2130d58 3362
a46d2c0e 3363 connState->readSomeData();
62e76326 3364
9a0a18de 3365#if USE_DELAY_POOLS
449f0115 3366 fd_table[params.conn->fd].clientInfo = NULL;
b4cd430a 3367
f33d34a8 3368 if (Config.onoff.client_db) {
b4cd430a
CT
3369 /* it was said several times that client write limiter does not work if client_db is disabled */
3370
3371 ClientDelayPools& pools(Config.ClientDelay.pools);
2efeb0b7 3372 ACLFilledChecklist ch(NULL, NULL, NULL);
b4cd430a 3373
2efeb0b7
AJ
3374 // TODO: we check early to limit error response bandwith but we
3375 // should recheck when we can honor delay_pool_uses_indirect
3376 // TODO: we should also pass the port details for myportname here.
449f0115
AJ
3377 ch.src_addr = params.conn->remote;
3378 ch.my_addr = params.conn->local;
b4cd430a 3379
95dc7ff4 3380 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
b4cd430a 3381
2efeb0b7
AJ
3382 /* pools require explicit 'allow' to assign a client into them */
3383 if (pools[pool].access) {
3384 ch.accessList = pools[pool].access;
3385 allow_t answer = ch.fastCheck();
3386 if (answer == ACCESS_ALLOWED) {
3387
3388 /* request client information from db after we did all checks
3389 this will save hash lookup if client failed checks */
449f0115 3390 ClientInfo * cli = clientdbGetInfo(params.conn->remote);
2efeb0b7
AJ
3391 assert(cli);
3392
3393 /* put client info in FDE */
449f0115 3394 fd_table[params.conn->fd].clientInfo = cli;
2efeb0b7
AJ
3395
3396 /* setup write limiter for this request */
3397 const double burst = floor(0.5 +
3398 (pools[pool].highwatermark * Config.ClientDelay.initial)/100.0);
3399 cli->setWriteLimiter(pools[pool].rate, burst, pools[pool].highwatermark);
3400 break;
3401 } else {
3402 debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
3403 }
b4cd430a
CT
3404 }
3405 }
3406 }
3407#endif
7a2f978b 3408}
3409
1f7c9178 3410#if USE_SSL
3411
63be0a78 3412/** Create SSL connection structure and update fd_table */
ae7ff0b8 3413static SSL *
449f0115 3414httpsCreate(const Comm::ConnectionPointer &conn, SSL_CTX *sslContext)
ae7ff0b8 3415{
3416 SSL *ssl = SSL_new(sslContext);
3417
3418 if (!ssl) {
3419 const int ssl_error = ERR_get_error();
449f0115
AJ
3420 debugs(83, DBG_IMPORTANT, "ERROR: httpsAccept: Error allocating handle: " << ERR_error_string(ssl_error, NULL) );
3421 conn->close();
ae7ff0b8 3422 return NULL;
3423 }
3424
449f0115
AJ
3425 SSL_set_fd(ssl, conn->fd);
3426 fd_table[conn->fd].ssl = ssl;
3427 fd_table[conn->fd].read_method = &ssl_read_method;
3428 fd_table[conn->fd].write_method = &ssl_write_method;
ae7ff0b8 3429
449f0115
AJ
3430 debugs(33, 5, "httpsCreate: will negotate SSL on " << conn);
3431 fd_note(conn->fd, "client https start");
ae7ff0b8 3432
3433 return ssl;
3434}
3435
63be0a78 3436/** negotiate an SSL connection */
1f7c9178 3437static void
3438clientNegotiateSSL(int fd, void *data)
3439{
e6ccf245 3440 ConnStateData *conn = (ConnStateData *)data;
1f7c9178 3441 X509 *client_cert;
a7ad6e4e 3442 SSL *ssl = fd_table[fd].ssl;
1f7c9178 3443 int ret;
3444
a7ad6e4e 3445 if ((ret = SSL_accept(ssl)) <= 0) {
62e76326 3446 int ssl_error = SSL_get_error(ssl, ret);
3447
3448 switch (ssl_error) {
3449
3450 case SSL_ERROR_WANT_READ:
d841c88d 3451 Comm::SetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
62e76326 3452 return;
3453
3454 case SSL_ERROR_WANT_WRITE:
d841c88d 3455 Comm::SetSelect(fd, COMM_SELECT_WRITE, clientNegotiateSSL, conn, 0);
62e76326 3456 return;
3457
6de9e64b 3458 case SSL_ERROR_SYSCALL:
3459
3460 if (ret == 0) {
bf8fe701 3461 debugs(83, 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Aborted by client");
6de9e64b 3462 comm_close(fd);
3463 return;
3464 } else {
3465 int hard = 1;
3466
3467 if (errno == ECONNRESET)
3468 hard = 0;
3469
9e008dda 3470 debugs(83, hard ? 1 : 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
bf8fe701 3471 fd << ": " << strerror(errno) << " (" << errno << ")");
6de9e64b 3472
3473 comm_close(fd);
3474
3475 return;
3476 }
3477
3478 case SSL_ERROR_ZERO_RETURN:
e0236918 3479 debugs(83, DBG_IMPORTANT, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Closed by client");
6de9e64b 3480 comm_close(fd);
3481 return;
3482
62e76326 3483 default:
e0236918 3484 debugs(83, DBG_IMPORTANT, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
9e008dda
AJ
3485 fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
3486 " (" << ssl_error << "/" << ret << ")");
62e76326 3487 comm_close(fd);
3488 return;
3489 }
3490
3491 /* NOTREACHED */
1f7c9178 3492 }
62e76326 3493
6de9e64b 3494 if (SSL_session_reused(ssl)) {
bf8fe701 3495 debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
3496 " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
6de9e64b 3497 } else {
3498 if (do_debug(83, 4)) {
3499 /* Write out the SSL session details.. actually the call below, but
3500 * OpenSSL headers do strange typecasts confusing GCC.. */
3501 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
afdd443f 3502#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
9f3de01a 3503 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 3504
0fd2205b 3505#elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2930f303 3506
0fd2205b 3507 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
3508 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
3509 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
3510 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
3511 * Because there are two possible usable cast, if you get an error here, try the other
3512 * commented line. */
3513
3514 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
3515 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
2930f303 3516
0e33d58c 3517#else
3518
bf8fe701 3519 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
0fd2205b 3520
0e33d58c 3521#endif
6de9e64b 3522 /* Note: This does not automatically fflush the log file.. */
3523 }
3524
bf8fe701 3525 debugs(83, 2, "clientNegotiateSSL: New session " <<
3526 SSL_get_session(ssl) << " on FD " << fd << " (" <<
3527 fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
3528 ")");
6de9e64b 3529 }
3530
bf8fe701 3531 debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
3532 SSL_get_cipher(ssl));
1f7c9178 3533
6de9e64b 3534 client_cert = SSL_get_peer_certificate(ssl);
62e76326 3535
1f7c9178 3536 if (client_cert != NULL) {
bf8fe701 3537 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3538 " client certificate: subject: " <<
3539 X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
3540
3541 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3542 " client certificate: issuer: " <<
3543 X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
1f7c9178 3544
1f7c9178 3545
62e76326 3546 X509_free(client_cert);
1f7c9178 3547 } else {
bf8fe701 3548 debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
3549 " has no certificate.");
1f7c9178 3550 }
3551
a46d2c0e 3552 conn->readSomeData();
1f7c9178 3553}
3554
379e8c1c 3555/**
ab2e0682
CT
3556 * If SSL_CTX is given, starts reading the SSL handshake.
3557 * Otherwise, calls switchToHttps to generate a dynamic SSL_CTX.
379e8c1c
AR
3558 */
3559static void
caf3666d 3560httpsEstablish(ConnStateData *connState, SSL_CTX *sslContext, Ssl::BumpMode bumpMode)
379e8c1c
AR
3561{
3562 SSL *ssl = NULL;
3563 assert(connState);
3564 const Comm::ConnectionPointer &details = connState->clientConnection;
3565
3566 if (sslContext && !(ssl = httpsCreate(details, sslContext)))
3567 return;
3568
7a957a93
AR
3569 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3570 AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
87f237a9 3571 connState, ConnStateData::requestTimeout);
379e8c1c
AR
3572 commSetConnTimeout(details, Config.Timeout.request, timeoutCall);
3573
87f237a9 3574 if (ssl)
379e8c1c
AR
3575 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
3576 else {
3577 char buf[MAX_IPSTRLEN];
caf3666d 3578 assert(bumpMode != Ssl::bumpNone && bumpMode != Ssl::bumpEnd);
65ba7f04
CT
3579 HttpRequest *fakeRequest = new HttpRequest;
3580 fakeRequest->SetHost(details->local.NtoA(buf, sizeof(buf)));
3581 fakeRequest->port = details->local.GetPort();
3582 fakeRequest->clientConnectionManager = connState;
3583 fakeRequest->client_addr = connState->clientConnection->remote;
3584#if FOLLOW_X_FORWARDED_FOR
3585 fakeRequest->indirect_client_addr = connState->clientConnection->remote;
3586#endif
3587 fakeRequest->my_addr = connState->clientConnection->local;
f2f98cca 3588 fakeRequest->flags.spoof_client_ip = ((connState->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
6df4ecae 3589 fakeRequest->flags.intercepted = ((connState->clientConnection->flags & COMM_INTERCEPTION) != 0);
379e8c1c 3590 debugs(33, 4, HERE << details << " try to generate a Dynamic SSL CTX");
caf3666d 3591 connState->switchToHttps(fakeRequest, bumpMode);
379e8c1c
AR
3592 }
3593}
3594
3595/**
87f237a9 3596 * A callback function to use with the ACLFilledChecklist callback.
7a957a93
AR
3597 * In the case of ACCES_ALLOWED answer initializes a bumped SSL connection,
3598 * else reverts the connection to tunnel mode.
379e8c1c
AR
3599 */
3600static void
3601httpsSslBumpAccessCheckDone(allow_t answer, void *data)
3602{
3603 ConnStateData *connState = (ConnStateData *) data;
3604
7a957a93 3605 // if the connection is closed or closing, just return.
379e8c1c
AR
3606 if (!connState->isOpen())
3607 return;
3608
7a957a93 3609 // Require both a match and a positive bump mode to work around exceptional
caf3666d
AR
3610 // cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
3611 if (answer == ACCESS_ALLOWED && answer.kind != Ssl::bumpNone) {
08097970
AR
3612 debugs(33, 2, HERE << "sslBump needed for " << connState->clientConnection);
3613 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
caf3666d 3614 httpsEstablish(connState, NULL, (Ssl::BumpMode)answer.kind);
379e8c1c 3615 } else {
08097970
AR
3616 debugs(33, 2, HERE << "sslBump not needed for " << connState->clientConnection);
3617 connState->sslBumpMode = Ssl::bumpNone;
379e8c1c 3618
08097970 3619 // fake a CONNECT request to force connState to tunnel
379e8c1c
AR
3620 static char ip[MAX_IPSTRLEN];
3621 static char reqStr[MAX_IPSTRLEN + 80];
3622 connState->clientConnection->local.NtoA(ip, sizeof(ip));
ab2e0682 3623 snprintf(reqStr, sizeof(reqStr), "CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\n\r\n", ip, connState->clientConnection->local.GetPort(), ip);
379e8c1c
AR
3624 bool ret = connState->handleReadData(reqStr, strlen(reqStr));
3625 if (ret)
3626 ret = connState->clientParseRequests();
3627
3628 if (!ret) {
3629 debugs(33, 2, HERE << "Failed to start fake CONNECT request for ssl bumped connection: " << connState->clientConnection);
3630 connState->clientConnection->close();
3631 }
3632 }
3633}
3634
63be0a78 3635/** handle a new HTTPS connection */
1f7c9178 3636static void
449f0115 3637httpsAccept(const CommAcceptCbParams &params)
1f7c9178 3638{
65d448bc 3639 AnyP::PortCfg *s = static_cast<AnyP::PortCfg *>(params.data);
c4b7a5a9 3640
449f0115 3641 if (params.flag != COMM_OK) {
cbff89ba 3642 // Its possible the call was still queued when the client disconnected
449f0115 3643 debugs(33, 2, "httpsAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
62e76326 3644 return;
c4b7a5a9 3645 }
62e76326 3646
449f0115
AJ
3647 debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
3648 fd_note(params.conn->fd, "client https connect");
62e76326 3649
859741ed
AJ
3650 if (s->tcp_keepalive.enabled) {
3651 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
b2130d58 3652 }
3653
95dc7ff4 3654 ++incoming_sockets_accepted;
62e76326 3655
89aec9b6 3656 // Socket is ready, setup the connection manager to start using it
859741ed 3657 ConnStateData *connState = connStateCreate(params.conn, s);
62e76326 3658
379e8c1c 3659 if (s->sslBump) {
379e8c1c 3660 debugs(33, 5, "httpsAccept: accept transparent connection: " << params.conn);
62e76326 3661
379e8c1c
AR
3662 if (!Config.accessList.ssl_bump) {
3663 httpsSslBumpAccessCheckDone(ACCESS_DENIED, connState);
3664 return;
3665 }
87f237a9 3666
379e8c1c 3667 // Create a fake HTTP request for ssl_bump ACL check,
38450a50 3668 // using tproxy/intercept provided destination IP and port.
379e8c1c
AR
3669 HttpRequest *request = new HttpRequest();
3670 static char ip[MAX_IPSTRLEN];
d7ce0bcd 3671 assert(params.conn->flags & (COMM_TRANSPARENT | COMM_INTERCEPTION));
379e8c1c
AR
3672 request->SetHost(params.conn->local.NtoA(ip, sizeof(ip)));
3673 request->port = params.conn->local.GetPort();
3674 request->myportname = s->name;
87f237a9 3675
379e8c1c
AR
3676 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, NULL);
3677 acl_checklist->src_addr = params.conn->remote;
3678 acl_checklist->my_addr = s->s;
3679 acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, connState);
3680 return;
3681 } else {
3682 SSL_CTX *sslContext = s->staticSslContext.get();
caf3666d 3683 httpsEstablish(connState, sslContext, Ssl::bumpNone);
379e8c1c 3684 }
1f7c9178 3685}
3686
95d2589c
CT
3687void
3688ConnStateData::sslCrtdHandleReplyWrapper(void *data, char *reply)
ae7ff0b8 3689{
95d2589c
CT
3690 ConnStateData * state_data = (ConnStateData *)(data);
3691 state_data->sslCrtdHandleReply(reply);
3692}
ae7ff0b8 3693
95d2589c
CT
3694void
3695ConnStateData::sslCrtdHandleReply(const char * reply)
3696{
3697 if (!reply) {
e0236918 3698 debugs(1, DBG_IMPORTANT, HERE << "\"ssl_crtd\" helper return <NULL> reply");
95d2589c
CT
3699 } else {
3700 Ssl::CrtdMessage reply_message;
3701 if (reply_message.parse(reply, strlen(reply)) != Ssl::CrtdMessage::OK) {
fb2178bb 3702 debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslConnectHostOrIp << " is incorrect");
95d2589c 3703 } else {
419ac015 3704 if (reply_message.getCode() != "OK") {
fb2178bb 3705 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
95d2589c 3706 } else {
fb2178bb 3707 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
a594dbfa
CT
3708 SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str());
3709 getSslContextDone(ctx, true);
95d2589c
CT
3710 return;
3711 }
3712 }
3713 }
3714 getSslContextDone(NULL);
3715}
ae7ff0b8 3716
06997a38 3717void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties)
fb2178bb 3718{
06997a38 3719 certProperties.commonName = sslCommonName.defined() ? sslCommonName.termedBuf() : sslConnectHostOrIp.termedBuf();
fb2178bb
CT
3720
3721 // fake certificate adaptation requires bump-server-first mode
caf3666d
AR
3722 if (!sslServerBump) {
3723 assert(port->signingCert.get());
3724 certProperties.signWithX509.resetAndLock(port->signingCert.get());
3725 if (port->signPkey.get())
3726 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3727 certProperties.signAlgorithm = Ssl::algSignTrusted;
fb2178bb 3728 return;
caf3666d 3729 }
fb2178bb 3730
7a957a93
AR
3731 // In case of an error while connecting to the secure server, use a fake
3732 // trusted certificate, with no mimicked fields and no adaptation
3733 // algorithms. There is nothing we can mimic so we want to minimize the
3734 // number of warnings the user will have to see to get to the error page.
fd4624d7
CT
3735 assert(sslServerBump->entry);
3736 if (sslServerBump->entry->isEmpty()) {
3737 if (X509 *mimicCert = sslServerBump->serverCert.get())
59a49556
CT
3738 certProperties.mimicCert.resetAndLock(mimicCert);
3739
87f237a9 3740 ACLFilledChecklist checklist(NULL, sslServerBump->request,
59a49556
CT
3741 clientConnection != NULL ? clientConnection->rfc931 : dash_str);
3742 checklist.conn(this);
7a957a93 3743 checklist.sslErrors = cbdataReference(sslServerBump->sslErrors);
59a49556
CT
3744
3745 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != NULL; ca = ca->next) {
7a957a93 3746 // If the algorithm already set, then ignore it.
a06042fa 3747 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
87f237a9
A
3748 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
3749 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
a06042fa
CT
3750 continue;
3751
59a49556
CT
3752 if (ca->aclList && checklist.fastCheck(ca->aclList) == ACCESS_ALLOWED) {
3753 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
3754 const char *param = ca->param;
87f237a9 3755
7a957a93
AR
3756 // For parameterless CN adaptation, use hostname from the
3757 // CONNECT request.
a06042fa 3758 if (ca->alg == Ssl::algSetCommonName) {
59a49556
CT
3759 if (!param)
3760 param = sslConnectHostOrIp.termedBuf();
3761 certProperties.commonName = param;
3762 certProperties.setCommonName = true;
87f237a9 3763 } else if (ca->alg == Ssl::algSetValidAfter)
59a49556 3764 certProperties.setValidAfter = true;
87f237a9 3765 else if (ca->alg == Ssl::algSetValidBefore)
59a49556
CT
3766 certProperties.setValidBefore = true;
3767
87f237a9 3768 debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
8f9720ce 3769 alg << " param: " << (param ? param : "-"));
aebe6888 3770 }
fb2178bb 3771 }
aebe6888 3772
59a49556
CT
3773 certProperties.signAlgorithm = Ssl::algSignEnd;
3774 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
3775 if (sg->aclList && checklist.fastCheck(sg->aclList) == ACCESS_ALLOWED) {
3776 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
3777 break;
3778 }
aebe6888 3779 }
fd4624d7 3780 } else {// if (!sslServerBump->entry->isEmpty())
59a49556
CT
3781 // Use trusted certificate for a Squid-generated error
3782 // or the user would have to add a security exception
3783 // just to see the error page. We will close the connection
3784 // so that the trust is not extended to non-Squid content.
3785 certProperties.signAlgorithm = Ssl::algSignTrusted;
aebe6888
CT
3786 }
3787
10d914f6 3788 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
aebe6888
CT
3789
3790 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
f4e4d4d6 3791 assert(port->untrustedSigningCert.get());
95588170
CT
3792 certProperties.signWithX509.resetAndLock(port->untrustedSigningCert.get());
3793 certProperties.signWithPkey.resetAndLock(port->untrustedSignPkey.get());
87f237a9 3794 } else {
f4e4d4d6
CT
3795 assert(port->signingCert.get());
3796 certProperties.signWithX509.resetAndLock(port->signingCert.get());
aebe6888
CT
3797
3798 if (port->signPkey.get())
3799 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3800 }
3801 signAlgorithm = certProperties.signAlgorithm;
fb2178bb
CT
3802}
3803
1ce2822d 3804void
95d2589c
CT
3805ConnStateData::getSslContextStart()
3806{
129fe2a1
CT
3807 assert(areAllContextsForThisConnection());
3808 freeAllContexts();
3809 /* careful: freeAllContexts() above frees request, host, etc. */
3810
fb2178bb 3811 if (port->generateHostCertificates) {
aebe6888 3812 Ssl::CertificateProperties certProperties;
06997a38
CT
3813 buildSslCertGenerationParams(certProperties);
3814 sslBumpCertKey = certProperties.dbKey().c_str();
fb2178bb
CT
3815 assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
3816
aebe6888 3817 debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache");
95d2589c 3818 Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
fb2178bb 3819 SSL_CTX * dynCtx = ssl_ctx_cache.find(sslBumpCertKey.termedBuf());
95d2589c 3820 if (dynCtx) {
fb2178bb 3821 debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " have found in cache");
4ece76b2 3822 if (Ssl::verifySslCertificate(dynCtx, certProperties)) {
fb2178bb 3823 debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is valid");
1ce2822d
AR
3824 getSslContextDone(dynCtx);
3825 return;
95d2589c 3826 } else {
fb2178bb
CT
3827 debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
3828 ssl_ctx_cache.remove(sslBumpCertKey.termedBuf());
95d2589c
CT
3829 }
3830 } else {
fb2178bb 3831 debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
95d2589c
CT
3832 }
3833
b5faa519 3834#if USE_SSL_CRTD
00fc192d 3835 try {
87f237a9
A
3836 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
3837 Ssl::CrtdMessage request_message;
3838 request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
3839 request_message.composeRequest(certProperties);
3840 debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
3841 Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
3842 return;
3843 } catch (const std::exception &e) {
00fc192d
AR
3844 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
3845 "request for " << certProperties.commonName <<
3846 " certificate: " << e.what() << "; will now block to " <<
3847 "generate that certificate.");
3848 // fall through to do blocking in-process generation.
3849 }
3850#endif // USE_SSL_CRTD
3851
aebe6888
CT
3852 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
3853 dynCtx = Ssl::generateSslContext(certProperties);
1ce2822d
AR
3854 getSslContextDone(dynCtx, true);
3855 return;
95d2589c 3856 }
1ce2822d 3857 getSslContextDone(NULL);
95d2589c
CT
3858}
3859
1ce2822d 3860void
95d2589c
CT
3861ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew)
3862{
3863 // Try to add generated ssl context to storage.
3864 if (port->generateHostCertificates && isNew) {
a594dbfa 3865
aebe6888
CT
3866 if (signAlgorithm == Ssl::algSignTrusted)
3867 Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
3868 //else it is self-signed or untrusted do not attrach any certificate
a594dbfa 3869
95d2589c 3870 Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
fb2178bb
CT
3871 assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
3872 if (sslContext) {
3873 if (!ssl_ctx_cache.add(sslBumpCertKey.termedBuf(), sslContext)) {
95d2589c 3874 // If it is not in storage delete after using. Else storage deleted it.
73c36fd9 3875 fd_table[clientConnection->fd].dynamicSslContext = sslContext;
95d2589c
CT
3876 }
3877 } else {
fb2178bb 3878 debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslConnectHostOrIp);
95d2589c
CT
3879 }
3880 }
3881
3882 // If generated ssl context = NULL, try to use static ssl context.
3883 if (!sslContext) {
3884 if (!port->staticSslContext) {
e0236918 3885 debugs(83, DBG_IMPORTANT, "Closing SSL " << clientConnection->remote << " as lacking SSL context");
73c36fd9 3886 clientConnection->close();
1ce2822d 3887 return;
95d2589c
CT
3888 } else {
3889 debugs(33, 5, HERE << "Using static ssl context.");
3890 sslContext = port->staticSslContext.get();
3891 }
3892 }
ae7ff0b8 3893
ae7ff0b8 3894 SSL *ssl = NULL;
73c36fd9 3895 if (!(ssl = httpsCreate(clientConnection, sslContext)))
1ce2822d 3896 return;
ae7ff0b8 3897
8d77a37c 3898 // commSetConnTimeout() was called for this request before we switched.
ae7ff0b8 3899
3900 // Disable the client read handler until peer selection is complete
73c36fd9
AJ
3901 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
3902 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
ae7ff0b8 3903 switchedToHttps_ = true;
ae7ff0b8 3904}
3905
1ce2822d 3906void
caf3666d 3907ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode)
95d2589c
CT
3908{
3909 assert(!switchedToHttps_);
3910
65ba7f04
CT
3911 sslConnectHostOrIp = request->GetHost();
3912 sslCommonName = request->GetHost();
95d2589c 3913
83d4cd15
CT
3914 // We are going to read new request
3915 flags.readMore = true;
73c36fd9 3916 debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
95d2589c 3917
2bd84e5f
CT
3918 // If sslServerBump is set, then we have decided to deny CONNECT
3919 // and now want to switch to SSL to send the error to the client
3920 // without even peeking at the origin server certificate.
caf3666d 3921 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
65ba7f04
CT
3922 request->flags.sslPeek = 1;
3923 sslServerBump = new Ssl::ServerBump(request);
fd4624d7 3924
d7ce0bcd 3925 // will call httpsPeeked() with certificate and connection, eventually
fd4624d7 3926 FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request);
d7ce0bcd
AR
3927 return;
3928 }
3929
fb2178bb 3930 // otherwise, use sslConnectHostOrIp
d7ce0bcd
AR
3931 getSslContextStart();
3932}
3933
3934void
3935ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
3936{
fd4624d7 3937 Must(sslServerBump != NULL);
819c207f 3938
061bbdec
CT
3939 if (Comm::IsConnOpen(serverConnection)) {
3940 SSL *ssl = fd_table[serverConnection->fd].ssl;
3941 assert(ssl);
3942 Ssl::X509_Pointer serverCert(SSL_get_peer_certificate(ssl));
3943 assert(serverCert.get() != NULL);
fb2178bb 3944 sslCommonName = Ssl::CommonHostName(serverCert.get());
87f237a9 3945 debugs(33, 5, HERE << "HTTPS server CN: " << sslCommonName <<
59a49556 3946 " bumped: " << *serverConnection);
061bbdec
CT
3947
3948 pinConnection(serverConnection, NULL, NULL, false);
3949
fb2178bb 3950 debugs(33, 5, HERE << "bumped HTTPS server: " << sslConnectHostOrIp);
59a49556 3951 } else {
13858f50 3952 debugs(33, 5, HERE << "Error while bumping: " << sslConnectHostOrIp);
8499a1de
CT
3953 Ip::Address intendedDest;
3954 intendedDest = sslConnectHostOrIp.termedBuf();
35cd4281
CT
3955 const bool isConnectRequest = !port->spoof_client_ip && !port->intercepted;
3956
8499a1de
CT
3957 // Squid serves its own error page and closes, so we want
3958 // a CN that causes no additional browser errors. Possible
35cd4281
CT
3959 // only when bumping CONNECT with a user-typed address.
3960 if (intendedDest.IsAnyAddr() || isConnectRequest)
8499a1de 3961 sslCommonName = sslConnectHostOrIp;
fd4624d7
CT
3962 else if (sslServerBump->serverCert.get())
3963 sslCommonName = Ssl::CommonHostName(sslServerBump->serverCert.get());
129fe2a1
CT
3964
3965 // copy error detail from bump-server-first request to CONNECT request
3966 if (currentobject != NULL && currentobject->http != NULL && currentobject->http->request)
3967 currentobject->http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
59a49556 3968 }
061bbdec 3969
1ce2822d 3970 getSslContextStart();
95d2589c
CT
3971}
3972
1f7c9178 3973#endif /* USE_SSL */
3974
00516be1
AR
3975/// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
3976static bool
73c36fd9 3977OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
00516be1 3978{
73c36fd9 3979 if (!Comm::IsConnOpen(c)) {
00516be1
AR
3980 Must(NHttpSockets > 0); // we tried to open some
3981 --NHttpSockets; // there will be fewer sockets than planned
3982 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3983
3984 if (!NHttpSockets) // we could not open any listen sockets at all
cbff89ba 3985 fatalf("Unable to open %s",FdNote(portType));
00516be1
AR
3986
3987 return false;
3988 }
3989 return true;
3990}
3991
3992/// find any unused HttpSockets[] slot and store fd there or return false
3993static bool
e0d28505 3994AddOpenedHttpSocket(const Comm::ConnectionPointer &conn)
00516be1
AR
3995{
3996 bool found = false;
95dc7ff4 3997 for (int i = 0; i < NHttpSockets && !found; ++i) {
00516be1 3998 if ((found = HttpSockets[i] < 0))
e0d28505 3999 HttpSockets[i] = conn->fd;
00516be1
AR
4000 }
4001 return found;
4002}
15df8349 4003
d193a436 4004static void
15df8349 4005clientHttpConnectionsOpen(void)
4006{
65d448bc 4007 AnyP::PortCfg *s = NULL;
62e76326 4008
7e3ce7b9 4009 for (s = Config.Sockaddr.http; s; s = s->next) {
65d448bc 4010 if (MAXTCPLISTENPORTS == NHttpSockets) {
e0236918
FC
4011 debugs(1, DBG_IMPORTANT, "WARNING: You have too many 'http_port' lines.");
4012 debugs(1, DBG_IMPORTANT, " The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
62e76326 4013 continue;
4014 }
4015
ae7ff0b8 4016#if USE_SSL
3d61b18e 4017 if (s->sslBump && !Config.accessList.ssl_bump) {
859741ed 4018 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << s->protocol << "_port " << s->s);
3d61b18e
AJ
4019 s->sslBump = 0;
4020 }
4021
4022 if (s->sslBump && !s->staticSslContext && !s->generateHostCertificates) {
859741ed 4023 debugs(1, DBG_IMPORTANT, "Will not bump SSL at http_port " << s->s << " due to SSL initialization failure.");
ae7ff0b8 4024 s->sslBump = 0;
4025 }
95d2589c 4026 if (s->sslBump) {
95d2589c
CT
4027 // Create ssl_ctx cache for this port.
4028 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
4029 }
ae7ff0b8 4030#endif
4031
e0d28505 4032 // Fill out a Comm::Connection which IPC will open as a listener for us
8bbb16e3 4033 // then pass back when active so we can start a TcpAcceptor subscription.
e0d28505
AJ
4034 s->listenConn = new Comm::Connection;
4035 s->listenConn->local = s->s;
40d34a62 4036 s->listenConn->flags = COMM_NONBLOCKING | (s->spoof_client_ip ? COMM_TRANSPARENT : 0) | (s->intercepted ? COMM_INTERCEPTION : 0);
c303f6e3 4037
cbff89ba
AJ
4038 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTP
4039 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4040 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpAccept", CommAcceptCbPtrFun(httpAccept, s));
4041 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
62e76326 4042
cbff89ba 4043 AsyncCall::Pointer listenCall = asyncCall(33,2, "clientListenerConnectionOpened",
8bbb16e3
AJ
4044 ListeningStartedDialer(&clientListenerConnectionOpened, s, Ipc::fdnHttpSocket, sub));
4045 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpSocket, listenCall);
62e76326 4046
a38ec4b1
FC
4047 HttpSockets[NHttpSockets] = -1; // set in clientListenerConnectionOpened
4048 ++NHttpSockets;
15df8349 4049 }
d193a436 4050}
4051
4052#if USE_SSL
4053static void
4054clientHttpsConnectionsOpen(void)
4055{
65d448bc 4056 AnyP::PortCfg *s;
62e76326 4057
859741ed 4058 for (s = Config.Sockaddr.https; s; s = s->next) {
65d448bc 4059 if (MAXTCPLISTENPORTS == NHttpSockets) {
e0236918
FC
4060 debugs(1, DBG_IMPORTANT, "Ignoring 'https_port' lines exceeding the limit.");
4061 debugs(1, DBG_IMPORTANT, "The limit is " << MAXTCPLISTENPORTS << " HTTPS ports.");
62e76326 4062 continue;
4063 }
4064
95d2589c 4065 if (!s->staticSslContext) {
e0236918 4066 debugs(1, DBG_IMPORTANT, "Ignoring https_port " << s->s <<
ae7ff0b8 4067 " due to SSL initialization failure.");
4068 continue;
f9ad0106 4069 }
4070
d7ce0bcd
AR
4071 // TODO: merge with similar code in clientHttpConnectionsOpen()
4072 if (s->sslBump && !Config.accessList.ssl_bump) {
03f00a11 4073 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << s->protocol << "_port " << s->s);
d7ce0bcd
AR
4074 s->sslBump = 0;
4075 }
4076
4077 if (s->sslBump && !s->staticSslContext && !s->generateHostCertificates) {
03f00a11 4078 debugs(1, DBG_IMPORTANT, "Will not bump SSL at http_port " << s->s << " due to SSL initialization failure.");
d7ce0bcd
AR
4079 s->sslBump = 0;
4080 }
4081
4082 if (s->sslBump) {
4083 // Create ssl_ctx cache for this port.
4084 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
4085 }
4086
e0d28505 4087 // Fill out a Comm::Connection which IPC will open as a listener for us
859741ed
AJ
4088 s->listenConn = new Comm::Connection;
4089 s->listenConn->local = s->s;
4090 s->listenConn->flags = COMM_NONBLOCKING | (s->spoof_client_ip ? COMM_TRANSPARENT : 0) |
4091 (s->intercepted ? COMM_INTERCEPTION : 0);
62e76326 4092
cbff89ba
AJ
4093 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTPS
4094 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4095 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpsAccept", CommAcceptCbPtrFun(httpsAccept, s));
4096 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
62e76326 4097
cbff89ba 4098 AsyncCall::Pointer listenCall = asyncCall(33, 2, "clientListenerConnectionOpened",
8bbb16e3 4099 ListeningStartedDialer(&clientListenerConnectionOpened,
859741ed 4100 s, Ipc::fdnHttpsSocket, sub));
8bbb16e3 4101 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpsSocket, listenCall);
a38ec4b1
FC
4102 HttpSockets[NHttpSockets] = -1;
4103 ++NHttpSockets;
cbff89ba 4104 }
d193a436 4105}
d193a436 4106#endif
4107
e0d28505 4108/// process clientHttpConnectionsOpen result
00516be1 4109static void
65d448bc 4110clientListenerConnectionOpened(AnyP::PortCfg *s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub)
00516be1 4111{
8bbb16e3 4112 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
00516be1 4113 return;
62e76326 4114
00516be1 4115 Must(s);
e0d28505 4116 Must(Comm::IsConnOpen(s->listenConn));
62e76326 4117
8bbb16e3
AJ
4118 // TCP: setup a job to handle accept() with subscribed handler
4119 AsyncJob::Start(new Comm::TcpAcceptor(s->listenConn, FdNote(portTypeNote), sub));
4120
e0236918 4121 debugs(1, DBG_IMPORTANT, "Accepting " <<
1b76e6c1
AJ
4122 (s->intercepted ? "NAT intercepted " : "") <<
4123 (s->spoof_client_ip ? "TPROXY spoofing " : "") <<
4124 (s->sslBump ? "SSL bumped " : "") <<
4125 (s->accel ? "reverse-proxy " : "")
8bbb16e3
AJ
4126 << FdNote(portTypeNote) << " connections at "
4127 << s->listenConn);
62e76326 4128
e0d28505 4129 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
d193a436 4130}
4131
d193a436 4132void
4133clientOpenListenSockets(void)
4134{
4135 clientHttpConnectionsOpen();
4136#if USE_SSL
4137 clientHttpsConnectionsOpen();
1f7c9178 4138#endif
62e76326 4139
15df8349 4140 if (NHttpSockets < 1)
598c41da 4141 fatal("No HTTP or HTTPS ports configured");
15df8349 4142}
edce4d98 4143
c0fbae16 4144void
4145clientHttpConnectionsClose(void)
4146{
65d448bc 4147 for (AnyP::PortCfg *s = Config.Sockaddr.http; s; s = s->next) {
00406b24 4148 if (s->listenConn != NULL) {
e0236918 4149 debugs(1, DBG_IMPORTANT, "Closing HTTP port " << s->listenConn->local);
00406b24
AJ
4150 s->listenConn->close();
4151 s->listenConn = NULL;
04f55905
AJ
4152 }
4153 }
62e76326 4154
04f55905 4155#if USE_SSL
65d448bc 4156 for (AnyP::PortCfg *s = Config.Sockaddr.https; s; s = s->next) {
00406b24 4157 if (s->listenConn != NULL) {
e0236918 4158 debugs(1, DBG_IMPORTANT, "Closing HTTPS port " << s->listenConn->local);
00406b24
AJ
4159 s->listenConn->close();
4160 s->listenConn = NULL;
62e76326 4161 }
c0fbae16 4162 }
04f55905
AJ
4163#endif
4164
4165 // TODO see if we can drop HttpSockets array entirely */
95dc7ff4 4166 for (int i = 0; i < NHttpSockets; ++i) {
04f55905
AJ
4167 HttpSockets[i] = -1;
4168 }
62e76326 4169
c0fbae16 4170 NHttpSockets = 0;
4171}
f66a9ef4 4172
4173int
190154cf 4174varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
f66a9ef4 4175{
4176 const char *vary = request->vary_headers;
a9925b40 4177 int has_vary = entry->getReply()->header.has(HDR_VARY);
f66a9ef4 4178#if X_ACCELERATOR_VARY
62e76326 4179
edce4d98 4180 has_vary |=
a9925b40 4181 entry->getReply()->header.has(HDR_X_ACCELERATOR_VARY);
f66a9ef4 4182#endif
62e76326 4183
f66a9ef4 4184 if (!has_vary || !entry->mem_obj->vary_headers) {
62e76326 4185 if (vary) {
4186 /* Oops... something odd is going on here.. */
e0236918 4187 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
9e008dda 4188 entry->mem_obj->url << "' '" << vary << "'");
62e76326 4189 safe_free(request->vary_headers);
4190 return VARY_CANCEL;
4191 }
4192
4193 if (!has_vary) {
4194 /* This is not a varying object */
4195 return VARY_NONE;
4196 }
4197
4198 /* virtual "vary" object found. Calculate the vary key and
4199 * continue the search
4200 */
4201 vary = httpMakeVaryMark(request, entry->getReply());
4202
4203 if (vary) {
4204 request->vary_headers = xstrdup(vary);
4205 return VARY_OTHER;
4206 } else {
4207 /* Ouch.. we cannot handle this kind of variance */
4208 /* XXX This cannot really happen, but just to be complete */
4209 return VARY_CANCEL;
4210 }
f66a9ef4 4211 } else {
62e76326 4212 if (!vary) {
4213 vary = httpMakeVaryMark(request, entry->getReply());
4214
4215 if (vary)
4216 request->vary_headers = xstrdup(vary);
4217 }
4218
4219 if (!vary) {
4220 /* Ouch.. we cannot handle this kind of variance */
4221 /* XXX This cannot really happen, but just to be complete */
4222 return VARY_CANCEL;
4223 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
4224 return VARY_MATCH;
4225 } else {
4226 /* Oops.. we have already been here and still haven't
4227 * found the requested variant. Bail out
4228 */
e0236918 4229 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
9e008dda 4230 entry->mem_obj->url << "' '" << vary << "'");
62e76326 4231 return VARY_CANCEL;
4232 }
f66a9ef4 4233 }
4234}
28d4805a 4235
c0941a6a 4236ACLFilledChecklist *
59a1efb2 4237clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
28d4805a 4238{
1cf238db 4239 ConnStateData * conn = http->getConn();
c0941a6a 4240 ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request,
73c36fd9 4241 cbdataReferenceValid(conn) && conn != NULL && conn->clientConnection != NULL ? conn->clientConnection->rfc931 : dash_str);
28d4805a 4242
4243 /*
4244 * hack for ident ACL. It needs to get full addresses, and a place to store
4245 * the ident result on persistent connections...
4246 */
4247 /* connection oriented auth also needs these two lines for it's operation. */
4248 /*
4249 * Internal requests do not have a connection reference, because: A) their
4250 * byte count may be transformed before being applied to an outbound
4251 * connection B) they are internal - any limiting on them should be done on
4252 * the server end.
4253 */
62e76326 4254
1cf238db 4255 if (conn != NULL)
c0941a6a 4256 ch->conn(conn); /* unreferenced in FilledCheckList.cc */
28d4805a 4257
4258 return ch;
4259}
a46d2c0e 4260
4261CBDATA_CLASS_INIT(ConnStateData);
4262
5c336a3b
AJ
4263ConnStateData::ConnStateData() :
4264 AsyncJob("ConnStateData"),
061bbdec 4265#if USE_SSL
08097970 4266 sslBumpMode(Ssl::bumpEnd),
cf6eb29e 4267 switchedToHttps_(false),
fd4624d7 4268 sslServerBump(NULL),
061bbdec 4269#endif
cf6eb29e
CT
4270 stoppedSending_(NULL),
4271 stoppedReceiving_(NULL)
b65351fa 4272{
d67acb4e
AJ
4273 pinning.pinned = false;
4274 pinning.auth = false;
b65351fa 4275}
a46d2c0e 4276
4277bool
4278ConnStateData::transparent() const
4279{
40d34a62 4280 return clientConnection != NULL && (clientConnection->flags & (COMM_TRANSPARENT|COMM_INTERCEPTION));
a46d2c0e 4281}
4282
4283bool
4284ConnStateData::reading() const
4285{
f84dd7eb 4286 return reader != NULL;
a46d2c0e 4287}
4288
4289void
f84dd7eb 4290ConnStateData::stopReading()
a46d2c0e 4291{
f84dd7eb 4292 if (reading()) {
73c36fd9 4293 comm_read_cancel(clientConnection->fd, reader);
f84dd7eb
AR
4294 reader = NULL;
4295 }
a46d2c0e 4296}
4297
5f8252d2 4298
4299BodyPipe::Pointer
3e62bd58 4300ConnStateData::expectRequestBody(int64_t size)
5f8252d2 4301{
4302 bodyPipe = new BodyPipe(this);
39cb8c41
AR
4303 if (size >= 0)
4304 bodyPipe->setBodySize(size);
4305 else
4306 startDechunkingRequest();
5f8252d2 4307 return bodyPipe;
4308}
4309
39cb8c41
AR
4310int64_t
4311ConnStateData::mayNeedToReadMoreBody() const
4312{
4313 if (!bodyPipe)
4314 return 0; // request without a body or read/produced all body bytes
4315
4316 if (!bodyPipe->bodySizeKnown())
4317 return -1; // probably need to read more, but we cannot be sure
4318
4319 const int64_t needToProduce = bodyPipe->unproducedSize();
4320 const int64_t haveAvailable = static_cast<int64_t>(in.notYetUsed);
4321
4322 if (needToProduce <= haveAvailable)
4323 return 0; // we have read what we need (but are waiting for pipe space)
4324
4325 return needToProduce - haveAvailable;
4326}
4327
55e44db9 4328void
cf6eb29e 4329ConnStateData::stopReceiving(const char *error)
55e44db9 4330{
cf6eb29e
CT
4331 debugs(33, 4, HERE << "receiving error (" << clientConnection << "): " << error <<
4332 "; old sending error: " <<
4333 (stoppedSending() ? stoppedSending_ : "none"));
5f8252d2 4334
cf6eb29e
CT
4335 if (const char *oldError = stoppedReceiving()) {
4336 debugs(33, 3, HERE << "already stopped receiving: " << oldError);
4337 return; // nothing has changed as far as this connection is concerned
4338 }
5f8252d2 4339
cf6eb29e 4340 stoppedReceiving_ = error;
5f8252d2 4341
cf6eb29e
CT
4342 if (const char *sendError = stoppedSending()) {
4343 debugs(33, 3, HERE << "closing because also stopped sending: " << sendError);
4344 clientConnection->close();
4345 }
55e44db9 4346}
4347
eb44b2d7 4348void
e29ccb57
A
4349ConnStateData::expectNoForwarding()
4350{
eb44b2d7
CT
4351 if (bodyPipe != NULL) {
4352 debugs(33, 4, HERE << "no consumer for virgin body " << bodyPipe->status());
4353 bodyPipe->expectNoConsumption();
4354 }
4355}
4356
39cb8c41 4357/// initialize dechunking state
3ff65596 4358void
39cb8c41 4359ConnStateData::startDechunkingRequest()
3ff65596 4360{
39cb8c41
AR
4361 Must(bodyPipe != NULL);
4362 debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
3ff65596
AR
4363 assert(!in.bodyParser);
4364 in.bodyParser = new ChunkedCodingParser;
3ff65596
AR
4365}
4366
39cb8c41 4367/// put parsed content into input buffer and clean up
3ff65596 4368void
39cb8c41 4369ConnStateData::finishDechunkingRequest(bool withSuccess)
3ff65596 4370{
39cb8c41 4371 debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
3ff65596 4372
39cb8c41
AR
4373 if (bodyPipe != NULL) {
4374 debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
4375 BodyPipe::Pointer myPipe = bodyPipe;
4376 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
4377 Must(!bodyPipe); // we rely on it being nil after we are done with body
4378 if (withSuccess) {
4379 Must(myPipe->bodySizeKnown());
4380 ClientSocketContext::Pointer context = getCurrentContext();
4381 if (context != NULL && context->http && context->http->request)
4382 context->http->request->setContentLength(myPipe->bodySize());
4383 }
3ff65596 4384 }
3ff65596 4385
39cb8c41
AR
4386 delete in.bodyParser;
4387 in.bodyParser = NULL;
3ff65596
AR
4388}
4389
a46d2c0e 4390char *
4391ConnStateData::In::addressToReadInto() const
4392{
4393 return buf + notYetUsed;
4394}
4395
3ff65596 4396ConnStateData::In::In() : bodyParser(NULL),
39cb8c41 4397 buf (NULL), notYetUsed (0), allocatedSize (0)
a46d2c0e 4398{}
4399
4400ConnStateData::In::~In()
4401{
4402 if (allocatedSize)
4403 memFreeBuf(allocatedSize, buf);
39cb8c41 4404 delete bodyParser; // TODO: pool
a46d2c0e 4405}
d67acb4e 4406
655daa06
AR
4407void
4408ConnStateData::sendControlMsg(HttpControlMsg msg)
4409{
eedd4182
AR
4410 if (!isOpen()) {
4411 debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
655daa06
AR
4412 return;
4413 }
4414
eedd4182
AR
4415 ClientSocketContext::Pointer context = getCurrentContext();
4416 if (context != NULL) {
4417 context->writeControlMsg(msg); // will call msg.cbSuccess
655daa06
AR
4418 return;
4419 }
4420
4421 debugs(33, 3, HERE << " closing due to missing context for 1xx");
73c36fd9 4422 clientConnection->close();
655daa06
AR
4423}
4424
d7ce0bcd 4425/// Our close handler called by Comm when the pinned connection is closed
d67acb4e
AJ
4426void
4427ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
4428{
7a957a93
AR
4429 // FwdState might repin a failed connection sooner than this close
4430 // callback is called for the failed connection.
85563fd9
AR
4431 if (pinning.serverConnection == io.conn) {
4432 pinning.closeHandler = NULL; // Comm unregisters handlers before calling
4433 unpinConnection();
4434 }
d67acb4e
AJ
4435}
4436
b1cf2350 4437void
73c36fd9 4438ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, struct peer *aPeer, bool auth)
9e008dda 4439{
d67acb4e
AJ
4440 char desc[FD_DESC_SZ];
4441
73c36fd9
AJ
4442 if (Comm::IsConnOpen(pinning.serverConnection)) {
4443 if (pinning.serverConnection->fd == pinServer->fd)
e3a4aecc 4444 return;
d7ce0bcd 4445 }
9e008dda 4446
7a957a93 4447 unpinConnection(); // closes pinned connection, if any, and resets fields
9e008dda 4448
73c36fd9 4449 pinning.serverConnection = pinServer;
d7ce0bcd 4450
85563fd9
AR
4451 debugs(33, 3, HERE << pinning.serverConnection);
4452
d7ce0bcd
AR
4453 // when pinning an SSL bumped connection, the request may be NULL
4454 const char *pinnedHost = "[unknown]";
4455 if (request) {
4456 pinning.host = xstrdup(request->GetHost());
4457 pinning.port = request->port;
4458 pinnedHost = pinning.host;
4459 } else {
4460 pinning.port = pinServer->remote.GetPort();
4461 }
d67acb4e 4462 pinning.pinned = true;
8bcf08e0
FC
4463 if (aPeer)
4464 pinning.peer = cbdataReference(aPeer);
d67acb4e 4465 pinning.auth = auth;
e3a4aecc
AJ
4466 char stmp[MAX_IPSTRLEN];
4467 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
d7ce0bcd
AR
4468 (auth || !aPeer) ? pinnedHost : aPeer->name,
4469 clientConnection->remote.ToURL(stmp,MAX_IPSTRLEN),
4470 clientConnection->fd);
73c36fd9 4471 fd_note(pinning.serverConnection->fd, desc);
9e008dda 4472
d67acb4e 4473 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4299f876 4474 pinning.closeHandler = JobCallback(33, 5,
4cb2536f 4475 Dialer, this, ConnStateData::clientPinnedConnectionClosed);
85563fd9
AR
4476 // remember the pinned connection so that cb does not unpin a fresher one
4477 typedef CommCloseCbParams Params;
4478 Params &params = GetCommParams<Params>(pinning.closeHandler);
4479 params.conn = pinning.serverConnection;
73c36fd9 4480 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
d67acb4e
AJ
4481}
4482
e3a4aecc 4483const Comm::ConnectionPointer
b1cf2350 4484ConnStateData::validatePinnedConnection(HttpRequest *request, const struct peer *aPeer)
d67acb4e 4485{
85563fd9
AR
4486 debugs(33, 7, HERE << pinning.serverConnection);
4487
d67acb4e 4488 bool valid = true;
73c36fd9 4489 if (!Comm::IsConnOpen(pinning.serverConnection))
e3a4aecc 4490 valid = false;
d67acb4e 4491 if (pinning.auth && request && strcasecmp(pinning.host, request->GetHost()) != 0) {
9e008dda 4492 valid = false;
d67acb4e 4493 }
9e008dda
AJ
4494 if (request && pinning.port != request->port) {
4495 valid = false;
d67acb4e 4496 }
9e008dda
AJ
4497 if (pinning.peer && !cbdataReferenceValid(pinning.peer)) {
4498 valid = false;
d67acb4e 4499 }
8bcf08e0 4500 if (aPeer != pinning.peer) {
9e008dda 4501 valid = false;
d67acb4e
AJ
4502 }
4503
9e008dda 4504 if (!valid) {
b1cf2350 4505 /* The pinning info is not safe, remove any pinning info */
9e008dda 4506 unpinConnection();
d67acb4e
AJ
4507 }
4508
73c36fd9 4509 return pinning.serverConnection;
d67acb4e
AJ
4510}
4511
b1cf2350
AJ
4512void
4513ConnStateData::unpinConnection()
d67acb4e 4514{
85563fd9
AR
4515 debugs(33, 3, HERE << pinning.serverConnection);
4516
9e008dda
AJ
4517 if (pinning.peer)
4518 cbdataReferenceDone(pinning.peer);
d67acb4e 4519
d7ce0bcd 4520 if (Comm::IsConnOpen(pinning.serverConnection)) {
87f237a9
A
4521 if (pinning.closeHandler != NULL) {
4522 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4523 pinning.closeHandler = NULL;
4524 }
4525 /// also close the server side socket, we should not use it for any future requests...
4526 // TODO: do not close if called from our close handler?
4527 pinning.serverConnection->close();
d67acb4e 4528 }
d7ce0bcd 4529
d67acb4e 4530 safe_free(pinning.host);
e3a4aecc
AJ
4531
4532 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
4533 * connection has gone away */
d67acb4e 4534}