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