Index: src/cf.data.pre
===================================================================
RCS file: /cvsroot/squid/squid/src/cf.data.pre,v
retrieving revision 1.105
diff -u -r1.105 cf.data.pre
--- src/cf.data.pre	22 May 2006 01:28:25 -0000	1.105
+++ src/cf.data.pre	22 May 2006 14:12:28 -0000
@@ -2014,6 +2014,9 @@
 	  concurrency=n	concurrency level per process. Use 0 for simple helpers
 			who can only process a single request at a time.
 	  cache=n	result cache size, 0 is unbounded (default)
+	  grace=	Percentage remaining of TTL where a refresh of a
+	  		cached entry should be initiated without needing to
+			wait for a new reply. (default 0 for no grace period)
 	  protocol=3.0	Use URL-escaped strings instead of quoting
 
 	FORMAT specifications
@@ -2021,10 +2024,13 @@
 	  %LOGIN	Authenticated user login name
 	  %IDENT	Ident user name
 	  %SRC		Client IP
+	  %SRCPORT	Client source port
 	  %DST		Requested host
 	  %PROTO	Requested protocol
 	  %PORT		Requested port
 	  %METHOD	Request method
+	  %MYADDR	Squid interface address
+	  %MYPORT	Squid http_port number
 	  %PATH		Requested URL-path (including query-string if any)
  	  %USER_CERT	SSL User certificate in PEM format
  	  %USER_CERTCHAIN SSL User certificate chain in PEM format
Index: src/client_side.c
===================================================================
RCS file: /cvsroot/squid/squid/src/client_side.c,v
retrieving revision 1.95
diff -u -r1.95 client_side.c
--- src/client_side.c	22 May 2006 01:28:25 -0000	1.95
+++ src/client_side.c	22 May 2006 14:12:30 -0000
@@ -444,6 +444,7 @@
 	new_request->client_addr = old_request->client_addr;
 	new_request->my_addr = old_request->my_addr;
 	new_request->my_port = old_request->my_port;
