Either set up a new branch from squid-2.6, or submit it via squid-dev.
The latter is perhaps most suitable as it's a small feature ready for
immediate merge.

Attached is the patch for:
1) external acl grace patch for squid 2.6, leaving 'level' for near (?) future.
2) adds 'srcport', 'myaddr', and 'myport' acl tags.

But bringing external-acl-fuzzy up to 2.6 is also a good idea for the
level discussion below.

> I've noticed the 'level' concept in squid2's branch
> external-acl-fuzzy, which is not present in squid3.  I would like to
> add support for this to squid3, unless someone object.

The cache level concept unfortunately did not work out quite the way I
had hoped for and is why it has not been merged. It solves some
problems, but many still unsolved. The problem needs a little more study
before deciding on what should be done.

The main problem with the existing cache level approach is how to handle
the acl arguments.

And I guess that a reply of level=N should delete lower levels
(level=n where n < N) from cache.  So, for each reply, many cached
entries would be deleted ('N-1' to be precise).

Perhaps the solution is to simply allow format tags in the acl arguments
as well, allowing the data to be freely reordered making the cache level
concept fit much better, or a new format tag which expands into the
arguments.

Reordering and combining perhaps?  Allowing combining would raise the
number of cached entries invalidations from N-2 to (2**N)-2 (I am not
counting current reply as an invalidation).

Thinking about it a bit further I start to like the idea of introducing
a new format tag for the acl arguments, with a default of adding the
arguments added at the end of the request if this tag is not used in the
external_acl_type specification.

The format tag should expand to some string we are sure is not present
in any other tags, which is something difficult to assure since we
have %{Header:member} tag.  Adding 'level' support for external acl
cache implies the request/reply pair need some higher level structure
(say XML, or HTTP-alike), unless I am missing something.

Regards,

--
Gonzalo A. Arana
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;















Reply via email to