2 * DEBUG: section 17 Request Forwarding
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 #include "acl/FilledChecklist.h"
36 #include "acl/Gadgets.h"
37 #include "CacheManager.h"
38 #include "comm/Connection.h"
39 #include "comm/ConnOpener.h"
40 #include "CommCalls.h"
42 #include "errorpage.h"
45 #include "hier_code.h"
46 #include "HttpReply.h"
47 #include "HttpRequest.h"
48 #include "ip/QosConfig.h"
49 #include "MemObject.h"
51 #include "PeerSelectState.h"
52 #include "SquidTime.h"
54 #include "icmp/net_db.h"
55 #include "ip/Intercept.h"
58 static PSC fwdPeerSelectionCompleteWrapper
;
59 static PF fwdServerClosedWrapper
;
61 static PF fwdNegotiateSSLWrapper
;
63 static CNCB fwdConnectDoneWrapper
;
67 #define MAX_FWD_STATS_IDX 9
68 static int FwdReplyCodes
[MAX_FWD_STATS_IDX
+ 1][HTTP_INVALID_HEADER
+ 1];
71 static void fwdLog(FwdState
* fwdState
);
72 static Logfile
*logfile
= NULL
;
75 static PconnPool
*fwdPconnPool
= new PconnPool("server-side");
76 CBDATA_CLASS_INIT(FwdState
);
79 FwdState::abort(void* d
)
81 FwdState
* fwd
= (FwdState
*)d
;
82 Pointer tmp
= fwd
; // Grab a temporary pointer to keep the object alive during our scope.
84 if (Comm::IsConnOpen(fwd
->serverConnection())) {
85 comm_remove_close_handler(fwd
->serverConnection()->fd
, fwdServerClosedWrapper
, fwd
);
87 fwd
->serverDestinations
.clean();
91 /**** PUBLIC INTERFACE ********************************************************/
93 FwdState::FwdState(const Comm::ConnectionPointer
&client
, StoreEntry
* e
, HttpRequest
* r
)
95 debugs(17, 1, HERE
<< "Forwarding client request " << client
<< ", url=" << e
->url() );
98 request
= HTTPMSGLOCK(r
);
99 start_t
= squid_curtime
;
100 serverDestinations
.reserve(Config
.forward_max_tries
);
102 EBIT_SET(e
->flags
, ENTRY_FWD_HDR_WAIT
);
105 // Called once, right after object creation, when it is safe to set self
106 void FwdState::start(Pointer aSelf
)
108 // Protect ourselves from being destroyed when the only Server pointing
109 // to us is gone (while we expect to talk to more Servers later).
110 // Once we set self, we are responsible for clearing it when we do not
111 // expect to talk to any servers.
112 self
= aSelf
; // refcounted
114 // We hope that either the store entry aborts or peer is selected.
115 // Otherwise we are going to leak our object.
117 entry
->registerAbort(FwdState::abort
, this);
118 peerSelect(&serverDestinations
, request
, entry
, fwdPeerSelectionCompleteWrapper
, this);
122 FwdState::completed()
124 if (flags
.forward_completed
== 1) {
125 debugs(17, 1, HERE
<< "FwdState::completed called on a completed request! Bad!");
129 flags
.forward_completed
= 1;
131 #if URL_CHECKSUM_DEBUG
133 entry
->mem_obj
->checkUrlChecksum();
140 if (entry
->store_status
== STORE_PENDING
) {
141 if (entry
->isEmpty()) {
143 errorAppendEntry(entry
, err
);
146 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
148 entry
->releaseRequest();
152 if (storePendingNClients(entry
) > 0)
153 assert(!EBIT_TEST(entry
->flags
, ENTRY_FWD_HDR_WAIT
));
157 FwdState::~FwdState()
159 debugs(17, 3, HERE
<< "FwdState destructor starting");
161 if (! flags
.forward_completed
)
166 HTTPMSGUNLOCK(request
);
171 entry
->unregisterAbort();
177 if (Comm::IsConnOpen(serverConn
)) {
178 comm_remove_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
179 debugs(17, 3, HERE
<< "closing FD " << serverConnection()->fd
);
183 serverDestinations
.clean();
185 debugs(17, 3, HERE
<< "FwdState destructor done");
189 * This is the entry point for client-side to start forwarding
190 * a transaction. It is a static method that may or may not
191 * allocate a FwdState.
194 FwdState::fwdStart(const Comm::ConnectionPointer
&clientConn
, StoreEntry
*entry
, HttpRequest
*request
)
197 * client_addr == no_addr indicates this is an "internal" request
198 * from peer_digest.c, asn.c, netdb.c, etc and should always
199 * be allowed. yuck, I know.
202 if ( Config
.accessList
.miss
&& !request
->client_addr
.IsNoAddr() &&
203 request
->protocol
!= PROTO_INTERNAL
&& request
->protocol
!= PROTO_CACHEOBJ
) {
205 * Check if this host is allowed to fetch MISSES from us (miss_access)
207 ACLFilledChecklist
ch(Config
.accessList
.miss
, request
, NULL
);
208 ch
.src_addr
= request
->client_addr
;
209 ch
.my_addr
= request
->my_addr
;
210 int answer
= ch
.fastCheck();
214 page_id
= aclGetDenyInfoPage(&Config
.denyInfoList
, AclMatchedName
, 1);
216 if (page_id
== ERR_NONE
)
217 page_id
= ERR_FORWARDING_DENIED
;
219 ErrorState
*anErr
= errorCon(page_id
, HTTP_FORBIDDEN
, request
);
221 errorAppendEntry(entry
, anErr
); // frees anErr
227 debugs(17, 3, HERE
<< "'" << entry
->url() << "'");
229 * This seems like an odd place to bind mem_obj and request.
230 * Might want to assert that request is NULL at this point
232 entry
->mem_obj
->request
= HTTPMSGLOCK(request
);
233 #if URL_CHECKSUM_DEBUG
235 entry
->mem_obj
->checkUrlChecksum();
240 ErrorState
*anErr
= errorCon(ERR_SHUTTING_DOWN
, HTTP_SERVICE_UNAVAILABLE
, request
);
241 errorAppendEntry(entry
, anErr
); // frees anErr
245 switch (request
->protocol
) {
248 internalStart(request
, entry
);
252 CacheManager::GetInstance()->Start(clientConn
, request
, entry
);
256 urnStart(request
, entry
);
260 FwdState::Pointer fwd
= new FwdState(clientConn
, entry
, request
);
269 FwdState::startConnectionOrFail()
271 debugs(17, 3, HERE
<< entry
->url() );
273 if (serverDestinations
.size() > 0) {
276 debugs(17, 3, HERE
<< entry
->url() );
277 ErrorState
*anErr
= errorCon(ERR_CANNOT_FORWARD
, HTTP_SERVICE_UNAVAILABLE
, request
);
278 anErr
->xerrno
= errno
;
280 self
= NULL
; // refcounted
285 FwdState::fail(ErrorState
* errorState
)
287 debugs(17, 3, HERE
<< err_type_str
[errorState
->type
] << " \"" << httpStatusString(errorState
->httpStatus
) << "\"\n\t" << entry
->url() );
294 if (!errorState
->request
)
295 errorState
->request
= HTTPMSGLOCK(request
);
299 * Frees fwdState without closing FD or generating an abort
302 FwdState::unregister(Comm::ConnectionPointer
&conn
)
304 debugs(17, 3, HERE
<< entry
->url() );
305 assert(serverConnection() == conn
);
306 assert(Comm::IsConnOpen(conn
));
307 comm_remove_close_handler(conn
->fd
, fwdServerClosedWrapper
, this);
311 // Legacy method to be removed in favor of the above as soon as possible
313 FwdState::unregister(int fd
)
315 debugs(17, 3, HERE
<< entry
->url() );
316 assert(fd
== serverConnection()->fd
);
317 unregister(serverConn
);
321 * server-side modules call fwdComplete() when they are done
322 * downloading an object. Then, we either 1) re-forward the
323 * request somewhere else if needed, or 2) call storeComplete()
329 assert(entry
->store_status
== STORE_PENDING
);
330 debugs(17, 3, HERE
<< entry
->url() << "\n\tstatus " << entry
->getReply()->sline
.status
);
331 #if URL_CHECKSUM_DEBUG
333 entry
->mem_obj
->checkUrlChecksum();
336 logReplyStatus(n_tries
, entry
->getReply()->sline
.status
);
339 assert(serverDestinations
.size() > 0);
340 debugs(17, 3, HERE
<< "re-forwarding " << entry
->getReply()->sline
.status
<< " " << entry
->url());
342 if (Comm::IsConnOpen(serverConn
))
343 unregister(serverConn
);
347 /* the call to reforward() has already dropped the last path off the
348 * selection list. all we have now are the next path(s) to be tried.
352 if (Comm::IsConnOpen(serverConn
))
353 debugs(17, 3, HERE
<< "server FD " << serverConnection()->fd
<< " not re-forwarding status " << entry
->getReply()->sline
.status
);
355 debugs(17, 3, HERE
<< "server (FD closed) not re-forwarding status " << entry
->getReply()->sline
.status
);
356 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
359 if (!Comm::IsConnOpen(serverConn
))
362 self
= NULL
; // refcounted
367 /**** CALLBACK WRAPPERS ************************************************************/
370 fwdPeerSelectionCompleteWrapper(Comm::ConnectionList
* unused
, void *data
)
372 FwdState
*fwd
= (FwdState
*) data
;
373 fwd
->startConnectionOrFail();
377 fwdServerClosedWrapper(int fd
, void *data
)
379 FwdState
*fwd
= (FwdState
*) data
;
380 fwd
->serverClosed(fd
);
385 fwdConnectStartWrapper(void *data
)
387 FwdState
*fwd
= (FwdState
*) data
;
394 fwdNegotiateSSLWrapper(int fd
, void *data
)
396 FwdState
*fwd
= (FwdState
*) data
;
397 fwd
->negotiateSSL(fd
);
402 fwdConnectDoneWrapper(const Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
, void *data
)
404 FwdState
*fwd
= (FwdState
*) data
;
405 fwd
->connectDone(conn
, status
, xerrno
);
408 /**** PRIVATE *****************************************************************/
411 * FwdState::checkRetry
413 * Return TRUE if the request SHOULD be retried. This method is
414 * called when the HTTP connection fails, or when the connection
415 * is closed before server-side read the end of HTTP headers.
418 FwdState::checkRetry()
423 if (!self
) { // we have aborted before the server called us back
424 debugs(17, 5, HERE
<< "not retrying because of earlier abort");
425 // we will be destroyed when the server clears its Pointer to us
429 if (entry
->store_status
!= STORE_PENDING
)
432 if (!entry
->isEmpty())
438 if (origin_tries
> 2)
441 if (squid_curtime
- start_t
> Config
.Timeout
.forward
)
444 if (flags
.dont_retry
)
447 if (!checkRetriable())
450 if (request
->bodyNibbled())
457 * FwdState::checkRetriable
459 * Return TRUE if this is the kind of request that can be retried
460 * after a failure. If the request is not retriable then we don't
461 * want to risk sending it on a persistent connection. Instead we'll
462 * force it to go on a new HTTP connection.
465 FwdState::checkRetriable()
467 /* If there is a request body then Squid can only try once
468 * even if the method is indempotent
471 if (request
->body_pipe
!= NULL
)
474 /* RFC2616 9.1 Safe and Idempotent Methods */
475 switch (request
->method
.id()) {
476 /* 9.1.1 Safe Methods */
481 /* 9.1.2 Idempotent Methods */
500 FwdState::serverClosed(int fd
)
502 debugs(17, 2, HERE
<< "FD " << fd
<< " " << entry
->url());
507 FwdState::retryOrBail()
510 debugs(17, 3, HERE
<< "re-forwarding (" << n_tries
<< " tries, " << (squid_curtime
- start_t
) << " secs)");
512 serverDestinations
.shift(); // last one failed. try another.
514 if (serverDestinations
.size() > 0) {
515 /* Ditch error page if it was created before.
516 * A new one will be created if there's another problem */
525 // else bail. no more serverDestinations possible to try.
527 // AYJ: cannot-forward error ??
528 // is this hack needed since we now have doneWithRetries() below?
529 // ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
530 // errorAppendEntry(entry, anErr);
533 // TODO: should we call completed() here and move doneWithRetries there?
536 if (self
!= NULL
&& !err
&& shutting_down
) {
537 ErrorState
*anErr
= errorCon(ERR_SHUTTING_DOWN
, HTTP_SERVICE_UNAVAILABLE
, request
);
538 errorAppendEntry(entry
, anErr
);
541 self
= NULL
; // refcounted
544 // If the Server quits before nibbling at the request body, the body sender
545 // will not know (so that we can retry). Call this if we will not retry. We
546 // will notify the sender so that it does not get stuck waiting for space.
548 FwdState::doneWithRetries()
550 if (request
&& request
->body_pipe
!= NULL
)
551 request
->body_pipe
->expectNoConsumption();
554 // called by the server that failed after calling unregister()
556 FwdState::handleUnregisteredServerEnd()
558 debugs(17, 2, HERE
<< "self=" << self
<< " err=" << err
<< ' ' << entry
->url());
559 assert(!Comm::IsConnOpen(serverConn
));
565 FwdState::negotiateSSL(int fd
)
567 SSL
*ssl
= fd_table
[fd
].ssl
;
570 if ((ret
= SSL_connect(ssl
)) <= 0) {
571 int ssl_error
= SSL_get_error(ssl
, ret
);
575 case SSL_ERROR_WANT_READ
:
576 commSetSelect(fd
, COMM_SELECT_READ
, fwdNegotiateSSLWrapper
, this, 0);
579 case SSL_ERROR_WANT_WRITE
:
580 commSetSelect(fd
, COMM_SELECT_WRITE
, fwdNegotiateSSLWrapper
, this, 0);
584 debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd
<<
585 ": " << ERR_error_string(ERR_get_error(), NULL
) << " (" << ssl_error
<<
586 "/" << ret
<< "/" << errno
<< ")");
587 ErrorState
*const anErr
= makeConnectingError(ERR_SECURE_CONNECT_FAIL
);
590 anErr
->xerrno
= EPROTO
;
593 anErr
->xerrno
= EACCES
;
598 if (serverConnection()->getPeer()) {
599 peerConnectFailed(serverConnection()->getPeer());
607 if (serverConnection()->getPeer() && !SSL_session_reused(ssl
)) {
608 if (serverConnection()->getPeer()->sslSession
)
609 SSL_SESSION_free(serverConnection()->getPeer()->sslSession
);
611 serverConnection()->getPeer()->sslSession
= SSL_get1_session(ssl
);
618 FwdState::initiateSSL()
621 SSL_CTX
*sslContext
= NULL
;
622 const peer
*peer
= serverConnection()->getPeer();
623 int fd
= serverConnection()->fd
;
626 assert(peer
->use_ssl
);
627 sslContext
= peer
->sslContext
;
629 sslContext
= Config
.ssl_client
.sslContext
;
634 if ((ssl
= SSL_new(sslContext
)) == NULL
) {
635 debugs(83, 1, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL
) );
636 ErrorState
*anErr
= errorCon(ERR_SOCKET_FAILURE
, HTTP_INTERNAL_SERVER_ERROR
, request
);
637 anErr
->xerrno
= errno
;
639 self
= NULL
; // refcounted
647 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->ssldomain
);
652 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->name
);
657 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->host
);
659 if (peer
->sslSession
)
660 SSL_set_session(ssl
, peer
->sslSession
);
663 SSL_set_ex_data(ssl
, ssl_ex_index_server
, (void*)request
->GetHost());
666 // Create the ACL check list now, while we have access to more info.
667 // The list is used in ssl_verify_cb() and is freed in ssl_free().
668 if (acl_access
*acl
= Config
.ssl_client
.cert_error
) {
669 ACLFilledChecklist
*check
= new ACLFilledChecklist(acl
, request
, dash_str
);
671 SSL_set_ex_data(ssl
, ssl_ex_index_cert_error_check
, check
);
674 fd_table
[fd
].ssl
= ssl
;
675 fd_table
[fd
].read_method
= &ssl_read_method
;
676 fd_table
[fd
].write_method
= &ssl_write_method
;
683 FwdState::connectDone(const Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
)
685 if (status
!= COMM_OK
) {
686 ErrorState
*const anErr
= makeConnectingError(ERR_CONNECT_FAIL
);
687 anErr
->xerrno
= xerrno
;
690 /* it might have been a timeout with a partially open link */
693 peerConnectFailed(conn
->getPeer());
704 if (Config
.onoff
.log_ip_on_direct
&& serverConnection()->peerType
== HIER_DIRECT
)
705 updateHierarchyInfo();
708 debugs(17, 3, HERE
<< serverConnection() << ": '" << entry
->url() << "'" );
710 comm_add_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
712 if (serverConnection()->getPeer())
713 peerConnectSucceded(serverConnection()->getPeer());
715 updateHierarchyInfo();
718 if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl
) ||
719 (!serverConnection()->getPeer() && request
->protocol
== PROTO_HTTPS
)) {
729 FwdState::connectTimeout(int fd
)
731 debugs(17, 2, "fwdConnectTimeout: FD " << fd
<< ": '" << entry
->url() << "'" );
732 assert(serverDestinations
[0] != NULL
);
733 assert(fd
== serverDestinations
[0]->fd
);
735 if (Config
.onoff
.log_ip_on_direct
&& serverDestinations
[0]->peerType
== HIER_DIRECT
)
736 updateHierarchyInfo();
738 if (entry
->isEmpty()) {
739 ErrorState
*anErr
= errorCon(ERR_CONNECT_FAIL
, HTTP_GATEWAY_TIMEOUT
, request
);
740 anErr
->xerrno
= ETIMEDOUT
;
743 /* This marks the peer DOWN ... */
744 if (serverDestinations
[0]->getPeer())
745 peerConnectFailed(serverDestinations
[0]->getPeer());
748 if (Comm::IsConnOpen(serverDestinations
[0])) {
749 serverDestinations
[0]->close();
754 * Called after Forwarding path selection (via peer select) has taken place.
755 * And whenever forwarding needs to attempt a new connection (routing failover)
756 * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
759 FwdState::connectStart()
761 assert(serverDestinations
.size() > 0);
763 debugs(17, 3, "fwdConnectStart: " << entry
->url());
765 if (n_tries
== 0) // first attempt
766 request
->hier
.first_conn_start
= current_time
;
768 /* connection timeout */
770 if (serverDestinations
[0]->getPeer()) {
771 ctimeout
= serverDestinations
[0]->getPeer()->connect_timeout
> 0 ?
772 serverDestinations
[0]->getPeer()->connect_timeout
: Config
.Timeout
.peer_connect
;
774 ctimeout
= Config
.Timeout
.connect
;
777 /* calculate total forwarding timeout ??? */
778 int ftimeout
= Config
.Timeout
.forward
- (squid_curtime
- start_t
);
782 if (ftimeout
< ctimeout
)
785 request
->flags
.pinned
= 0;
786 if (serverDestinations
[0]->peerType
== PINNED
) {
787 ConnStateData
*pinned_connection
= request
->pinnedConnection();
788 assert(pinned_connection
);
789 serverDestinations
[0]->fd
= pinned_connection
->validatePinnedConnection(request
, serverDestinations
[0]->getPeer());
790 if (Comm::IsConnOpen(serverDestinations
[0])) {
791 serverConn
= serverDestinations
[0];
792 pinned_connection
->unpinConnection();
794 if (!serverDestinations
[0]->getPeer())
795 serverDestinations
[0]->peerType
= HIER_DIRECT
;
798 request
->flags
.pinned
= 1;
799 if (pinned_connection
->pinnedAuth())
800 request
->flags
.auth
= 1;
801 updateHierarchyInfo();
805 /* Failure. Fall back on next path */
806 debugs(17,2,HERE
<< " Pinned connection " << pinned_connection
<< " not valid. Releasing.");
807 request
->releasePinnedConnection();
808 serverDestinations
.shift();
809 startConnectionOrFail();
813 // Use pconn to avoid opening a new connection.
816 if (serverDestinations
[0]->getPeer()) {
817 host
= serverDestinations
[0]->getPeer()->host
;
818 port
= serverDestinations
[0]->getPeer()->http_port
;
820 host
= request
->GetHost();
821 port
= request
->port
;
823 serverDestinations
[0]->remote
.SetPort(port
);
824 Comm::ConnectionPointer temp
= fwdPconnPool
->pop(serverDestinations
[0], host
, checkRetriable());
826 // if we found an open persistent connection to use. use it.
827 if (temp
!= NULL
&& Comm::IsConnOpen(temp
)) {
829 debugs(17, 3, HERE
<< "reusing pconn " << serverConnection());
832 if (!serverConnection()->getPeer())
835 updateHierarchyInfo();
836 comm_add_close_handler(serverConnection()->fd
, fwdServerClosedWrapper
, this);
838 /* Update server side TOS and Netfilter mark on the connection. */
839 if (Ip::Qos::TheConfig
.isAclTosActive()) {
840 temp
->tos
= GetTosToServer(request
);
841 Ip::Qos::setSockTos(temp
, temp
->tos
);
844 if (Ip::Qos::TheConfig
.isAclNfmarkActive()) {
845 temp
->nfmark
= GetNfmarkToServer(request
);
846 Ip::Qos::setSockNfmark(temp
, temp
->nfmark
);
854 #if URL_CHECKSUM_DEBUG
855 entry
->mem_obj
->checkUrlChecksum();
858 /* Get the server side TOS and Netfilter mark to be set on the connection. */
859 if (Ip::Qos::TheConfig
.isAclTosActive()) {
860 serverDestinations
[0]->tos
= GetTosToServer(request
);
863 if (Ip::Qos::TheConfig
.isAclNfmarkActive()) {
864 serverDestinations
[0]->nfmark
= GetNfmarkToServer(request
);
868 AsyncCall::Pointer call
= commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper
, this));
869 Comm::ConnOpener
*cs
= new Comm::ConnOpener(serverDestinations
[0], call
, ctimeout
);
877 debugs(17, 3, HERE
<< clientConn
<< ": Fetching '" << RequestMethodStr(request
->method
) << " " << entry
->url() << "'");
879 * Assert that server_fd is set. This is to guarantee that fwdState
880 * is attached to something and will be deallocated when server_fd
883 assert(Comm::IsConnOpen(serverConn
));
885 fd_note(serverConnection()->fd
, entry
->url());
887 fd_table
[serverConnection()->fd
].noteUse(fwdPconnPool
);
889 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
890 assert(entry
->ping_status
!= PING_WAITING
);
892 assert(entry
->lock_count
);
894 EBIT_SET(entry
->flags
, ENTRY_DISPATCHED
);
896 netdbPingSite(request
->GetHost());
898 /* Retrieves remote server TOS or MARK value, and stores it as part of the
899 * original client request FD object. It is later used to forward
900 * remote server's TOS/MARK in the response to the client in case of a MISS.
902 if (Ip::Qos::TheConfig
.isHitNfmarkActive()) {
903 if (Comm::IsConnOpen(clientConn
) && Comm::IsConnOpen(serverConnection())) {
904 fde
* clientFde
= &fd_table
[clientConn
->fd
]; // XXX: move the fd_table access into Ip::Qos
905 /* Get the netfilter mark for the connection */
906 Ip::Qos::getNfmarkFromServer(serverConnection(), clientFde
);
911 /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */
912 if (Ip::Qos::TheConfig
.isHitTosActive()) {
913 if (Comm::IsConnOpen(clientConn
)) {
914 fde
* clientFde
= &fd_table
[clientConn
->fd
]; // XXX: move the fd_table access into Ip::Qos
915 /* Get the TOS value for the packet */
916 Ip::Qos::getTosFromServer(serverConnection(), clientFde
);
921 if (serverConnection()->getPeer() != NULL
) {
922 serverConnection()->getPeer()->stats
.fetches
++;
923 request
->peer_login
= serverConnection()->getPeer()->login
;
924 request
->peer_domain
= serverConnection()->getPeer()->domain
;
927 request
->peer_login
= NULL
;
928 request
->peer_domain
= NULL
;
930 switch (request
->protocol
) {
955 fatal_dump("Should never get here");
962 case PROTO_WAIS
: /* Not implemented */
965 debugs(17, 1, "fwdDispatch: Cannot retrieve '" << entry
->url() << "'" );
966 ErrorState
*anErr
= errorCon(ERR_UNSUP_REQ
, HTTP_BAD_REQUEST
, request
);
969 * Force a persistent connection to be closed because
970 * some Netscape browsers have a bug that sends CONNECT
971 * requests as GET's over persistent connections.
973 request
->flags
.proxy_keepalive
= 0;
975 * Set the dont_retry flag because this is not a
976 * transient (network) error; its a bug.
978 flags
.dont_retry
= 1;
979 if (Comm::IsConnOpen(serverConn
)) {
988 * FwdState::reforward
990 * returns TRUE if the transaction SHOULD be re-forwarded to the
991 * next choice in the FwdServers list. This method is called when
992 * server-side communication completes normally, or experiences
993 * some error after receiving the end of HTTP headers.
996 FwdState::reforward()
998 StoreEntry
*e
= entry
;
1000 assert(e
->store_status
== STORE_PENDING
);
1002 #if URL_CHECKSUM_DEBUG
1004 e
->mem_obj
->checkUrlChecksum();
1007 debugs(17, 3, HERE
<< e
->url() << "?" );
1009 if (!EBIT_TEST(e
->flags
, ENTRY_FWD_HDR_WAIT
)) {
1010 debugs(17, 3, HERE
<< "No, ENTRY_FWD_HDR_WAIT isn't set");
1014 if (n_tries
> Config
.forward_max_tries
)
1017 if (origin_tries
> 1)
1020 if (request
->bodyNibbled())
1023 serverDestinations
.shift();
1025 if (serverDestinations
.size() == 0) {
1026 debugs(17, 3, HERE
<< "No alternative forwarding paths left");
1030 s
= e
->getReply()->sline
.status
;
1031 debugs(17, 3, HERE
<< "status " << s
);
1032 return reforwardableStatus(s
);
1036 * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
1037 * on whether this is a validation request. RFC 2616 says that we MUST reply
1038 * with "504 Gateway Timeout" if validation fails and cached reply has
1039 * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
1042 FwdState::makeConnectingError(const err_type type
) const
1044 return errorCon(type
, request
->flags
.need_validation
?
1045 HTTP_GATEWAY_TIMEOUT
: HTTP_SERVICE_UNAVAILABLE
, request
);
1049 fwdStats(StoreEntry
* s
)
1053 storeAppendPrintf(s
, "Status");
1055 for (j
= 0; j
<= MAX_FWD_STATS_IDX
; j
++) {
1056 storeAppendPrintf(s
, "\ttry#%d", j
+ 1);
1059 storeAppendPrintf(s
, "\n");
1061 for (i
= 0; i
<= (int) HTTP_INVALID_HEADER
; i
++) {
1062 if (FwdReplyCodes
[0][i
] == 0)
1065 storeAppendPrintf(s
, "%3d", i
);
1067 for (j
= 0; j
<= MAX_FWD_STATS_IDX
; j
++) {
1068 storeAppendPrintf(s
, "\t%d", FwdReplyCodes
[j
][i
]);
1071 storeAppendPrintf(s
, "\n");
1076 /**** STATIC MEMBER FUNCTIONS *************************************************/
1079 FwdState::reforwardableStatus(http_status s
)
1083 case HTTP_BAD_GATEWAY
:
1085 case HTTP_GATEWAY_TIMEOUT
:
1088 case HTTP_FORBIDDEN
:
1090 case HTTP_INTERNAL_SERVER_ERROR
:
1092 case HTTP_NOT_IMPLEMENTED
:
1094 case HTTP_SERVICE_UNAVAILABLE
:
1095 return Config
.retry
.onerror
;
1105 * Decide where details need to be gathered to correctly describe a persistent connection.
1107 * - the address/port details about this link
1108 * - domain name of server at other end of this link (either peer or requested host)
1111 FwdState::pconnPush(Comm::ConnectionPointer
&conn
, const char *domain
)
1113 if (conn
->getPeer()) {
1114 fwdPconnPool
->push(conn
, conn
->getPeer()->name
);
1116 fwdPconnPool
->push(conn
, domain
);
1121 FwdState::initModule()
1127 else if (NULL
== Config
.Log
.forward
)
1130 logfile
= logfileOpen(Config
.Log
.forward
, 0, 1);
1134 RegisterWithCacheManager();
1138 FwdState::RegisterWithCacheManager(void)
1140 CacheManager::GetInstance()->
1141 registerAction("forward", "Request Forwarding Statistics", fwdStats
, 0, 1);
1145 FwdState::logReplyStatus(int tries
, http_status status
)
1147 if (status
> HTTP_INVALID_HEADER
)
1152 if (tries
> MAX_FWD_STATS_IDX
)
1153 tries
= MAX_FWD_STATS_IDX
;
1155 FwdReplyCodes
[tries
][status
]++;
1158 /** From Comment #5 by Henrik Nordstrom made at
1159 http://www.squid-cache.org/bugs/show_bug.cgi?id=2391 on 2008-09-19
1161 updateHierarchyInfo should be called each time a new path has been
1162 selected or when more information about the path is available (i.e. the
1163 server IP), and when it's called it needs to be given reasonable
1164 arguments describing the now selected path..
1166 It does not matter from a functional perspective if it gets called a few
1167 times more than what is really needed, but calling it too often may
1168 obviously hurt performance.
1170 // updates HierarchyLogEntry, guessing nextHop and its format
1172 FwdState::updateHierarchyInfo()
1176 assert(serverDestinations
.size() > 0);
1180 if (serverConnection()->getPeer()) {
1181 // went to peer, log peer host name
1182 snprintf(nextHop
,256,"%s", serverConnection()->getPeer()->name
);
1184 // went DIRECT, must honor log_ip_on_direct
1185 if (!Config
.onoff
.log_ip_on_direct
)
1186 snprintf(nextHop
,256,"%s",request
->GetHost()); // domain name
1188 serverConnection()->remote
.NtoA(nextHop
, 256);
1191 request
->hier
.peer_local_port
= serverConnection()->local
.GetPort();
1194 hierarchyNote(&request
->hier
, serverConnection()->peerType
, nextHop
);
1198 /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
1202 * Formerly static, but now used by client_side_request.cc
1204 /// Checks for a TOS value to apply depending on the ACL
1206 aclMapTOS(acl_tos
* head
, ACLChecklist
* ch
)
1210 for (l
= head
; l
; l
= l
->next
) {
1211 if (!l
->aclList
|| ch
->matchAclListFast(l
->aclList
))
1218 /// Checks for a netfilter mark value to apply depending on the ACL
1220 aclMapNfmark(acl_nfmark
* head
, ACLChecklist
* ch
)
1224 for (l
= head
; l
; l
= l
->next
) {
1225 if (!l
->aclList
|| ch
->matchAclListFast(l
->aclList
))
1233 getOutgoingAddress(HttpRequest
* request
, Comm::ConnectionPointer conn
)
1235 /* skip if an outgoing address is already set. */
1236 if (!conn
->local
.IsAnyAddr()) return;
1238 // maybe use TPROXY client address
1239 if (request
&& request
->flags
.spoof_client_ip
) {
1240 if (!conn
->getPeer() || !conn
->getPeer()->options
.no_tproxy
) {
1241 #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1242 if (Config
.onoff
.tproxy_uses_indirect_client
)
1243 conn
->local
= request
->indirect_client_addr
;
1246 conn
->local
= request
->client_addr
;
1247 // some flags need setting on the socket to use this address
1248 conn
->flags
|= COMM_DOBIND
;
1249 conn
->flags
|= COMM_TRANSPARENT
;
1252 // else no tproxy today ...
1255 if (!Config
.accessList
.outgoing_address
) {
1256 return; // anything will do.
1259 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1260 ch
.dst_peer
= conn
->getPeer();
1261 ch
.dst_addr
= conn
->remote
;
1263 // TODO use the connection details in ACL.
1264 // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
1267 #if FOLLOW_X_FORWARDED_FOR
1268 if (Config
.onoff
.acl_uses_indirect_client
)
1269 ch
.src_addr
= request
->indirect_client_addr
;
1272 ch
.src_addr
= request
->client_addr
;
1273 ch
.my_addr
= request
->my_addr
;
1277 for (l
= Config
.accessList
.outgoing_address
; l
; l
= l
->next
) {
1279 /* check if the outgoing address is usable to the destination */
1280 if (conn
->remote
.IsIPv4() != l
->addr
.IsIPv4()) continue;
1282 /* check ACLs for this outgoing address */
1283 if (!l
->aclList
|| ch
.matchAclListFast(l
->aclList
)) {
1284 conn
->local
= l
->addr
;
1290 // XXX: convert this to accepting a serverConn and migrate to QosConfig.cc
1292 GetTosToServer(HttpRequest
* request
)
1294 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1297 ch
.src_addr
= request
->client_addr
;
1298 ch
.my_addr
= request
->my_addr
;
1301 return aclMapTOS(Ip::Qos::TheConfig
.tosToServer
, &ch
);
1304 // XXX: convert this to accepting a serverConn and migrate to QosConfig.cc
1306 GetNfmarkToServer(HttpRequest
* request
)
1308 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1311 ch
.src_addr
= request
->client_addr
;
1312 ch
.my_addr
= request
->my_addr
;
1315 return aclMapNfmark(Ip::Qos::TheConfig
.nfmarkToServer
, &ch
);
1319 /**** WIP_FWD_LOG *************************************************************/
1325 if (NULL
== logfile
)
1328 logfileClose(logfile
);
1337 logfileRotate(logfile
);
1343 if (NULL
== logfile
)
1346 logfilePrintf(logfile
, "%9d.%03d %03d %s %s\n",
1347 (int) current_time
.tv_sec
,
1348 (int) current_time
.tv_usec
/ 1000,
1350 RequestMethodStr(request
->method
),
1351 request
->canonical
);
1355 FwdState::status(http_status s
)