+	new_request->client_port = old_request->client_port;
 	new_request->flags = old_request->flags;
 	new_request->flags.redirected = 1;
 	if (old_request->auth_user_request) {
@@ -3499,6 +3500,7 @@
 	    request->client_addr = conn->peer.sin_addr;
 	    request->my_addr = conn->me.sin_addr;
 	    request->my_port = ntohs(conn->me.sin_port);
+	    request->client_port = ntohs(conn->peer.sin_port);
 	    request->http_ver = http->http_ver;
 	    if (!urlCheckRequest(request) ||
 		httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) {
Index: src/external_acl.c
===================================================================
RCS file: /cvsroot/squid/squid/src/external_acl.c,v
retrieving revision 1.17
diff -u -r1.17 external_acl.c
--- src/external_acl.c	22 May 2006 01:28:30 -0000	1.17
+++ src/external_acl.c	22 May 2006 14:12:31 -0000
@@ -55,6 +55,7 @@
 static char *makeExternalAclKey(aclCheck_t * ch, external_acl_data * acl_data);
 static void external_acl_cache_delete(external_acl * def, external_acl_entry * entry);
 static int external_acl_entry_expired(external_acl * def, external_acl_entry * entry);
+static int external_acl_grace_expired(external_acl * def, external_acl_entry * entry);
 static void external_acl_cache_touch(external_acl * def, external_acl_entry * entry);
 
 /*******************************************************************
@@ -80,6 +81,7 @@
     external_acl *next;
     int ttl;
     int negative_ttl;
+    int grace;
     char *name;
     external_acl_format *format;
     wordlist *cmdline;
@@ -106,6 +108,9 @@
 	EXT_ACL_IDENT,
 #endif
 	EXT_ACL_SRC,
+	EXT_ACL_SRCPORT,
+	EXT_ACL_MYADDR,
+	EXT_ACL_MYPORT,
 	EXT_ACL_DST,
 	EXT_ACL_PROTO,
 	EXT_ACL_PORT,
@@ -209,6 +214,8 @@
 	    a->quote = QUOTE_METHOD_URL;
 	} else if (strcmp(token, "quote=shell") == 0) {
 	    a->quote = QUOTE_METHOD_SHELL;
+	} else if (strncmp(token, "grace=", 6) == 0) {
+	    a->grace = atoi(token + 6);
 	} else {
 	    break;
 	}
@@ -271,6 +278,12 @@
 #endif
 	else if (strcmp(token, "%SRC") == 0)
 	    format->type = EXT_ACL_SRC;
+	else if (strcmp(token, "%SRCPORT") == 0)
+	    format->type = EXT_ACL_SRCPORT;
+	else if (strcmp(token, "%MYADDR") == 0)
+	    format->type = EXT_ACL_MYADDR;
+	else if (strcmp(token, "%MYPORT") == 0)
+	    format->type = EXT_ACL_MYPORT;
 	else if (strcmp(token, "%DST") == 0)
 	    format->type = EXT_ACL_DST;
 	else if (strcmp(token, "%PROTO") == 0)
@@ -333,10 +346,14 @@
 	    storeAppendPrintf(sentry, " ttl=%d", node->ttl);
 	if (node->negative_ttl != node->ttl)
 	    storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl);
+	if (node->grace)
+	    storeAppendPrintf(sentry, " grace=%d", node->grace);
 	if (node->children != DEFAULT_EXTERNAL_ACL_CHILDREN)
 	    storeAppendPrintf(sentry, " children=%d", node->children);
 	if (node->concurrency)
 	    storeAppendPrintf(sentry, " concurrency=%d", node->concurrency);
+	if (node->cache_size)
+	    storeAppendPrintf(sentry, " cache=%d", node->cache_size);
 	for (format = node->format; format; format = format->next) {
 	    switch (format->type) {
 	    case EXT_ACL_HEADER:
@@ -356,6 +373,9 @@
 		DUMP_EXT_ACL_TYPE(IDENT);
 #endif
 		DUMP_EXT_ACL_TYPE(SRC);
+		DUMP_EXT_ACL_TYPE(SRCPORT);
+		DUMP_EXT_ACL_TYPE(MYADDR);
+		DUMP_EXT_ACL_TYPE(MYPORT);
 		DUMP_EXT_ACL_TYPE(DST);
 		DUMP_EXT_ACL_TYPE(PROTO);
 		DUMP_EXT_ACL_TYPE(PORT);
@@ -500,20 +520,22 @@
     }
     if (!entry) {
 	entry = hash_lookup(acl->def->cache, key);
-	if (entry && external_acl_entry_expired(acl->def, entry)) {
-	    /* Expired entry, ignore */
-	    debug(82, 2) ("external_acl_cache_lookup: '%s' = expired\n", key);
-	    entry = NULL;
+	if (!entry || external_acl_grace_expired(acl->def, entry)) {
+	    debug(82, 2) ("aclMatchExternal: %s(\"%s\") = lookup needed\n", acl->def->name, key);
+	    if (acl->def->helper->stats.queue_size <= acl->def->helper->n_running) {
+		ch->state[ACL_EXTERNAL] = ACL_LOOKUP_NEEDED;
+		return -1;
+	    } else {
+		if (!entry) {
+		    debug(82, 1) ("aclMatchExternal: '%s' queue overload. Request rejected '%s'.\n", acl->def->name, key);
+		    return -1;
+		} else {
+		    debug(82, 1) ("aclMatchExternal: '%s' queue overload. Using stale result. '%s'\n", acl->def->name, key);
+		    /* Fall thru to processing below */
+		}
+	    }
 	}
     }
-    if (!entry || entry->result == -1) {
-	debug(82, 2) ("aclMatchExternal: %s(\"%s\") = lookup needed\n", acl->def->name, key);
-	if (acl->def->helper->stats.queue_size >= acl->def->helper->n_running)
-	    debug(82, 1) ("aclMatchExternal: '%s' queue overload. Request rejected.\n", acl->def->name);
-	else
-	    ch->state[ACL_EXTERNAL] = ACL_LOOKUP_NEEDED;
-	return -1;
-    }
     external_acl_cache_touch(acl->def, entry);
     result = entry->result;
     external_acl_message = entry->message;
@@ -591,6 +613,17 @@
 	case EXT_ACL_SRC:
 	    str = inet_ntoa(ch->src_addr);
 	    break;
+	case EXT_ACL_SRCPORT:
+	    snprintf(buf, sizeof(buf), "%d", request->client_port);
+	    str = buf;
+	    break;
+	case EXT_ACL_MYADDR:
+	    str = inet_ntoa(request->my_addr);
+	    break;
+	case EXT_ACL_MYPORT:
+	    snprintf(buf, sizeof(buf), "%d", request->my_port);
+	    str = buf;
+	    break;
 	case EXT_ACL_DST:
 	    str = request->host;
 	    break;
@@ -700,6 +733,18 @@
 	return 0;
 }
 
