]> git.ipfire.org Git - thirdparty/squid.git/blame - src/FwdState.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / FwdState.cc
CommitLineData
41462d93 1/*
bde978a6 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
41462d93 7 */
8
bbc27441
AJ
9/* DEBUG: section 17 Request Forwarding */
10
582c2af2 11#include "squid.h"
4bf68cfa 12#include "AccessLogEntry.h"
289848ca 13#include "acl/Address.h"
c0941a6a
AR
14#include "acl/FilledChecklist.h"
15#include "acl/Gadgets.h"
5ae65581 16#include "anyp/PortCfg.h"
25b481e6 17#include "CacheManager.h"
602d9612 18#include "CachePeer.h"
582c2af2 19#include "client_side.h"
92ae4c86 20#include "clients/forward.h"
f9b72e0c 21#include "comm/Connection.h"
aed188fd 22#include "comm/ConnOpener.h"
d841c88d 23#include "comm/Loops.h"
582c2af2 24#include "CommCalls.h"
aa839030 25#include "errorpage.h"
582c2af2 26#include "event.h"
fc54b8d2 27#include "fd.h"
aa839030 28#include "fde.h"
eb13c21e 29#include "FwdState.h"
582c2af2 30#include "globals.h"
fc54b8d2 31#include "gopher.h"
bbaf2685 32#include "hier_code.h"
fc54b8d2 33#include "http.h"
924f73bc 34#include "HttpReply.h"
aa839030 35#include "HttpRequest.h"
582c2af2 36#include "icmp/net_db.h"
fc54b8d2 37#include "internal.h"
582c2af2 38#include "ip/Intercept.h"
425de4c8 39#include "ip/QosConfig.h"
582c2af2 40#include "ip/tools.h"
aa839030 41#include "MemObject.h"
582c2af2 42#include "mgr/Registration.h"
fc54b8d2 43#include "neighbors.h"
781ce8ff 44#include "pconn.h"
e8dca475 45#include "PeerPoolMgr.h"
cfd66529 46#include "PeerSelectState.h"
4d5904f7 47#include "SquidConfig.h"
aa839030 48#include "SquidTime.h"
49#include "Store.h"
e87137f1 50#include "StoreClient.h"
5eb529cb 51#include "urn.h"
fc54b8d2 52#include "whois.h"
cb4f4424 53#if USE_OPENSSL
2cef0ca6
AR
54#include "ssl/cert_validate_message.h"
55#include "ssl/Config.h"
4d16918e 56#include "ssl/ErrorDetail.h"
602d9612 57#include "ssl/helper.h"
a23223bf 58#include "ssl/PeerConnector.h"
fd4624d7 59#include "ssl/ServerBump.h"
602d9612 60#include "ssl/support.h"
4db984be 61#endif
1a30fdf5
AJ
62
63#include <cerrno>
cfd66529 64
6b679a01 65static PSC fwdPeerSelectionCompleteWrapper;
575d05c4 66static CLCB fwdServerClosedWrapper;
b6b6f466 67static CNCB fwdConnectDoneWrapper;
68
8ddcc35d 69static OBJH fwdStats;
70
71#define MAX_FWD_STATS_IDX 9
955394ce 72static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][Http::scInvalidHeader + 1];
db1cd23c 73
d5430dc8 74static PconnPool *fwdPconnPool = new PconnPool("server-peers", NULL);
b6b6f466 75CBDATA_CLASS_INIT(FwdState);
781ce8ff 76
a23223bf
CT
77#if USE_OPENSSL
78class FwdStatePeerAnswerDialer: public CallDialer, public Ssl::PeerConnector::CbDialer
79{
80public:
81 typedef void (FwdState::*Method)(Ssl::PeerConnectorAnswer &);
82
83 FwdStatePeerAnswerDialer(Method method, FwdState *fwd):
f53969cc 84 method_(method), fwd_(fwd), answer_() {}
a23223bf
CT
85
86 /* CallDialer API */
87 virtual bool canDial(AsyncCall &call) { return fwd_.valid(); }
88 void dial(AsyncCall &call) { ((&(*fwd_))->*method_)(answer_); }
89 virtual void print(std::ostream &os) const {
e2849af8
A
90 os << '(' << fwd_.get() << ", " << answer_ << ')';
91 }
a23223bf
CT
92
93 /* Ssl::PeerConnector::CbDialer API */
94 virtual Ssl::PeerConnectorAnswer &answer() { return answer_; }
95
96private:
97 Method method_;
98 CbcPointer<FwdState> fwd_;
99 Ssl::PeerConnectorAnswer answer_;
100};
101#endif
102
429871db 103void
104FwdState::abort(void* d)
105{
106 FwdState* fwd = (FwdState*)d;
6ecaf21a 107 Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
429871db 108
6b679a01 109 if (Comm::IsConnOpen(fwd->serverConnection())) {
e8dca475 110 fwd->closeServerConnection("store entry aborted");
6dd9a2e4
AJ
111 } else {
112 debugs(17, 7, HERE << "store entry aborted; no connection to close");
cff02fa6 113 }
c33a88ca 114 fwd->serverDestinations.clear();
429871db 115 fwd->self = NULL;
116}
117
e8dca475
CT
118void
119FwdState::closeServerConnection(const char *reason)
120{
121 debugs(17, 3, "because " << reason << "; " << serverConn);
122 comm_remove_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
123 fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
124 serverConn->close();
125}
126
b6b6f466 127/**** PUBLIC INTERFACE ********************************************************/
c7f9eb6d 128
4bf68cfa 129FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r, const AccessLogEntryPointer &alp):
f53969cc 130 al(alp)
db1cd23c 131{
3157a816 132 debugs(17, 2, HERE << "Forwarding client request " << client << ", url=" << e->url() );
b6b6f466 133 entry = e;
5c336a3b 134 clientConn = client;
b248c2a3
AJ
135 request = r;
136 HTTPMSGLOCK(request);
bc81ee68 137 pconnRace = raceImpossible;
b6b6f466 138 start_t = squid_curtime;
00ae51e4 139 serverDestinations.reserve(Config.forward_max_tries);
1bfe9ade 140 e->lock("FwdState");
b6b6f466 141 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
7a0fb323 142}
143
144// Called once, right after object creation, when it is safe to set self
fc68f6b1 145void FwdState::start(Pointer aSelf)
146{
7a0fb323 147 // Protect ourselves from being destroyed when the only Server pointing
148 // to us is gone (while we expect to talk to more Servers later).
149 // Once we set self, we are responsible for clearing it when we do not
150 // expect to talk to any servers.
151 self = aSelf; // refcounted
152
153 // We hope that either the store entry aborts or peer is selected.
154 // Otherwise we are going to leak our object.
34266cde 155
3900307b 156 entry->registerAbort(FwdState::abort, this);
bfe4e2fe 157
32c32865 158#if STRICT_ORIGINAL_DST
bfe4e2fe
AJ
159 // Bug 3243: CVE 2009-0801
160 // Bypass of browser same-origin access control in intercepted communication
161 // To resolve this we must force DIRECT and only to the original client destination.
0d901ef4 162 const bool isIntercepted = request && !request->flags.redirected && (request->flags.intercepted || request->flags.interceptTproxy);
2962f8b8
AJ
163 const bool useOriginalDst = Config.onoff.client_dst_passthru || (request && !request->flags.hostVerified);
164 if (isIntercepted && useOriginalDst) {
d7ce0bcd 165 selectPeerForIntercepted();
7177edfb
AJ
166 // 3.2 does not suppro re-wrapping inside CONNECT.
167 // our only alternative is to fake destination "found" and continue with the forwarding.
bfe4e2fe 168 startConnectionOrFail();
7177edfb 169 return;
bfe4e2fe 170 }
32c32865
AJ
171#endif
172
7177edfb 173 // do full route options selection
d4806c91 174 peerSelect(&serverDestinations, request, al, entry, fwdPeerSelectionCompleteWrapper, this);
db1cd23c 175}
176
32c32865 177#if STRICT_ORIGINAL_DST
d7ce0bcd
AR
178/// bypasses peerSelect() when dealing with intercepted requests
179void
180FwdState::selectPeerForIntercepted()
181{
182 // use pinned connection if available
183 Comm::ConnectionPointer p;
693cb033 184 if (ConnStateData *client = request->pinnedConnection()) {
d7ce0bcd 185 p = client->validatePinnedConnection(request, NULL);
693cb033
CT
186 if (Comm::IsConnOpen(p)) {
187 /* duplicate peerSelectPinned() effects */
188 p->peerType = PINNED;
189 entry->ping_status = PING_DONE; /* Skip ICP */
d7ce0bcd 190
693cb033
CT
191 debugs(17, 3, "reusing a pinned conn: " << *p);
192 serverDestinations.push_back(p);
193 } else {
194 debugs(17,2, "Pinned connection is not valid: " << p);
955394ce 195 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
693cb033
CT
196 fail(anErr);
197 }
198 // Either use the valid pinned connection or fail if it is invalid.
199 return;
d7ce0bcd
AR
200 }
201
7177edfb
AJ
202 // use client original destination as second preferred choice
203 p = new Comm::Connection();
204 p->peerType = ORIGINAL_DST;
205 p->remote = clientConn->local;
206 getOutgoingAddress(request, p);
207
208 debugs(17, 3, HERE << "using client original destination: " << *p);
d7ce0bcd
AR
209 serverDestinations.push_back(p);
210}
32c32865 211#endif
d7ce0bcd 212
802a8c1d 213void
214FwdState::completed()
41462d93 215{
e857372a 216 if (flags.forward_completed) {
e0236918 217 debugs(17, DBG_IMPORTANT, HERE << "FwdState::completed called on a completed request! Bad!");
fc68f6b1 218 return;
219 }
220
e857372a 221 flags.forward_completed = true;
802a8c1d 222
16b70e2a
CT
223 request->hier.stopPeerClock(false);
224
c4a88a3e
CT
225 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
226 debugs(17, 3, HERE << "entry aborted");
227 return ;
228 }
229
bc87dc25 230#if URL_CHECKSUM_DEBUG
62e76326 231
b6b6f466 232 entry->mem_obj->checkUrlChecksum();
225644d7 233#endif
62e76326 234
b6b6f466 235 if (entry->store_status == STORE_PENDING) {
236 if (entry->isEmpty()) {
a04b7e6e 237 if (!err) // we quit (e.g., fd closed) before an error or content
955394ce 238 fail(new ErrorState(ERR_READ_ERROR, Http::scBadGateway, request));
b6b6f466 239 assert(err);
240 errorAppendEntry(entry, err);
241 err = NULL;
cb4f4424 242#if USE_OPENSSL
2cef0ca6
AR
243 if (request->flags.sslPeek && request->clientConnectionManager.valid()) {
244 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
245 ConnStateData::httpsPeeked, Comm::ConnectionPointer(NULL));
246 }
247#endif
62e76326 248 } else {
b6b6f466 249 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
250 entry->complete();
d88e3c49 251 entry->releaseRequest();
62e76326 252 }
f563eea9 253 }
62e76326 254
b6b6f466 255 if (storePendingNClients(entry) > 0)
256 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
62e76326 257
802a8c1d 258}
259
260FwdState::~FwdState()
261{
262 debugs(17, 3, HERE << "FwdState destructor starting");
fc68f6b1 263
802a8c1d 264 if (! flags.forward_completed)
fc68f6b1 265 completed();
802a8c1d 266
9d2760b6
AR
267 doneWithRetries();
268
6dd9f4bd 269 HTTPMSGUNLOCK(request);
62e76326 270
913524f0 271 delete err;
62e76326 272
3900307b 273 entry->unregisterAbort();
429871db 274
1bfe9ade 275 entry->unlock("FwdState");
62e76326 276
b6b6f466 277 entry = NULL;
62e76326 278
e3a4aecc
AJ
279 if (calls.connector != NULL) {
280 calls.connector->cancel("FwdState destructed");
281 calls.connector = NULL;
282 }
283
e8dca475
CT
284 if (Comm::IsConnOpen(serverConn))
285 closeServerConnection("~FwdState");
fc68f6b1 286
c33a88ca 287 serverDestinations.clear();
cfd66529 288
b6b6f466 289 debugs(17, 3, HERE << "FwdState destructor done");
290}
62e76326 291
38413773 292/**
b6b6f466 293 * This is the entry point for client-side to start forwarding
294 * a transaction. It is a static method that may or may not
295 * allocate a FwdState.
296 */
be0c6690 297void
4bf68cfa 298FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request, const AccessLogEntryPointer &al)
b6b6f466 299{
b50e327b 300 /** \note
b6b6f466 301 * client_addr == no_addr indicates this is an "internal" request
302 * from peer_digest.c, asn.c, netdb.c, etc and should always
303 * be allowed. yuck, I know.
304 */
62e76326 305
4dd643d5 306 if ( Config.accessList.miss && !request->client_addr.isNoAddr() &&
4e3f4dc7 307 !request->flags.internal && request->url.getScheme() != AnyP::PROTO_CACHE_OBJECT) {
b50e327b 308 /**
fbd3636b
NR
309 * Check if this host is allowed to fetch MISSES from us (miss_access).
310 * Intentionally replace the src_addr automatically selected by the checklist code
311 * we do NOT want the indirect client address to be tested here.
b6b6f466 312 */
c0941a6a 313 ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
b6b6f466 314 ch.src_addr = request->client_addr;
2efeb0b7 315 if (ch.fastCheck() == ACCESS_DENIED) {
b6b6f466 316 err_type page_id;
9ce7856a 317 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
b6b6f466 318
319 if (page_id == ERR_NONE)
320 page_id = ERR_FORWARDING_DENIED;
321
955394ce 322 ErrorState *anErr = new ErrorState(page_id, Http::scForbidden, request);
f53969cc 323 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 324 return;
b6b6f466 325 }
326 }
327
cfd66529 328 debugs(17, 3, HERE << "'" << entry->url() << "'");
f4ef658f 329 /*
330 * This seems like an odd place to bind mem_obj and request.
331 * Might want to assert that request is NULL at this point
332 */
b248c2a3
AJ
333 entry->mem_obj->request = request;
334 HTTPMSGLOCK(entry->mem_obj->request);
b6b6f466 335#if URL_CHECKSUM_DEBUG
336
337 entry->mem_obj->checkUrlChecksum();
338#endif
339
340 if (shutting_down) {
341 /* more yuck */
955394ce 342 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
f53969cc 343 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 344 return;
6801f8a8 345 }
62e76326 346
4e3f4dc7
AJ
347 if (request->flags.internal) {
348 debugs(17, 2, "calling internalStart() due to request flag");
e37bd29b 349 internalStart(clientConn, request, entry);
be0c6690 350 return;
4e3f4dc7
AJ
351 }
352
353 switch (request->url.getScheme()) {
b6b6f466 354
39a19cb7 355 case AnyP::PROTO_CACHE_OBJECT:
4e3f4dc7 356 debugs(17, 2, "calling CacheManager due to request scheme " << request->url.getScheme());
5c336a3b 357 CacheManager::GetInstance()->Start(clientConn, request, entry);
be0c6690 358 return;
b6b6f466 359
0c3d3f65 360 case AnyP::PROTO_URN:
b6b6f466 361 urnStart(request, entry);
be0c6690 362 return;
b6b6f466 363
364 default:
4bf68cfa 365 FwdState::Pointer fwd = new FwdState(clientConn, entry, request, al);
7a0fb323 366 fwd->start(fwd);
be0c6690 367 return;
b6b6f466 368 }
369
370 /* NOTREACHED */
41462d93 371}
372
4bf68cfa
AR
373void
374FwdState::fwdStart(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request)
375{
376 // Hides AccessLogEntry.h from code that does not supply ALE anyway.
377 Start(clientConn, entry, request, NULL);
378}
379
cfd66529 380void
6b679a01 381FwdState::startConnectionOrFail()
cfd66529 382{
8652f8e7 383 debugs(17, 3, HERE << entry->url());
cfd66529 384
00ae51e4 385 if (serverDestinations.size() > 0) {
8652f8e7
AJ
386 // Ditch error page if it was created before.
387 // A new one will be created if there's another problem
913524f0
AJ
388 delete err;
389 err = NULL;
8652f8e7
AJ
390
391 // Update the logging information about this new server connection.
392 // Done here before anything else so the errors get logged for
393 // this server link regardless of what happens when connecting to it.
394 // IF sucessfuly connected this top destination will become the serverConnection().
395 request->hier.note(serverDestinations[0], request->GetHost());
129fe2a1 396 request->clearError();
8652f8e7 397
cfd66529
AJ
398 connectStart();
399 } else {
95b83010
CT
400 debugs(17, 3, HERE << "Connection failed: " << entry->url());
401 if (!err) {
955394ce 402 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request);
95b83010 403 fail(anErr);
8652f8e7 404 } // else use actual error from last connection attempt
cfd66529
AJ
405 self = NULL; // refcounted
406 }
407}
408
b6b6f466 409void
410FwdState::fail(ErrorState * errorState)
411{
9b769c67 412 debugs(17, 3, err_type_str[errorState->type] << " \"" << Http::StatusCodeString(errorState->httpStatus) << "\"\n\t" << entry->url());
b6b6f466 413
913524f0 414 delete err;
b6b6f466 415 err = errorState;
416
b248c2a3
AJ
417 if (!errorState->request) {
418 errorState->request = request;
419 HTTPMSGLOCK(errorState->request);
420 }
64b66b76 421
693cb033
CT
422 if (err->type != ERR_ZERO_SIZE_OBJECT)
423 return;
424
425 if (pconnRace == racePossible) {
bc81ee68
AR
426 debugs(17, 5, HERE << "pconn race happened");
427 pconnRace = raceHappened;
428 }
693cb033
CT
429
430 if (ConnStateData *pinned_connection = request->pinnedConnection()) {
431 pinned_connection->pinning.zeroReply = true;
432 flags.dont_retry = true; // we want to propagate failure to the client
433 debugs(17, 4, "zero reply on pinned connection");
434 }
b6b6f466 435}
436
38413773 437/**
b6b6f466 438 * Frees fwdState without closing FD or generating an abort
439 */
440void
00ae51e4 441FwdState::unregister(Comm::ConnectionPointer &conn)
5229395c
AJ
442{
443 debugs(17, 3, HERE << entry->url() );
444 assert(serverConnection() == conn);
6b679a01 445 assert(Comm::IsConnOpen(conn));
5229395c 446 comm_remove_close_handler(conn->fd, fwdServerClosedWrapper, this);
00ae51e4 447 serverConn = NULL;
5229395c
AJ
448}
449
d5430dc8 450// \deprecated use unregister(Comm::ConnectionPointer &conn) instead
5229395c 451void
b6b6f466 452FwdState::unregister(int fd)
453{
5229395c
AJ
454 debugs(17, 3, HERE << entry->url() );
455 assert(fd == serverConnection()->fd);
00ae51e4 456 unregister(serverConn);
b6b6f466 457}
458
38413773 459/**
d5430dc8 460 * FooClient modules call fwdComplete() when they are done
b6b6f466 461 * downloading an object. Then, we either 1) re-forward the
462 * request somewhere else if needed, or 2) call storeComplete()
463 * to finish it off
464 */
465void
466FwdState::complete()
467{
9b769c67 468 debugs(17, 3, HERE << entry->url() << "\n\tstatus " << entry->getReply()->sline.status());
b6b6f466 469#if URL_CHECKSUM_DEBUG
470
471 entry->mem_obj->checkUrlChecksum();
472#endif
473
9b769c67 474 logReplyStatus(n_tries, entry->getReply()->sline.status());
b6b6f466 475
476 if (reforward()) {
9b769c67 477 debugs(17, 3, HERE << "re-forwarding " << entry->getReply()->sline.status() << " " << entry->url());
b6b6f466 478
6b679a01 479 if (Comm::IsConnOpen(serverConn))
00ae51e4 480 unregister(serverConn);
b6b6f466 481
cfd66529
AJ
482 entry->reset();
483
8652f8e7 484 // drop the last path off the selection list. try the next one.
523c3de3 485 serverDestinations.erase(serverDestinations.begin());
8652f8e7
AJ
486 startConnectionOrFail();
487
b6b6f466 488 } else {
6b679a01 489 if (Comm::IsConnOpen(serverConn))
9b769c67 490 debugs(17, 3, HERE << "server FD " << serverConnection()->fd << " not re-forwarding status " << entry->getReply()->sline.status());
6b679a01 491 else
9b769c67 492 debugs(17, 3, HERE << "server (FD closed) not re-forwarding status " << entry->getReply()->sline.status());
9e5c22cf
AJ
493 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
494 entry->complete();
fc68f6b1 495
6b679a01 496 if (!Comm::IsConnOpen(serverConn))
fc68f6b1 497 completed();
498
7a0fb323 499 self = NULL; // refcounted
b6b6f466 500 }
501}
502
b6b6f466 503/**** CALLBACK WRAPPERS ************************************************************/
504
505static void
ced8def3 506fwdPeerSelectionCompleteWrapper(Comm::ConnectionList *, ErrorState *err, void *data)
b6b6f466 507{
508 FwdState *fwd = (FwdState *) data;
a37fdd8a
AJ
509 if (err)
510 fwd->fail(err);
6b679a01 511 fwd->startConnectionOrFail();
b6b6f466 512}
513
514static void
575d05c4 515fwdServerClosedWrapper(const CommCloseCbParams &params)
b6b6f466 516{
575d05c4
AJ
517 FwdState *fwd = (FwdState *)params.data;
518 fwd->serverClosed(params.fd);
b6b6f466 519}
520
cfd66529 521void
c8407295 522fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
b6b6f466 523{
524 FwdState *fwd = (FwdState *) data;
aed188fd 525 fwd->connectDone(conn, status, xerrno);
b6b6f466 526}
527
528/**** PRIVATE *****************************************************************/
529
545782b8 530/*
531 * FwdState::checkRetry
26ac0430 532 *
545782b8 533 * Return TRUE if the request SHOULD be retried. This method is
534 * called when the HTTP connection fails, or when the connection
d5430dc8 535 * is closed before reading the end of HTTP headers from the server.
545782b8 536 */
b6b6f466 537bool
538FwdState::checkRetry()
68bd6892 539{
d8fd0f18 540 if (shutting_down)
b6b6f466 541 return false;
62e76326 542
9d2760b6
AR
543 if (!self) { // we have aborted before the server called us back
544 debugs(17, 5, HERE << "not retrying because of earlier abort");
545 // we will be destroyed when the server clears its Pointer to us
546 return false;
547 }
548
b6b6f466 549 if (entry->store_status != STORE_PENDING)
550 return false;
62e76326 551
b6b6f466 552 if (!entry->isEmpty())
553 return false;
62e76326 554
23a1e39c 555 if (n_tries > Config.forward_max_tries)
b6b6f466 556 return false;
4ed0e075 557
b6b6f466 558 if (squid_curtime - start_t > Config.Timeout.forward)
559 return false;
62e76326 560
b6b6f466 561 if (flags.dont_retry)
562 return false;
62e76326 563
3b9899f7
AR
564 if (request->bodyNibbled())
565 return false;
566
0bbd5532
AJ
567 // NP: not yet actually connected anywhere. retry is safe.
568 if (!flags.connected_okay)
569 return true;
570
5d4989a8 571 if (!checkRetriable())
572 return false;
573
b6b6f466 574 return true;
68bd6892 575}
576
545782b8 577/*
578 * FwdState::checkRetriable
579 *
580 * Return TRUE if this is the kind of request that can be retried
581 * after a failure. If the request is not retriable then we don't
582 * want to risk sending it on a persistent connection. Instead we'll
583 * force it to go on a new HTTP connection.
584 */
b6b6f466 585bool
586FwdState::checkRetriable()
cb928909 587{
ccbcff0e
AR
588 // Optimize: A compliant proxy may retry PUTs, but Squid lacks the [rather
589 // complicated] code required to protect the PUT request body from being
590 // nibbled during the first try. Thus, Squid cannot retry some PUTs today.
591 if (request->body_pipe != NULL)
592 return false;
593
c2a7cefd
AJ
594 // RFC2616 9.1 Safe and Idempotent Methods
595 return (request->method.isHttpSafe() || request->method.isIdempotent());
cb928909 596}
597
b6b6f466 598void
599FwdState::serverClosed(int fd)
910169e5 600{
8ed21336 601 // XXX: fd is often -1 here
e8dca475 602 debugs(17, 2, "FD " << fd << " " << entry->url() << " after " <<
8ed21336
AR
603 (fd >= 0 ? fd_table[fd].pconn.uses : -1) << " requests");
604 if (fd >= 0 && serverConnection()->fd == fd)
e8dca475 605 fwdPconnPool->noteUses(fd_table[fd].pconn.uses);
3e8c047e 606 retryOrBail();
607}
608
609void
26ac0430
AJ
610FwdState::retryOrBail()
611{
b6b6f466 612 if (checkRetry()) {
cfd66529 613 debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
bc81ee68
AR
614 // we should retry the same destination if it failed due to pconn race
615 if (pconnRace == raceHappened)
616 debugs(17, 4, HERE << "retrying the same destination");
617 else
b181c1df 618 serverDestinations.erase(serverDestinations.begin()); // last one failed. try another.
8652f8e7
AJ
619 startConnectionOrFail();
620 return;
d8fd0f18 621 }
62e76326 622
9d2760b6
AR
623 // TODO: should we call completed() here and move doneWithRetries there?
624 doneWithRetries();
625
16b70e2a
CT
626 request->hier.stopPeerClock(false);
627
9d2760b6 628 if (self != NULL && !err && shutting_down) {
955394ce 629 ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
f01d4b80 630 errorAppendEntry(entry, anErr);
f563eea9 631 }
62e76326 632
f53969cc 633 self = NULL; // refcounted
910169e5 634}
635
9d2760b6
AR
636// If the Server quits before nibbling at the request body, the body sender
637// will not know (so that we can retry). Call this if we will not retry. We
638// will notify the sender so that it does not get stuck waiting for space.
639void
640FwdState::doneWithRetries()
641{
642 if (request && request->body_pipe != NULL)
643 request->body_pipe->expectNoConsumption();
644}
645
3e8c047e 646// called by the server that failed after calling unregister()
647void
648FwdState::handleUnregisteredServerEnd()
649{
cfd66529 650 debugs(17, 2, HERE << "self=" << self << " err=" << err << ' ' << entry->url());
6b679a01 651 assert(!Comm::IsConnOpen(serverConn));
3e8c047e 652 retryOrBail();
653}
654
b6b6f466 655void
c8407295 656FwdState::connectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno)
41462d93 657{
c8407295 658 if (status != Comm::OK) {
9c8a6c3b 659 ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
b6b6f466 660 anErr->xerrno = xerrno;
b6b6f466 661 fail(anErr);
62e76326 662
2f538b78 663 /* it might have been a timeout with a partially open link */
00ae51e4
AJ
664 if (conn != NULL) {
665 if (conn->getPeer())
666 peerConnectFailed(conn->getPeer());
62e76326 667
80463bb4 668 conn->close();
2f538b78 669 }
aed188fd 670 retryOrBail();
2f538b78
AJ
671 return;
672 }
62e76326 673
00ae51e4 674 serverConn = conn;
3b9899f7 675 flags.connected_okay = true;
00ae51e4 676
f01d4b80 677 debugs(17, 3, HERE << serverConnection() << ": '" << entry->url() << "'" );
62e76326 678
5229395c 679 comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
2f538b78 680
5229395c
AJ
681 if (serverConnection()->getPeer())
682 peerConnectSucceded(serverConnection()->getPeer());
2f538b78 683
cb4f4424 684#if USE_OPENSSL
693cb033 685 if (!request->flags.pinned) {
39322eba 686 if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl) ||
4e3f4dc7 687 (!serverConnection()->getPeer() && request->url.getScheme() == AnyP::PROTO_HTTPS) ||
37276dbb 688 request->flags.sslPeek) {
a23223bf
CT
689
690 HttpRequest::Pointer requestPointer = request;
691 AsyncCall::Pointer callback = asyncCall(17,4,
e2849af8
A
692 "FwdState::ConnectedToPeer",
693 FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
8aec3e1b
CT
694 // Use positive timeout when less than one second is left.
695 const time_t sslNegotiationTimeout = max(static_cast<time_t>(1), timeLeft());
a23223bf 696 Ssl::PeerConnector *connector =
d1f3d8f8 697 new Ssl::PeerConnector(requestPointer, serverConnection(), clientConn, callback, sslNegotiationTimeout);
a23223bf 698 AsyncJob::Start(connector); // will call our callback
39322eba
J
699 return;
700 }
41462d93 701 }
2f538b78
AJ
702#endif
703
92ae4c86
AR
704 // should reach ConnStateData before the dispatched Client job starts
705 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
706 ConnStateData::notePeerConnection, serverConnection());
434a79b0 707
2f538b78 708 dispatch();
41462d93 709}
710
a23223bf
CT
711#if USE_OPENSSL
712void
713FwdState::connectedToPeer(Ssl::PeerConnectorAnswer &answer)
714{
715 if (ErrorState *error = answer.error.get()) {
716 fail(error);
717 answer.error.clear(); // preserve error for errorSendComplete()
718 self = NULL;
719 return;
720 }
721
722 dispatch();
723}
724#endif
725
b6b6f466 726void
727FwdState::connectTimeout(int fd)
41462d93 728{
bf8fe701 729 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
6b679a01
AJ
730 assert(serverDestinations[0] != NULL);
731 assert(fd == serverDestinations[0]->fd);
62e76326 732
528b2c61 733 if (entry->isEmpty()) {
f11c8e2f 734 ErrorState *anErr = new ErrorState(ERR_CONNECT_FAIL, Http::scGatewayTimeout, request);
b6b6f466 735 anErr->xerrno = ETIMEDOUT;
736 fail(anErr);
62e76326 737
cfd66529 738 /* This marks the peer DOWN ... */
6b679a01
AJ
739 if (serverDestinations[0]->getPeer())
740 peerConnectFailed(serverDestinations[0]->getPeer());
41462d93 741 }
62e76326 742
6b679a01
AJ
743 if (Comm::IsConnOpen(serverDestinations[0])) {
744 serverDestinations[0]->close();
746beefe 745 }
41462d93 746}
747
8aec3e1b
CT
748time_t
749FwdState::timeLeft() const
41462d93 750{
cfd66529
AJ
751 /* connection timeout */
752 int ctimeout;
6b679a01
AJ
753 if (serverDestinations[0]->getPeer()) {
754 ctimeout = serverDestinations[0]->getPeer()->connect_timeout > 0 ?
755 serverDestinations[0]->getPeer()->connect_timeout : Config.Timeout.peer_connect;
db1cd23c 756 } else {
62e76326 757 ctimeout = Config.Timeout.connect;
db1cd23c 758 }
62e76326 759
cfd66529
AJ
760 /* calculate total forwarding timeout ??? */
761 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
777831e0 762 if (ftimeout < 0)
763 ftimeout = 5;
764
765 if (ftimeout < ctimeout)
8aec3e1b
CT
766 return (time_t)ftimeout;
767 else
768 return (time_t)ctimeout;
769}
770
771/**
772 * Called after forwarding path selection (via peer select) has taken place
773 * and whenever forwarding needs to attempt a new connection (routing failover).
774 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
775 */
776void
777FwdState::connectStart()
778{
779 assert(serverDestinations.size() > 0);
780
781 debugs(17, 3, "fwdConnectStart: " << entry->url());
782
16b70e2a 783 request->hier.startPeerClock();
777831e0 784
450fe1cb 785 if (serverDestinations[0]->getPeer() && request->flags.sslBumped) {
2cf8425b 786 debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parent proxy are not allowed");
955394ce 787 ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request);
b68415a1
CT
788 fail(anErr);
789 self = NULL; // refcounted
790 return;
985bfe6b 791 }
d67acb4e 792
e857372a 793 request->flags.pinned = false; // XXX: what if the ConnStateData set this to flag existing credentials?
2efeb0b7
AJ
794 // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below.
795 // XXX: also, logs will now lie if pinning is broken and leads to an error message.
6b679a01 796 if (serverDestinations[0]->peerType == PINNED) {
26ac0430 797 ConnStateData *pinned_connection = request->pinnedConnection();
693cb033 798 debugs(17,7, "pinned peer connection: " << pinned_connection);
bc81ee68
AR
799 // pinned_connection may become nil after a pconn race
800 if (pinned_connection)
89b1d7a2 801 serverConn = pinned_connection->borrowPinnedConnection(request, serverDestinations[0]->getPeer());
bc81ee68
AR
802 else
803 serverConn = NULL;
e3a4aecc 804 if (Comm::IsConnOpen(serverConn)) {
7ac40923 805 pinned_connection->stopPinnedConnectionMonitoring();
3b9899f7 806 flags.connected_okay = true;
95dc7ff4 807 ++n_tries;
e857372a 808 request->flags.pinned = true;
35894b9a 809 request->hier.note(serverConn, pinned_connection->pinning.host);
d67acb4e 810 if (pinned_connection->pinnedAuth())
e857372a 811 request->flags.auth = true;
23211a9f 812 comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
c6f168c1
CT
813
814 /* Update server side TOS and Netfilter mark on the connection. */
815 if (Ip::Qos::TheConfig.isAclTosActive()) {
816 debugs(17, 3, HERE << "setting tos for pinned connection to " << (int)serverConn->tos );
817 serverConn->tos = GetTosToServer(request);
818 Ip::Qos::setSockTos(serverConn, serverConn->tos);
819 }
820#if SO_MARK
821 if (Ip::Qos::TheConfig.isAclNfmarkActive()) {
822 serverConn->nfmark = GetNfmarkToServer(request);
823 Ip::Qos::setSockNfmark(serverConn, serverConn->nfmark);
824 }
825#endif
826
bc81ee68
AR
827 // the server may close the pinned connection before this request
828 pconnRace = racePossible;
6b679a01 829 dispatch();
d67acb4e
AJ
830 return;
831 }
693cb033 832 // Pinned connection failure.
85563fd9 833 debugs(17,2,HERE << "Pinned connection failed: " << pinned_connection);
955394ce 834 ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
693cb033
CT
835 fail(anErr);
836 self = NULL; // refcounted
837 return;
26ac0430 838 }
d67acb4e 839
642a305c 840 // Use pconn to avoid opening a new connection.
7a150c71
AJ
841 const char *host = NULL;
842 if (!serverDestinations[0]->getPeer())
06093389 843 host = request->GetHost();
bc81ee68
AR
844
845 Comm::ConnectionPointer temp;
846 // Avoid pconns after races so that the same client does not suffer twice.
847 // This does not increase the total number of connections because we just
848 // closed the connection that failed the race. And re-pinning assumes this.
849 if (pconnRace != raceHappened)
e8dca475 850 temp = pconnPop(serverDestinations[0], host);
bbf706ff 851
bc81ee68
AR
852 const bool openedPconn = Comm::IsConnOpen(temp);
853 pconnRace = openedPconn ? racePossible : raceImpossible;
cfd66529 854
6b679a01 855 // if we found an open persistent connection to use. use it.
bc81ee68 856 if (openedPconn) {
80463bb4 857 serverConn = temp;
3b9899f7 858 flags.connected_okay = true;
642a305c 859 debugs(17, 3, HERE << "reusing pconn " << serverConnection());
95dc7ff4 860 ++n_tries;
4ed0e075 861
5229395c 862 comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
b5523edc
AJ
863
864 /* Update server side TOS and Netfilter mark on the connection. */
865 if (Ip::Qos::TheConfig.isAclTosActive()) {
f123f5e9
CT
866 const tos_t tos = GetTosToServer(request);
867 Ip::Qos::setSockTos(temp, tos);
b5523edc
AJ
868 }
869#if SO_MARK
870 if (Ip::Qos::TheConfig.isAclNfmarkActive()) {
f123f5e9
CT
871 const nfmark_t nfmark = GetNfmarkToServer(request);
872 Ip::Qos::setSockNfmark(temp, nfmark);
b5523edc
AJ
873 }
874#endif
875
c8ceec27 876 dispatch();
c8ceec27 877 return;
41462d93 878 }
62e76326 879
bc81ee68
AR
880 // We will try to open a new connection, possibly to the same destination.
881 // We reset serverDestinations[0] in case we are using it again because
882 // ConnOpener modifies its destination argument.
4dd643d5 883 serverDestinations[0]->local.port(0);
bc81ee68
AR
884 serverConn = NULL;
885
bc87dc25 886#if URL_CHECKSUM_DEBUG
b6b6f466 887 entry->mem_obj->checkUrlChecksum();
bc87dc25 888#endif
62e76326 889
6ee88490 890 GetMarkingsToServer(request, *serverDestinations[0]);
b5523edc 891
e3a4aecc 892 calls.connector = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
8aec3e1b 893 Comm::ConnOpener *cs = new Comm::ConnOpener(serverDestinations[0], calls.connector, timeLeft());
7a150c71
AJ
894 if (host)
895 cs->setHost(host);
855150a4 896 AsyncJob::Start(cs);
41462d93 897}
898
b6b6f466 899void
900FwdState::dispatch()
41462d93 901{
7f06a3d8 902 debugs(17, 3, clientConn << ": Fetching " << request->method << ' ' << entry->url());
e0ebe27c 903 /*
904 * Assert that server_fd is set. This is to guarantee that fwdState
905 * is attached to something and will be deallocated when server_fd
906 * is closed.
907 */
6b679a01 908 assert(Comm::IsConnOpen(serverConn));
62e76326 909
5229395c 910 fd_note(serverConnection()->fd, entry->url());
62e76326 911
e8dca475 912 fd_table[serverConnection()->fd].noteUse();
62e76326 913
a7ad6e4e 914 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
915 assert(entry->ping_status != PING_WAITING);
62e76326 916
1bfe9ade 917 assert(entry->locked());
62e76326 918
a7ad6e4e 919 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 920
cc192b50 921 netdbPingSite(request->GetHost());
62e76326 922
425de4c8 923 /* Retrieves remote server TOS or MARK value, and stores it as part of the
7172612f 924 * original client request FD object. It is later used to forward
425de4c8 925 * remote server's TOS/MARK in the response to the client in case of a MISS.
7172612f 926 */
425de4c8 927 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
b5523edc
AJ
928 if (Comm::IsConnOpen(clientConn) && Comm::IsConnOpen(serverConnection())) {
929 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
425de4c8 930 /* Get the netfilter mark for the connection */
b5523edc 931 Ip::Qos::getNfmarkFromServer(serverConnection(), clientFde);
425de4c8
AJ
932 }
933 }
934
935#if _SQUID_LINUX_
936 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
937 if (Ip::Qos::TheConfig.isHitTosActive()) {
b5523edc
AJ
938 if (Comm::IsConnOpen(clientConn)) {
939 fde * clientFde = &fd_table[clientConn->fd]; // XXX: move the fd_table access into Ip::Qos
425de4c8 940 /* Get the TOS value for the packet */
b5523edc 941 Ip::Qos::getTosFromServer(serverConnection(), clientFde);
7172612f 942 }
26ac0430 943 }
7172612f
AJ
944#endif
945
cb4f4424 946#if USE_OPENSSL
2c065fc8 947 if (request->flags.sslPeek) {
d7ce0bcd 948 CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
061bbdec 949 ConnStateData::httpsPeeked, serverConnection());
d7ce0bcd
AR
950 unregister(serverConn); // async call owns it now
951 complete(); // destroys us
952 return;
061bbdec 953 }
d7ce0bcd
AR
954#endif
955
5229395c 956 if (serverConnection()->getPeer() != NULL) {
95dc7ff4 957 ++ serverConnection()->getPeer()->stats.fetches;
5229395c
AJ
958 request->peer_login = serverConnection()->getPeer()->login;
959 request->peer_domain = serverConnection()->getPeer()->domain;
b6b6f466 960 httpStart(this);
41462d93 961 } else {
2c065fc8 962 assert(!request->flags.sslPeek);
b6b6f466 963 request->peer_login = NULL;
964 request->peer_domain = NULL;
62e76326 965
4e3f4dc7 966 switch (request->url.getScheme()) {
cb4f4424 967#if USE_OPENSSL
62e76326 968
0c3d3f65 969 case AnyP::PROTO_HTTPS:
b6b6f466 970 httpStart(this);
62e76326 971 break;
a7ad6e4e 972#endif
62e76326 973
0c3d3f65 974 case AnyP::PROTO_HTTP:
b6b6f466 975 httpStart(this);
62e76326 976 break;
977
0c3d3f65 978 case AnyP::PROTO_GOPHER:
b6b6f466 979 gopherStart(this);
62e76326 980 break;
981
0c3d3f65 982 case AnyP::PROTO_FTP:
92ae4c86 983 if (request->flags.ftpNative)
5517260a 984 Ftp::StartRelay(this);
434a79b0 985 else
5517260a 986 Ftp::StartGateway(this);
62e76326 987 break;
988
39a19cb7 989 case AnyP::PROTO_CACHE_OBJECT:
62e76326 990
0c3d3f65 991 case AnyP::PROTO_URN:
62e76326 992 fatal_dump("Should never get here");
993 break;
994
0c3d3f65 995 case AnyP::PROTO_WHOIS:
b6b6f466 996 whoisStart(this);
62e76326 997 break;
998
f53969cc 999 case AnyP::PROTO_WAIS: /* Not implemented */
fc68f6b1 1000
62e76326 1001 default:
7be06178 1002 debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
955394ce 1003 ErrorState *anErr = new ErrorState(ERR_UNSUP_REQ, Http::scBadRequest, request);
b6b6f466 1004 fail(anErr);
7be06178 1005 // Set the dont_retry flag because this is not a transient (network) error.
e857372a 1006 flags.dont_retry = true;
6b679a01 1007 if (Comm::IsConnOpen(serverConn)) {
00ae51e4 1008 serverConn->close();
746beefe 1009 }
62e76326 1010 break;
1011 }
41462d93 1012 }
1013}
1014
545782b8 1015/*
1016 * FwdState::reforward
1017 *
1018 * returns TRUE if the transaction SHOULD be re-forwarded to the
8bbb16e3 1019 * next choice in the serverDestinations list. This method is called when
d5430dc8 1020 * peer communication completes normally, or experiences
545782b8 1021 * some error after receiving the end of HTTP headers.
1022 */
b6b6f466 1023int
1024FwdState::reforward()
db1cd23c 1025{
b6b6f466 1026 StoreEntry *e = entry;
c4a88a3e
CT
1027
1028 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
1029 debugs(17, 3, HERE << "entry aborted");
1030 return 0;
1031 }
1032
db1cd23c 1033 assert(e->store_status == STORE_PENDING);
1034 assert(e->mem_obj);
bc87dc25 1035#if URL_CHECKSUM_DEBUG
62e76326 1036
528b2c61 1037 e->mem_obj->checkUrlChecksum();
bc87dc25 1038#endif
62e76326 1039
cfd66529 1040 debugs(17, 3, HERE << e->url() << "?" );
62e76326 1041
d6eb18d6 1042 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
cfd66529 1043 debugs(17, 3, HERE << "No, ENTRY_FWD_HDR_WAIT isn't set");
62e76326 1044 return 0;
d6eb18d6 1045 }
62e76326 1046
437823b4 1047 if (n_tries > Config.forward_max_tries)
62e76326 1048 return 0;
1049
58217e94 1050 if (request->bodyNibbled())
62e76326 1051 return 0;
1052
8652f8e7
AJ
1053 if (serverDestinations.size() <= 1) {
1054 // NP: <= 1 since total count includes the recently failed one.
cfd66529 1055 debugs(17, 3, HERE << "No alternative forwarding paths left");
62e76326 1056 return 0;
db1cd23c 1057 }
62e76326 1058
9b769c67 1059 const Http::StatusCode s = e->getReply()->sline.status();
cfd66529 1060 debugs(17, 3, HERE << "status " << s);
b6b6f466 1061 return reforwardableStatus(s);
db1cd23c 1062}
1063
2ac4f6b5
AR
1064/**
1065 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1066 * on whether this is a validation request. RFC 2616 says that we MUST reply
1067 * with "504 Gateway Timeout" if validation fails and cached reply has
1068 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1069 */
1070ErrorState *
1071FwdState::makeConnectingError(const err_type type) const
1072{
450fe1cb 1073 return new ErrorState(type, request->flags.needValidation ?
f11c8e2f 1074 Http::scGatewayTimeout : Http::scServiceUnavailable, request);
2ac4f6b5
AR
1075}
1076
b6b6f466 1077static void
1078fwdStats(StoreEntry * s)
64d8034e 1079{
b6b6f466 1080 int i;
1081 int j;
1082 storeAppendPrintf(s, "Status");
62e76326 1083
95dc7ff4
FC
1084 for (j = 1; j < MAX_FWD_STATS_IDX; ++j) {
1085 storeAppendPrintf(s, "\ttry#%d", j);
64d8034e 1086 }
64d8034e 1087
b6b6f466 1088 storeAppendPrintf(s, "\n");
0185bd6f 1089
955394ce 1090 for (i = 0; i <= (int) Http::scInvalidHeader; ++i) {
b6b6f466 1091 if (FwdReplyCodes[0][i] == 0)
1092 continue;
0185bd6f 1093
b6b6f466 1094 storeAppendPrintf(s, "%3d", i);
0185bd6f 1095
95dc7ff4 1096 for (j = 0; j <= MAX_FWD_STATS_IDX; ++j) {
b6b6f466 1097 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1098 }
62e76326 1099
b6b6f466 1100 storeAppendPrintf(s, "\n");
e0ebe27c 1101 }
7197b20d 1102}
1103
b6b6f466 1104/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1105
b6b6f466 1106bool
955394ce 1107FwdState::reforwardableStatus(const Http::StatusCode s) const
db1cd23c 1108{
b6b6f466 1109 switch (s) {
62e76326 1110
955394ce 1111 case Http::scBadGateway:
62e76326 1112
f11c8e2f 1113 case Http::scGatewayTimeout:
b6b6f466 1114 return true;
62e76326 1115
955394ce 1116 case Http::scForbidden:
62e76326 1117
955394ce 1118 case Http::scInternalServerError:
62e76326 1119
955394ce 1120 case Http::scNotImplemented:
62e76326 1121
955394ce 1122 case Http::scServiceUnavailable:
b6b6f466 1123 return Config.retry.onerror;
62e76326 1124
b6b6f466 1125 default:
1126 return false;
db1cd23c 1127 }
b6b6f466 1128
1129 /* NOTREACHED */
db1cd23c 1130}
8ddcc35d 1131
06093389
AJ
1132/**
1133 * Decide where details need to be gathered to correctly describe a persistent connection.
1134 * What is needed:
642a305c
AJ
1135 * - the address/port details about this link
1136 * - domain name of server at other end of this link (either peer or requested host)
06093389 1137 */
781ce8ff 1138void
642a305c 1139FwdState::pconnPush(Comm::ConnectionPointer &conn, const char *domain)
781ce8ff 1140{
642a305c 1141 if (conn->getPeer()) {
7a150c71 1142 fwdPconnPool->push(conn, NULL);
06093389 1143 } else {
642a305c 1144 fwdPconnPool->push(conn, domain);
06093389 1145 }
781ce8ff 1146}
1147
e8dca475
CT
1148Comm::ConnectionPointer
1149FwdState::pconnPop(const Comm::ConnectionPointer &dest, const char *domain)
1150{
1151 // always call shared pool first because we need to close an idle
1152 // connection there if we have to use a standby connection.
1153 Comm::ConnectionPointer conn = fwdPconnPool->pop(dest, domain, checkRetriable());
1154 if (!Comm::IsConnOpen(conn)) {
1155 // either there was no pconn to pop or this is not a retriable xaction
1156 if (CachePeer *peer = dest->getPeer()) {
1157 if (peer->standby.pool)
1158 conn = peer->standby.pool->pop(dest, domain, true);
1159 }
1160 }
1161 return conn; // open, closed, or nil
1162}
1163
8ddcc35d 1164void
b6b6f466 1165FwdState::initModule()
8ddcc35d 1166{
6852be71 1167 RegisterWithCacheManager();
8ddcc35d 1168}
1169
62ee09ca 1170void
84f50787 1171FwdState::RegisterWithCacheManager(void)
62ee09ca 1172{
8822ebee 1173 Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
62ee09ca 1174}
1175
b6b6f466 1176void
955394ce 1177FwdState::logReplyStatus(int tries, const Http::StatusCode status)
8ddcc35d 1178{
955394ce 1179 if (status > Http::scInvalidHeader)
62e76326 1180 return;
1181
75eb730e 1182 assert(tries >= 0);
62e76326 1183
8ddcc35d 1184 if (tries > MAX_FWD_STATS_IDX)
62e76326 1185 tries = MAX_FWD_STATS_IDX;
1186
95dc7ff4 1187 ++ FwdReplyCodes[tries][status];
8ddcc35d 1188}
1189
b6b6f466 1190/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1191
057f5854 1192/*
1193 * DPW 2007-05-19
1194 * Formerly static, but now used by client_side_request.cc
1195 */
425de4c8
AJ
1196/// Checks for a TOS value to apply depending on the ACL
1197tos_t
b6b6f466 1198aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1199{
b6b6f466 1200 acl_tos *l;
62e76326 1201
b6b6f466 1202 for (l = head; l; l = l->next) {
2efeb0b7 1203 if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
b6b6f466 1204 return l->tos;
1205 }
5894ad28 1206
b6b6f466 1207 return 0;
1208}
5894ad28 1209
425de4c8
AJ
1210/// Checks for a netfilter mark value to apply depending on the ACL
1211nfmark_t
1212aclMapNfmark(acl_nfmark * head, ACLChecklist * ch)
1213{
1214 acl_nfmark *l;
1215
1216 for (l = head; l; l = l->next) {
2efeb0b7 1217 if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
425de4c8
AJ
1218 return l->nfmark;
1219 }
1220
1221 return 0;
1222}
1223
cfd66529 1224void
f9b72e0c 1225getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn)
b6b6f466 1226{
07f889c1 1227 // skip if an outgoing address is already set.
4dd643d5 1228 if (!conn->local.isAnyAddr()) return;
cfd66529 1229
07f889c1 1230 // ensure that at minimum the wildcard local matches remote protocol
4dd643d5
AJ
1231 if (conn->remote.isIPv4())
1232 conn->local.setIPv4();
07f889c1 1233
cfd66529 1234 // maybe use TPROXY client address
450fe1cb 1235 if (request && request->flags.spoofClientIp) {
739b352a 1236 if (!conn->getPeer() || !conn->getPeer()->options.no_tproxy) {
96d64448
AJ
1237#if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1238 if (Config.onoff.tproxy_uses_indirect_client)
45906573 1239 conn->local = request->indirect_client_addr;
96d64448
AJ
1240 else
1241#endif
45906573 1242 conn->local = request->client_addr;
cfd66529
AJ
1243 // some flags need setting on the socket to use this address
1244 conn->flags |= COMM_DOBIND;
1245 conn->flags |= COMM_TRANSPARENT;
1246 return;
1247 }
b0758e04
AJ
1248 // else no tproxy today ...
1249 }
c303f6e3 1250
b50e327b 1251 if (!Config.accessList.outgoing_address) {
cfd66529 1252 return; // anything will do.
b50e327b
AJ
1253 }
1254
c0941a6a 1255 ACLFilledChecklist ch(NULL, request, NULL);
739b352a 1256 ch.dst_peer = conn->getPeer();
cfd66529
AJ
1257 ch.dst_addr = conn->remote;
1258
1259 // TODO use the connection details in ACL.
1260 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
6db78a1a 1261
289848ca 1262 for (Acl::Address *l = Config.accessList.outgoing_address; l; l = l->next) {
cfd66529
AJ
1263
1264 /* check if the outgoing address is usable to the destination */
4dd643d5 1265 if (conn->remote.isIPv4() != l->addr.isIPv4()) continue;
cfd66529
AJ
1266
1267 /* check ACLs for this outgoing address */
2efeb0b7 1268 if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
cfd66529
AJ
1269 conn->local = l->addr;
1270 return;
1271 }
1272 }
b6b6f466 1273}
62e76326 1274
425de4c8
AJ
1275tos_t
1276GetTosToServer(HttpRequest * request)
1277{
1278 ACLFilledChecklist ch(NULL, request, NULL);
425de4c8
AJ
1279 return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
1280}
1281
1282nfmark_t
1283GetNfmarkToServer(HttpRequest * request)
b6b6f466 1284{
c0941a6a 1285 ACLFilledChecklist ch(NULL, request, NULL);
425de4c8 1286 return aclMapNfmark(Ip::Qos::TheConfig.nfmarkToServer, &ch);
b6a2f15e 1287}
6ee88490
CT
1288
1289void
1290GetMarkingsToServer(HttpRequest * request, Comm::Connection &conn)
1291{
1292 // Get the server side TOS and Netfilter mark to be set on the connection.
1293 if (Ip::Qos::TheConfig.isAclTosActive()) {
1294 conn.tos = GetTosToServer(request);
1295 debugs(17, 3, "from " << conn.local << " tos " << int(conn.tos));
1296 }
1297
1298#if SO_MARK && USE_LIBCAP
1299 conn.nfmark = GetNfmarkToServer(request);
1300 debugs(17, 3, "from " << conn.local << " netfilter mark " << conn.nfmark);
1301#else
1302 conn.nfmark = 0;
1303#endif
1304}
f53969cc 1305