This is the connect-retry patch which allows multi-address hosts to be retried until all addresses fail, and configurable multiple tries on single-address hosts. This patch is functionally identical to the original retry-1.1.20.patch, except modified to use #ifdefs throughout the code to allow the patch to be turned on and off easily. The patch is on by default when it is first applied. To turn it off edit src/Makefile and comment out the "-DRETRY_PATCH" line. This patch and RETRY_CONNECT are incompatible. If RETRY_PATCH is on, RETRY_CONNECT is disabled. -Mike Pelletier. (Feb 23, 1998) --- include/version.h.orig Mon Feb 23 11:31:58 1998 +++ include/version.h Mon Feb 23 13:44:20 1998 @@ -3,7 +3,11 @@ * SQUID_VERSION - String for version id of this distribution */ #ifndef SQUID_VERSION +#ifdef RETRY_PATCH +#define SQUID_VERSION "1.1.20+retry" +#else #define SQUID_VERSION "1.1.20" +#endif /* RETRY_PATCH */ #endif #ifndef SQUID_RELEASE_TIME --- src/Makefile.in.orig Fri Oct 31 17:38:13 1997 +++ src/Makefile.in Tue Feb 24 14:42:59 1998 @@ -18,12 +18,14 @@ USE_BIN_TREE = # -DUSE_BIN_TREE RELOAD_INTO_IMS = # -DRELOAD_INTO_IMS UNDERSCORES_OPT = # -DALLOW_HOSTNAME_UNDERSCORES +RETRY_PATCH = -DRETRY_PATCH DEFINES = $(HOST_OPT) $(AUTH_OPT) $(LOG_HDRS_OPT) \ $(ICMP_OPT) $(DELAY_HACK) $(USERAGENT_OPT) \ $(KILL_PARENT_OPT) $(USE_POLL_OPT) \ $(USE_SPLAY_TREE) $(USE_BIN_TREE) \ - $(RELOAD_INTO_IMS) $(UNDERSCORES_OPT) + $(RELOAD_INTO_IMS) $(UNDERSCORES_OPT) \ + $(RETRY_PATCH) prefix = @prefix@ exec_prefix = @exec_prefix@ --- src/cache_cf.c.orig Mon Feb 23 11:32:16 1998 +++ src/cache_cf.c Mon Feb 23 14:06:58 1998 @@ -136,6 +136,11 @@ #define DefaultRedirectChildren 5 /* 5 processes */ #define DefaultMaxRequestSize (100 << 10) /* 100Kb */ +#ifdef RETRY_PATCH +#define DefaultSingleAddrTries 3 +#define DefaultMinRetryTimeout 5 +#endif /* RETRY_PATCH */ + #define DefaultHttpPortNum CACHE_HTTP_PORT #define DefaultIcpPortNum CACHE_ICP_PORT @@ -1400,6 +1405,12 @@ parseIntegerValue(&Config.Netdb.low); else if (!strcmp(token, "netdb_ping_period")) parseTimeLine(&Config.Netdb.period, "seconds"); +#ifdef RETRY_PATCH + else if (!strcmp(token, "minimum_retry_timeout")) + parseTimeLine(&Config.Retry.min_timeout, "seconds"); + else if (!strcmp(token, "maximum_single_addr_tries")) + parseIntegerValue(&Config.Retry.max_single_addr); +#endif /* RETRY_PATCH */ /* If unknown, treat as a comment line */ else { @@ -1410,6 +1421,44 @@ } /* Sanity checks */ +#ifdef RETRY_PATCH +/* If connect_timeout is shorter than the default, don't bug the admin + * with this message unless retry timeout is greater than connect timeout. */ + if (Config.Retry.min_timeout > Config.connectTimeout / 2 + && (Config.Retry.min_timeout > 60 + || Config.Retry.min_timeout >= Config.connectTimeout)) { + printf("WARNING: minimum_retry_timeout is more than half of connect_timeout\n"); + printf(" This can cause very long waits for multi-address host retries.\n"); + printf(" Resetting half of connect_timeout (%d seconds).\n", + Config.connectTimeout / 2); + Config.Retry.min_timeout = Config.connectTimeout / 2; + fflush(stdout); + } +/* Make sure we, or they, didn't reset it to below five seconds */ + if (Config.Retry.min_timeout < 5) { + printf("WARNING: minimum_retry_timeout is less than five seconds.\n"); + printf(" This can cause spurious timeouts on multi-address hosts.\n"); + printf(" Resetting to 5 seconds.\n"); + Config.Retry.min_timeout = 5; + fflush(stdout); + } + if (Config.Retry.max_single_addr > 255) { /* value is used in uchar vars */ + printf("WARNING: maximum_single_addr_tries set to a bad value: %d\n", + Config.Retry.max_single_addr); + printf(" Setting it to the maximum (255).\n"); + Config.Retry.max_single_addr = 255; + fflush(stdout); + } + if (Config.Retry.max_single_addr > 10) { + printf("WARNING: maximum_single_addr_tries is larger than 10.\n"); + printf(" This can cause increased network traffic and very\n"); + printf(" long delays to display an error for a nonexistant,\n"); + printf(" busy, or unavailable site.\n"); + fflush(stdout); + } else if (Config.Retry.max_single_addr < 1) + Config.Retry.max_single_addr = 1; +#endif /* RETRY_PATCH */ + if (Config.lifetimeDefault < Config.readTimeout) { printf("WARNING: client_lifetime (%d seconds) is less than read_timeout (%d seconds).\n", Config.lifetimeDefault, Config.readTimeout); @@ -1618,6 +1667,10 @@ #ifdef RELOAD_INTO_IMS Config.Options.reload_into_ims = 0; #endif /* RELOAD_INTO_IMS */ +#ifdef RETRY_PATCH + Config.Retry.min_timeout = DefaultMinRetryTimeout; + Config.Retry.max_single_addr = DefaultSingleAddrTries; +#endif /* RETRY_PATCH */ } static void --- src/cache_cf.h.orig Mon Feb 23 12:01:32 1998 +++ src/cache_cf.h Mon Feb 23 12:02:34 1998 @@ -284,6 +284,12 @@ int test_reachability; } Options; char *fake_ua; +#ifdef RETRY_PATCH + struct { + int min_timeout; + int max_single_addr; + } Retry; +#endif /* RETRY_PATCH */ }; extern struct SquidConfig Config; --- src/comm.c.orig Mon Feb 23 12:02:41 1998 +++ src/comm.c Mon Feb 23 13:58:28 1998 @@ -150,6 +150,11 @@ #endif static void commSetTcpRcvbuf _PARAMS((int, int)); static void commConnectFree _PARAMS((int fd, void *data)); +#ifdef RETRY_PATCH +static int commResetFd _PARAMS((int, ConnectStateData *)); +static time_t commBackoffTimeout _PARAMS((unsigned char)); +static void commConnectRetry _PARAMS((int fd, void *data)); +#endif /* RETRY_PATCH */ static void commConnectHandle _PARAMS((int fd, void *data)); static void commHandleWrite _PARAMS((int fd, RWStateData * state)); @@ -283,12 +288,33 @@ } if (addr.s_addr != no_addr.s_addr) if (commBind(new_socket, addr, port) != COMM_OK) +#ifdef RETRY_PATCH + { + fdstat_close(new_socket); + memset(conn, '\0', sizeof(FD_ENTRY)); + conn->lifetime = -1; + close(new_socket); return COMM_ERROR; + } +#else /* RETRY_PATCH */ + return COMM_ERROR; +#endif /* RETRY_PATCH */ conn->local_port = port; if (BIT_TEST(flags, COMM_NONBLOCKING)) if (commSetNonBlocking(new_socket) == COMM_ERROR) +#ifdef RETRY_PATCH + { + fdstat_close(new_socket); + memset(conn, '\0', sizeof(FD_ENTRY)); + conn->lifetime = -1; + close(new_socket); + return COMM_ERROR; + } +#else /* RETRY_PATCH */ return COMM_ERROR; +#endif /* RETRY_PATCH */ + #ifdef TCP_NODELAY if (sock_type == SOCK_STREAM) commSetTcpNoDelay(new_socket); @@ -322,11 +348,21 @@ void commConnectStart(int fd, const char *host, u_short port, CCH callback, void *data) { +#ifdef RETRY_PATCH + FD_ENTRY *conn = &fd_table[fd]; +#endif /* RETRY_PATCH */ + ConnectStateData *cs = xcalloc(1, sizeof(ConnectStateData)); cs->host = xstrdup(host); cs->port = port; cs->callback = callback; cs->data = data; +#ifdef RETRY_PATCH + cs->conntimeout = Config.connectTimeout; + cs->otimeout_handler = conn->timeout_handler; + cs->otimeout_data = conn->timeout_data; + cs->otimeout_delta = conn->timeout_delta; +#endif /* RETRY_PATCH */ comm_add_close_handler(fd, commConnectFree, cs); commConnectHandle(fd, cs); } @@ -339,6 +375,37 @@ xfree(cs); } +#ifdef RETRY_PATCH +/* Resets an FD so that it can be re-connect()ed */ +static int +commResetFd(int fd, ConnectStateData *cs) +{ + int fd2; + fd2 = comm_open(SOCK_STREAM, + 0, + Config.Addrs.tcp_outgoing, + 0, + COMM_NONBLOCKING, + NULL); + if (fd2 == COMM_ERROR) { + debug(50,0,"commResetFd: comm_open: %s\n", xstrerror()); + return FALSE; + } + fdstat_close(fd2); + memset((void *)&fd_table[fd2], '\0', sizeof(FD_ENTRY)); + comm_set_fd_lifetime(fd2, -1); /* denotes invalid */ + + if (dup2(fd2, fd) < 0) { + debug(50,0,"commResetFd: dup2: %s\n", xstrerror()); + close(fd2); + return FALSE; + } + close(fd2); + cs->conntimeout = commBackoffTimeout(cs->addrcount); + debug(50,10,"commResetFd: fd %d reset successfully\n", fd); + return TRUE; +} +#else /* RETRY_PATCH */ static int commRetryConnect(int fd, ConnectStateData * connectState) { @@ -361,8 +428,25 @@ #else debug(5, 2, "commRetryConnect not supported\n"); return 0; -#endif +#endif /* RETRY_CONNECT */ +} +#endif /* RETRY_PATCH */ + +#ifdef RETRY_PATCH +/* Back off the socket timeout if there's several addresses available */ +static time_t +commBackoffTimeout(unsigned char numaddrs) +{ + time_t timeout; + timeout = (time_t)Config.connectTimeout; + if (numaddrs > 2) { + timeout = (time_t)(Config.connectTimeout / numaddrs); + if (timeout < Config.Retry.min_timeout) + timeout = (time_t)Config.Retry.min_timeout; + } + return timeout; } +#endif /* RETRY_PATCH */ /* Connect SOCK to specified DEST_PORT at DEST_HOST. */ static void @@ -382,10 +466,31 @@ } connectState->S.sin_family = AF_INET; connectState->S.sin_addr = ia->in_addrs[ia->cur]; +#ifdef RETRY_PATCH + ipcacheCycleAddr(connectState->host); +#endif /* RETRY_PATCH */ connectState->S.sin_port = htons(connectState->port); +#ifdef RETRY_PATCH + connectState->addrcount = ia->count; + connectState->connstart = getCurrentTime(); + connectState->conntimeout = commBackoffTimeout(ia->count); +#endif /* RETRY_PATCH */ if (Config.Log.log_fqdn) fqdncache_gethostbyaddr(connectState->S.sin_addr, FQDN_LOOKUP_IF_MISS); } +#ifdef RETRY_PATCH + + /* + * Set timeout handler to retry connection + */ + commSetSelect(fd, + COMM_SELECT_TIMEOUT, + commConnectRetry, + (void *) connectState, + connectState->conntimeout); + + /* Try connection */ +#endif /* RETRY_PATCH */ switch (comm_connect_addr(fd, &connectState->S)) { case COMM_INPROGRESS: commSetSelect(fd, @@ -397,10 +502,40 @@ case COMM_OK: if (vizSock > -1) vizHackSendPkt(&connectState->S, 2); +#ifdef RETRY_PATCH + ipcacheMarkGoodAddr(connectState->host, connectState->S.sin_addr); + if (connectState->tries > 0) + if (safe_inet_addr(connectState->host, NULL)) /* numeric? */ + debug(5,1, + "commConnectHandle: FD %d %s conn succeeded (try %d)\n", + fd, + connectState->host, + connectState->tries + 1); + else + debug(5,1, + "commConnectHandle: FD %d %s[%s] conn succeeded (try %d)\n", + fd, + connectState->host, + inet_ntoa(connectState->S.sin_addr), + connectState->tries + 1); + + /* Restore timeout handler */ + commSetSelect(fd, + COMM_SELECT_TIMEOUT, + connectState->otimeout_handler, + (void *) connectState->otimeout_data, + connectState->otimeout_delta); + + /* connection done */ +#else /* RETRY_PATCH */ ipcacheCycleAddr(connectState->host); +#endif /* RETRY_PATCH */ connectState->callback(fd, COMM_OK, connectState->data); break; default: +#ifdef RETRY_PATCH + commConnectRetry(fd, connectState); +#else /* RETRY_PATCH */ if (commRetryConnect(fd, connectState)) { debug(5, 1, "Retrying connection to %s\n", connectState->host); connectState->S.sin_addr.s_addr = 0; @@ -412,10 +547,52 @@ netdbDeleteAddrNetwork(connectState->S.sin_addr); connectState->callback(fd, COMM_ERROR, connectState->data); } +#endif /* RETRY_PATCH */ break; } } +#ifdef RETRY_PATCH +static void +commConnectRetry(int fd, void *data) +{ + ConnectStateData *connectState = data; + + connectState->tries++; + ipcacheMarkBadAddr(connectState->host, connectState->S.sin_addr); + getCurrentTime(); +/* Retry single addr hosts three times, but only if the connect time + * was smaller than half of the default connect timeout. Timeouts + * on multi-addr hosts *are* retried, however */ + if ((connectState->addrcount == 1 + && connectState->tries < Config.Retry.max_single_addr + && (squid_curtime - connectState->connstart) < /* conn time */ + (int)(Config.connectTimeout / 2)) + || (connectState->addrcount > 1 + && connectState->tries <= connectState->addrcount)) { + commResetFd(fd, connectState); + if (connectState->addrcount == 1) /* set min timeout for retry */ + connectState->conntimeout = commBackoffTimeout((unsigned char)100); + if (safe_inet_addr(connectState->host, NULL)) /* numeric? */ + debug(5,2,"commConnectHandle: FD %d %s conn fail, retrying\n", + fd, + connectState->host); + else + debug(5,2, + "commConnectHandle: FD %d %s[%s] conn fail, retrying\n", + fd, + connectState->host, + inet_ntoa(connectState->S.sin_addr)); + connectState->S.sin_addr.s_addr = (long)0; + commConnectHandle(fd, connectState); + } else { + if (Config.Options.test_reachability) + netdbDeleteAddrNetwork(connectState->S.sin_addr); + connectState->callback(fd, COMM_ERROR, connectState->data); + } +} +#endif /* RETRY_PATCH */ + int comm_set_fd_lifetime(int fd, int lifetime) { @@ -455,14 +635,19 @@ if (connect(sock, (struct sockaddr *) address, sizeof(struct sockaddr_in)) < 0) { cerrno = errno; switch (cerrno) { +#ifndef RETRY_PATCH case EALREADY: return COMM_ERROR; /* NOTREACHED */ +#endif /* RETRY_PATCH */ #if EAGAIN != EWOULDBLOCK case EAGAIN: #endif case EWOULDBLOCK: case EINPROGRESS: +#ifdef RETRY_PATCH + case EALREADY: +#endif /* RETRY_PATCH */ status = COMM_INPROGRESS; break; case EISCONN: @@ -488,10 +673,12 @@ lft = comm_set_fd_lifetime(sock, Config.lifetimeDefault); debug(5, 10, "comm_connect_addr: FD %d connected to %s:%d, lifetime %d.\n", sock, conn->ipaddr, conn->remote_port, lft); +#ifndef RETRY_PATCH } else if (status == COMM_INPROGRESS) { lft = comm_set_fd_lifetime(sock, Config.connectTimeout); debug(5, 10, "comm_connect_addr: FD %d connection pending, lifetime %d\n", sock, lft); +#endif /* RETRY_PATCH */ } /* Add new socket to list of open sockets. */ conn->sender = 1; --- src/comm.h.orig Mon Feb 23 12:53:52 1998 +++ src/comm.h Mon Feb 23 12:55:27 1998 @@ -143,9 +143,19 @@ struct sockaddr_in S; CCH callback; void *data; +#ifdef RETRY_PATCH + int connstart; + time_t conntimeout; /* Multi-addr retry connect timeout */ + PF otimeout_handler; /* previous Timeout handler. */ + time_t otimeout_delta; /* previous timeout delta requested */ + void *otimeout_data; /* previous App. data w/ timeout handler. */ + unsigned char tries; + unsigned char addrcount; +#else /* RETRY_PATCH */ #if RETRY_CONNECT int tries; #endif +#endif /* RETRY_PATCH */ } ConnectStateData; typedef struct fde { --- src/errorpage.c.orig Mon Feb 23 12:55:38 1998 +++ src/errorpage.c Mon Feb 23 12:57:19 1998 @@ -91,7 +91,11 @@ "Client(s) dropped connection before transmission completed.\nObject fetching is aborted.",}, {"ERR_CONNECT_FAIL", "Connection Failed", +#ifdef RETRY_PATCH + "The remote site or server may be busy or down. Please try again later."}, +#else "The remote site or server may be down. Please try again soon."}, +#endif {"ERR_INVALID_REQUEST", "Invalid HTTP request", "Please double check it, or ask for assistance."}, @@ -106,7 +110,11 @@ "The cache is currently very busy. Please try again."}, {"ERR_DNS_FAIL", "DNS name lookup failure", +#ifdef RETRY_PATCH + "The specified host does not exist or its address can't be found."}, +#else "The named host probably does not exist."}, +#endif {"ERR_NOT_IMPLEMENTED", "Protocol Not Supported", "The cache does not know about the requested protocol."}, --- src/ftp.c.orig Mon Feb 23 12:57:31 1998 +++ src/ftp.c Mon Feb 23 12:58:55 1998 @@ -617,6 +617,15 @@ (PF) ftpStateFree, (void *) ftpData); +#ifdef RETRY_PATCH + /* Set timeout handler */ + commSetSelect(ftpData->ftp_fd, + COMM_SELECT_TIMEOUT, + (PF) ftpLifetimeExpire, + (void *) ftpData, + Config.connectTimeout); +#endif /* RETRY_PATCH */ + /* Now connect ... */ commConnectStart(ftpData->ftp_fd, localhost, --- src/gopher.c.orig Mon Feb 23 12:59:02 1998 +++ src/gopher.c Mon Feb 23 14:31:36 1998 @@ -966,6 +966,15 @@ (PF) gopherStateFree, (void *) data); +#ifdef RETRY_PATCH + /* set timeout handler */ + commSetSelect(sock, + COMM_SELECT_TIMEOUT, + (PF) gopherReadReplyTimeout, + (void *) data, + Config.connectTimeout); +#endif /* RETRY_PATCH */ + /* check if IP is already in cache. It must be. * It should be done before this route is called. * Otherwise, we cannot check return code for connect. */ --- src/icp.c.orig Mon Feb 23 13:00:10 1998 +++ src/icp.c Mon Feb 23 13:03:25 1998 @@ -1743,6 +1743,11 @@ char *wbuf = NULL; int size; int len; +#ifdef RETRY_PATCH + int lft = -1; + + lft = comm_set_fd_lifetime(fd, Config.readTimeout); /* die when the read timeout expires */ +#endif /* RETRY_PATCH */ len = icpState->inbufsize - icpState->in_offset - 1; debug(12, 4, "clientReadRequest: FD %d: reading request...\n", fd); @@ -1803,6 +1808,10 @@ xfree); return; } +#ifdef RETRY_PATCH + /* have request, extend life */ + lft = comm_set_fd_lifetime(fd, Config.lifetimeDefault); +#endif /* RETRY_PATCH */ icpState->request = requestLink(request); clientAccessCheck(icpState, clientAccessCheckDone); } else if (parser_return_code == 0) { @@ -1900,7 +1909,12 @@ if (vizSock > -1) vizHackSendPkt(&peer, 1); /* set the hardwired lifetime */ +#ifdef RETRY_PATCH + /* if no data, die soon */ + lft = comm_set_fd_lifetime(fd, Config.connectTimeout); +#else lft = comm_set_fd_lifetime(fd, Config.lifetimeDefault); +#endif /* RETRY_PATCH */ ntcpconn++; debug(12, 4, "asciiHandleConn: FD %d: accepted, lifetime %d\n", fd, lft); @@ -2093,7 +2107,11 @@ int len; /* set the hardwired lifetime */ +#ifdef RETRY_PATCH + lft = comm_set_fd_lifetime(fd, Config.readTimeout); +#else lft = comm_set_fd_lifetime(fd, Config.lifetimeDefault); +#endif /* RETRY_PATCH */ len = sizeof(struct sockaddr_in); memset(&me, '\0', len); --- src/ipcache.c.orig Mon Feb 23 13:03:32 1998 +++ src/ipcache.c Mon Feb 23 13:43:30 1998 @@ -246,6 +246,9 @@ } if (i->status == IP_CACHED) { safe_free(i->addrs.in_addrs); +#ifdef RETRY_PATCH + safe_free(i->addrs.bad_addrs); +#endif /* RETRY_PATCH */ debug(14, 5, "ipcache_release: Released IP cached record for '%s'.\n", i->name); } @@ -416,14 +419,28 @@ int addr_count = 0; int k; safe_free(i->addrs.in_addrs); +#ifdef RETRY_PATCH + safe_free(i->addrs.bad_addrs); +#endif /* RETRY_PATCH */ while ((addr_count < 255) && *(hp->h_addr_list + addr_count)) ++addr_count; i->addrs.count = (unsigned char) addr_count; i->addrs.in_addrs = xcalloc(addr_count, sizeof(struct in_addr)); +#ifdef RETRY_PATCH + i->addrs.bad_addrs = xcalloc(addr_count, sizeof(unsigned char)); + i->addrs.badcount = 0; + for (k = 0; k < addr_count; k++) { + xmemcpy(&i->addrs.in_addrs[k].s_addr, + *(hp->h_addr_list + k), + hp->h_length); + i->addrs.bad_addrs[k] = FALSE; + } +#else for (k = 0; k < addr_count; k++) xmemcpy(&i->addrs.in_addrs[k].s_addr, *(hp->h_addr_list + k), hp->h_length); +#endif /* RETRY_PATCH */ } static ipcache_entry * @@ -515,8 +532,15 @@ i.addrs.count = (unsigned char) ipcount; if (ipcount == 0) { i.addrs.in_addrs = NULL; +#ifdef RETRY_PATCH + i.addrs.bad_addrs = NULL; } else { i.addrs.in_addrs = xcalloc(ipcount, sizeof(struct in_addr)); + i.addrs.bad_addrs = xcalloc(ipcount, sizeof(unsigned char)); +#else /* RETRY_PATCH */ + } else { + i.addrs.in_addrs = xcalloc(ipcount, sizeof(struct in_addr)); +#endif /* RETRY_PATCH */ } for (k = 0; k < ipcount; k++) { if ((token = strtok(NULL, w_space)) == NULL) @@ -772,6 +796,9 @@ ip_table = hash_create(urlcmp, 229, hash4); /* small hash table */ memset(&static_addrs, '\0', sizeof(ipcache_addrs)); static_addrs.in_addrs = xcalloc(1, sizeof(struct in_addr)); +#ifdef RETRY_PATCH + static_addrs.bad_addrs = xcalloc(1, sizeof(unsigned char)); +#endif /* RETRY_PATCH */ ipcache_high = (long) (((float) Config.ipcache.size * (float) Config.ipcache.high) / (float) 100); @@ -853,6 +880,10 @@ /* only dnsHandleRead() can change from DISPATCHED to CACHED */ static_addrs.count = 1; static_addrs.cur = 0; +#ifdef RETRY_PATCH + static_addrs.badcount = 0; + static_addrs.bad_addrs[0] = FALSE; +#endif /* RETRY_PATCH */ xmemcpy(&static_addrs.in_addrs[0].s_addr, *(hp->h_addr_list), hp->h_length); @@ -884,15 +915,29 @@ ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry) { int k; +#ifdef RETRY_PATCH + storeAppendPrintf(sentry, " {%-32.32s %c%c %6d %6d %2d(%2d)", +#else storeAppendPrintf(sentry, " {%-32.32s %c%c %6d %6d %d", +#endif /* RETRY_PATCH */ i->name, ipcache_status_char[i->status], i->locks ? 'L' : ' ', (int) (squid_curtime - i->lastref), (int) (i->expires - squid_curtime), +#ifdef RETRY_PATCH + (int) i->addrs.count, + (int) i->addrs.badcount); +#else (int) i->addrs.count); +#endif /* RETRY_PATCH */ for (k = 0; k < (int) i->addrs.count; k++) +#ifdef RETRY_PATCH + storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]), + i->addrs.bad_addrs[k] ? "BAD" : "OK "); +#else storeAppendPrintf(sentry, " %15s", inet_ntoa(i->addrs.in_addrs[k])); +#endif /* RETRY_PATCH */ storeAppendPrintf(sentry, close_bracket); } @@ -927,7 +972,11 @@ IpcacheStats.avg_svc_time); storeAppendPrintf(sentry, "}\n\n"); storeAppendPrintf(sentry, "{IP Cache Contents:\n\n"); +#ifdef RETRY_PATCH + storeAppendPrintf(sentry, " {%-29.29s %5s %6s %6s %1s}\n", +#else storeAppendPrintf(sentry, " {%-29.29s %5s %6s %6s %1s}\n", +#endif /* RETRY_PATCH */ "Hostname", "Flags", "lstref", @@ -1003,6 +1052,10 @@ static_addrs.count = 1; static_addrs.cur = 0; static_addrs.in_addrs[0].s_addr = ip.s_addr; +#ifdef RETRY_PATCH + static_addrs.bad_addrs[0] = FALSE; + static_addrs.badcount = 0; +#endif /* RETRY_PATCH */ return &static_addrs; } @@ -1033,6 +1086,88 @@ ipcache_release(i); } +#ifdef RETRY_PATCH +/* Cycles to next not-marked-bad address, or next address if all are bad */ +void +ipcacheCycleAddr(const char *name) +{ + ipcache_entry *i; + ipcache_addrs *ia; + unsigned char fullcircle; + if ((i = ipcache_get(name)) == NULL) + return; + if (i->status != IP_CACHED) + return; + ia = &i->addrs; + if (++ia->cur == ia->count) ia->cur = 0; + fullcircle = ia->cur; + while (ia->bad_addrs[ia->cur]) { + if (++ia->cur == ia->count) ia->cur = 0; + if (ia->cur == fullcircle) /* All bad, just use next one */ + break; + } +} + +/* "MarkBad" function must leave the "cur" pointer at the next + * available good address, or the next bad address, in the list. + * This simulates the functionality of RemoveBadAddr() which it + * replaces. Marking, instead of removing, allows bad addresses + * to be retried as a last resort before returning an error to + * the user. + */ +void +ipcacheMarkBadAddr(const char *name, struct in_addr addr) +{ + ipcache_entry *i; + ipcache_addrs *ia; + int k; + if ((i = ipcache_get(name)) == NULL) + return; + ia = &i->addrs; + for (k = 0; k < (int) ia->count; k++) { + if (ia->in_addrs[k].s_addr == addr.s_addr) + break; + } + if (k == (int) ia->count) + return; + if (!ia->bad_addrs[k]) { + ia->bad_addrs[k] = TRUE; ia->badcount++; + debug(14, 1, "%s(%s) marked bad\n", name, inet_ntoa(ia->in_addrs[k])); + if (ia->badcount != ia->count) { /* at least one good address left */ + i->expires = squid_curtime + Config.positiveDnsTtl; + while (ia->bad_addrs[ia->cur]) + if (++ia->cur == ia->count) ia->cur = 0; + return; + } + } + if (++ia->cur == ia->count) ia->cur = 0; +} + +void +ipcacheMarkGoodAddr(const char *name, struct in_addr addr) +{ + ipcache_entry *i; + ipcache_addrs *ia; + int k; + if ((i = ipcache_get(name)) == NULL) + return; + ia = &i->addrs; + for (k = 0; k < (int) ia->count; k++) { + if (ia->in_addrs[k].s_addr == addr.s_addr) + break; + } + if (k == (int) ia->count) + return; + i->expires = squid_curtime + Config.positiveDnsTtl; + if (ia->bad_addrs[k]) { + ia->bad_addrs[k] = FALSE; ia->badcount--; + i->expires = squid_curtime + Config.positiveDnsTtl; + debug(14, 1, "%s(%s) marked good\n", name, inet_ntoa(ia->in_addrs[k])); + } +} + +#else /* RETRY_PATCH */ + void ipcacheCycleAddr(const char *name) { @@ -1066,6 +1204,7 @@ if (ia->cur >= ia->count) ia->cur = 0; } +#endif /* RETRY_PATCH */ void ipcacheFreeMemory(void) @@ -1084,6 +1223,9 @@ for (j = 0; j < k; j++) { i = *(list + j); safe_free(i->addrs.in_addrs); +#ifdef RETRY_PATCH + safe_free(i->addrs.bad_addrs); +#endif safe_free(i->name); safe_free(i->error_message); safe_free(i); --- src/ipcache.h.orig Mon Feb 23 13:29:39 1998 +++ src/ipcache.h Mon Feb 23 13:31:24 1998 @@ -121,6 +121,10 @@ typedef struct { unsigned char count; unsigned char cur; +#ifdef RETRY_PATCH + unsigned char badcount; + unsigned char *bad_addrs; +#endif /* RETRY_PATCH */ struct in_addr *in_addrs; } ipcache_addrs; @@ -155,7 +159,12 @@ extern int ipcacheQueueDrain _PARAMS((void)); extern void ipcacheOpenServers _PARAMS((void)); extern void ipcacheCycleAddr _PARAMS((const char *name)); +#ifdef RETRY_PATCH +extern void ipcacheMarkBadAddr _PARAMS((const char *name, struct in_addr)); +extern void ipcacheMarkGoodAddr _PARAMS((const char *name, struct in_addr)); +#else extern void ipcacheRemoveBadAddr _PARAMS((const char *name, struct in_addr)); +#endif /* RETRY_PATCH */ extern void ipcacheFreeMemory _PARAMS((void)); extern ipcache_addrs *ipcacheCheckNumeric _PARAMS((const char *name)); extern void ipcache_restart _PARAMS((void)); --- src/squid.conf.pre.in.orig Mon Feb 23 13:31:39 1998 +++ src/squid.conf.pre.in Mon Feb 23 13:36:12 1998 @@ -1225,6 +1225,38 @@ # #icp_hit_stale off +# TAG: minimum_retry_timeout (Mike Pelletier ) +# This specifies the minimum connect timeout for the retry +# patch, for instances when the connect timeout is reduced +# to compensate for the availability of multiple IP addresses. +# +# When a connection to a host is initiated, and that host has +# several IP addresses, the default connection timeout is +# reduced by dividing it by the number of addresses. So, +# a site with 15 addresses would then have a timeout of 8 +# seconds for each address attempted. To avoid having the +# timeout reduced to the point where even a working host +# would not have a chance to respond, this setting is +# provided. The default, and the minimum value, is five +# seconds, and the maximum value is sixty seconds, or half of +# connect_timeout, whichever is greater and less than +# connect_timeout. This feature is not compiled in by +# default. You must add -DRETRY_PATCH in src/Makefile. +# +#minimum_retry_timeout 5 + +# TAG: maximum_single_addr_tries +# This sets the maximum number of connection attempts for a +# host that only has one address (for multiple-address hosts, +# each address is tried once) for the retry patch. +# +# The default value is three tries, the (not recommended) +# maximum is 255 tries. A warning message will be generated if +# it is set to a value greater than ten. You must add +# -DRETRY_PATCH in src/Makefile. +# +#maximum_single_addr_tries 3 + # TAG: reload_into_ims # Enable this if you want to turn 'Pragma: no-cache' requests # into If-Modified-Since requests. Off by default, use at your --- src/wais.c.orig Mon Feb 23 13:36:17 1998 +++ src/wais.c Mon Feb 23 14:35:40 1998 @@ -362,6 +362,16 @@ comm_add_close_handler(waisState->fd, (PF) waisStateFree, (void *) waisState); + +#ifdef RETRY_PATCH + /* set timeout handler */ + commSetSelect(fd, + COMM_SELECT_TIMEOUT, + (PF) waisReadReplyTimeout, + (void *) waisState, + Config.connectTimeout); +#endif /* RETRY_PATCH */ + waisState->ip_lookup_pending = 1; ipcache_nbgethostbyname(waisState->relayhost, waisState->fd,