+static int
+external_acl_grace_expired(external_acl * def, external_acl_entry * entry)
+{
+    int ttl;
+    ttl = entry->result == 1 ? def->ttl : def->negative_ttl;
+    ttl = (ttl * (100 - def->grace)) / 100;
+    if (entry->date + ttl < squid_curtime)
+	return 1;
+    else
+	return 0;
+}
+
 static void
 free_external_acl_entry(void *data)
 {
@@ -876,7 +921,7 @@
 	else
 	    external_acl_message = NULL;
 
-	if (cbdataValid(state->callback_data))
+	if (state->callback && cbdataValid(state->callback_data))
 	    state->callback(state->callback_data, entry);
 	cbdataUnlock(state->callback_data);
 	state->callback_data = NULL;
@@ -902,6 +947,10 @@
     const char *key;
     external_acl_entry *entry;
     externalAclState *state;
+    dlink_node *node;
+    externalAclState *oldstate = NULL;
+    int graceful = 0;
+
     if (acl->def->require_auth) {
 	int ti;
 	/* Make sure the user is authenticated */
@@ -920,51 +969,66 @@
     }
     debug(82, 2) ("externalAclLookup: lookup in '%s' for '%s'\n", def->name, key);
     entry = hash_lookup(def->cache, key);
-    state = cbdataAlloc(externalAclState);
-    state->def = def;
-    cbdataLock(state->def);
-    state->callback = callback;
-    state->callback_data = callback_data;
-    state->key = xstrdup(key);
-    cbdataLock(state->callback_data);
-    if (entry && !external_acl_entry_expired(def, entry)) {
-	if (entry->result == -1) {
-	    /* There is a pending lookup. Hook into it */
-	    dlink_node *node;
-	    for (node = def->queue.head; node; node = node->next) {
-		externalAclState *oldstate = node->data;
-		if (strcmp(state->key, oldstate->key) == 0) {
-		    state->queue = oldstate->queue;
-		    oldstate->queue = state;
-		    return;
-		}
-	    }
-	} else {
-	    /* There is a cached valid result.. use it */
-	    /* This should not really happen, but what the heck.. */
-	    external_acl_message = entry->message;
+    if (entry && external_acl_entry_expired(def, entry))
+	entry = NULL;
+
+    /* Check for a pending lookup to hook into */
+    for (node = def->queue.head; node; node = node->next) {
+	externalAclState *oldstatetmp = node->data;
+	if (strcmp(key, oldstatetmp->key) == 0) {
+	    oldstate = oldstatetmp;
+	    break;
+	}
+    }
+
+    /* No need to refresh a already pending lookup during grace period */
+    if (entry && external_acl_grace_expired(def, entry)) {
+	if (oldstate) {
 	    callback(callback_data, entry);
-	    cbdataFree(state);
 	    return;
+	} else {
+	    graceful = 1;
 	}
     }
-    /* Check for queue overload */
-    if (def->helper->stats.queue_size >= def->helper->n_running) {
-	external_acl_entry *entry = hash_lookup(def->cache, key);
-	debug(82, 1) ("externalAclLookup: '%s' queue overload\n", def->name);
-	if (entry)
-	    external_acl_message = entry->message;
-	cbdataFree(state);
+    if (!graceful && entry && !external_acl_grace_expired(def, entry)) {
+	/* Should not really happen, but why not.. */
 	callback(callback_data, entry);
 	return;
     }
-    /* Send it off to the helper */
-    memBufDefInit(&buf);
-    memBufPrintf(&buf, "%s\n", key);
-    helperSubmit(def->helper, buf.buf, externalAclHandleReply, state);
-    external_acl_cache_add(def, key, -1, NULL, NULL, NULL, NULL);
-    dlinkAdd(state, &state->list, &def->queue);
-    memBufClean(&buf);
+    /* No pending lookup found. Sumbit to helper */
+    state = cbdataAlloc(externalAclState);
+    state->def = def;
+    cbdataLock(state->def);
+    state->key = xstrdup(key);
+    if (!graceful) {
+	state->callback = callback;
+	state->callback_data = callback_data;
+	cbdataLock(state->callback_data);
+    }
+    if (oldstate) {
+	/* Hook into pending lookup */
+	state->queue = oldstate->queue;
+	oldstate->queue = state;
+    } else {
+	/* Check for queue overload */
+	if (def->helper->stats.queue_size >= def->helper->n_running) {
+	    debug(82, 1) ("externalAclLookup: '%s' queue overload\n", def->name);
+ 	    cbdataFree(state);
+	    callback(callback_data, entry);
+ 	    return;
+ 	}
+	/* Send it off to the helper */
+	memBufDefInit(&buf);
+	memBufPrintf(&buf, "%s\n", key);
+	helperSubmit(def->helper, buf.buf, externalAclHandleReply, state);
+	dlinkAdd(state, &state->list, &def->queue);
+	memBufClean(&buf);
+    }
+    if (graceful) {
+	/* No need to wait during grace period */
+ 	callback(callback_data, entry);
+ 	return;
+    }
 }
 
 int
Index: src/structs.h
===================================================================
RCS file: /cvsroot/squid/squid/src/structs.h,v
retrieving revision 1.88
diff -u -r1.88 structs.h
--- src/structs.h	22 May 2006 01:28:30 -0000	1.88
+++ src/structs.h	22 May 2006 14:12:32 -0000
@@ -1811,6 +1811,7 @@
     struct in_addr client_addr;
     struct in_addr my_addr;
     unsigned short my_port;
+    unsigned short client_port;
     HttpHeader header;
     squid_off_t content_length;
     HierarchyLogEntry hier;
















