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