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