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.
36 #include "acl/FilledChecklist.h"
37 #include "acl/Gadgets.h"
38 #include "CacheManager.h"
40 #include "errorpage.h"
42 #include "hier_code.h"
43 #include "HttpReply.h"
44 #include "HttpRequest.h"
45 #include "MemObject.h"
47 #include "SquidTime.h"
49 #include "icmp/net_db.h"
50 #include "ip/Intercept.h"
52 static PSC fwdStartCompleteWrapper
;
53 static PF fwdServerClosedWrapper
;
55 static PF fwdNegotiateSSLWrapper
;
57 static PF fwdConnectTimeoutWrapper
;
58 static EVH fwdConnectStartWrapper
;
59 static CNCB fwdConnectDoneWrapper
;
62 static void fwdServerFree(FwdServer
* fs
);
64 #define MAX_FWD_STATS_IDX 9
65 static int FwdReplyCodes
[MAX_FWD_STATS_IDX
+ 1][HTTP_INVALID_HEADER
+ 1];
68 static void fwdLog(FwdState
* fwdState
);
69 static Logfile
*logfile
= NULL
;
72 static PconnPool
*fwdPconnPool
= new PconnPool("server-side");
73 CBDATA_CLASS_INIT(FwdState
);
76 FwdState::abort(void* d
)
78 FwdState
* fwd
= (FwdState
*)d
;
79 Pointer tmp
= fwd
; // Grab a temporary pointer to keep the object alive during our scope.
81 if (fwd
->server_fd
>= 0) {
82 comm_close(fwd
->server_fd
);
89 /**** PUBLIC INTERFACE ********************************************************/
91 FwdState::FwdState(int fd
, StoreEntry
* e
, HttpRequest
* r
)
96 request
= HTTPMSGLOCK(r
);
97 start_t
= squid_curtime
;
100 EBIT_SET(e
->flags
, ENTRY_FWD_HDR_WAIT
);
103 // Called once, right after object creation, when it is safe to set self
104 void FwdState::start(Pointer aSelf
)
106 // Protect ourselves from being destroyed when the only Server pointing
107 // to us is gone (while we expect to talk to more Servers later).
108 // Once we set self, we are responsible for clearing it when we do not
109 // expect to talk to any servers.
110 self
= aSelf
; // refcounted
112 // We hope that either the store entry aborts or peer is selected.
113 // Otherwise we are going to leak our object.
115 entry
->registerAbort(FwdState::abort
, this);
116 peerSelect(request
, entry
, fwdStartCompleteWrapper
, this);
118 // TODO: set self _after_ the peer is selected because we do not need
119 // self until we start talking to some Server.
123 FwdState::completed()
125 if (flags
.forward_completed
== 1) {
126 debugs(17, 1, HERE
<< "FwdState::completed called on a completed request! Bad!");
130 flags
.forward_completed
= 1;
132 #if URL_CHECKSUM_DEBUG
134 entry
->mem_obj
->checkUrlChecksum();
141 if (entry
->store_status
== STORE_PENDING
) {
142 if (entry
->isEmpty()) {
144 errorAppendEntry(entry
, err
);
147 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
149 entry
->releaseRequest();
153 if (storePendingNClients(entry
) > 0)
154 assert(!EBIT_TEST(entry
->flags
, ENTRY_FWD_HDR_WAIT
));
158 FwdState::~FwdState()
160 debugs(17, 3, HERE
<< "FwdState destructor starting");
162 if (! flags
.forward_completed
)
165 serversFree(&servers
);
167 HTTPMSGUNLOCK(request
);
172 entry
->unregisterAbort();
182 comm_remove_close_handler(fd
, fwdServerClosedWrapper
, this);
183 debugs(17, 3, "fwdStateFree: closing FD " << fd
);
187 debugs(17, 3, HERE
<< "FwdState destructor done");
191 * This is the entry point for client-side to start forwarding
192 * a transaction. It is a static method that may or may not
193 * allocate a FwdState.
196 FwdState::fwdStart(int client_fd
, StoreEntry
*entry
, HttpRequest
*request
)
199 * client_addr == no_addr indicates this is an "internal" request
200 * from peer_digest.c, asn.c, netdb.c, etc and should always
201 * be allowed. yuck, I know.
204 if ( Config
.accessList
.miss
&& !request
->client_addr
.IsNoAddr() &&
205 request
->protocol
!= PROTO_INTERNAL
&& request
->protocol
!= PROTO_CACHEOBJ
) {
207 * Check if this host is allowed to fetch MISSES from us (miss_access)
209 ACLFilledChecklist
ch(Config
.accessList
.miss
, request
, NULL
);
210 ch
.src_addr
= request
->client_addr
;
211 ch
.my_addr
= request
->my_addr
;
212 int answer
= ch
.fastCheck();
216 page_id
= aclGetDenyInfoPage(&Config
.denyInfoList
, AclMatchedName
, 1);
218 if (page_id
== ERR_NONE
)
219 page_id
= ERR_FORWARDING_DENIED
;
221 ErrorState
*anErr
= errorCon(page_id
, HTTP_FORBIDDEN
, request
);
223 errorAppendEntry(entry
, anErr
); // frees anErr
229 debugs(17, 3, "FwdState::start() '" << entry
->url() << "'");
231 * This seems like an odd place to bind mem_obj and request.
232 * Might want to assert that request is NULL at this point
234 entry
->mem_obj
->request
= HTTPMSGLOCK(request
);
235 #if URL_CHECKSUM_DEBUG
237 entry
->mem_obj
->checkUrlChecksum();
242 ErrorState
*anErr
= errorCon(ERR_SHUTTING_DOWN
, HTTP_SERVICE_UNAVAILABLE
, request
);
243 errorAppendEntry(entry
, anErr
); // frees anErr
247 switch (request
->protocol
) {
250 internalStart(request
, entry
);
254 CacheManager::GetInstance()->Start(client_fd
, request
, entry
);
258 urnStart(request
, entry
);
262 FwdState::Pointer fwd
= new FwdState(client_fd
, entry
, request
);
264 /* If we need to transparently proxy the request
265 * then we need the client source protocol, address and port */
266 if (request
->flags
.spoof_client_ip
) {
267 fwd
->src
= request
->client_addr
;
278 FwdState::fail(ErrorState
* errorState
)
280 debugs(17, 3, HERE
<< err_type_str
[errorState
->type
] << " \"" << httpStatusString(errorState
->httpStatus
) << "\"\n\t" << entry
->url() );
287 if (!errorState
->request
)
288 errorState
->request
= HTTPMSGLOCK(request
);
292 * Frees fwdState without closing FD or generating an abort
295 FwdState::unregister(int fd
)
297 debugs(17, 3, HERE
<< entry
->url() );
298 assert(fd
== server_fd
);
300 comm_remove_close_handler(fd
, fwdServerClosedWrapper
, this);
305 * server-side modules call fwdComplete() when they are done
306 * downloading an object. Then, we either 1) re-forward the
307 * request somewhere else if needed, or 2) call storeComplete()
313 StoreEntry
*e
= entry
;
314 assert(entry
->store_status
== STORE_PENDING
);
315 debugs(17, 3, HERE
<< e
->url() << "\n\tstatus " << entry
->getReply()->sline
.status
);
316 #if URL_CHECKSUM_DEBUG
318 entry
->mem_obj
->checkUrlChecksum();
321 logReplyStatus(n_tries
, entry
->getReply()->sline
.status
);
324 debugs(17, 3, "fwdComplete: re-forwarding " << entry
->getReply()->sline
.status
<< " " << e
->url());
327 unregister(server_fd
);
331 startComplete(servers
);
333 debugs(17, 3, "fwdComplete: server FD " << server_fd
<< " not re-forwarding status " << entry
->getReply()->sline
.status
);
334 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
340 self
= NULL
; // refcounted
345 /**** CALLBACK WRAPPERS ************************************************************/
348 fwdStartCompleteWrapper(FwdServer
* servers
, void *data
)
350 FwdState
*fwd
= (FwdState
*) data
;
351 fwd
->startComplete(servers
);
355 fwdServerClosedWrapper(int fd
, void *data
)
357 FwdState
*fwd
= (FwdState
*) data
;
358 fwd
->serverClosed(fd
);
362 fwdConnectStartWrapper(void *data
)
364 FwdState
*fwd
= (FwdState
*) data
;
370 fwdNegotiateSSLWrapper(int fd
, void *data
)
372 FwdState
*fwd
= (FwdState
*) data
;
373 fwd
->negotiateSSL(fd
);
379 fwdConnectDoneWrapper(int server_fd
, const DnsLookupDetails
&dns
, comm_err_t status
, int xerrno
, void *data
)
381 FwdState
*fwd
= (FwdState
*) data
;
382 fwd
->connectDone(server_fd
, dns
, status
, xerrno
);
386 fwdConnectTimeoutWrapper(int fd
, void *data
)
388 FwdState
*fwd
= (FwdState
*) data
;
389 fwd
->connectTimeout(fd
);
393 * Accounts for closed persistent connections
396 fwdPeerClosed(int fd
, void *data
)
398 peer
*p
= (peer
*)data
;
399 p
->stats
.conn_open
--;
402 /**** PRIVATE *****************************************************************/
405 * FwdState::checkRetry
407 * Return TRUE if the request SHOULD be retried. This method is
408 * called when the HTTP connection fails, or when the connection
409 * is closed before server-side read the end of HTTP headers.
412 FwdState::checkRetry()
417 if (entry
->store_status
!= STORE_PENDING
)
420 if (!entry
->isEmpty())
426 if (origin_tries
> 2)
429 if (squid_curtime
- start_t
> Config
.Timeout
.forward
)
432 if (flags
.dont_retry
)
435 if (!checkRetriable())
438 if (request
->bodyNibbled())
445 * FwdState::checkRetriable
447 * Return TRUE if this is the kind of request that can be retried
448 * after a failure. If the request is not retriable then we don't
449 * want to risk sending it on a persistent connection. Instead we'll
450 * force it to go on a new HTTP connection.
453 FwdState::checkRetriable()
455 /* If there is a request body then Squid can only try once
456 * even if the method is indempotent
459 if (request
->body_pipe
!= NULL
)
462 /* RFC2616 9.1 Safe and Idempotent Methods */
463 switch (request
->method
.id()) {
464 /* 9.1.1 Safe Methods */
469 /* 9.1.2 Idempotent Methods */
488 FwdState::serverClosed(int fd
)
490 debugs(17, 2, "fwdServerClosed: FD " << fd
<< " " << entry
->url());
491 assert(server_fd
== fd
);
498 FwdState::retryOrBail()
500 if (!self
) { // we have aborted before the server called us back
501 debugs(17, 5, HERE
<< "not retrying because of earlier abort");
502 // we will be destroyed when the server clears its Pointer to us
507 int originserver
= (servers
->_peer
== NULL
);
508 debugs(17, 3, "fwdServerClosed: re-forwarding (" << n_tries
<< " tries, " << (squid_curtime
- start_t
) << " secs)");
511 /* use next, or cycle if origin server isn't last */
512 FwdServer
*fs
= servers
;
513 FwdServer
**T
, *T2
= NULL
;
516 for (T
= &servers
; *T
; T2
= *T
, T
= &(*T
)->next
);
517 if (T2
&& T2
->_peer
) {
522 /* Use next. The last "direct" entry is retried multiple times */
529 /* Ditch error page if it was created before.
530 * A new one will be created if there's another problem */
533 /* use eventAdd to break potential call sequence loops and to slow things down a little */
534 eventAdd("fwdConnectStart", fwdConnectStartWrapper
, this, originserver
? 0.05 : 0.005, 0);
539 if (!err
&& shutting_down
) {
540 errorCon(ERR_SHUTTING_DOWN
, HTTP_SERVICE_UNAVAILABLE
, request
);
543 self
= NULL
; // refcounted
546 // called by the server that failed after calling unregister()
548 FwdState::handleUnregisteredServerEnd()
550 debugs(17, 2, "handleUnregisteredServerEnd: self=" << self
<<
551 " err=" << err
<< ' ' << entry
->url());
552 assert(server_fd
< 0);
558 FwdState::negotiateSSL(int fd
)
560 FwdServer
*fs
= servers
;
561 SSL
*ssl
= fd_table
[fd
].ssl
;
564 if ((ret
= SSL_connect(ssl
)) <= 0) {
565 int ssl_error
= SSL_get_error(ssl
, ret
);
569 case SSL_ERROR_WANT_READ
:
570 commSetSelect(fd
, COMM_SELECT_READ
, fwdNegotiateSSLWrapper
, this, 0);
573 case SSL_ERROR_WANT_WRITE
:
574 commSetSelect(fd
, COMM_SELECT_WRITE
, fwdNegotiateSSLWrapper
, this, 0);
578 debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd
<<
579 ": " << ERR_error_string(ERR_get_error(), NULL
) << " (" << ssl_error
<<
580 "/" << ret
<< "/" << errno
<< ")");
581 ErrorState
*anErr
= errorCon(ERR_SECURE_CONNECT_FAIL
, HTTP_SERVICE_UNAVAILABLE
, request
);
584 anErr
->xerrno
= EPROTO
;
587 anErr
->xerrno
= EACCES
;
593 peerConnectFailed(fs
->_peer
);
594 fs
->_peer
->stats
.conn_open
--;
602 if (fs
->_peer
&& !SSL_session_reused(ssl
)) {
603 if (fs
->_peer
->sslSession
)
604 SSL_SESSION_free(fs
->_peer
->sslSession
);
606 fs
->_peer
->sslSession
= SSL_get1_session(ssl
);
613 FwdState::initiateSSL()
615 FwdServer
*fs
= servers
;
618 SSL_CTX
*sslContext
= NULL
;
619 peer
*peer
= fs
->_peer
;
622 assert(peer
->use_ssl
);
623 sslContext
= peer
->sslContext
;
625 sslContext
= Config
.ssl_client
.sslContext
;
630 if ((ssl
= SSL_new(sslContext
)) == NULL
) {
631 debugs(83, 1, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL
) );
632 ErrorState
*anErr
= errorCon(ERR_SOCKET_FAILURE
, HTTP_INTERNAL_SERVER_ERROR
, request
);
633 anErr
->xerrno
= errno
;
635 self
= NULL
; // refcounted
643 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->ssldomain
);
648 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->name
);
653 SSL_set_ex_data(ssl
, ssl_ex_index_server
, peer
->host
);
655 if (peer
->sslSession
)
656 SSL_set_session(ssl
, peer
->sslSession
);
659 SSL_set_ex_data(ssl
, ssl_ex_index_server
, (void*)request
->GetHost());
662 // Create the ACL check list now, while we have access to more info.
663 // The list is used in ssl_verify_cb() and is freed in ssl_free().
664 if (acl_access
*acl
= Config
.ssl_client
.cert_error
) {
665 ACLFilledChecklist
*check
= new ACLFilledChecklist(acl
, request
, dash_str
);
667 SSL_set_ex_data(ssl
, ssl_ex_index_cert_error_check
, check
);
670 fd_table
[fd
].ssl
= ssl
;
671 fd_table
[fd
].read_method
= &ssl_read_method
;
672 fd_table
[fd
].write_method
= &ssl_write_method
;
679 FwdState::connectDone(int aServerFD
, const DnsLookupDetails
&dns
, comm_err_t status
, int xerrno
)
681 FwdServer
*fs
= servers
;
682 assert(server_fd
== aServerFD
);
684 request
->recordLookup(dns
);
686 if (Config
.onoff
.log_ip_on_direct
&& status
!= COMM_ERR_DNS
&& fs
->code
== HIER_DIRECT
)
687 updateHierarchyInfo();
689 if (status
== COMM_ERR_DNS
) {
691 * Only set the dont_retry flag if the DNS lookup fails on
692 * a direct connection. If DNS lookup fails when trying
693 * a neighbor cache, we may want to retry another option.
696 if (NULL
== fs
->_peer
)
697 flags
.dont_retry
= 1;
699 debugs(17, 4, "fwdConnectDone: Unknown host: " << request
->GetHost());
701 ErrorState
*anErr
= errorCon(ERR_DNS_FAIL
, HTTP_SERVICE_UNAVAILABLE
, request
);
703 anErr
->dnsError
= dns
.error
;
707 comm_close(server_fd
);
708 } else if (status
!= COMM_OK
) {
710 ErrorState
*anErr
= errorCon(ERR_CONNECT_FAIL
, HTTP_SERVICE_UNAVAILABLE
, request
);
711 anErr
->xerrno
= xerrno
;
716 peerConnectFailed(fs
->_peer
);
718 comm_close(server_fd
);
720 debugs(17, 3, "fwdConnectDone: FD " << server_fd
<< ": '" << entry
->url() << "'" );
723 peerConnectSucceded(fs
->_peer
);
727 if ((fs
->_peer
&& fs
->_peer
->use_ssl
) ||
728 (!fs
->_peer
&& request
->protocol
== PROTO_HTTPS
)) {
739 FwdState::connectTimeout(int fd
)
741 FwdServer
*fs
= servers
;
743 debugs(17, 2, "fwdConnectTimeout: FD " << fd
<< ": '" << entry
->url() << "'" );
744 assert(fd
== server_fd
);
746 if (Config
.onoff
.log_ip_on_direct
&& fs
->code
== HIER_DIRECT
&& fd_table
[fd
].ipaddr
[0])
747 updateHierarchyInfo();
749 if (entry
->isEmpty()) {
750 ErrorState
*anErr
= errorCon(ERR_CONNECT_FAIL
, HTTP_GATEWAY_TIMEOUT
, request
);
751 anErr
->xerrno
= ETIMEDOUT
;
754 * This marks the peer DOWN ...
759 peerConnectFailed(servers
->_peer
);
766 FwdState::connectStart()
768 const char *url
= entry
->url();
770 FwdServer
*fs
= servers
;
774 int ftimeout
= Config
.Timeout
.forward
- (squid_curtime
- start_t
);
776 Ip::Address outgoing
;
778 Ip::Address client_addr
;
780 assert(server_fd
== -1);
781 debugs(17, 3, "fwdConnectStart: " << url
);
783 if (n_tries
== 0) // first attempt
784 request
->hier
.first_conn_start
= current_time
;
787 ctimeout
= fs
->_peer
->connect_timeout
> 0 ? fs
->_peer
->connect_timeout
788 : Config
.Timeout
.peer_connect
;
790 ctimeout
= Config
.Timeout
.connect
;
793 if (request
->flags
.spoof_client_ip
) {
794 if (!fs
->_peer
|| !fs
->_peer
->options
.no_tproxy
)
795 client_addr
= request
->client_addr
;
796 // else no tproxy today ...
802 if (ftimeout
< ctimeout
)
806 request
->flags
.pinned
= 0;
807 if (fs
->code
== PINNED
) {
808 ConnStateData
*pinned_connection
= request
->pinnedConnection();
809 assert(pinned_connection
);
810 fd
= pinned_connection
->validatePinnedConnection(request
, fs
->_peer
);
812 pinned_connection
->unpinConnection();
815 fs
->code
= HIER_DIRECT
;
819 request
->flags
.pinned
= 1;
820 if (pinned_connection
->pinnedAuth())
821 request
->flags
.auth
= 1;
822 comm_add_close_handler(fd
, fwdServerClosedWrapper
, this);
823 updateHierarchyInfo();
824 connectDone(fd
, DnsLookupDetails(), COMM_OK
, 0);
827 /* Failure. Fall back on next path */
828 debugs(17,2,HERE
<< " Pinned connection " << pinned_connection
<< " not valid. Releasing.");
829 request
->releasePinnedConnection();
837 host
= fs
->_peer
->host
;
838 port
= fs
->_peer
->http_port
;
839 fd
= fwdPconnPool
->pop(fs
->_peer
->name
, fs
->_peer
->http_port
, request
->GetHost(), client_addr
, checkRetriable());
841 host
= request
->GetHost();
842 port
= request
->port
;
843 fd
= fwdPconnPool
->pop(host
, port
, NULL
, client_addr
, checkRetriable());
846 debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd
);
853 updateHierarchyInfo();
855 comm_add_close_handler(fd
, fwdServerClosedWrapper
, this);
862 #if URL_CHECKSUM_DEBUG
863 entry
->mem_obj
->checkUrlChecksum();
866 outgoing
= getOutgoingAddr(request
, fs
->_peer
);
868 tos
= getOutgoingTOS(request
);
870 debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing
<< ", tos " << tos
);
872 int commFlags
= COMM_NONBLOCKING
;
873 if (request
->flags
.spoof_client_ip
) {
874 if (!fs
->_peer
|| !fs
->_peer
->options
.no_tproxy
)
875 commFlags
|= COMM_TRANSPARENT
;
876 // else no tproxy today ...
879 fd
= comm_openex(SOCK_STREAM
, IPPROTO_TCP
, outgoing
, commFlags
, tos
, url
);
881 debugs(17, 3, "fwdConnectStart: got TCP FD " << fd
);
884 debugs(50, 4, "fwdConnectStart: " << xstrerror());
885 ErrorState
*anErr
= errorCon(ERR_SOCKET_FAILURE
, HTTP_INTERNAL_SERVER_ERROR
, request
);
886 anErr
->xerrno
= errno
;
888 self
= NULL
; // refcounted
899 * stats.conn_open is used to account for the number of
900 * connections that we have open to the peer, so we can limit
901 * based on the max-conn option. We need to increment here,
902 * even if the connection may fail.
906 fs
->_peer
->stats
.conn_open
++;
907 comm_add_close_handler(fd
, fwdPeerClosed
, fs
->_peer
);
910 comm_add_close_handler(fd
, fwdServerClosedWrapper
, this);
912 commSetTimeout(fd
, ctimeout
, fwdConnectTimeoutWrapper
, this);
914 updateHierarchyInfo();
915 commConnectStart(fd
, host
, port
, fwdConnectDoneWrapper
, this);
919 FwdState::startComplete(FwdServer
* theServers
)
921 debugs(17, 3, "fwdStartComplete: " << entry
->url() );
923 if (theServers
!= NULL
) {
924 servers
= theServers
;
932 FwdState::startFail()
934 debugs(17, 3, "fwdStartFail: " << entry
->url() );
935 ErrorState
*anErr
= errorCon(ERR_CANNOT_FORWARD
, HTTP_SERVICE_UNAVAILABLE
, request
);
936 anErr
->xerrno
= errno
;
938 self
= NULL
; // refcounted
945 debugs(17, 3, "fwdDispatch: FD " << client_fd
<< ": Fetching '" << RequestMethodStr(request
->method
) << " " << entry
->url() << "'" );
947 * Assert that server_fd is set. This is to guarantee that fwdState
948 * is attached to something and will be deallocated when server_fd
951 assert(server_fd
> -1);
953 fd_note(server_fd
, entry
->url());
955 fd_table
[server_fd
].noteUse(fwdPconnPool
);
957 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
958 assert(entry
->ping_status
!= PING_WAITING
);
960 assert(entry
->lock_count
);
962 EBIT_SET(entry
->flags
, ENTRY_DISPATCHED
);
964 netdbPingSite(request
->GetHost());
966 #if USE_ZPH_QOS && defined(_SQUID_LINUX_)
967 /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
969 /* Retrieves remote server TOS value, and stores it as part of the
970 * original client request FD object. It is later used to forward
971 * remote server's TOS in the response to the client in case of a MISS.
973 fde
* clientFde
= &fd_table
[client_fd
];
976 int tos_len
= sizeof(tos
);
977 clientFde
->upstreamTOS
= 0;
978 if (setsockopt(server_fd
,SOL_IP
,IP_RECVTOS
,&tos
,tos_len
)==0) {
979 unsigned char buf
[512];
981 if (getsockopt(server_fd
,SOL_IP
,IP_PKTOPTIONS
,buf
,(socklen_t
*)&len
) == 0) {
982 /* Parse the PKTOPTIONS structure to locate the TOS data message
983 * prepared in the kernel by the ZPH incoming TCP TOS preserving
986 unsigned char * pbuf
= buf
;
987 while (pbuf
-buf
< len
) {
988 struct cmsghdr
*o
= (struct cmsghdr
*)pbuf
;
992 if (o
->cmsg_level
== SOL_IP
&& o
->cmsg_type
== IP_TOS
) {
993 int *tmp
= (int*)CMSG_DATA(o
);
994 clientFde
->upstreamTOS
= (unsigned char)*tmp
;
997 pbuf
+= CMSG_LEN(o
->cmsg_len
);
1000 debugs(33, 1, "ZPH: error in getsockopt(IP_PKTOPTIONS) on FD "<<server_fd
<<" "<<xstrerror());
1003 debugs(33, 1, "ZPH: error in setsockopt(IP_RECVTOS) on FD "<<server_fd
<<" "<<xstrerror());
1008 if (servers
&& (p
= servers
->_peer
)) {
1010 request
->peer_login
= p
->login
;
1011 request
->peer_domain
= p
->domain
;
1014 request
->peer_login
= NULL
;
1015 request
->peer_domain
= NULL
;
1017 switch (request
->protocol
) {
1037 case PROTO_CACHEOBJ
:
1039 case PROTO_INTERNAL
:
1042 fatal_dump("Should never get here");
1049 case PROTO_WAIS
: /* Not implemented */
1052 debugs(17, 1, "fwdDispatch: Cannot retrieve '" << entry
->url() << "'" );
1053 ErrorState
*anErr
= errorCon(ERR_UNSUP_REQ
, HTTP_BAD_REQUEST
, request
);
1056 * Force a persistent connection to be closed because
1057 * some Netscape browsers have a bug that sends CONNECT
1058 * requests as GET's over persistent connections.
1060 request
->flags
.proxy_keepalive
= 0;
1062 * Set the dont_retry flag becuase this is not a
1063 * transient (network) error; its a bug.
1065 flags
.dont_retry
= 1;
1066 comm_close(server_fd
);
1073 * FwdState::reforward
1075 * returns TRUE if the transaction SHOULD be re-forwarded to the
1076 * next choice in the FwdServers list. This method is called when
1077 * server-side communication completes normally, or experiences
1078 * some error after receiving the end of HTTP headers.
1081 FwdState::reforward()
1083 StoreEntry
*e
= entry
;
1084 FwdServer
*fs
= servers
;
1086 assert(e
->store_status
== STORE_PENDING
);
1088 #if URL_CHECKSUM_DEBUG
1090 e
->mem_obj
->checkUrlChecksum();
1093 debugs(17, 3, "fwdReforward: " << e
->url() << "?" );
1095 if (!EBIT_TEST(e
->flags
, ENTRY_FWD_HDR_WAIT
)) {
1096 debugs(17, 3, "fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set");
1100 if (n_tries
> Config
.forward_max_tries
)
1103 if (origin_tries
> 1)
1106 if (request
->bodyNibbled())
1115 if (servers
== NULL
) {
1116 debugs(17, 3, "fwdReforward: No forward-servers left");
1120 s
= e
->getReply()->sline
.status
;
1121 debugs(17, 3, "fwdReforward: status " << s
);
1122 return reforwardableStatus(s
);
1126 fwdStats(StoreEntry
* s
)
1130 storeAppendPrintf(s
, "Status");
1132 for (j
= 0; j
<= MAX_FWD_STATS_IDX
; j
++) {
1133 storeAppendPrintf(s
, "\ttry#%d", j
+ 1);
1136 storeAppendPrintf(s
, "\n");
1138 for (i
= 0; i
<= (int) HTTP_INVALID_HEADER
; i
++) {
1139 if (FwdReplyCodes
[0][i
] == 0)
1142 storeAppendPrintf(s
, "%3d", i
);
1144 for (j
= 0; j
<= MAX_FWD_STATS_IDX
; j
++) {
1145 storeAppendPrintf(s
, "\t%d", FwdReplyCodes
[j
][i
]);
1148 storeAppendPrintf(s
, "\n");
1153 /**** STATIC MEMBER FUNCTIONS *************************************************/
1156 FwdState::reforwardableStatus(http_status s
)
1160 case HTTP_BAD_GATEWAY
:
1162 case HTTP_GATEWAY_TIMEOUT
:
1165 case HTTP_FORBIDDEN
:
1167 case HTTP_INTERNAL_SERVER_ERROR
:
1169 case HTTP_NOT_IMPLEMENTED
:
1171 case HTTP_SERVICE_UNAVAILABLE
:
1172 return Config
.retry
.onerror
;
1182 * Decide where details need to be gathered to correctly describe a persistent connection.
1184 * - host name of server at other end of this link (either peer or requested host)
1185 * - port to which we connected the other end of this link (for peer or request)
1186 * - domain for which the connection is supposed to be used
1187 * - address of the client for which we made the connection
1190 FwdState::pconnPush(int fd
, const peer
*_peer
, const HttpRequest
*req
, const char *domain
, Ip::Address
&client_addr
)
1193 fwdPconnPool
->push(fd
, _peer
->name
, _peer
->http_port
, domain
, client_addr
);
1195 /* small performance improvement, using NULL for domain instead of listing it twice */
1196 /* although this will leave a gap open for url-rewritten domains to share a link */
1197 fwdPconnPool
->push(fd
, req
->GetHost(), req
->port
, NULL
, client_addr
);
1202 FwdState::initModule()
1204 memDataInit(MEM_FWD_SERVER
, "FwdServer", sizeof(FwdServer
), 0);
1210 else if (NULL
== Config
.Log
.forward
)
1213 logfile
= logfileOpen(Config
.Log
.forward
, 0, 1);
1217 RegisterWithCacheManager();
1221 FwdState::RegisterWithCacheManager(void)
1223 CacheManager::GetInstance()->
1224 registerAction("forward", "Request Forwarding Statistics", fwdStats
, 0, 1);
1228 FwdState::logReplyStatus(int tries
, http_status status
)
1230 if (status
> HTTP_INVALID_HEADER
)
1237 if (tries
> MAX_FWD_STATS_IDX
)
1238 tries
= MAX_FWD_STATS_IDX
;
1240 FwdReplyCodes
[tries
][status
]++;
1244 FwdState::serversFree(FwdServer
** FSVR
)
1248 while ((fs
= *FSVR
)) {
1254 /** From Comment #5 by Henrik Nordstrom made at
1255 http://www.squid-cache.org/bugs/show_bug.cgi?id=2391 on 2008-09-19
1257 updateHierarchyInfo should be called each time a new path has been
1258 selected or when more information about the path is available (i.e. the
1259 server IP), and when it's called it needs to be given reasonable
1260 arguments describing the now selected path..
1262 It does not matter from a functional perspective if it gets called a few
1263 times more than what is really needed, but calling it too often may
1264 obviously hurt performance.
1266 // updates HierarchyLogEntry, guessing nextHop and its format
1268 FwdState::updateHierarchyInfo()
1272 FwdServer
*fs
= servers
;
1275 const char *nextHop
= NULL
;
1278 // went to peer, log peer host name
1279 nextHop
= fs
->_peer
->name
;
1281 // went DIRECT, must honor log_ip_on_direct
1283 // XXX: or should we use request->host_addr here? how?
1284 assert(server_fd
>= 0);
1285 nextHop
= fd_table
[server_fd
].ipaddr
;
1286 if (!Config
.onoff
.log_ip_on_direct
|| !nextHop
[0])
1287 nextHop
= request
->GetHost(); // domain name
1291 hierarchyNote(&request
->hier
, fs
->code
, nextHop
);
1295 /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
1298 fwdServerFree(FwdServer
* fs
)
1300 cbdataReferenceDone(fs
->_peer
);
1301 memFree(fs
, MEM_FWD_SERVER
);
1305 aclMapAddr(acl_address
* head
, ACLChecklist
* ch
)
1311 for (l
= head
; l
; l
= l
->next
) {
1312 if (!l
->aclList
|| ch
->matchAclListFast(l
->aclList
))
1322 * Formerly static, but now used by client_side_request.cc
1325 aclMapTOS(acl_tos
* head
, ACLChecklist
* ch
)
1329 for (l
= head
; l
; l
= l
->next
) {
1330 if (!l
->aclList
|| ch
->matchAclListFast(l
->aclList
))
1338 getOutgoingAddr(HttpRequest
* request
, struct peer
*dst_peer
)
1340 if (request
&& request
->flags
.spoof_client_ip
) {
1341 if (!dst_peer
|| !dst_peer
->options
.no_tproxy
) {
1342 #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
1343 if (Config
.onoff
.tproxy_uses_indirect_client
)
1344 return request
->indirect_client_addr
;
1347 return request
->client_addr
;
1349 // else no tproxy today ...
1352 if (!Config
.accessList
.outgoing_address
) {
1353 return Ip::Address(); // anything will do.
1356 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1357 ch
.dst_peer
= dst_peer
;
1360 #if FOLLOW_X_FORWARDED_FOR
1361 if (Config
.onoff
.acl_uses_indirect_client
)
1362 ch
.src_addr
= request
->indirect_client_addr
;
1365 ch
.src_addr
= request
->client_addr
;
1366 ch
.my_addr
= request
->my_addr
;
1369 return aclMapAddr(Config
.accessList
.outgoing_address
, &ch
);
1373 getOutgoingTOS(HttpRequest
* request
)
1375 ACLFilledChecklist
ch(NULL
, request
, NULL
);
1378 ch
.src_addr
= request
->client_addr
;
1379 ch
.my_addr
= request
->my_addr
;
1382 return aclMapTOS(Config
.accessList
.outgoing_tos
, &ch
);
1386 /**** WIP_FWD_LOG *************************************************************/
1392 if (NULL
== logfile
)
1395 logfileClose(logfile
);
1404 logfileRotate(logfile
);
1410 if (NULL
== logfile
)
1413 logfilePrintf(logfile
, "%9d.%03d %03d %s %s\n",
1414 (int) current_time
.tv_sec
,
1415 (int) current_time
.tv_usec
/ 1000,
1417 RequestMethodStr(request
->method
),
1418 request
->canonical
);
1422 FwdState::status(http_status s
)