/*
- * $Id: forward.cc,v 1.132 2005/12/08 20:08:46 wessels Exp $
+ * $Id: forward.cc,v 1.133 2006/01/03 17:22:31 wessels Exp $
*
* DEBUG: section 17 Request Forwarding
* AUTHOR: Duane Wessels
#include "squid.h"
+#include "forward.h"
#include "Store.h"
#include "HttpRequest.h"
#include "fde.h"
#include "HttpReply.h"
#include "pconn.h"
-static PSC fwdStartComplete;
-static void fwdDispatch(FwdState *);
-static void fwdConnectStart(void *); /* should be same as EVH */
-static void fwdStateFree(FwdState * fwdState);
-static PF fwdConnectTimeout;
-static PF fwdServerClosed;
-static PF fwdPeerClosed;
-static CNCB fwdConnectDone;
-static int fwdCheckRetry(FwdState * fwdState);
-static int fwdReforward(FwdState *);
-static void fwdStartFail(FwdState *);
-static void fwdLogReplyStatus(int tries, http_status status);
+static PSC fwdStartCompleteWrapper;
+static PF fwdServerClosedWrapper;
+#if USE_SSL
+static PF fwdNegotiateSSLWrapper;
+#endif
+static PF fwdConnectTimeoutWrapper;
+static EVH fwdConnectStartWrapper;
+static CNCB fwdConnectDoneWrapper;
+
static OBJH fwdStats;
-static STABH fwdAbort;
-static peer *fwdStateServerPeer(FwdState *);
+static void fwdServerFree(FwdServer * fs);
#define MAX_FWD_STATS_IDX 9
static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
#endif
static PconnPool *fwdPconnPool = new PconnPool("server-side");
+CBDATA_CLASS_INIT(FwdState);
-static peer *
-fwdStateServerPeer(FwdState * fwdState)
-{
- if (NULL == fwdState)
- return NULL;
-
- if (NULL == fwdState->servers)
- return NULL;
-
- return fwdState->servers->_peer;
-}
+/**** PUBLIC INTERFACE ********************************************************/
-static void
-fwdServerFree(FwdServer * fs)
+FwdState::FwdState(int fd, StoreEntry * e, HttpRequest * r)
{
- cbdataReferenceDone(fs->_peer);
- memFree(fs, MEM_FWD_SERVER);
+ entry = e;
+ client_fd = fd;
+ server_fd = -1;
+ request = requestLink(r);
+ start_t = squid_curtime;
+ storeLockObject(e);
+ EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
+ self = this; // refcounted
}
-static void
-fwdStateFree(FwdState * fwdState)
+FwdState::~FwdState()
{
- StoreEntry *e = fwdState->entry;
- int sfd;
- peer *p;
- debug(17, 3) ("fwdStateFree: %p\n", fwdState);
- assert(e->mem_obj);
+ debugs(17, 3, HERE << "FwdState destructor starting");
#if URL_CHECKSUM_DEBUG
- e->mem_obj->checkUrlChecksum();
+ entry->mem_obj->checkUrlChecksum();
#endif
#if WIP_FWD_LOG
- fwdLog(fwdState);
+ log();
#endif
- if (e->store_status == STORE_PENDING) {
- if (e->isEmpty()) {
- assert(fwdState->err);
- errorAppendEntry(e, fwdState->err);
- fwdState->err = NULL;
+ if (entry->store_status == STORE_PENDING) {
+ if (entry->isEmpty()) {
+ assert(err);
+ errorAppendEntry(entry, err);
+ err = NULL;
} else {
- EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
- e->complete();
- storeReleaseRequest(e);
+ EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
+ entry->complete();
+ storeReleaseRequest(entry);
}
}
- if (storePendingNClients(e) > 0)
- assert(!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT));
+ if (storePendingNClients(entry) > 0)
+ assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
- p = fwdStateServerPeer(fwdState);
+ serversFree(&servers);
- fwdServersFree(&fwdState->servers);
+ requestUnlink(request);
- requestUnlink(fwdState->request);
+ request = NULL;
- fwdState->request = NULL;
+ if (err)
+ errorStateFree(err);
- if (fwdState->err)
- errorStateFree(fwdState->err);
+ storeUnlockObject(entry);
- storeUnregisterAbort(e);
+ entry = NULL;
- storeUnlockObject(e);
+ if (server_fd > -1) {
+ comm_remove_close_handler(server_fd, fwdServerClosedWrapper, this);
+ server_fd = -1;
+ debug(17, 3) ("fwdStateFree: closing FD %d\n", server_fd);
+ comm_close(server_fd);
+ }
- fwdState->entry = NULL;
+ debugs(17, 3, HERE << "FwdState destructor done");
+}
- sfd = fwdState->server_fd;
+/*
+ * This is the entry point for client-side to start forwarding
+ * a transaction. It is a static method that may or may not
+ * allocate a FwdState.
+ */
+void
+FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request)
+{
+ /*
+ * client_addr == no_addr indicates this is an "internal" request
+ * from peer_digest.c, asn.c, netdb.c, etc and should always
+ * be allowed. yuck, I know.
+ */
- if (sfd > -1) {
- comm_remove_close_handler(sfd, fwdServerClosed, fwdState);
- fwdState->server_fd = -1;
- debug(17, 3) ("fwdStateFree: closing FD %d\n", sfd);
- comm_close(sfd);
+ if (request->client_addr.s_addr != no_addr.s_addr && request->protocol != PROTO_INTERNAL && request->protocol != PROTO_CACHEOBJ) {
+ /*
+ * Check if this host is allowed to fetch MISSES from us (miss_access)
+ */
+ ACLChecklist ch;
+ ch.src_addr = request->client_addr;
+ ch.my_addr = request->my_addr;
+ ch.my_port = request->my_port;
+ ch.request = requestLink(request);
+ ch.accessList = cbdataReference(Config.accessList.miss);
+ /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
+ int answer = ch.fastCheck();
+
+ if (answer == 0) {
+ err_type page_id;
+ page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
+
+ if (page_id == ERR_NONE)
+ page_id = ERR_FORWARDING_DENIED;
+
+ ErrorState *anErr = errorCon(page_id, HTTP_FORBIDDEN);
+
+ anErr->request = requestLink(request);
+
+ anErr->src_addr = request->client_addr;
+
+ errorAppendEntry(entry, anErr); // frees anErr
+
+ return;
+ }
+ }
+
+ debug(17, 3) ("FwdState::start() '%s'\n", storeUrl(entry));
+ entry->mem_obj->request = requestLink(request);
+#if URL_CHECKSUM_DEBUG
+
+ entry->mem_obj->checkUrlChecksum();
+#endif
+
+ if (shutting_down) {
+ /* more yuck */
+ ErrorState *anErr = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE);
+ anErr->request = requestLink(request);
+ errorAppendEntry(entry, anErr); // frees anErr
+ return;
}
- cbdataFree(fwdState);
+ switch (request->protocol) {
+
+ case PROTO_INTERNAL:
+ internalStart(request, entry);
+ return;
+
+ case PROTO_CACHEOBJ:
+ cachemgrStart(client_fd, request, entry);
+ return;
+
+ case PROTO_URN:
+ urnStart(request, entry);
+ return;
+
+ default:
+ FwdState *fwd = new FwdState(client_fd, entry, request);
+ peerSelect(request, entry, fwdStartCompleteWrapper, fwd);
+ return;
+ }
+
+ /* NOTREACHED */
}
-static int
-fwdCheckRetry(FwdState * fwdState)
+void
+FwdState::fail(ErrorState * errorState)
+{
+ debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n",
+ err_type_str[errorState->type],
+ httpStatusString(errorState->httpStatus),
+ storeUrl(entry));
+
+ if (err)
+ errorStateFree(err);
+
+ err = errorState;
+
+ if (!errorState->request)
+ errorState->request = requestLink(request);
+}
+
+/*
+ * Frees fwdState without closing FD or generating an abort
+ */
+void
+FwdState::unregister(int fd)
+{
+ debug(17, 3) ("fwdUnregister: %s\n", storeUrl(entry));
+ assert(fd == server_fd);
+ assert(fd > -1);
+ comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
+ server_fd = -1;
+}
+
+/*
+ * server-side modules call fwdComplete() when they are done
+ * downloading an object. Then, we either 1) re-forward the
+ * request somewhere else if needed, or 2) call storeComplete()
+ * to finish it off
+ */
+void
+FwdState::complete()
+{
+ StoreEntry *e = entry;
+ assert(entry->store_status == STORE_PENDING);
+ debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e),
+ entry->getReply()->sline.status);
+#if URL_CHECKSUM_DEBUG
+
+ entry->mem_obj->checkUrlChecksum();
+#endif
+
+ logReplyStatus(n_tries, entry->getReply()->sline.status);
+
+ if (reforward()) {
+ debug(17, 3) ("fwdComplete: re-forwarding %d %s\n",
+ entry->getReply()->sline.status,
+ storeUrl(e));
+
+ if (server_fd > -1)
+ unregister(server_fd);
+
+ storeEntryReset(e);
+
+ startComplete(servers);
+ } else {
+ debug(17, 3) ("fwdComplete: not re-forwarding status %d\n",
+ entry->getReply()->sline.status);
+ EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
+ entry->complete();
+ }
+}
+
+
+/**** CALLBACK WRAPPERS ************************************************************/
+
+static void
+fwdStartCompleteWrapper(FwdServer * servers, void *data)
+{
+ FwdState *fwd = (FwdState *) data;
+ fwd->startComplete(servers);
+}
+
+static void
+fwdServerClosedWrapper(int fd, void *data)
+{
+ FwdState *fwd = (FwdState *) data;
+ fwd->serverClosed(fd);
+}
+
+static void
+fwdConnectStartWrapper(void *data)
+{
+ FwdState *fwd = (FwdState *) data;
+ fwd->connectStart();
+}
+
+#if USE_SSL
+static void
+fwdNegotiateSSLWrapper(int fd, void *data)
+{
+ FwdState *fwd = (FwdState *) data;
+ fwd->negotiateSSL(fd);
+}
+
+#endif
+
+static void
+fwdConnectDoneWrapper(int server_fd, comm_err_t status, int xerrno, void *data)
+{
+ FwdState *fwd = (FwdState *) data;
+ fwd->connectDone(server_fd, status, xerrno);
+}
+
+static void
+fwdConnectTimeoutWrapper(int fd, void *data)
+{
+ FwdState *fwd = (FwdState *) data;
+ fwd->connectTimeout(fd);
+}
+
+/*
+ * Accounts for closed persistent connections
+ */
+static void
+fwdPeerClosed(int fd, void *data)
+{
+ peer *p = (peer *)data;
+ p->stats.conn_open--;
+}
+
+/**** PRIVATE *****************************************************************/
+
+bool
+FwdState::checkRetry()
{
if (shutting_down)
- return 0;
+ return false;
- if (fwdState->entry->store_status != STORE_PENDING)
- return 0;
+ if (entry->store_status != STORE_PENDING)
+ return false;
- if (!fwdState->entry->isEmpty())
- return 0;
+ if (!entry->isEmpty())
+ return false;
- if (fwdState->n_tries > 10)
- return 0;
+ if (n_tries > 10)
+ return false;
- if (fwdState->origin_tries > 2)
- return 0;
+ if (origin_tries > 2)
+ return false;
- if (squid_curtime - fwdState->start > Config.Timeout.forward)
- return 0;
+ if (squid_curtime - start_t > Config.Timeout.forward)
+ return false;
- if (fwdState->flags.dont_retry)
- return 0;
+ if (flags.dont_retry)
+ return false;
- if (fwdState->request->flags.body_sent)
- return 0;
+ if (request->flags.body_sent)
+ return false;
- return 1;
+ return true;
}
-static int
-fwdCheckRetriable(FwdState * fwdState)
+bool
+FwdState::checkRetriable()
{
/* If there is a request body then Squid can only try once
* even if the method is indempotent
*/
- if (fwdState->request->body_connection.getRaw() != NULL)
- return 0;
+ if (request->body_connection.getRaw() != NULL)
+ return false;
/* RFC2616 9.1 Safe and Idempotent Methods */
- switch (fwdState->request->method) {
+ switch (request->method) {
/* 9.1.1 Safe Methods */
case METHOD_GET:
break;
default:
- return 0;
+ return false;
}
- return 1;
+ return true;
}
-static void
-fwdServerClosed(int fd, void *data)
+void
+FwdState::serverClosed(int fd)
{
- FwdState *fwdState = (FwdState *)data;
- debug(17, 2) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(fwdState->entry));
- assert(fwdState->server_fd == fd);
- fwdState->server_fd = -1;
+ debug(17, 2) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(entry));
+ assert(server_fd == fd);
+ server_fd = -1;
- if (fwdCheckRetry(fwdState)) {
- int originserver = (fwdState->servers->_peer == NULL);
+ if (checkRetry()) {
+ int originserver = (servers->_peer == NULL);
debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n",
- fwdState->n_tries,
- (int) (squid_curtime - fwdState->start));
+ n_tries,
+ (int) (squid_curtime - start_t));
- if (fwdState->servers->next) {
+ if (servers->next) {
/* use next, or cycle if origin server isn't last */
- FwdServer *fs = fwdState->servers;
+ FwdServer *fs = servers;
FwdServer **T, *T2 = NULL;
- fwdState->servers = fs->next;
+ servers = fs->next;
- for (T = &fwdState->servers; *T; T2 = *T, T = &(*T)->next)
+ for (T = &servers; *T; T2 = *T, T = &(*T)->next)
;
if (T2 && T2->_peer) {
fs->next = NULL;
} else {
/* Use next. The last "direct" entry is retried multiple times */
- fwdState->servers = fs->next;
+ servers = fs->next;
fwdServerFree(fs);
originserver = 0;
}
}
/* use eventAdd to break potential call sequence loops and to slow things down a little */
- eventAdd("fwdConnectStart", fwdConnectStart, fwdState, originserver ? 0.05 : 0.005, 0);
+ eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0);
return;
}
- if (!fwdState->err && shutting_down) {
- fwdState->err =errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE);
- fwdState->err->request = requestLink(fwdState->request);
+ if (!err && shutting_down) {
+ ErrorState *anErr = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE);
+ anErr->request = requestLink(request);
}
- fwdStateFree(fwdState);
+ self = NULL; // refcounted
}
#if USE_SSL
-static void
-fwdNegotiateSSL(int fd, void *data)
+void
+FwdState::negotiateSSL(int fd)
{
- FwdState *fwdState = (FwdState *)data;
- FwdServer *fs = fwdState->servers;
+ FwdServer *fs = servers;
SSL *ssl = fd_table[fd].ssl;
int ret;
- ErrorState *err;
- HttpRequest *request = fwdState->request;
if ((ret = SSL_connect(ssl)) <= 0) {
int ssl_error = SSL_get_error(ssl, ret);
switch (ssl_error) {
case SSL_ERROR_WANT_READ:
- commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSL, fwdState, 0);
+ commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
return;
case SSL_ERROR_WANT_WRITE:
- commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSL, fwdState, 0);
+ commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
return;
default:
debug(81, 1) ("fwdNegotiateSSL: Error negotiating SSL connection on FD %d: %s (%d/%d/%d)\n", fd, ERR_error_string(ERR_get_error(), NULL), ssl_error, ret, errno);
- err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
+ ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
#ifdef EPROTO
- err->xerrno = EPROTO;
+ anErr->xerrno = EPROTO;
#else
- err->xerrno = EACCES;
+ anErr->xerrno = EACCES;
#endif
if (fs->_peer) {
- err->host = xstrdup(fs->_peer->host);
- err->port = fs->_peer->http_port;
+ anErr->host = xstrdup(fs->_peer->host);
+ anErr->port = fs->_peer->http_port;
} else {
- err->host = xstrdup(request->host);
- err->port = request->port;
+ anErr->host = xstrdup(request->host);
+ anErr->port = request->port;
}
- err->request = requestLink(request);
- fwdFail(fwdState, err);
+ anErr->request = requestLink(request);
+ fail(anErr);
if (fs->_peer) {
peerConnectFailed(fs->_peer);
fs->_peer->sslSession = SSL_get1_session(ssl);
}
- fwdDispatch(fwdState);
+ dispatch();
}
-static void
-fwdInitiateSSL(FwdState * fwdState)
+void
+FwdState::initiateSSL()
{
- FwdServer *fs = fwdState->servers;
- int fd = fwdState->server_fd;
+ FwdServer *fs = servers;
+ int fd = server_fd;
SSL *ssl;
SSL_CTX *sslContext = NULL;
peer *peer = fs->_peer;
assert(sslContext);
if ((ssl = SSL_new(sslContext)) == NULL) {
- ErrorState *err;
debug(83, 1) ("fwdInitiateSSL: Error allocating handle: %s\n",
ERR_error_string(ERR_get_error(), NULL));
- err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
- err->xerrno = errno;
- err->request = requestLink(fwdState->request);
- fwdFail(fwdState, err);
- fwdStateFree(fwdState);
+ ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
+ anErr->xerrno = errno;
+ anErr->request = requestLink(request);
+ fail(anErr);
+ self = NULL; // refcounted
return;
}
SSL_set_session(ssl, peer->sslSession);
} else {
- SSL_set_ex_data(ssl, ssl_ex_index_server, fwdState->request->host);
+ SSL_set_ex_data(ssl, ssl_ex_index_server, request->host);
}
fd_table[fd].ssl = ssl;
fd_table[fd].read_method = &ssl_read_method;
fd_table[fd].write_method = &ssl_write_method;
- fwdNegotiateSSL(fd, fwdState);
+ negotiateSSL(fd);
}
#endif
-static void
-fwdConnectDone(int server_fd, comm_err_t status, int xerrno, void *data)
+void
+FwdState::connectDone(int aServerFD, comm_err_t status, int xerrno)
{
- FwdState *fwdState = (FwdState *)data;
- FwdServer *fs = fwdState->servers;
- ErrorState *err;
- HttpRequest *request = fwdState->request;
- assert(fwdState->server_fd == server_fd);
+ FwdServer *fs = servers;
+ assert(server_fd == aServerFD);
if (status == COMM_ERR_DNS) {
/*
*/
if (NULL == fs->_peer)
- fwdState->flags.dont_retry = 1;
+ flags.dont_retry = 1;
debug(17, 4) ("fwdConnectDone: Unknown host: %s\n",
request->host);
- err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
+ ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
- err->dnsserver_msg = xstrdup(dns_error_message);
+ anErr->dnsserver_msg = xstrdup(dns_error_message);
- fwdFail(fwdState, err);
+ fail(anErr);
comm_close(server_fd);
} else if (status != COMM_OK) {
assert(fs);
- err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
- err->xerrno = xerrno;
+ ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
+ anErr->xerrno = xerrno;
if (fs->_peer) {
- err->host = xstrdup(fs->_peer->host);
- err->port = fs->_peer->http_port;
+ anErr->host = xstrdup(fs->_peer->host);
+ anErr->port = fs->_peer->http_port;
} else {
- err->host = xstrdup(request->host);
- err->port = request->port;
+ anErr->host = xstrdup(request->host);
+ anErr->port = request->port;
}
- fwdFail(fwdState, err);
+ fail(anErr);
if (fs->_peer)
peerConnectFailed(fs->_peer);
comm_close(server_fd);
} else {
- debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(fwdState->entry));
+ debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(entry));
if (fs->_peer)
peerConnectSucceded(fs->_peer);
if ((fs->_peer && fs->_peer->use_ssl) ||
(!fs->_peer && request->protocol == PROTO_HTTPS)) {
- fwdInitiateSSL(fwdState);
+ initiateSSL();
return;
}
#endif
- fwdDispatch(fwdState);
+ dispatch();
}
}
-static void
-fwdConnectTimeout(int fd, void *data)
+void
+FwdState::connectTimeout(int fd)
{
- FwdState *fwdState = (FwdState *)data;
- StoreEntry *entry = fwdState->entry;
- ErrorState *err;
debug(17, 2) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
- assert(fd == fwdState->server_fd);
+ assert(fd == server_fd);
if (entry->isEmpty()) {
- err = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT);
- err->xerrno = ETIMEDOUT;
- fwdFail(fwdState, err);
+ ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT);
+ anErr->xerrno = ETIMEDOUT;
+ fail(anErr);
/*
* This marks the peer DOWN ...
*/
- if (fwdState->servers)
- if (fwdState->servers->_peer)
- peerConnectFailed(fwdState->servers->_peer);
+ if (servers)
+ if (servers->_peer)
+ peerConnectFailed(servers->_peer);
}
comm_close(fd);
}
-static struct IN_ADDR
- aclMapAddr(acl_address * head, ACLChecklist * ch)
-{
- acl_address *l;
-
- struct IN_ADDR addr;
-
- for (l = head; l; l = l->next)
- {
- if (ch->matchAclListFast(l->aclList))
- return l->addr;
- }
-
- addr.s_addr = INADDR_ANY;
- return addr;
-}
-
-static int
-aclMapTOS(acl_tos * head, ACLChecklist * ch)
-{
- acl_tos *l;
-
- for (l = head; l; l = l->next) {
- if (ch->matchAclListFast(l->aclList))
- return l->tos;
- }
-
- return 0;
-}
-
-struct IN_ADDR
- getOutgoingAddr(HttpRequest * request)
-{
- ACLChecklist ch;
-
- if (request)
- {
- ch.src_addr = request->client_addr;
- ch.my_addr = request->my_addr;
- ch.my_port = request->my_port;
- ch.request = requestLink(request);
- }
-
- return aclMapAddr(Config.accessList.outgoing_address, &ch);
-}
-
-unsigned long
-getOutgoingTOS(HttpRequest * request)
-{
- ACLChecklist ch;
-
- if (request) {
- ch.src_addr = request->client_addr;
- ch.my_addr = request->my_addr;
- ch.my_port = request->my_port;
- ch.request = requestLink(request);
- }
-
- return aclMapTOS(Config.accessList.outgoing_tos, &ch);
-}
-
-static void
-fwdConnectStart(void *data)
+void
+FwdState::connectStart()
{
- FwdState *fwdState = (FwdState *)data;
- const char *url = storeUrl(fwdState->entry);
+ const char *url = storeUrl(entry);
int fd = -1;
- ErrorState *err;
- FwdServer *fs = fwdState->servers;
+ FwdServer *fs = servers;
const char *host;
unsigned short port;
const char *domain = NULL;
int ctimeout;
- int ftimeout = Config.Timeout.forward - (squid_curtime - fwdState->start);
+ int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
struct IN_ADDR outgoing;
unsigned short tos;
assert(fs);
- assert(fwdState->server_fd == -1);
+ assert(server_fd == -1);
debug(17, 3) ("fwdConnectStart: %s\n", url);
if (fs->_peer) {
: Config.Timeout.peer_connect;
if (fs->_peer->options.originserver)
- domain = fwdState->request->host;
+ domain = request->host;
} else {
- host = fwdState->request->host;
- port = fwdState->request->port;
+ host = request->host;
+ port = request->port;
ctimeout = Config.Timeout.connect;
}
ctimeout = ftimeout;
if ((fd = fwdPconnPool->pop(host, port, domain)) >= 0) {
- if (fwdCheckRetriable(fwdState)) {
+ if (checkRetriable()) {
debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
- fwdState->server_fd = fd;
- fwdState->n_tries++;
+ server_fd = fd;
+ n_tries++;
if (!fs->_peer)
- fwdState->origin_tries++;
+ origin_tries++;
- comm_add_close_handler(fd, fwdServerClosed, fwdState);
+ comm_add_close_handler(fd, fwdServerClosedWrapper, this);
- fwdDispatch(fwdState);
+ dispatch();
return;
} else {
}
#if URL_CHECKSUM_DEBUG
- fwdState->entry->mem_obj->checkUrlChecksum();
+ entry->mem_obj->checkUrlChecksum();
#endif
- outgoing = getOutgoingAddr(fwdState->request);
+ outgoing = getOutgoingAddr(request);
- tos = getOutgoingTOS(fwdState->request);
+ tos = getOutgoingTOS(request);
debug(17, 3) ("fwdConnectStart: got addr %s, tos %d\n",
inet_ntoa(outgoing), tos);
if (fd < 0) {
debug(50, 4) ("fwdConnectStart: %s\n", xstrerror());
- err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
- err->xerrno = errno;
- fwdFail(fwdState, err);
- fwdStateFree(fwdState);
+ ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
+ anErr->xerrno = errno;
+ fail(anErr);
+ self = NULL; // refcounted
return;
}
- fwdState->server_fd = fd;
- fwdState->n_tries++;
+ server_fd = fd;
+ n_tries++;
if (!fs->_peer)
- fwdState->origin_tries++;
+ origin_tries++;
/*
* stats.conn_open is used to account for the number of
comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
}
- comm_add_close_handler(fd, fwdServerClosed, fwdState);
+ comm_add_close_handler(fd, fwdServerClosedWrapper, this);
- commSetTimeout(fd,
- ctimeout,
- fwdConnectTimeout,
- fwdState);
+ commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
- commConnectStart(fd, host, port, fwdConnectDone, fwdState);
+ commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
}
-static void
-fwdStartComplete(FwdServer * servers, void *data)
+void
+FwdState::startComplete(FwdServer * theServers)
{
- FwdState *fwdState = (FwdState *)data;
- debug(17, 3) ("fwdStartComplete: %s\n", storeUrl(fwdState->entry));
+ debug(17, 3) ("fwdStartComplete: %s\n", storeUrl(entry));
- if (servers != NULL) {
- fwdState->servers = servers;
- fwdConnectStart(fwdState);
+ if (theServers != NULL) {
+ servers = theServers;
+ connectStart();
} else {
- fwdStartFail(fwdState);
+ startFail();
}
}
-static void
-fwdStartFail(FwdState * fwdState)
+void
+FwdState::startFail()
{
- ErrorState *err;
- debug(17, 3) ("fwdStartFail: %s\n", storeUrl(fwdState->entry));
- err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE);
- err->xerrno = errno;
- fwdFail(fwdState, err);
- fwdStateFree(fwdState);
+ debug(17, 3) ("fwdStartFail: %s\n", storeUrl(entry));
+ ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE);
+ anErr->xerrno = errno;
+ fail(anErr);
+ self = NULL; // refcounted
}
-static void
-fwdDispatch(FwdState * fwdState)
+void
+FwdState::dispatch()
{
peer *p = NULL;
- HttpRequest *request = fwdState->request;
- StoreEntry *entry = fwdState->entry;
- ErrorState *err;
- FwdServer *fs = fwdState->servers;
- int server_fd = fwdState->server_fd;
+ FwdServer *fs = servers;
debug(17, 3) ("fwdDispatch: FD %d: Fetching '%s %s'\n",
- fwdState->client_fd,
+ client_fd,
RequestMethodStr[request->method],
storeUrl(entry));
/*
assert(server_fd > -1);
if (fs->_peer)
- hierarchyNote(&fwdState->request->hier, fs->code, fs->_peer->host);
+ hierarchyNote(&request->hier, fs->code, fs->_peer->host);
else if (Config.onoff.log_ip_on_direct)
- hierarchyNote(&fwdState->request->hier, fs->code, fd_table[server_fd].ipaddr);
+ hierarchyNote(&request->hier, fs->code, fd_table[server_fd].ipaddr);
else
- hierarchyNote(&fwdState->request->hier, fs->code, request->host);
+ hierarchyNote(&request->hier, fs->code, request->host);
- fd_note(server_fd, storeUrl(fwdState->entry));
+ fd_note(server_fd, storeUrl(entry));
fd_table[server_fd].noteUse(fwdPconnPool);
netdbPingSite(request->host);
- if (fwdState->servers && (p = fwdState->servers->_peer)) {
+ if (servers && (p = servers->_peer)) {
p->stats.fetches++;
- fwdState->request->peer_login = p->login;
- fwdState->request->peer_domain = p->domain;
- httpStart(fwdState);
+ request->peer_login = p->login;
+ request->peer_domain = p->domain;
+ httpStart(this);
} else {
- fwdState->request->peer_login = NULL;
- fwdState->request->peer_domain = NULL;
+ request->peer_login = NULL;
+ request->peer_domain = NULL;
switch (request->protocol) {
#if USE_SSL
case PROTO_HTTPS:
- httpStart(fwdState);
+ httpStart(this);
break;
#endif
case PROTO_HTTP:
- httpStart(fwdState);
+ httpStart(this);
break;
case PROTO_GOPHER:
- gopherStart(fwdState);
+ gopherStart(this);
break;
case PROTO_FTP:
- ftpStart(fwdState);
+ ftpStart(this);
break;
case PROTO_WAIS:
- waisStart(fwdState);
+ waisStart(this);
break;
case PROTO_CACHEOBJ:
break;
case PROTO_WHOIS:
- whoisStart(fwdState);
+ whoisStart(this);
break;
default:
debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n",
storeUrl(entry));
- err = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST);
- fwdFail(fwdState, err);
+ ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST);
+ fail(anErr);
/*
* Force a persistent connection to be closed because
* some Netscape browsers have a bug that sends CONNECT
* Set the dont_retry flag becuase this is not a
* transient (network) error; its a bug.
*/
- fwdState->flags.dont_retry = 1;
- comm_close(fwdState->server_fd);
+ flags.dont_retry = 1;
+ comm_close(server_fd);
break;
}
}
+
+ /*
+ * remove our self-refcount now that we've handed off the request
+ * to a server-side module
+ */
+ self = NULL;
}
-static int
-fwdReforward(FwdState * fwdState)
+int
+FwdState::reforward()
{
- StoreEntry *e = fwdState->entry;
- FwdServer *fs = fwdState->servers;
+ StoreEntry *e = entry;
+ FwdServer *fs = servers;
http_status s;
assert(e->store_status == STORE_PENDING);
assert(e->mem_obj);
return 0;
}
- if (fwdState->n_tries > 9)
+ if (n_tries > 9)
return 0;
- if (fwdState->origin_tries > 1)
+ if (origin_tries > 1)
return 0;
- if (fwdState->request->flags.body_sent)
+ if (request->flags.body_sent)
return 0;
assert(fs);
- fwdState->servers = fs->next;
+ servers = fs->next;
fwdServerFree(fs);
- if (fwdState->servers == NULL) {
+ if (servers == NULL) {
debug(17, 3) ("fwdReforward: No forward-servers left\n");
return 0;
}
s = e->getReply()->sline.status;
debug(17, 3) ("fwdReforward: status %d\n", (int) s);
- return fwdReforwardableStatus(s);
+ return reforwardableStatus(s);
}
-/* PUBLIC FUNCTIONS */
-
-void
-fwdServersFree(FwdServer ** FSVR)
+static void
+fwdStats(StoreEntry * s)
{
- FwdServer *fs;
+ int i;
+ int j;
+ storeAppendPrintf(s, "Status");
- while ((fs = *FSVR)) {
- *FSVR = fs->next;
- fwdServerFree(fs);
+ for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
+ storeAppendPrintf(s, "\ttry#%d", j + 1);
}
-}
-void
-fwdStart(int fd, StoreEntry * e, HttpRequest * r)
-{
- FwdState *fwdState;
- int answer;
- ErrorState *err;
- /*
- * client_addr == no_addr indicates this is an "internal" request
- * from peer_digest.c, asn.c, netdb.c, etc and should always
- * be allowed. yuck, I know.
- */
-
- if (r->client_addr.s_addr != no_addr.s_addr && r->protocol != PROTO_INTERNAL && r->protocol != PROTO_CACHEOBJ) {
- /*
- * Check if this host is allowed to fetch MISSES from us (miss_access)
- */
- ACLChecklist ch;
- ch.src_addr = r->client_addr;
- ch.my_addr = r->my_addr;
- ch.my_port = r->my_port;
- ch.request = requestLink(r);
- ch.accessList = cbdataReference(Config.accessList.miss);
- /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
- answer = ch.fastCheck();
-
- if (answer == 0) {
- err_type page_id;
- page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
-
- if (page_id == ERR_NONE)
- page_id = ERR_FORWARDING_DENIED;
-
- err = errorCon(page_id, HTTP_FORBIDDEN);
-
- err->request = requestLink(r);
+ storeAppendPrintf(s, "\n");
- err->src_addr = r->client_addr;
+ for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
+ if (FwdReplyCodes[0][i] == 0)
+ continue;
- errorAppendEntry(e, err);
+ storeAppendPrintf(s, "%3d", i);
- return;
+ for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
+ storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
}
- }
- debug(17, 3) ("fwdStart: '%s'\n", storeUrl(e));
- e->mem_obj->request = requestLink(r);
-#if URL_CHECKSUM_DEBUG
-
- e->mem_obj->checkUrlChecksum();
-#endif
-
- if (shutting_down) {
- /* more yuck */
- err = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE);
- err->request = requestLink(r);
- errorAppendEntry(e, err);
- return;
- }
-
- switch (r->protocol) {
- /*
- * Note, don't create fwdState for these requests
- */
-
- case PROTO_INTERNAL:
- internalStart(r, e);
- return;
-
- case PROTO_CACHEOBJ:
- cachemgrStart(fd, r, e);
- return;
-
- case PROTO_URN:
- urnStart(r, e);
- return;
-
- default:
- break;
+ storeAppendPrintf(s, "\n");
}
-
- fwdState = cbdataAlloc(FwdState);
- fwdState->entry = e;
- fwdState->client_fd = fd;
- fwdState->server_fd = -1;
- fwdState->request = requestLink(r);
- fwdState->start = squid_curtime;
- storeLockObject(e);
- EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
- storeRegisterAbort(e, fwdAbort, fwdState);
- peerSelect(r, e, fwdStartComplete, fwdState);
-}
-
-void
-fwdFail(FwdState * fwdState, ErrorState * errorState)
-{
- debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n",
- err_type_str[errorState->type],
- httpStatusString(errorState->httpStatus),
- storeUrl(fwdState->entry));
-
- if (fwdState->err)
- errorStateFree(fwdState->err);
-
- fwdState->err = errorState;
-
- if (!errorState->request)
- errorState->request = requestLink(fwdState->request);
-}
-
-/*
- * Called when someone else calls StoreAbort() on this entry
- */
-static void
-fwdAbort(void *data)
-{
- FwdState *fwdState = (FwdState *)data;
- debug(17, 2) ("fwdAbort: %s\n", storeUrl(fwdState->entry));
- fwdStateFree(fwdState);
}
-/*
- * Accounts for closed persistent connections
- */
-static void
-fwdPeerClosed(int fd, void *data)
-{
- peer *p = (peer *)data;
- p->stats.conn_open--;
-}
-/*
- * Frees fwdState without closing FD or generating an abort
- */
-void
-fwdUnregister(int fd, FwdState * fwdState)
-{
- debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry));
- assert(fd == fwdState->server_fd);
- assert(fd > -1);
- comm_remove_close_handler(fd, fwdServerClosed, fwdState);
- fwdState->server_fd = -1;
-}
+/**** STATIC MEMBER FUNCTIONS *************************************************/
-/*
- * server-side modules call fwdComplete() when they are done
- * downloading an object. Then, we either 1) re-forward the
- * request somewhere else if needed, or 2) call storeComplete()
- * to finish it off
- */
-void
-fwdComplete(FwdState * fwdState)
+bool
+FwdState::reforwardableStatus(http_status s)
{
- StoreEntry *e = fwdState->entry;
- assert(e->store_status == STORE_PENDING);
- debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e),
- e->getReply()->sline.status);
-#if URL_CHECKSUM_DEBUG
+ switch (s) {
- e->mem_obj->checkUrlChecksum();
-#endif
+ case HTTP_BAD_GATEWAY:
- fwdLogReplyStatus(fwdState->n_tries, e->getReply()->sline.status);
+ case HTTP_GATEWAY_TIMEOUT:
+ return true;
- if (fwdReforward(fwdState)) {
- debug(17, 3) ("fwdComplete: re-forwarding %d %s\n",
- e->getReply()->sline.status,
- storeUrl(e));
+ case HTTP_FORBIDDEN:
- if (fwdState->server_fd > -1)
- fwdUnregister(fwdState->server_fd, fwdState);
+ case HTTP_INTERNAL_SERVER_ERROR:
- storeEntryReset(e);
+ case HTTP_NOT_IMPLEMENTED:
- fwdStartComplete(fwdState->servers, fwdState);
- } else {
- debug(17, 3) ("fwdComplete: not re-forwarding status %d\n",
- e->getReply()->sline.status);
- EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
- e->complete();
- /*
- * If fwdState isn't associated with a server FD, it
- * won't get freed unless we do it here.
- */
+ case HTTP_SERVICE_UNAVAILABLE:
+ return Config.retry.onerror;
- if (fwdState->server_fd < 0)
- fwdStateFree(fwdState);
+ default:
+ return false;
}
+
+ /* NOTREACHED */
}
void
-fwdPconnPush(int fd, const char *host, int port, const char *domain)
+FwdState::pconnPush(int fd, const char *host, int port, const char *domain)
{
fwdPconnPool->push(fd, host, port, domain);
}
void
-fwdInit(void)
+FwdState::initModule()
{
cachemgrRegister("forward",
"Request Forwarding Statistics",
fwdStats, 0, 1);
+ memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
+
#if WIP_FWD_LOG
if (logfile)
#endif
}
-static void
-fwdLogReplyStatus(int tries, http_status status)
+void
+FwdState::logReplyStatus(int tries, http_status status)
{
if (status > HTTP_INVALID_HEADER)
return;
FwdReplyCodes[tries][status]++;
}
-static void
-fwdStats(StoreEntry * s)
+void
+FwdState::serversFree(FwdServer ** FSVR)
{
- int i;
- int j;
- storeAppendPrintf(s, "Status");
+ FwdServer *fs;
- for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
- storeAppendPrintf(s, "\ttry#%d", j + 1);
+ while ((fs = *FSVR)) {
+ *FSVR = fs->next;
+ fwdServerFree(fs);
}
+}
- storeAppendPrintf(s, "\n");
+/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
- for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
- if (FwdReplyCodes[0][i] == 0)
- continue;
+static void
+fwdServerFree(FwdServer * fs)
+{
+ cbdataReferenceDone(fs->_peer);
+ memFree(fs, MEM_FWD_SERVER);
+}
- storeAppendPrintf(s, "%3d", i);
+static struct IN_ADDR
+ aclMapAddr(acl_address * head, ACLChecklist * ch)
+{
+ acl_address *l;
- for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
- storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
- }
+ struct IN_ADDR addr;
- storeAppendPrintf(s, "\n");
+ for (l = head; l; l = l->next)
+ {
+ if (ch->matchAclListFast(l->aclList))
+ return l->addr;
}
+
+ addr.s_addr = INADDR_ANY;
+ return addr;
}
-int
-fwdReforwardableStatus(http_status s)
+static int
+aclMapTOS(acl_tos * head, ACLChecklist * ch)
{
- switch (s) {
+ acl_tos *l;
- case HTTP_BAD_GATEWAY:
+ for (l = head; l; l = l->next) {
+ if (ch->matchAclListFast(l->aclList))
+ return l->tos;
+ }
- case HTTP_GATEWAY_TIMEOUT:
- return 1;
+ return 0;
+}
- case HTTP_FORBIDDEN:
+struct IN_ADDR
+ getOutgoingAddr(HttpRequest * request)
+{
+ ACLChecklist ch;
- case HTTP_INTERNAL_SERVER_ERROR:
+ if (request)
+ {
+ ch.src_addr = request->client_addr;
+ ch.my_addr = request->my_addr;
+ ch.my_port = request->my_port;
+ ch.request = requestLink(request);
+ }
- case HTTP_NOT_IMPLEMENTED:
+ return aclMapAddr(Config.accessList.outgoing_address, &ch);
+}
- case HTTP_SERVICE_UNAVAILABLE:
- return Config.retry.onerror;
+unsigned long
+getOutgoingTOS(HttpRequest * request)
+{
+ ACLChecklist ch;
- default:
- return 0;
+ if (request) {
+ ch.src_addr = request->client_addr;
+ ch.my_addr = request->my_addr;
+ ch.my_port = request->my_port;
+ ch.request = requestLink(request);
}
- /* NOTREACHED */
+ return aclMapTOS(Config.accessList.outgoing_tos, &ch);
}
+
+/**** WIP_FWD_LOG *************************************************************/
+
#if WIP_FWD_LOG
void
fwdUninit(void)
}
static void
-fwdLog(FwdState * fwdState)
+FwdState::log()
{
if (NULL == logfile)
return;
logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
(int) current_time.tv_sec,
(int) current_time.tv_usec / 1000,
- fwdState->last_status,
- RequestMethodStr[fwdState->request->method],
- fwdState->request->canonical);
+ last_status,
+ RequestMethodStr[request->method],
+ request->canonical);
}
void
-fwdStatus(FwdState * fwdState, http_status s)
+FwdState::status(http_status s)
{
- fwdState->last_status = s;
+ last_status = s;
}
#endif