This patch is generated from the rproxy branch of HEAD in squid
Wed May 17 00:16:13 2006 GMT
See http://devel.squid-cache.org/

Index: squid/acconfig.h
diff -u squid/acconfig.h:1.24 squid/acconfig.h:1.24.2.1
--- squid/acconfig.h:1.24	Mon May 15 08:52:04 2006
+++ squid/acconfig.h	Mon May 15 18:01:31 2006
@@ -407,6 +407,11 @@
  */
 #undef X_ACCELERATOR_VARY
 
+/*
+ * Enable authentication support in accelerators
+ */
+#undef AUTH_ON_ACCELERATION
+
 @BOTTOM@
 
 #endif /* __CONFIGURE_H__ */
Index: squid/configure.in
diff -u squid/configure.in:1.84 squid/configure.in:1.84.2.1
--- squid/configure.in:1.84	Mon May 15 15:50:48 2006
+++ squid/configure.in	Mon May 15 18:01:31 2006
@@ -679,6 +679,15 @@
   fi
 ])
 
+AC_ARG_ENABLE(auth-on-acceleration,
+[  --enable-auth-on-acceleration
+                          Enable authentication in accelerators],
+[ if test "$enableval" = "yes" ; then
+    echo "AUTH_ON_ACCELERATION enabled"
+    AC_DEFINE(AUTH_ON_ACCELERATION)
+  fi
+])
+
 dnl Select Default Error language
 AC_ARG_ENABLE(default-err-language,
 [  --enable-default-err-language=lang
Index: squid/include/cache_snmp.h
diff -u squid/include/cache_snmp.h:1.4 squid/include/cache_snmp.h:1.4.2.1
--- squid/include/cache_snmp.h:1.4	Sun May 14 02:50:55 2006
+++ squid/include/cache_snmp.h	Mon May 15 18:01:31 2006
@@ -124,6 +124,8 @@
     MESH_PTBL_IGN,
     MESH_PTBL_KEEPAL_S,
     MESH_PTBL_KEEPAL_R,
+    MESH_PTBL_INDEX,
+    MESH_PTBL_HOST,
     MESH_PTBL_END
 };
 
Index: squid/src/HttpHeader.c
diff -u squid/src/HttpHeader.c:1.18 squid/src/HttpHeader.c:1.18.2.1
--- squid/src/HttpHeader.c:1.18	Fri May 12 15:51:56 2006
+++ squid/src/HttpHeader.c	Mon May 15 18:01:31 2006
@@ -130,6 +130,8 @@
 #if X_ACCELERATOR_VARY
     {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr},
 #endif
+    {"X-Error-URL", HDR_X_ERROR_URL, ftStr},
+    {"X-Error-Status", HDR_X_ERROR_STATUS, ftInt},
     {"Other:", HDR_OTHER, ftStr}	/* ':' will not allow matches */
 };
 static HttpHeaderFieldInfo *Headers = NULL;
Index: squid/src/HttpRequest.c
diff -u squid/src/HttpRequest.c:1.8 squid/src/HttpRequest.c:1.8.4.1
--- squid/src/HttpRequest.c:1.8	Fri Apr 28 04:10:50 2006
+++ squid/src/HttpRequest.c	Mon May 15 18:01:31 2006
@@ -61,6 +61,7 @@
 	authenticateAuthUserRequestUnlock(req->auth_user_request);
     safe_free(req->canonical);
     safe_free(req->vary_headers);
+    safe_free(req->urlgroup);
     stringClean(&req->urlpath);
     httpHeaderClean(&req->header);
     if (req->cache_control)
Index: squid/src/Makefile.am
diff -u squid/src/Makefile.am:1.33 squid/src/Makefile.am:1.33.2.1
--- squid/src/Makefile.am:1.33	Mon May 15 08:52:05 2006
+++ squid/src/Makefile.am	Mon May 15 18:01:31 2006
@@ -131,6 +131,7 @@
 	errorpage.c \
 	ETag.c \
 	event.c \
+	errormap.c \
 	external_acl.c \
 	fd.c \
 	filemap.c \
@@ -160,6 +161,7 @@
 	ipc.c \
 	ipcache.c \
 	$(LEAKFINDERSOURCE) \
+	locrewrite.c \
 	logfile.c \
 	main.c \
 	mem.c \
@@ -172,7 +174,10 @@
 	Packer.c \
 	pconn.c \
 	peer_digest.c \
+	peer_monitor.c \
 	peer_select.c \
+	peer_sourcehash.c \
+	peer_userhash.c \
 	protos.h \
 	redirect.c \
 	referer.c \
Index: squid/src/acl.c
diff -u squid/src/acl.c:1.63 squid/src/acl.c:1.63.4.1
--- squid/src/acl.c:1.63	Fri Apr 28 04:10:50 2006
+++ squid/src/acl.c	Mon May 15 18:01:31 2006
@@ -40,9 +40,7 @@
 static void aclParseUserList(void **current);
 static void aclParseIpList(void *curlist);
 static void aclParseIntlist(void *curlist);
-#if SQUID_SNMP
 static void aclParseWordList(void *curlist);
-#endif
 static void aclParseProtoList(void *curlist);
 static void aclParseMethodList(void *curlist);
 static void aclParseTimeSpec(void *curlist);
@@ -58,9 +56,7 @@
 static int aclMatchIp(void *dataptr, struct in_addr c);
 static int aclMatchDomainList(void *dataptr, const char *);
 static int aclMatchIntegerRange(intrange * data, int i);
-#if SQUID_SNMP
 static int aclMatchWordList(wordlist *, const char *);
-#endif
 static void aclParseUserMaxIP(void *data);
 static void aclDestroyUserMaxIP(void *data);
 static wordlist *aclDumpUserMaxIP(void *data);
@@ -144,6 +140,8 @@
     if (!strcmp(s, "ident_regex"))
 	return ACL_IDENT_REGEX;
 #endif
+    if (!strncmp(s, "type", 5))
+	return ACL_TYPE;
     if (!strncmp(s, "proto", 5))
 	return ACL_PROTO;
     if (!strcmp(s, "method"))
@@ -186,6 +184,8 @@
 	return ACL_EXTERNAL;
     if (!strcmp(s, "urllogin"))
 	return ACL_URLLOGIN;
+    if (!strcmp(s, "urlgroup"))
+	return ACL_URLGROUP;
     return ACL_NONE;
 }
 
@@ -224,6 +224,8 @@
     if (type == ACL_IDENT_REGEX)
 	return "ident_regex";
 #endif
+    if (type == ACL_TYPE)
+	return "type";
     if (type == ACL_PROTO)
 	return "proto";
     if (type == ACL_METHOD)
@@ -266,6 +268,8 @@
 	return "external";
     if (type == ACL_URLLOGIN)
 	return "urllogin";
+    if (type == ACL_URLGROUP)
+	return "urlgroup";
     return "ERROR";
 }
 
@@ -346,6 +350,36 @@
     }
 }
 
+static void
+aclParseType(void *current)
+{
+    acl_request_type **p = current;
+    acl_request_type *type;
+    char *t = NULL;
+    if (!*p)
+	*p = memAllocate(MEM_ACL_REQUEST_TYPE);
+    type = *p;
+    while ((t = strtokFile())) {
+	if (strcmp(t, "accelerated") == 0) {
+	    type->accelerated = 1;
+	    continue;
+	}
+	if (strcmp(t, "accel") == 0) {
+	    type->accelerated = 1;
+	    continue;
+	}
+	if (strcmp(t, "internal") == 0) {
+	    type->internal = 1;
+	    continue;
+	}
+	if (strcmp(t, "auth") == 0) {
+	    type->internal = 1;
+	    continue;
+	}
+	fatalf("unknown acl type argument '%s'\n", t);
+    }
+}
+
 /*
  * Decode a ascii representation (asc) of a IP adress, and place
  * adress and netmask information in addr and mask.
@@ -706,7 +740,6 @@
     return W;
 }
 
-#if SQUID_SNMP
 static void
 aclParseWordList(void *curlist)
 {
@@ -714,7 +747,6 @@
     while ((t = strtokFile()))
 	wordlistAdd(curlist, t);
 }
-#endif
 
 static void
 aclParseUserList(void **current)
@@ -878,6 +910,9 @@
 	aclParseRegexList(&A->data);
 	break;
 #endif
+    case ACL_TYPE:
+	aclParseType(&A->data);
+	break;
     case ACL_PROTO:
 	aclParseProtoList(&A->data);
 	break;
@@ -923,6 +958,9 @@
     case ACL_EXTERNAL:
 	aclParseExternal(&A->data);
 	break;
+    case ACL_URLGROUP:
+	aclParseWordList(&A->data);
+	break;
     case ACL_NONE:
     case ACL_ENUM_MAX:
 	fatal("Bad ACL type");
@@ -1493,7 +1531,6 @@
     return 0;
 }
 
-#if SQUID_SNMP
 static int
 aclMatchWordList(wordlist * w, const char *word)
 {
@@ -1506,7 +1543,16 @@
     }
     return 0;
 }
-#endif
+
+static int
+aclMatchType(acl_request_type * type, request_t * request)
+{
+    if (type->accelerated && request->flags.accelerated)
+	return 1;
+    if (type->internal && request->flags.internal)
+	return 1;
+    return 0;
+}
 
 int
 aclAuthenticated(aclCheck_t * checklist)
@@ -1578,6 +1624,7 @@
     case ACL_DST_IP:
     case ACL_MAX_USER_IP:
     case ACL_METHOD:
+    case ACL_TYPE:
     case ACL_PROTO:
     case ACL_PROXY_AUTH:
     case ACL_PROXY_AUTH_REGEX:
@@ -1735,6 +1782,8 @@
 	}
 	/* NOTREACHED */
 #endif
+    case ACL_TYPE:
+	return aclMatchType(ae->data, r);
     case ACL_PROTO:
 	return aclMatchInteger(ae->data, r->protocol);
 	/* NOTREACHED */
@@ -1824,6 +1873,11 @@
     case ACL_EXTERNAL:
 	return aclMatchExternal(ae->data, checklist);
 	/* NOTREACHED */
+    case ACL_URLGROUP:
+	if (!checklist->request->urlgroup)
+	    return 0;
+	return aclMatchWordList(ae->data, checklist->request->urlgroup);
+	/* NOTREACHED */
     case ACL_NONE:
     case ACL_ENUM_MAX:
 	break;
@@ -2238,6 +2292,11 @@
     memFree(d, MEM_ACL_USER_DATA);
 }
 
+static void
+aclDestroyType(acl_request_type * type)
+{
+    memFree(type, MEM_ACL_REQUEST_TYPE);
+}
 
 void
 aclDestroyAcls(acl ** head)
@@ -2291,6 +2350,9 @@
 	case ACL_REQ_MIME_TYPE:
 	    aclDestroyRegexList(a->data);
 	    break;
+	case ACL_TYPE:
+	    aclDestroyType(a->data);
+	    break;
 	case ACL_REP_HEADER:
 	case ACL_REQ_HEADER:
 	    aclDestroyHeader(a->data);
@@ -2315,6 +2377,9 @@
 	case ACL_EXTERNAL:
 	    aclDestroyExternal(&a->data);
 	    break;
+	case ACL_URLGROUP:
+	    wordlistDestroy((wordlist **) & a->data);
+	    break;
 	case ACL_NONE:
 	case ACL_ENUM_MAX:
 	    debug(28, 1) ("aclDestroyAcls: no case for ACL type %d\n", a->type);
@@ -2673,6 +2738,17 @@
     return W;
 }
 
+static wordlist *
+aclDumpType(acl_request_type * type)
+{
+    wordlist *W = NULL;
+    if (type->accelerated)
+	wordlistAdd(&W, "accelerated");
+    if (type->internal)
+	wordlistAdd(&W, "internal");
+    return W;
+}
+
 wordlist *
 aclDumpGeneric(const acl * a)
 {
@@ -2722,6 +2798,8 @@
     case ACL_URL_PORT:
     case ACL_MY_PORT:
 	return aclDumpIntRangeList(a->data);
+    case ACL_TYPE:
+	return aclDumpType(a->data);
     case ACL_PROTO:
 	return aclDumpProtoList(a->data);
     case ACL_METHOD:
@@ -2732,6 +2810,8 @@
 #endif
     case ACL_EXTERNAL:
 	return aclDumpExternal(a->data);
+    case ACL_URLGROUP:
+	return wordlistDup(a->data);
     case ACL_NONE:
     case ACL_ENUM_MAX:
 	break;
Index: squid/src/cache_cf.c
diff -u squid/src/cache_cf.c:1.59 squid/src/cache_cf.c:1.59.2.1
--- squid/src/cache_cf.c:1.59	Mon May 15 15:50:48 2006
+++ squid/src/cache_cf.c	Mon May 15 18:01:31 2006
@@ -102,10 +102,16 @@
 static void parse_denyinfo(acl_deny_info_list ** var);
 static void dump_denyinfo(StoreEntry * entry, const char *name, acl_deny_info_list * var);
 static void free_denyinfo(acl_deny_info_list ** var);
+#if CURRENTLY_UNUSED
 static void parse_sockaddr_in_list(sockaddr_in_list **);
 static void dump_sockaddr_in_list(StoreEntry *, const char *, const sockaddr_in_list *);
 static void free_sockaddr_in_list(sockaddr_in_list **);
 static int check_null_sockaddr_in_list(const sockaddr_in_list *);
+#endif /* CURRENTLY_UNUSED */
+static void parse_http_port_list(http_port_list **);
+static void dump_http_port_list(StoreEntry *, const char *, const http_port_list *);
+static void free_http_port_list(http_port_list **);
+static int check_null_http_port_list(const http_port_list *);
 #if USE_SSL
 static void parse_https_port_list(https_port_list **);
 static void dump_https_port_list(StoreEntry *, const char *, const https_port_list *);
@@ -333,7 +339,6 @@
 static void
 configDoConfigure(void)
 {
-    LOCAL_ARRAY(char, buf, BUFSIZ);
     memset(&Config2, '\0', sizeof(SquidConfig2));
     /* init memory as early as possible */
     memConfigure();
@@ -361,16 +366,17 @@
     if (Config.dnsChildren < 1)
 	fatal("No dnsservers allocated");
 #endif
-    if (Config.Program.redirect) {
-	if (Config.redirectChildren < 1) {
-	    Config.redirectChildren = 0;
-	    wordlistDestroy(&Config.Program.redirect);
+    if (Config.Program.url_rewrite.command) {
+	if (Config.Program.url_rewrite.children < 1) {
+	    Config.Program.url_rewrite.children = 0;
+	    wordlistDestroy(&Config.Program.url_rewrite.command);
 	}
     }
-    if (Config.Accel.host) {
-	snprintf(buf, BUFSIZ, "http://%s:%d", Config.Accel.host, Config.Accel.port);
-	Config2.Accel.prefix = xstrdup(buf);
-	Config2.Accel.on = 1;
+    if (Config.Program.location_rewrite.command) {
+	if (Config.Program.location_rewrite.children < 1) {
+	    Config.Program.location_rewrite.children = 0;
+	    wordlistDestroy(&Config.Program.location_rewrite.command);
+	}
     }
     if (Config.appendDomain)
 	if (*Config.appendDomain != '.')
@@ -378,13 +384,6 @@
     if (Config.errHtmlText == NULL)
 	Config.errHtmlText = xstrdup(null_string);
     storeConfigure();
-    if (Config2.Accel.on && !strcmp(Config.Accel.host, "virtual")) {
-	vhost_mode = 1;
-	if (Config.Accel.port == 0)
-	    vport_mode = 1;
-    }
-    if (Config.Sockaddr.http == NULL)
-	fatal("No http_port specified!");
     snprintf(ThisCache, sizeof(ThisCache), "%s:%d (%s)",
 	uniqueHostname(),
 	(int) ntohs(Config.Sockaddr.http->s.sin_port),
@@ -418,8 +417,10 @@
 #if USE_UNLINKD
     requirePathnameExists("unlinkd_program", Config.Program.unlinkd);
 #endif
-    if (Config.Program.redirect)
-	requirePathnameExists("redirect_program", Config.Program.redirect->key);
+    if (Config.Program.url_rewrite.command)
+	requirePathnameExists("url_rewrite_program", Config.Program.url_rewrite.command->key);
+    if (Config.Program.location_rewrite.command)
+	requirePathnameExists("location_rewrite_program", Config.Program.location_rewrite.command->key);
     requirePathnameExists("Icon Directory", Config.icons.directory);
     requirePathnameExists("Error Directory", Config.errorDirectory);
 #if HTTP_VIOLATIONS
@@ -1510,7 +1511,7 @@
 		d->domain);
 	}
 	if (p->access) {
-	    snprintf(xname, 128, "cache_peer_access %s", p->host);
+	    snprintf(xname, 128, "cache_peer_access %s", p->name);
 	    dump_acl_access(entry, xname, p->access);
 	}
 	for (t = p->typelist; t; t = t->next) {
@@ -1534,9 +1535,11 @@
     p->icp.port = CACHE_ICP_PORT;
     p->weight = 1;
     p->stats.logged_state = PEER_ALIVE;
+    p->monitor.state = PEER_ALIVE;
     if ((token = strtok(NULL, w_space)) == NULL)
 	self_destruct();
     p->host = xstrdup(token);
+    p->name = xstrdup(token);
     if ((token = strtok(NULL, w_space)) == NULL)
 	self_destruct();
     p->type = parseNeighborType(token);
@@ -1567,6 +1570,10 @@
 	    p->options.default_parent = 1;
 	} else if (!strcasecmp(token, "round-robin")) {
 	    p->options.roundrobin = 1;
+	} else if (!strcasecmp(token, "userhash")) {
+	    p->options.userhash = 1;
+	} else if (!strcasecmp(token, "sourcehash")) {
+	    p->options.sourcehash = 1;
 #if USE_HTCP
 	} else if (!strcasecmp(token, "htcp")) {
 	    p->options.htcp = 1;
@@ -1597,11 +1604,40 @@
 	    p->options.allow_miss = 1;
 	} else if (!strncasecmp(token, "max-conn=", 9)) {
 	    p->max_conn = atoi(token + 9);
+	} else if (!strcasecmp(token, "originserver")) {
+	    p->options.originserver = 1;
+	} else if (!strncasecmp(token, "name=", 5)) {
+	    safe_free(p->name);
+	    if (token[5])
+		p->name = xstrdup(token + 5);
+	} else if (!strncasecmp(token, "monitorurl=", 11)) {
+	    safe_free(p->monitor.url);
+	    if (token[11])
+		p->monitor.url = xstrdup(token + 11);
+	} else if (!strncasecmp(token, "monitorsize=", 12)) {
+	    p->monitor.min = atoi(token + 12);
+	    p->monitor.max = -1;
+	    if (strchr(token + 12, ','))
+		token = strchr(token + 12, ',');
+	    else
+		token = strchr(token + 12, '-');
+	    if (token)
+		p->monitor.max = atoi(token + 1);
+	} else if (!strncasecmp(token, "monitorinterval=", 16)) {
+	    p->monitor.interval = atoi(token + 16);
+	} else if (!strncasecmp(token, "monitortimeout=", 15)) {
+	    p->monitor.timeout = atoi(token + 15);
+	} else if (!strncasecmp(token, "forceddomain=", 13)) {
+	    safe_free(p->domain);
+	    if (token[13])
+		p->domain = xstrdup(token + 13);
 	} else {
 	    debug(3, 0) ("parse_peer: token='%s'\n", token);
 	    self_destruct();
 	}
     }
+    if (peerFindByName(p->name))
+	fatalf("ERROR: cache_peer %s specified twice\n", p->name);
     if (p->weight < 1)
 	p->weight = 1;
     p->icp.version = ICP_VERSION_CURRENT;
@@ -2370,6 +2406,62 @@
     storeAppendPrintf(entry, "\n");
 }
 
+static void
+parse_errormap(errormap ** head)
+{
+    errormap *m = xcalloc(1, sizeof(*m));
+    char *url = strtok(NULL, w_space);
+    char *token;
+    struct error_map_entry **tail = &m->map;
+    if (!url)
+	self_destruct();
+    m->url = xstrdup(url);
+    while ((token = strtok(NULL, w_space))) {
+	struct error_map_entry *e = xcalloc(1, sizeof(*e));
+	e->value = xstrdup(token);
+	e->status = atoi(token);
+	if (!e->status)
+	    e->status = -errorPageId(token);
+	if (!e->status)
+	    debug(15, 0) ("WARNING: Unknown errormap code: %s\n", token);
+	*tail = e;
+	tail = &e->next;
+    }
+    while (*head)
+	head = &(*head)->next;
+    *head = m;
+}
+
+static void
+dump_errormap(StoreEntry * entry, const char *name, errormap * map)
+{
+    while (map) {
+	struct error_map_entry *me;
+	storeAppendPrintf(entry, "%s %s",
+	    name, map->url);
+	for (me = map->map; me; me = me->next)
+	    storeAppendPrintf(entry, " %s", me->value);
+	storeAppendPrintf(entry, "\n");
+	map = map->next;
+    }
+}
+
+static void
+free_errormap(errormap ** head)
+{
+    while (*head) {
+	errormap *map = *head;
+	*head = map->next;
+	while (map->map) {
+	    struct error_map_entry *me = map->map;
+	    map->map = me->next;
+	    safe_free(me->value);
+	    safe_free(me);
+	}
+	safe_free(map->url);
+	safe_free(map);
+    }
+}
 
 #include "cf_parser.h"
 
@@ -2390,6 +2482,10 @@
     return PEER_SIBLING;
 }
 
+#if CURRENTLY_UNUSED
+/* This code was previously used by http_port. Left as it really should
+ * be used by icp_port and htcp_port
+ */
 static void
 parse_sockaddr_in_list(sockaddr_in_list ** head)
 {
@@ -2457,22 +2553,15 @@
 {
     return NULL == s;
 }
+#endif /* CURRENTLY_UNUSED */
 
-#if USE_SSL
 static void
-parse_https_port_list(https_port_list ** head)
+parse_http_port_specification(http_port_list * s, char *token)
 {
-    char *token;
-    char *t;
-    char *host;
+    char *host = NULL;
     const struct hostent *hp;
-    unsigned short port;
-    https_port_list *s;
-    token = strtok(NULL, w_space);
-    if (!token)
-	self_destruct();
-    host = NULL;
-    port = 0;
+    unsigned short port = 0;
+    char *t;
     if ((t = strchr(token, ':'))) {
 	/* host:port */
 	host = token;
@@ -2485,16 +2574,150 @@
     } else {
 	self_destruct();
     }
-    s = xcalloc(1, sizeof(*s));
     s->s.sin_port = htons(port);
     if (NULL == host)
 	s->s.sin_addr = any_addr;
     else if (1 == safe_inet_addr(host, &s->s.sin_addr))
 	(void) 0;
-    else if ((hp = gethostbyname(host)))	/* dont use ipcache */
+    else if ((hp = gethostbyname(host))) {
+	/* dont use ipcache */
 	s->s.sin_addr = inaddrFromHostent(hp);
-    else
+	s->defaultsite = xstrdup(host);
+    } else
+	self_destruct();
+}
+
+static void
+parse_http_port_option(http_port_list * s, char *token)
+{
+    if (strncmp(token, "defaultsite=", 12) == 0) {
+	safe_free(s->defaultsite);
+	s->defaultsite = xstrdup(token + 12);
+	s->accel = 1;
+    } else if (strncmp(token, "name=", 5) == 0) {
+	safe_free(s->name);
+	s->name = xstrdup(token + 5);
+    } else if (strcmp(token, "transparent") == 0) {
+	s->transparent = 1;
+    } else if (strcmp(token, "vhost") == 0) {
+	s->vhost = 1;
+	s->accel = 1;
+    } else if (strcmp(token, "vport") == 0) {
+	s->vport = ntohs(s->s.sin_port);
+	s->accel = 1;
+    } else if (strncmp(token, "vport=", 6) == 0) {
+	s->vport = atoi(token + 6);
+	s->accel = 1;
+    } else if (strncmp(token, "urlgroup=", 9) == 0) {
+	s->urlgroup = xstrdup(token + 9);
+    } else if (strncmp(token, "protocol=", 9) == 0) {
+	s->protocol = xstrdup(token + 9);
+    } else {
+	self_destruct();
+    }
+}
+
+static void
+free_generic_http_port_data(http_port_list * s)
+{
+    safe_free(s->name);
+    safe_free(s->defaultsite);
+}
+
+static void
+cbdataFree_http_port(void *data)
+{
+    free_generic_http_port_data(data);
+}
+
+
+static void
+parse_http_port_list(http_port_list ** head)
+{
+    CBDATA_TYPE(http_port_list);
+    char *token;
+    http_port_list *s;
+    CBDATA_INIT_TYPE_FREECB(http_port_list, cbdataFree_http_port);
+    token = strtok(NULL, w_space);
+    if (!token)
 	self_destruct();
+    s = cbdataAlloc(http_port_list);
+    s->protocol = xstrdup("http");
+    parse_http_port_specification(s, token);
+    /* parse options ... */
+    while ((token = strtok(NULL, w_space))) {
+	parse_http_port_option(s, token);
+    }
+    while (*head)
+	head = &(*head)->next;
+    *head = s;
+}
+
+static void
+dump_generic_http_port(StoreEntry * e, const char *n, const http_port_list * s)
+{
+    storeAppendPrintf(e, "%s %s:%d",
+	n,
+	inet_ntoa(s->s.sin_addr),
+	ntohs(s->s.sin_port));
+    if (s->defaultsite)
+	storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
+    if (s->transparent)
+	storeAppendPrintf(e, " transparent");
+    if (s->vhost)
+	storeAppendPrintf(e, " vhost");
+    if (s->vport)
+	storeAppendPrintf(e, " vport");
+}
+static void
+dump_http_port_list(StoreEntry * e, const char *n, const http_port_list * s)
+{
+    while (s) {
+	dump_generic_http_port(e, n, s);
+	storeAppendPrintf(e, "\n");
+	s = s->next;
+    }
+}
+
+static void
+free_http_port_list(http_port_list ** head)
+{
+    http_port_list *s;
+    while ((s = *head) != NULL) {
+	*head = s->next;
+	cbdataFree(s);
+    }
+}
+
+static int
+check_null_http_port_list(const http_port_list * s)
+{
+    return NULL == s;
+}
+
+#if USE_SSL
+static void
+cbdataFree_https_port(void *data)
+{
+    https_port_list *s = data;
+    free_generic_http_port_data(&s->http);
+    safe_free(s->cert);
+    safe_free(s->key);
+}
+
+static void
+parse_https_port_list(https_port_list ** head)
+{
+    CBDATA_TYPE(https_port_list);
+    char *token;
+    https_port_list *s;
+    CBDATA_INIT_TYPE_FREECB(https_port_list, cbdataFree_https_port);
+    token = strtok(NULL, w_space);
+    if (!token)
+	self_destruct();
+    s = cbdataAlloc(https_port_list);
+    s->http.protocol = xstrdup("https");
+    parse_http_port_specification(&s->http, token);
     /* parse options ... */
     while ((token = strtok(NULL, w_space))) {
 	if (strncmp(token, "cert=", 5) == 0) {
@@ -2514,11 +2737,11 @@
 	    safe_free(s->cipher);
 	    s->cipher = xstrdup(token + 7);
 	} else {
-	    self_destruct();
+	    parse_http_port_option(&s->http, token);
 	}
     }
     while (*head)
-	head = &(*head)->next;
+	head = (https_port_list **) (&(*head)->http.next);
     *head = s;
 }
 
@@ -2526,12 +2749,11 @@
 dump_https_port_list(StoreEntry * e, const char *n, const https_port_list * s)
 {
     while (s) {
-	storeAppendPrintf(e, "%s %s:%d cert=\"%s\" key=\"%s\"",
-	    n,
-	    inet_ntoa(s->s.sin_addr),
-	    ntohs(s->s.sin_port),
-	    s->cert,
-	    s->key);
+	dump_generic_http_port(e, n, &s->http);
+	if (s->cert)
+	    storeAppendPrintf(e, " cert=%s", s->cert);
+	if (s->key)
+	    storeAppendPrintf(e, " key=%s", s->key);
 	if (s->version)
 	    storeAppendPrintf(e, " version=%d", s->version);
 	if (s->options)
@@ -2539,7 +2761,7 @@
 	if (s->cipher)
 	    storeAppendPrintf(e, " cipher=%s", s->cipher);
 	storeAppendPrintf(e, "\n");
-	s = s->next;
+	s = (https_port_list *) s->http.next;
     }
 }
 
@@ -2548,10 +2770,8 @@
 {
     https_port_list *s;
     while ((s = *head) != NULL) {
-	*head = s->next;
-	safe_free(s->cert);
-	safe_free(s->key);
-	safe_free(s);
+	*head = (https_port_list *) s->http.next;
+	cbdataFree(s);
     }
 }
 
@@ -2568,7 +2788,6 @@
 void
 configFreeMemory(void)
 {
-    safe_free(Config2.Accel.prefix);
     free_all();
 }
 
Index: squid/src/cf.data.pre
diff -u squid/src/cf.data.pre:1.95 squid/src/cf.data.pre:1.95.2.1
--- squid/src/cf.data.pre:1.95	Mon May 15 15:50:48 2006
+++ squid/src/cf.data.pre	Mon May 15 18:01:31 2006
@@ -54,14 +54,14 @@
 COMMENT_END
 
 NAME: http_port ascii_port
-TYPE: sockaddr_in_list
+TYPE: http_port_list
 DEFAULT: none
 DEFAULT_IF_NONE: @DEFAULT_HTTP_PORT@
 LOC: Config.Sockaddr.http
 DOC_START
-	Usage:	port
-		hostname:port
-		1.2.3.4:port
+	Usage:	port [options]
+		hostname:port [options]
+		1.2.3.4:port [options]
 
 	The socket addresses where Squid will listen for HTTP client
 	requests.  You may specify multiple socket addresses.
@@ -83,6 +83,19 @@
 
 	You may specify multiple socket addresses on multiple lines.
 
+	options are:
+		transparent	Support for transparent proxies
+		vhost		Accelerator using Host directive
+		vport		Accelerator with IP virtual host support
+		vport=		As above, but uses specified port number
+				rather than the http_port number.
+		defaultsite=	Main web site name for accelerators.
+		urlgroup=	Default urlgroup to mark requests
+				with (see also acl urlgroup and
+				url_rewrite_program)
+		protocol=	Protocol to reconstruct accelerated
+				requests with. Defaults to http.
+
 	If you run Squid on a dual-homed machine with an internal
 	and an external interface we recommend you to specify the
 	internal address:port in http_port. This way Squid will only be
@@ -109,6 +122,15 @@
 
 	Options:
 
+	   defaultsite=	The name of the https site presented on
+			this port.
+
+	   urlgroup=	Default urlgroup to mark requests with (see
+			also acl urlgroup and url_rewrite_program)
+
+	   protocol=	Protocol to reconstruct accelerated requests
+			with. Defaults to https.
+
 	   cert=	Path to SSL certificate (PEM format)
 
 	   key=		Path to SSL private key file (PEM format)
@@ -276,6 +298,16 @@
 		     max-conn
 		     htcp
 		     carp-load-factor
+		     originserver
+		     userhash
+		     sourcehash
+		     name=xxx
+		     monitorurl=url
+		     monitorsize=sizespec
+		     monitorinterval=seconds
+		     monitortimeout=seconds
+		     group=name
+		     forceddomain=name
 
 		     use 'proxy-only' to specify objects fetched
 		     from this cache should not be saved locally.
@@ -373,7 +405,42 @@
 		     cache as one participating in a CARP array.
 		     The 'f' values for all CARP parents must add
 		     up to 1.0.
-
+		 
+		     'originserver' causes this parent peer to be contacted as
+		     a origin server. Meant to be used in accelerator setups.
+
+		     use 'userhash' to load-balance amongs a set of parents
+		     based on the client proxy_auth or ident username.
+
+		     use 'sourcehash' to load-balanse amongs a set of parents
+		     based on the client source ip.
+
+		     use 'name=xxx' if you have multiple peers on the same
+		     host but different ports. This name can then be used to
+		     differentiate the peers in cache_peer_access and similar
+		     directives.
+
+		     use 'monitorurl=url' to have periodically request a given
+		     URL from the peer, and only concider the peer as alive
+		     if this monitoring is successful (default none)
+
+		     use 'monitorsize=min[-max]' to limit the size range of 
+		     'monitorurl' replies considered valid. Defaults to 0 to
+		     accept any size replies as valid.
+
+		     use 'monitorinterval=seconds' to change frequency of
+		     how often the peer is monitored with 'monitorurl'
+		     (default 300 for a 5 minute interval). If set to 0
+		     then monitoring is disabled even if a URL is defined.
+
+		     use 'monitortimeout=seconds' to change the timeout of
+		     'monitorurl'. Defaults to 'monitorinterval'.
+
+		     use 'forceddomain=name' to forcibly set the Host header
+		     of requests forwarded to this peer. Useful in accelerator
+		     setups where the server (peer) expects a certain domain
+		     name and using redirectors to feed this domainname
+		     is not feasible.
 
 	NOTE: non-ICP/HTCP neighbors must be specified as 'parent'.
 DOC_END
@@ -1309,22 +1376,36 @@
 DOC_END
 
 
-NAME: redirect_program
+NAME: url_rewrite_program redirect_program
 TYPE: wordlist
-LOC: Config.Program.redirect
+LOC: Config.Program.url_rewrite.command
 DEFAULT: none
 DOC_START
-	Specify the location of the executable for the URL redirector.
+	Specify the location of the executable for the URL rewriter.
 	Since they can perform almost any function there isn't one included.
-	See the FAQ (section 15) for information on how to write one.
-	By default, a redirector is not used.
-DOC_END
 
+	For each requested URL rewriter will receive on line with the format
+
+	URL <SP> client_ip "/" fqdn <SP> user <SP> method <SP> urlgroup <NL>
+
+	And the rewriter may return a rewritten URL. The other components of
+	the request line does not need to be returned (ignored if they are).
+
+	The rewriter can also indicate that a client-side redirect should
+	be performed to the new URL. This is done by prefixing the returned
+	URL with "301:" (moved permanently) or 302: (moved temporarily).
+	
+	It can also return a "urlgroup" that can subsequently be matched
+	in cache_peer_access and similar ACL driven rules. An urlgroup is
+	returned by prefixing the returned url with "!urlgroup!"
+
+	By default, a URL rewriter is not used.
+DOC_END
 
-NAME: redirect_children
+NAME: url_rewrite_children redirect_children
 TYPE: int
 DEFAULT: 5
-LOC: Config.redirectChildren
+LOC: Config.Program.url_rewrite.children
 DOC_START
 	The number of redirector processes to spawn. If you start
 	too few Squid will have to wait for them to process a backlog of
@@ -1332,7 +1413,7 @@
 	and other system resources.
 DOC_END
 
-NAME: redirect_rewrites_host_header
+NAME: url_rewrite_host_header redirect_rewrites_host_header
 TYPE: onoff
 DEFAULT: on
 LOC: Config.onoff.redir_rewrites_host
@@ -1340,18 +1421,64 @@
 	By default Squid rewrites any Host: header in redirected
 	requests.  If you are running an accelerator this may
 	not be a wanted effect of a redirector.
+
+	WARNING: Entries are cached on the result of the URL rewriting
+	process, so be careful if you have domain-virtual hosts.
 DOC_END
 
-NAME: redirector_access
+NAME: url_rewrite_access redirector_access
 TYPE: acl_access
 DEFAULT: none
-LOC: Config.accessList.redirector
+LOC: Config.accessList.url_rewrite
 DOC_START
 	If defined, this access list specifies which requests are
 	sent to the redirector processes.  By default all requests
 	are sent.
 DOC_END
 
+NAME: location_rewrite_program
+TYPE: wordlist
+LOC: Config.Program.location_rewrite.command
+DEFAULT: none
+DOC_START
+	Specify the location of the executable for the Location rewriter,
+	used to rewrite server generated redirects. Usually used in
+	conjunction with a url_rewrite_program
+
+	For each Location header received the location rewriter will receive
+	one line with the format:
+
+	   location URL <SP> requested URL <SP> urlgroup <NL>
+
+	And the rewriter may return a rewritten Location URL or a blank line.
+	The other components of the request line does not need to be returned
+	(ignored if they are).
+
+	By default, a Location rewriter is not used.
+DOC_END
+
+NAME: location_rewrite_children
+TYPE: int
+DEFAULT: 5
+LOC: Config.Program.location_rewrite.children
+DOC_START
+	The number of location rewriting processes to spawn. If you start
+	too few Squid will have to wait for them to process a backlog of
+	URLs, slowing it down. If you start too many they will use RAM
+	and other system resources.
+DOC_END
+
+NAME: location_rewrite_access
+TYPE: acl_access
+DEFAULT: none
+LOC: Config.accessList.location_rewrite
+DOC_START
+	If defined, this access list specifies which requests are
+	sent to the location rewriting processes.  By default all Location
+	headers are sent.
+DOC_END
+
+
 NAME: auth_param
 TYPE: authparam
 LOC: Config.authConfig
@@ -2246,6 +2373,9 @@
 	  # external ACL lookup via a helper class defined by the
 	  # external_acl_type directive.
 
+	acl urlgroup group1 ...
+	  # match against the urlgroup as indicated by redirectors
+
 Examples:
 acl macaddress arp 09:00:2b:23:45:67
 acl myexample dst_as 1241
@@ -2326,6 +2456,17 @@
 NOCOMMENT_END
 DOC_END
 
+NAME: http_access2
+TYPE: acl_access
+LOC: Config.accessList.http2
+DEFAULT: none
+DOC_START
+	Allowing or Denying access based on defined access lists
+
+	Identical to http_access, but runs after redirectors. If not set
+	then only http_access is used.
+DOC_END
+
 NAME: http_reply_access
 TYPE: acl_access
 LOC: Config.accessList.reply
@@ -2748,89 +2889,6 @@
  -----------------------------------------------------------------------------
 COMMENT_END
 
-NAME: httpd_accel_host
-TYPE: string
-LOC: Config.Accel.host
-DEFAULT: none
-DOC_NONE
-
-NAME: httpd_accel_port
-TYPE: ushort
-LOC: Config.Accel.port
-DEFAULT: 80
-DOC_START
-	If you want to run Squid as an httpd accelerator, define the
-	host name and port number where the real HTTP server is.
-
-	If you want IP based virtual host support specify the
-	hostname as "virtual". This will make Squid use the IP address
-	where it accepted the request as hostname in the URL.
-
-	If you want virtual port support specify the port as "0".
-
-	NOTE: enabling httpd_accel_host disables proxy-caching and
-	ICP.  If you want these features enabled also, set
-	the 'httpd_accel_with_proxy' option.
-DOC_END
-
-NAME: httpd_accel_single_host
-COMMENT: on|off
-TYPE: onoff
-LOC: Config.Accel.single_host
-DEFAULT: off
-DOC_START
-	If you are running Squid as an accelerator and have a single backend
-	server set this to on. This causes Squid to forward the request
-	to this server, regardless of what any redirectors or Host headers
-	say.
-
-	Leave this at off if you have multiple backend servers, and use a
-	redirector (or host table or private DNS) to map the requests to the
-	appropriate backend servers. Note that the mapping needs to be a
-	1-1 mapping between requested and backend (from redirector) domain
-	names or caching will fail, as caching is performed using the
-	URL returned from the redirector.
-
-	See also redirect_rewrites_host_header.
-DOC_END
-
-NAME: httpd_accel_with_proxy
-COMMENT: on|off
-TYPE: onoff
-DEFAULT: off
-LOC: Config.onoff.accel_with_proxy
-DOC_START
-	If you want to use Squid as both a local httpd accelerator
-	and as a proxy, change this to 'on'. Note however your
-	proxy users may have trouble to reach the accelerated domains
-	unless their browsers are configured not to use this proxy for
-	those domains (for example via the no_proxy browser configuration
-	setting)
-DOC_END
-
-NAME: httpd_accel_uses_host_header
-COMMENT: on|off
-TYPE: onoff
-DEFAULT: off
-LOC: Config.onoff.accel_uses_host_header
-DOC_START
-	HTTP/1.1 requests include a Host: header which is basically the
-	hostname from the URL.  The Host: header is used for domain based
-	virtual hosts. If your accelerator needs to provide domain based
-	virtual hosts on the same IP address you will need to turn this
-	on.
-
-	Note Squid does NOT check the value of the Host header matches
-	any of your accelerated server, so it may open a big security hole
-	unless you take care to set up access controls proper.  We recommend
-	this option remain disabled unless you are sure of what you
-	are doing.
-
-	However, you will need to enable this option if you run Squid
-	as a transparent proxy.  Otherwise, virtual servers which
-	require the Host: header will not be properly cached.
-DOC_END
-
 NAME: httpd_accel_no_pmtu_disc
 COMMENT: on|off
 TYPE: onoff
@@ -2913,6 +2971,45 @@
 	the default buffer size.
 DOC_END
 
+NAME: error_map
+TYPE: errormap
+LOC: Config.errorMapList
+DEFAULT: none
+DOC_START
+	Map errors to custom messages
+
+	    error_map message_url http_status ...
+
+	http_status ... is a list of HTTP status codes or Squid error
+	messages.
+
+	Use in accelerators to substitute the error messages returned
+	by servers with other custom errors. 
+
+	    error_map http://your.server/error/404.shtml 404
+	
+	Requests for error messages is a GET request for the configured
+	URL with the following special headers
+
+	    X-Error-Status:	The received HTTP status code (i.e. 404)
+	    X-Request-URI:	The requested URI where the error occurred
+	
+	In Addition the following headers are forwarded from the client
+	request:
+	    
+	    User-Agent, Cookie, X-Forwarded-For, Via, Authorization,
+	    Accept, Referer
+	
+	And the following headers from the server reply:
+
+	    Server, Via, Location, Content-Location
+	
+	The reply returned to the client will carry the original HTTP
+	headers from the real error message, but with the reply body
+	of the configured error message.
+
+DOC_END
+
 NAME: err_html_text
 TYPE: eol
 LOC: Config.errHtmlText
@@ -2928,7 +3025,6 @@
 	insert a %L tag in the error template file.
 DOC_END
 
-
 NAME: deny_info
 TYPE: denyinfo
 LOC: Config.denyInfoList
@@ -2995,6 +3091,16 @@
 	reduced memory thrashing in your malloc library.
 DOC_END
 
+NAME: via
+COMMENT: on|off
+TYPE: onoff
+DEFAULT: on
+LOC: Config.onoff.via
+DOC_START
+	If set (default), Squid will include a Via header in requests and
+	replies.
+DOC_END
+
 NAME: forwarded_for
 COMMENT: on|off
 TYPE: onoff
@@ -3012,6 +3118,7 @@
 		X-Forwarded-For: unknown
 DOC_END
 
+
 NAME: log_icp_queries
 COMMENT: on|off
 TYPE: onoff
Index: squid/src/client_side.c
diff -u squid/src/client_side.c:1.85 squid/src/client_side.c:1.85.2.1
--- squid/src/client_side.c:1.85	Mon May 15 15:50:48 2006
+++ squid/src/client_side.c	Mon May 15 18:01:31 2006
@@ -125,12 +125,12 @@
 static void clientCheckNoCacheDone(int answer, void *data);
 static STCB clientHandleIMSReply;
 static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request);
-static int checkAccelOnly(clientHttpRequest *);
 #if USE_IDENT
 static IDCB clientIdentDone;
 #endif
 static int clientOnlyIfCached(clientHttpRequest * http);
 static STCB clientSendMoreData;
+static STCB clientSendMoreHeaderData;
 static STCB clientCacheHit;
 static void clientSetKeepaliveFlag(clientHttpRequest *);
 static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb);
@@ -148,27 +148,11 @@
 static int clientRequestBodyTooLarge(squid_off_t clen);
 static void clientProcessBody(ConnStateData * conn);
 static void clientEatRequestBody(clientHttpRequest *);
+static void clientAccessCheckDone2(int answer, void *data);
+static void clientAccessCheck2(void *data);
 static BODY_HANDLER clientReadBody;
 static void clientAbortBody(request_t * req);
 
-static int
-checkAccelOnly(clientHttpRequest * http)
-{
-    /* return TRUE if someone makes a proxy request to us and
-     * we are in httpd-accel only mode */
-    if (!Config2.Accel.on)
-	return 0;
-    if (Config.onoff.accel_with_proxy)
-	return 0;
-    if (http->request->protocol == PROTO_CACHEOBJ)
-	return 0;
-    if (http->flags.accel)
-	return 0;
-    if (http->request->method == METHOD_PURGE)
-	return 0;
-    return 1;
-}
-
 #if USE_IDENT
 static void
 clientIdentDone(const char *ident, void *data)
@@ -203,16 +187,22 @@
 clientAccessCheck(void *data)
 {
     clientHttpRequest *http = data;
-    if (checkAccelOnly(http)) {
-	/* deny proxy requests in accel_only mode */
-	debug(33, 1) ("clientAccessCheck: proxy request denied in accel_only mode\n");
-	clientAccessCheckDone(ACCESS_DENIED, http);
-	return;
-    }
     http->acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
     aclNBCheck(http->acl_checklist, clientAccessCheckDone, http);
 }
 
+static void
+clientAccessCheck2(void *data)
+{
+    clientHttpRequest *http = data;
+    if (Config.accessList.http2 && !http->redirect.status) {
+	http->acl_checklist = clientAclChecklistCreate(Config.accessList.http2, http);
+	aclNBCheck(http->acl_checklist, clientAccessCheckDone2, http);
+    } else {
+	clientCheckNoCache(http);
+    }
+}
+
 /*
  * returns true if client specified that the object must come from the cache
  * without contacting origin server
@@ -243,7 +233,7 @@
 	delaySetStoreClient(h->sc, delayClient(h));
 #endif
     storeClientCopy(h->sc, e, 0, 0, CLIENT_SOCK_SZ,
-	memAllocate(MEM_CLIENT_SOCK_BUF), clientSendMoreData, h);
+	memAllocate(MEM_CLIENT_SOCK_BUF), clientSendMoreHeaderData, h);
     return e;
 }
 
@@ -300,7 +290,69 @@
 		page_id = ERR_ACCESS_DENIED;
 	}
 	err = errorCon(page_id, status);
-	err->request = requestLink(http->request);
+	err->request = requestLink(http->orig_request);
+	err->src_addr = http->conn->peer.sin_addr;
+	if (http->conn->auth_user_request)
+	    err->auth_user_request = http->conn->auth_user_request;
+	else if (http->request->auth_user_request)
+	    err->auth_user_request = http->request->auth_user_request;
+	/* lock for the error state */
+	if (err->auth_user_request)
+	    authenticateAuthUserRequestLock(err->auth_user_request);
+	err->callback_data = NULL;
+	errorAppendEntry(http->entry, err);
+    }
+}
+
+static void
+clientAccessCheckDone2(int answer, void *data)
+{
+    clientHttpRequest *http = data;
+    err_type page_id;
+    http_status status;
+    ErrorState *err = NULL;
+    char *proxy_auth_msg = NULL;
+    debug(33, 2) ("The request %s %s is %s, because it matched '%s'\n",
+	RequestMethodStr[http->request->method], http->uri,
+	answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
+	AclMatchedName ? AclMatchedName : "NO ACL's");
+    proxy_auth_msg = authenticateAuthUserRequestMessage(http->conn->auth_user_request ? http->conn->auth_user_request : http->request->auth_user_request);
+    http->acl_checklist = NULL;
+    if (answer == ACCESS_ALLOWED) {
+	clientCheckNoCache(http);
+    } else {
+	debug(33, 5) ("Access Denied: %s\n", http->uri);
+	debug(33, 5) ("AclMatchedName = %s\n",
+	    AclMatchedName ? AclMatchedName : "<null>");
+	debug(33, 5) ("Proxy Auth Message = %s\n",
+	    proxy_auth_msg ? proxy_auth_msg : "<null>");
+	/*
+	 * NOTE: get page_id here, based on AclMatchedName because
+	 * if USE_DELAY_POOLS is enabled, then AclMatchedName gets
+	 * clobbered in the clientCreateStoreEntry() call
+	 * just below.  Pedro Ribeiro <pribeiro@isel.pt>
+	 */
+	page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
+	http->log_type = LOG_TCP_DENIED;
+	http->entry = clientCreateStoreEntry(http, http->request->method,
+	    null_request_flags);
+	if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) {
+	    if (!http->flags.accel) {
+		/* Proxy authorisation needed */
+		status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
+	    } else {
+		/* WWW authorisation needed */
+		status = HTTP_UNAUTHORIZED;
+	    }
+	    if (page_id == ERR_NONE)
+		page_id = ERR_CACHE_ACCESS_DENIED;
+	} else {
+	    status = HTTP_FORBIDDEN;
+	    if (page_id == ERR_NONE)
+		page_id = ERR_ACCESS_DENIED;
+	}
+	err = errorCon(page_id, status);
+	err->request = requestLink(http->orig_request);
 	err->src_addr = http->conn->peer.sin_addr;
 	if (http->conn->auth_user_request)
 	    err->auth_user_request = http->conn->auth_user_request;
@@ -329,12 +381,12 @@
 clientRedirectStart(clientHttpRequest * http)
 {
     debug(33, 5) ("clientRedirectStart: '%s'\n", http->uri);
-    if (Config.Program.redirect == NULL) {
+    if (Config.Program.url_rewrite.command == NULL) {
 	clientRedirectDone(http, NULL);
 	return;
     }
-    if (Config.accessList.redirector) {
-	http->acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
+    if (Config.accessList.url_rewrite) {
+	http->acl_checklist = clientAclChecklistCreate(Config.accessList.url_rewrite, http);
 	aclNBCheck(http->acl_checklist, clientRedirectAccessCheckDone, http);
     } else {
 	redirectStart(http, clientRedirectDone, http);
@@ -347,12 +399,24 @@
     clientHttpRequest *http = data;
     request_t *new_request = NULL;
     request_t *old_request = http->request;
+    const char *urlgroup = http->conn->port->urlgroup;
     debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
 	result ? result : "NULL");
     assert(http->redirect_state == REDIRECT_PENDING);
     http->redirect_state = REDIRECT_DONE;
     if (result) {
-	http_status status = (http_status) atoi(result);
+	http_status status;
+	if (*result == '!') {
+	    char *t;
+	    if ((t = strchr(result + 1, '!')) != NULL) {
+		urlgroup = result + 1;
+		*t++ = '\0';
+		result = t;
+	    } else {
+		debug(33, 1) ("clientRedirectDone: bad input: %s\n", result);
+	    }
+	}
+	status = (http_status) atoi(result);
 	if (status == HTTP_MOVED_PERMANENTLY
 	    || status == HTTP_MOVED_TEMPORARILY
 	    || status == HTTP_SEE_OTHER
@@ -361,12 +425,14 @@
 	    if ((t = strchr(result, ':')) != NULL) {
 		http->redirect.status = status;
 		http->redirect.location = xstrdup(t + 1);
+		goto redirect_parsed;
 	    } else {
 		debug(33, 1) ("clientRedirectDone: bad input: %s\n", result);
 	    }
 	} else if (strcmp(result, http->uri))
 	    new_request = urlParse(old_request->method, result);
     }
+  redirect_parsed:
     if (new_request) {
 	safe_free(http->uri);
 	http->uri = xstrdup(urlCanonical(new_request));
@@ -390,13 +456,20 @@
 	new_request->content_length = old_request->content_length;
 	requestUnlink(old_request);
 	http->request = requestLink(new_request);
-    }
+    } else {
+	/* Don't mess with urlgroup on internal request */
+	if (old_request->flags.internal)
+	    urlgroup = NULL;
+    }
+    safe_free(http->request->urlgroup);		/* only paranoia. should not happen */
+    if (urlgroup && *urlgroup)
+	http->request->urlgroup = xstrdup(urlgroup);
     clientInterpretRequestHeaders(http);
 #if HEADERS_LOG
     headersLog(0, 1, request->method, request);
 #endif
     fd_note(http->conn->fd, http->uri);
-    clientCheckNoCache(http);
+    clientAccessCheck2(http);
 }
 
 static void
@@ -611,10 +684,10 @@
 	    http->out.offset,
 	    CLIENT_SOCK_SZ,
 	    buf,
-	    clientSendMoreData,
+	    clientSendMoreHeaderData,
 	    http);
     } else {
-	clientSendMoreData(data, buf, size);
+	clientSendMoreHeaderData(data, buf, size);
     }
 }
 
@@ -664,7 +737,7 @@
     if (!Config2.onoff.enable_purge) {
 	http->log_type = LOG_TCP_DENIED;
 	err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
-	err->request = requestLink(http->request);
+	err->request = requestLink(http->orig_request);
 	err->src_addr = http->conn->peer.sin_addr;
 	http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags);
 	errorAppendEntry(http->entry, err);
@@ -931,8 +1004,12 @@
 	storeUnlockObject(e);
     }
     requestUnlink(http->request);
+    http->request = NULL;
+    requestUnlink(http->orig_request);
+    http->orig_request = NULL;
     if (http->reply)
 	httpReplyDestroy(http->reply);
+    http->reply = NULL;
     assert(http != http->next);
     assert(http->conn->chr != NULL);
     /* Unlink us from the clients request list */
@@ -1483,6 +1560,18 @@
     }
     if (!Config.onoff.client_pconns && !request->flags.must_keepalive)
 	request->flags.proxy_keepalive = 0;
+    /* Append Via */
+    {
+	LOCAL_ARRAY(char, bbuf, MAX_URL + 32);
+	String strVia = httpHeaderGetList(hdr, HDR_VIA);
+	snprintf(bbuf, sizeof(bbuf), "%d.%d %s",
+	    rep->sline.version.major,
+	    rep->sline.version.minor, ThisCache);
+	strListAdd(&strVia, bbuf, ',');
+	httpHeaderDelById(hdr, HDR_VIA);
+	httpHeaderPutStr(hdr, HDR_VIA, strBuf(strVia));
+	stringClean(&strVia);
+    }
     /* Signal keep-alive if needed */
     httpHeaderPutStr(hdr,
 	http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION,
@@ -1631,13 +1720,18 @@
 	return;
     }
     http->flags.hit = 1;
-    if (checkNegativeHit(e)
+    if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
+	if (checkNegativeHit(e)
 #if HTTP_VIOLATIONS
-	&& !r->flags.nocache_hack
+	    && !r->flags.nocache_hack
 #endif
-	) {
-	http->log_type = LOG_TCP_NEGATIVE_HIT;
-	clientSendMoreData(data, buf, size);
+	    ) {
+	    http->log_type = LOG_TCP_NEGATIVE_HIT;
+	    clientSendMoreHeaderData(data, buf, size);
+	} else {
+	    http->log_type = LOG_TCP_MISS;
+	    clientProcessMiss(http);
+	}
     } else if (!Config.onoff.offline && refreshCheckHTTP(e, r) && !http->flags.internal) {
 	debug(33, 5) ("clientCacheHit: in refreshCheck() block\n");
 	/*
@@ -1693,7 +1787,7 @@
 	    clientProcessMiss(http);
 	} else if (modifiedSince(e, http->request)) {
 	    http->log_type = LOG_TCP_IMS_HIT;
-	    clientSendMoreData(data, buf, size);
+	    clientSendMoreHeaderData(data, buf, size);
 	} else {
 	    time_t timestamp = e->timestamp;
 	    MemBuf mb = httpPacked304Reply(e->mem_obj->reply);
@@ -1724,7 +1818,7 @@
 	    http->log_type = LOG_TCP_MEM_HIT;
 	else if (Config.onoff.offline)
 	    http->log_type = LOG_TCP_OFFLINE_HIT;
-	clientSendMoreData(data, buf, size);
+	clientSendMoreHeaderData(data, buf, size);
     }
 }
 
@@ -1969,13 +2063,33 @@
     }
 }
 
+typedef struct {
+    clientHttpRequest *http;
+    char *buf;
+    ssize_t size;
+    const char *body_buf;
+    ssize_t body_size;
+} clientCheckHeaderStateData;
+
+CBDATA_TYPE(clientCheckHeaderStateData);
+
+static void clientHttpLocationRewriteCheck(clientCheckHeaderStateData * state);
+static void clientHttpLocationRewriteCheckDone(int answer, void *data);
+static void clientHttpLocationRewrite(clientCheckHeaderStateData * state);
+static void clientHttpLocationRewriteDone(void *data, char *reply);
+static void clientHttpReplyAccessCheck(clientCheckHeaderStateData * state);
+static void clientHttpReplyAccessCheckDone(int answer, void *data);
+static void clientCheckErrorMap(clientCheckHeaderStateData * state);
+static void clientCheckHeaderDone(clientCheckHeaderStateData * state);
+
 /*
  * accepts chunk of a http message in buf, parses prefix, filters headers and
  * such, writes processed message to the client's socket
  */
 static void
-clientSendMoreData(void *data, char *buf, ssize_t size)
+clientSendMoreHeaderData(void *data, char *buf, ssize_t size)
 {
+    clientCheckHeaderStateData *state;
     clientHttpRequest *http = data;
     StoreEntry *entry = http->entry;
     ConnStateData *conn = http->conn;
@@ -1983,18 +2097,16 @@
     HttpReply *rep = NULL;
     const char *body_buf = buf;
     squid_off_t body_size = size;
-    MemBuf mb;
-    squid_off_t check_size = 0;
-    debug(33, 5) ("clientSendMoreData: %s, %d bytes\n", http->uri, (int) size);
+    debug(33, 5) ("clientSendMoreHeaderData: %s, %d bytes\n", http->uri, (int) size);
     assert(size <= CLIENT_SOCK_SZ);
     assert(http->request != NULL);
     dlinkDelete(&http->active, &ClientActiveRequests);
     dlinkAdd(http, &http->active, &ClientActiveRequests);
-    debug(33, 5) ("clientSendMoreData: FD %d '%s', out.offset=%ld \n",
+    debug(33, 5) ("clientSendMoreHeaderData: FD %d '%s', out.offset=%ld \n",
 	fd, storeUrl(entry), (long int) http->out.offset);
     if (conn->chr != http) {
 	/* there is another object in progress, defer this one */
-	debug(33, 2) ("clientSendMoreData: Deferring %s\n", storeUrl(entry));
+	debug(33, 2) ("clientSendMoreHeaderData: Deferring %s\n", storeUrl(entry));
 	memFree(buf, MEM_CLIENT_SOCK_BUF);
 	return;
     } else if (http->request->flags.reset_tcp) {
@@ -2017,115 +2129,282 @@
 	memFree(buf, MEM_CLIENT_SOCK_BUF);
 	return;
     }
-    if (http->out.offset == 0) {
-	rep = http->reply = clientBuildReply(http, buf, size);
-	if (rep) {
-	    aclCheck_t *ch;
-	    int rv;
-	    if (Config.onoff.log_mime_hdrs) {
-		size_t k;
-		if ((k = headersEnd(buf, size))) {
-		    safe_free(http->al.headers.reply);
-		    http->al.headers.reply = xcalloc(k + 1, 1);
-		    xstrncpy(http->al.headers.reply, buf, k);
-		}
-	    }
-	    clientMaxBodySize(http->request, http, rep);
-	    if (http->log_type != LOG_TCP_DENIED && clientReplyBodyTooLarge(http, rep->content_length)) {
-		ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN);
-		err->request = requestLink(http->request);
-		storeUnregister(http->sc, http->entry, http);
-		http->sc = NULL;
-		storeUnlockObject(http->entry);
-		http->log_type = LOG_TCP_DENIED;
-		http->entry = clientCreateStoreEntry(http, http->request->method,
-		    null_request_flags);
-		errorAppendEntry(http->entry, err);
-		memFree(buf, MEM_CLIENT_SOCK_BUF);
-		return;
-	    }
-	    body_size = size - rep->hdr_sz;
-	    assert(body_size >= 0);
-	    body_buf = buf + rep->hdr_sz;
-	    http->range_iter.prefix_size = rep->hdr_sz;
-	    debug(33, 3) ("clientSendMoreData: Appending %d bytes after %d bytes of headers\n",
-		(int) body_size, rep->hdr_sz);
-	    if (http->log_type != LOG_TCP_DENIED && !clientAlwaysAllowResponse(rep->sline.status)) {
-		ch = clientAclChecklistCreate(Config.accessList.reply, http);
-		ch->reply = rep;
-		rv = aclCheckFast(Config.accessList.reply, ch);
-		aclChecklistFree(ch);
-		ch = NULL;
-		debug(33, 2) ("The reply for %s %s is %s, because it matched '%s'\n",
-		    RequestMethodStr[http->request->method], http->uri,
-		    rv ? "ALLOWED" : "DENIED",
-		    AclMatchedName ? AclMatchedName : "NO ACL's");
-		if (!rv) {
-		    ErrorState *err;
-		    err_type page_id;
-		    page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
-		    if (page_id == ERR_NONE)
-			page_id = ERR_ACCESS_DENIED;
-		    err = errorCon(page_id, HTTP_FORBIDDEN);
-		    err->request = requestLink(http->request);
-		    storeUnregister(http->sc, http->entry, http);
-		    http->sc = NULL;
-		    storeUnlockObject(http->entry);
-		    http->log_type = LOG_TCP_DENIED;
-		    http->entry = clientCreateStoreEntry(http, http->request->method,
-			null_request_flags);
-		    errorAppendEntry(http->entry, err);
-		    memFree(buf, MEM_CLIENT_SOCK_BUF);
-		    return;
-		}
-	    }
-	}
-	/* reset range iterator */
-	http->range_iter.pos = HttpHdrRangeInitPos;
-    } else if (!http->request->range) {
-	/* Avoid copying to MemBuf for non-range requests */
-	/* Note, if we're here, then 'rep' is known to be NULL */
+    assert(http->out.offset == 0);
+    rep = http->reply = clientBuildReply(http, buf, size);
+    if (!rep) {
+	/* Forward as HTTP/0.9 body with no reply */
+	MemBuf mb;
+	memBufDefInit(&mb);
+	memBufAppend(&mb, buf, size);
+	memFree(buf, MEM_CLIENT_SOCK_BUF);
 	http->out.offset += body_size;
-	comm_write(fd, buf, size, clientWriteBodyComplete, http, NULL);
-	/* NULL because clientWriteBodyComplete frees it */
+	comm_write_mbuf(http->conn->fd, mb, clientWriteComplete, http);
 	return;
     }
-    if (http->request->method == METHOD_HEAD) {
-	if (rep) {
-	    /* do not forward body for HEAD replies */
-	    body_size = 0;
-	    http->flags.done_copying = 1;
+    if (Config.onoff.log_mime_hdrs) {
+	safe_free(http->al.headers.reply);
+	http->al.headers.reply = xcalloc(rep->hdr_sz + 1, 1);
+	xstrncpy(http->al.headers.reply, buf, rep->hdr_sz);
+    }
+    clientMaxBodySize(http->request, http, rep);
+    if (http->log_type != LOG_TCP_DENIED && clientReplyBodyTooLarge(http, rep->content_length)) {
+	ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN);
+	err->request = requestLink(http->orig_request);
+	storeUnregister(http->sc, http->entry, http);
+	http->sc = NULL;
+	storeUnlockObject(http->entry);
+	http->log_type = LOG_TCP_DENIED;
+	http->entry = clientCreateStoreEntry(http, http->request->method,
+	    null_request_flags);
+	errorAppendEntry(http->entry, err);
+	memFree(buf, MEM_CLIENT_SOCK_BUF);
+	return;
+    }
+    body_size = size - rep->hdr_sz;
+    body_buf = buf + rep->hdr_sz;
+    assert(body_size >= 0);
+    http->range_iter.prefix_size = rep->hdr_sz;
+    debug(33, 3) ("clientSendMoreHeaderData: Appending %d bytes after %d bytes of headers\n",
+	(int) body_size, rep->hdr_sz);
+    CBDATA_INIT_TYPE(clientCheckHeaderStateData);
+    state = cbdataAlloc(clientCheckHeaderStateData);
+    state->http = http;
+    cbdataLock(http);
+    state->buf = buf;
+    state->size = size;
+    state->body_buf = body_buf;
+    state->body_size = body_size;
+    clientHttpLocationRewriteCheck(state);
+}
+
+static void
+clientHttpLocationRewriteCheck(clientCheckHeaderStateData * state)
+{
+    HttpReply *rep = state->http->reply;
+    aclCheck_t *ch;
+    if (!cbdataValid(state->http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    if (!Config.Program.location_rewrite.command || !httpHeaderHas(&rep->header, HDR_LOCATION)) {
+	clientHttpLocationRewriteDone(state, NULL);
+	return;
+    }
+    if (Config.accessList.location_rewrite) {
+	ch = clientAclChecklistCreate(Config.accessList.location_rewrite, state->http);
+	ch->reply = state->http->reply;
+	aclNBCheck(ch, clientHttpLocationRewriteCheckDone, state);
+    } else {
+	clientHttpLocationRewriteCheckDone(ACCESS_ALLOWED, state);
+    }
+}
+
+static void
+clientHttpLocationRewriteCheckDone(int answer, void *data)
+{
+    clientCheckHeaderStateData *state = data;
+    if (!cbdataValid(state->http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    if (answer == ACCESS_ALLOWED) {
+	clientHttpLocationRewrite(state);
+    } else {
+	clientHttpLocationRewriteDone(state, NULL);
+    }
+}
+
+static void
+clientHttpLocationRewrite(clientCheckHeaderStateData * state)
+{
+    HttpReply *rep = state->http->reply;
+    if (!cbdataValid(state->http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    if (!httpHeaderHas(&rep->header, HDR_LOCATION))
+	clientHttpLocationRewriteDone(state, NULL);
+    else
+	locationRewriteStart(rep, state->http, clientHttpLocationRewriteDone, state);
+}
+
+static void
+clientHttpLocationRewriteDone(void *data, char *reply)
+{
+    clientCheckHeaderStateData *state = data;
+    clientHttpRequest *http = state->http;
+    HttpReply *rep = http->reply;
+    ConnStateData *conn = http->conn;
+    if (!cbdataValid(http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    if (reply && *reply) {
+	httpHeaderDelById(&rep->header, HDR_LOCATION);
+	if (*reply == '/') {
+	    /* We have to restore the URL as sent by the client */
+	    request_t *req = http->orig_request;
+	    const char *proto = conn->port->protocol;
+	    const char *host = httpHeaderGetStr(&req->header, HDR_HOST);
+	    if (!host)
+		host = req->host;
+	    httpHeaderPutStrf(&rep->header, HDR_LOCATION, "%s://%s%s", proto, host, reply);
 	} else {
-	    /*
-	     * If we are here, then store_status == STORE_OK and it
-	     * seems we have a HEAD repsponse which is missing the
-	     * empty end-of-headers line (home.mira.net, phttpd/0.99.72
-	     * does this).  Because clientBuildReply() fails we just
-	     * call this reply a body, set the done_copying flag and
-	     * continue...
-	     */
-	    http->flags.done_copying = 1;
-	    /*
-	     * And as this is a malformed HTTP reply we cannot keep
-	     * the connection persistent
-	     */
-	    http->request->flags.proxy_keepalive = 0;
+	    httpHeaderPutStr(&rep->header, HDR_LOCATION, reply);
 	}
     }
-    /* write headers and/or body if any */
-    assert(rep || (body_buf && body_size));
-    /* init mb; put status line and headers if any */
-    if (rep) {
+    clientHttpReplyAccessCheck(state);
+}
+
+static void
+clientHttpReplyAccessCheck(clientCheckHeaderStateData * state)
+{
+    aclCheck_t *ch;
+    if (!cbdataValid(state->http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    if (Config.accessList.reply && state->http->log_type != LOG_TCP_DENIED && !clientAlwaysAllowResponse(state->http->reply->sline.status)) {
+	ch = clientAclChecklistCreate(Config.accessList.reply, state->http);
+	ch->reply = state->http->reply;
+	aclNBCheck(ch, clientHttpReplyAccessCheckDone, state);
+    } else {
+	clientHttpReplyAccessCheckDone(ACCESS_ALLOWED, state);
+    }
+}
+
+/* Handle error mapping.
+ * 
+ *   1. Look up if there is a error map for the request
+ *   2. Start requesting the error URL
+ *   3. When headers are received, create a new reply structure and copy
+ *      over the relevant headers (start with the headers from the original
+ *      reply, and copy over Content-Length)
+ *   4. Make the new reply the current one
+ *   5. Detatch from the previous reply
+ *   6. Go to clientCheckHeaderDone, as if nothing had happened, but now
+ *      fetching from the new reply.
+ */
+static void
+clientHttpReplyAccessCheckDone(int answer, void *data)
+{
+    clientCheckHeaderStateData *state = data;
+    clientHttpRequest *http = state->http;
+    if (!cbdataValid(state->http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    debug(33, 2) ("The reply for %s %s is %s, because it matched '%s'\n",
+	RequestMethodStr[http->request->method], http->uri,
+	answer ? "ALLOWED" : "DENIED",
+	AclMatchedName ? AclMatchedName : "NO ACL's");
+    if (answer != ACCESS_ALLOWED) {
+	ErrorState *err;
+	err_type page_id;
+	page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
+	if (page_id == ERR_NONE)
+	    page_id = ERR_ACCESS_DENIED;
+	err = errorCon(page_id, HTTP_FORBIDDEN);
+	err->request = requestLink(http->orig_request);
+	storeUnregister(http->sc, http->entry, http);
+	http->sc = NULL;
+	storeUnlockObject(http->entry);
+	http->log_type = LOG_TCP_DENIED;
+	http->entry = clientCreateStoreEntry(http, http->request->method,
+	    null_request_flags);
+	errorAppendEntry(http->entry, err);
+	return;
+    }
+    clientCheckErrorMap(state);
+}
+
+static void
+clientCheckErrorMapDone(StoreEntry * e, int body_offset, squid_off_t content_length, void *data)
+{
+    clientCheckHeaderStateData *state = data;
+    if (!cbdataValid(state->http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    if (e) {
+	clientHttpRequest *http = state->http;
+	/* Get rid of the old request entry */
+	storeUnregister(http->sc, http->entry, http);
+	storeUnlockObject(http->entry);
+	/* Attach ourselves to the new request entry */
+	http->entry = e;
+	storeLockObject(e);
+	http->sc = storeClientListAdd(http->entry, http);
+	/* Adjust the header size */
+	state->http->reply->hdr_sz = body_offset;
+	/* Clean up any old body content */
+	httpBodyClean(&state->http->reply->body);
+	state->body_buf = NULL;
+	state->body_size = 0;
+	/* And finally, adjust content-length to the new value */
+	httpHeaderDelById(&state->http->reply->header, HDR_CONTENT_LENGTH);
+	if (content_length >= 0) {
+	    httpHeaderPutSize(&state->http->reply->header, HDR_CONTENT_LENGTH, content_length);
+	}
+    }
+    clientCheckHeaderDone(state);
+}
+static void
+clientCheckErrorMap(clientCheckHeaderStateData * state)
+{
+    clientHttpRequest *http = state->http;
+    HttpReply *rep = state->http->reply;
+    if (!cbdataValid(http)) {
+	/* oops.. */
+	clientCheckHeaderDone(state);
+	return;
+    }
+    if (rep->sline.status < 100 || rep->sline.status >= 400) {
+	request_t *request = http->orig_request;
+	/* XXX The NULL is meant to pass ACL name, but the ACL name is not
+	 * known here (AclMatchedName is no longer valid)
+	 */
+	if (errorMapStart(Config.errorMapList, request, rep, NULL, clientCheckErrorMapDone, state))
+	    return;
+    }
+    clientCheckHeaderDone(state);
+}
+
+static void
+clientCheckHeaderDone(clientCheckHeaderStateData * state)
+{
+    char *buf = state->buf;
+    const char *body_buf = state->body_buf;
+    ssize_t body_size = state->body_size;
+    HttpReply *rep = state->http->reply;
+    clientHttpRequest *http = state->http;
+    MemBuf mb;
+    cbdataFree(state);
+    if (!cbdataValid(http))
+	goto aborted;
+    /* reset range iterator */
+    http->range_iter.pos = HttpHdrRangeInitPos;
+    if (http->request->method == METHOD_HEAD) {
+	/* do not forward body for HEAD replies */
+	body_size = 0;
+	http->flags.done_copying = 1;
+    }
+    /* init mb; put status line and headers  */
+    if (http->http_ver.major >= 1)
 	mb = httpReplyPack(rep);
-	http->out.offset += rep->hdr_sz;
-	check_size += rep->hdr_sz;
+    else
+	memBufDefInit(&mb);
+    http->out.offset += rep->hdr_sz;
 #if HEADERS_LOG
-	headersLog(0, 0, http->request->method, rep);
+    headersLog(0, 0, http->request->method, rep);
 #endif
-	rep = NULL;
-    } else {
-	memBufDefInit(&mb);
-    }
     /* append body if any */
     if (http->request->range) {
 	/* Only GET requests should have ranges */
@@ -2136,11 +2415,94 @@
 	    http->flags.done_copying = 1;
     } else if (body_buf && body_size) {
 	http->out.offset += body_size;
-	check_size += body_size;
 	memBufAppend(&mb, body_buf, body_size);
     }
-    if (!http->request->range && http->request->method == METHOD_GET)
-	assert(check_size == size);
+    /* write */
+    comm_write_mbuf(http->conn->fd, mb, clientWriteComplete, http);
+    /* clean up */
+  aborted:
+    memFree(buf, MEM_CLIENT_SOCK_BUF);
+    cbdataUnlock(http);
+    http = NULL;
+}
+
+
+/*
+ * accepts chunk of a http message in buf, parses prefix, filters headers and
+ * such, writes processed message to the client's socket
+ */
+static void
+clientSendMoreData(void *data, char *buf, ssize_t size)
+{
+    clientHttpRequest *http = data;
+    StoreEntry *entry = http->entry;
+    ConnStateData *conn = http->conn;
+    int fd = conn->fd;
+    MemBuf mb;
+    debug(33, 5) ("clientSendMoreData: %s, %d bytes\n", http->uri, (int) size);
+    assert(size <= CLIENT_SOCK_SZ);
+    assert(http->request != NULL);
+    dlinkDelete(&http->active, &ClientActiveRequests);
+    dlinkAdd(http, &http->active, &ClientActiveRequests);
+    debug(33, 5) ("clientSendMoreData: FD %d '%s', out.offset=%d \n",
+	fd, storeUrl(entry), (int) http->out.offset);
+    if (conn->chr != http) {
+	/* there is another object in progress, defer this one */
+	debug(33, 1) ("clientSendMoreData: Deferring %s\n", storeUrl(entry));
+	memFree(buf, MEM_CLIENT_SOCK_BUF);
+	return;
+    } else if (entry && EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+	/* call clientWriteComplete so the client socket gets closed */
+	clientWriteComplete(fd, NULL, 0, COMM_OK, http);
+	memFree(buf, MEM_CLIENT_SOCK_BUF);
+	return;
+    } else if (size < 0) {
+	/* call clientWriteComplete so the client socket gets closed */
+	clientWriteComplete(fd, NULL, 0, COMM_OK, http);
+	memFree(buf, MEM_CLIENT_SOCK_BUF);
+	return;
+    } else if (size == 0) {
+	/* call clientWriteComplete so the client socket gets closed */
+	clientWriteComplete(fd, NULL, 0, COMM_OK, http);
+	memFree(buf, MEM_CLIENT_SOCK_BUF);
+	return;
+    }
+    if (!http->request->range) {
+	/* Avoid copying to MemBuf for non-range requests */
+	http->out.offset += size;
+	comm_write(fd, buf, size, clientWriteBodyComplete, http, NULL);
+	/* NULL because clientWriteBodyComplete frees it */
+	return;
+    }
+    if (http->request->method == METHOD_HEAD) {
+	/*
+	 * If we are here, then store_status == STORE_OK and it
+	 * seems we have a HEAD repsponse which is missing the
+	 * empty end-of-headers line (home.mira.net, phttpd/0.99.72
+	 * does this).  Because clientBuildReply() fails we just
+	 * call this reply a body, set the done_copying flag and
+	 * continue...
+	 */
+	http->flags.done_copying = 1;
+	/*
+	 * And as this is a malformed HTTP reply we cannot keep
+	 * the connection persistent
+	 */
+	http->request->flags.proxy_keepalive = 0;
+    }
+    /* init mb; put status line and headers if any */
+    memBufDefInit(&mb);
+    if (http->request->range) {
+	/* Only GET requests should have ranges */
+	assert(http->request->method == METHOD_GET);
+	/* clientPackMoreRanges() updates http->out.offset */
+	/* force the end of the transfer if we are done */
+	if (!clientPackMoreRanges(http, buf, size, &mb))
+	    http->flags.done_copying = 1;
+    } else {
+	http->out.offset += size;
+	memBufAppend(&mb, buf, size);
+    }
     /* write */
     comm_write_mbuf(fd, mb, clientWriteComplete, http);
     /* if we don't do it, who will? */
@@ -2214,7 +2576,7 @@
 		http->out.offset,
 		CLIENT_SOCK_SZ,
 		memAllocate(MEM_CLIENT_SOCK_BUF),
-		clientSendMoreData,
+		clientSendMoreHeaderData,
 		http);
 	}
     }
@@ -2324,7 +2686,7 @@
 	RequestMethodStr[r->method], url);
     http->al.http.code = HTTP_GATEWAY_TIMEOUT;
     err = errorCon(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT);
-    err->request = requestLink(r);
+    err->request = requestLink(http->orig_request);
     err->src_addr = http->conn->peer.sin_addr;
     if (http->entry) {
 	storeUnregister(http->sc, http->entry, http);
@@ -2557,7 +2919,7 @@
     if (http->flags.accel && r->flags.loopdetect) {
 	http->al.http.code = HTTP_FORBIDDEN;
 	err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
-	err->request = requestLink(r);
+	err->request = requestLink(http->orig_request);
 	err->src_addr = http->conn->peer.sin_addr;
 	http->log_type = LOG_TCP_DENIED;
 	http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
@@ -2614,7 +2976,6 @@
     char *url = NULL;
     char *req_hdr = NULL;
     http_version_t http_ver;
-    char *token = NULL;
     char *t = NULL;
     char *end;
     size_t header_sz;		/* size of headers, not including first line */
@@ -2623,34 +2984,40 @@
     size_t req_sz;
     method_t method;
     clientHttpRequest *http = NULL;
-#if IPF_TRANSPARENT
-    struct natlookup natLookup;
-    static int natfd = -1;
-    int x;
-#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
-    struct ipfobj obj;
-#else
-    static int siocgnatl_cmd = SIOCGNATL & 0xff;
-#endif
-#endif
-#if PF_TRANSPARENT
-    struct pfioc_natlook nl;
-    static int pffd = -1;
-#endif
-#if LINUX_NETFILTER
-    socklen_t sock_sz = sizeof(conn->me);
-#endif
+    int http_version_offset = 0;
 
     /* pre-set these values to make aborting simpler */
     *prefix_p = NULL;
     *method_p = METHOD_NONE;
     *status = -1;
 
-    if ((req_sz = headersEnd(conn->in.buf, conn->in.offset)) == 0) {
-	debug(33, 5) ("Incomplete request, waiting for end of headers\n");
+    if ((t = memchr(conn->in.buf, '\n', conn->in.offset)) == NULL) {
+	debug(33, 5) ("Incomplete request, waiting for end of request line\n");
 	*status = 0;
 	return NULL;
     }
+    *req_line_sz_p = req_sz = t - conn->in.buf + 1;	/* HTTP/0.9 requests */
+    while (t > conn->in.buf && xisspace(*t))
+	t--;
+    while (t > conn->in.buf && !xisspace(*t))
+	t--;
+    if (t > conn->in.buf && t < (conn->in.buf + conn->in.offset - 8) && strncasecmp(t + 1, "HTTP/", 5) == 0) {
+	if ((req_sz = headersEnd(conn->in.buf, conn->in.offset)) == 0) {
+	    debug(33, 5) ("Incomplete request, waiting for end of headers\n");
+	    *status = 0;
+	    return NULL;
+	}
+	http_version_offset = t - conn->in.buf;
+	if (sscanf(t + 6, "%d.%d", &http_ver.major, &http_ver.minor) != 2) {
+	    debug(33, 3) ("parseHttpRequest: Invalid HTTP identifier.\n");
+	    return parseHttpRequestAbort(conn, "error:invalid-http-ident");
+	}
+	debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n", http_ver.major, http_ver.minor);
+    } else {
+	debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
+	httpBuildVersion(&http_ver, 0, 9);	/* wild guess */
+    }
+
     assert(req_sz <= conn->in.offset);
     /* Use memcpy, not strdup! */
     inbuf = xmalloc(req_sz + 1);
@@ -2666,14 +3033,16 @@
     /* Barf on NULL characters in the headers */
     if (strlen(inbuf) != req_sz) {
 	debug(33, 1) ("parseHttpRequest: Requestheader contains NULL characters\n");
+#if TRY_TO_IGNORE_THIS
 	xfree(inbuf);
 	return parseHttpRequestAbort(conn, "error:invalid-request");
+#endif
     }
     /* Look for request method */
     if ((mstr = strtok(inbuf, "\t ")) == NULL) {
 	debug(33, 1) ("parseHttpRequest: Can't get request method\n");
 	xfree(inbuf);
-	return parseHttpRequestAbort(conn, "error:invalid-request-method");
+	return parseHttpRequestAbort(conn, "error:invalid-request");
     }
     method = urlParseMethod(mstr);
     if (method == METHOD_NONE) {
@@ -2690,56 +3059,32 @@
 	xfree(inbuf);
 	return parseHttpRequestAbort(conn, "error:missing-url");
     }
-    while (xisspace(*url))
-	url++;
-    t = url + strlen(url);
-    assert(*t == '\0');
-    token = NULL;
-    while (t > url) {
-	t--;
-	if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) {
-	    token = t + 1;
-	    break;
-	}
-    }
-    while (t > url && xisspace(*t))
-	*(t--) = '\0';
-    debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url);
-    if (token == NULL) {
-	debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
-#if RELAXED_HTTP_PARSER
-	httpBuildVersion(&http_ver, 0, 9);	/* wild guess */
-#else
-	xfree(inbuf);
-	return parseHttpRequestAbort(conn, "error:missing-http-ident");
-#endif
-    } else {
-	if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) {
-	    debug(33, 3) ("parseHttpRequest: Invalid HTTP identifier.\n");
+    if (http_version_offset) {
+	if (http_version_offset < url - inbuf) {
+	    debug(33, 1) ("parseHttpRequest: Missing URL\n");
 	    xfree(inbuf);
-	    return parseHttpRequestAbort(conn, "error:invalid-http-ident");
+	    return parseHttpRequestAbort(conn, "error:missing-url");
 	}
-	debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n", http_ver.major, http_ver.minor);
+	inbuf[http_version_offset] = '\0';
+    } else {
+	t = url + strlen(url) - 1;
+	while (t > url && *t == '\r')
+	    *t-- = '\0';
     }
+    while (xisspace(*url))
+	url++;
+    debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url);
 
     /*
      * Process headers after request line
      */
-    req_hdr = strtok(NULL, null_string);
-    header_sz = req_sz - (req_hdr - inbuf);
-    if (0 == header_sz) {
-	debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
-	*status = 0;
-	xfree(inbuf);
-	return NULL;
-    }
-    assert(header_sz > 0);
+    req_hdr = inbuf + *req_line_sz_p;
+    header_sz = req_sz - *req_line_sz_p;
     debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
     end = req_hdr + header_sz;
     debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
 
     prefix_sz = end - inbuf;
-    *req_line_sz_p = req_hdr - inbuf;
     debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
 	(int) prefix_sz, (int) *req_line_sz_p);
     assert(prefix_sz <= conn->in.offset);
@@ -2757,198 +3102,74 @@
     dlinkAdd(http, &http->active, &ClientActiveRequests);
 
     debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n", (*prefix_p) + *req_line_sz_p);
+
 #if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
     if ((t = strchr(url, '#')))	/* remove HTML anchors */
 	*t = '\0';
 #endif
 
-    /* handle direct internal objects */
-    if ((!Config2.Accel.on || Config.onoff.global_internal_static) && internalCheck(url)) {
-	/* prepend our name & port */
-	http->uri = xstrdup(internalLocalUri(NULL, url));
-	http->flags.accel = 1;
-    }
-    /* see if we running in Config2.Accel.on, if so got to convert it to URL */
-    else if (Config2.Accel.on && *url == '/') {
-	int vport;
-	if (vhost_mode) {
-#if IPF_TRANSPARENT
-	    static time_t last_reported = 0;
-#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
-	    obj.ipfo_rev = IPFILTER_VERSION;
-	    obj.ipfo_size = sizeof(natLookup);
-	    obj.ipfo_ptr = &natLookup;
-	    obj.ipfo_type = IPFOBJ_NATLOOKUP;
-	    obj.ipfo_offset = 0;
-#endif
-	    natLookup.nl_inport = http->conn->me.sin_port;
-	    natLookup.nl_outport = http->conn->peer.sin_port;
-	    natLookup.nl_inip = http->conn->me.sin_addr;
-	    natLookup.nl_outip = http->conn->peer.sin_addr;
-	    natLookup.nl_flags = IPN_TCP;
-	    if (natfd < 0) {
-		int save_errno;
-		enter_suid();
-#ifdef IPNAT_NAME
-		natfd = open(IPNAT_NAME, O_RDONLY, 0);
-#else
-		natfd = open(IPL_NAT, O_RDONLY, 0);
-#endif
-		save_errno = errno;
-		leave_suid();
-		errno = save_errno;
-	    }
-	    if (natfd < 0) {
-		if (squid_curtime - last_reported > 60) {
-		    debug(50, 1) ("parseHttpRequest: NAT open failed: %s\n", xstrerror());
-		    last_reported = squid_curtime;
-		}
-	    } else {
-#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
-		x = ioctl(natfd, SIOCGNATL, &obj);
-#else
-		/*
-		 * IP-Filter changed the type for SIOCGNATL between
-		 * 3.3 and 3.4.  It also changed the cmd value for
-		 * SIOCGNATL, so at least we can detect it.  We could
-		 * put something in configure and use ifdefs here, but
-		 * this seems simpler.
-		 */
-		if (63 == siocgnatl_cmd) {
-		    struct natlookup *nlp = &natLookup;
-		    x = ioctl(natfd, SIOCGNATL, &nlp);
-		} else {
-		    x = ioctl(natfd, SIOCGNATL, &natLookup);
-		}
-#endif
-		if (x < 0) {
-		    if (errno != ESRCH) {
-			if (squid_curtime - last_reported > 60) {
-			    debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL): %s\n", xstrerror());
-			    last_reported = squid_curtime;
-			}
-			close(natfd);
-			natfd = -1;
-		    }
-		} else {
-		    conn->me.sin_port = natLookup.nl_realport;
-		    conn->me.sin_addr = natLookup.nl_realip;
-		}
-	    }
-#elif PF_TRANSPARENT
-	    static time_t last_reported = 0;
-	    if (pffd < 0)
-		pffd = open("/dev/pf", O_RDWR);
-	    if (pffd < 0) {
-		if (squid_curtime - last_reported > 60) {
-		    debug(50, 1) ("parseHttpRequest: PF open failed: %s\n", xstrerror());
-		    last_reported = squid_curtime;
-		}
-	    } else {
-		memset(&nl, 0, sizeof(struct pfioc_natlook));
-		nl.saddr.v4.s_addr = http->conn->peer.sin_addr.s_addr;
-		nl.sport = http->conn->peer.sin_port;
-		nl.daddr.v4.s_addr = http->conn->me.sin_addr.s_addr;
-		nl.dport = http->conn->me.sin_port;
-		nl.af = AF_INET;
-		nl.proto = IPPROTO_TCP;
-		nl.direction = PF_OUT;
-		if (ioctl(pffd, DIOCNATLOOK, &nl)) {
-		    if (errno != ENOENT) {
-			if (squid_curtime - last_reported > 60) {
-			    debug(50, 1) ("parseHttpRequest: PF lookup failed: ioctl(DIOCNATLOOK): %s\n", xstrerror());
-			    last_reported = squid_curtime;
-			}
-			close(pffd);
-			pffd = -1;
-		    }
-		} else {
-		    conn->me.sin_port = nl.rdport;
-		    conn->me.sin_addr = nl.rdaddr.v4;
-		}
-	    }
-#elif LINUX_NETFILTER
-	    static time_t last_reported = 0;
-	    /* If the call fails the address structure will be unchanged */
-	    if (getsockopt(conn->fd, SOL_IP, SO_ORIGINAL_DST, &conn->me, &sock_sz) != 0) {
-		if (squid_curtime - last_reported > 60) {
-		    debug(50, 1) ("parseHttpRequest: NF getsockopt(SO_ORIGINAL_DST) failed: %s\n", xstrerror());
-		    last_reported = squid_curtime;
-		}
-	    }
-#endif
-	}
-	if (vport_mode)
-	    vport = (int) ntohs(http->conn->me.sin_port);
-	else
-	    vport = (int) Config.Accel.port;
-	/* prepend the accel prefix */
-	if (Config.onoff.accel_uses_host_header && (t = mime_get_header(req_hdr, "Host"))) {
-	    char *q;
-	    const char *protocol_name = "http";
-	    /* If a Host: header was specified, use it to build the URL 
-	     * instead of the one in the Config file. */
-	    /*
-	     * XXX Use of the Host: header here opens a potential
-	     * security hole.  There are no checks that the Host: value
-	     * corresponds to one of your servers.  It might, for example,
-	     * refer to www.playboy.com.  The 'dst' and/or 'dst_domain' ACL 
-	     * types should be used to prevent httpd-accelerators 
-	     * handling requests for non-local servers */
-	    strtok(t, " /;@");
-	    if ((q = strchr(t, ':'))) {
-		*q++ = '\0';
-		if (vport_mode)
-		    vport = atoi(q);
-	    }
+    /* handle "accelerated" objects (and internal) */
+    if (method == METHOD_CONNECT);	/* Nothing to do */
+    else if (*url == '/')
+  accel:{
+	int vhost = conn->port->vhost || conn->port->transparent;
+	int vport = conn->port->vport || conn->transparent;
+	if (internalCheck(url)) {
+	    /* prepend our name & port */
+	    http->uri = xstrdup(internalLocalUri(NULL, url));
+	    http->flags.internal = 1;
+	    http->flags.accel = 1;
+	    debug(33, 5) ("INTERNAL REWRITE: '%s'\n", http->uri);
+	} else if (vhost && (t = mime_get_header(req_hdr, "Host"))) {
 	    url_sz = strlen(url) + 32 + Config.appendDomainLen +
 		strlen(t);
 	    http->uri = xcalloc(url_sz, 1);
-
-#if SSL_FORWARDING_NOT_YET_DONE
-	    if (Config.Sockaddr.https->s.sin_port == http->conn->me.sin_port) {
-		protocol_name = "https";
-		vport = ntohs(http->conn->me.sin_port);
-	    }
-#endif
-	    snprintf(http->uri, url_sz, "%s://%s:%d%s",
-		protocol_name, t, vport, url);
-	} else if (vhost_mode) {
-	    /* Put the local socket IP address as the hostname */
-	    url_sz = strlen(url) + 32 + Config.appendDomainLen;
-	    http->uri = xcalloc(url_sz, 1);
-	    snprintf(http->uri, url_sz, "http://%s:%d%s",
-		inet_ntoa(http->conn->me.sin_addr),
-		vport, url);
+	    snprintf(http->uri, url_sz, "%s://%s%s",
+		conn->port->protocol, t, url);
 	    debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
-	} else if (vport_mode) {
-	    const char *protocol_name = "http";
+	} else if (conn->port->defaultsite) {
 	    url_sz = strlen(url) + 32 + Config.appendDomainLen +
-		strlen(Config.Accel.host);
+		strlen(conn->port->defaultsite);
+	    http->uri = xcalloc(url_sz, 1);
+	    snprintf(http->uri, url_sz, "%s://%s%s",
+		conn->port->protocol, conn->port->defaultsite, url);
+	    debug(33, 5) ("DEFAULTSITE REWRITE: '%s'\n", http->uri);
+	} else if (vport) {
+	    /* Put the local socket IP address as the hostname.
+	     * Note: In transparent mode clientNatLookup() has replaced
+	     * the local socket IP with the real destination
+	     */
+	    url_sz = strlen(url) + 32 + Config.appendDomainLen;
 	    http->uri = xcalloc(url_sz, 1);
 	    snprintf(http->uri, url_sz, "%s://%s:%d%s",
-		protocol_name, Config.Accel.host, vport, url);
+		http->conn->port->protocol,
+		inet_ntoa(http->conn->me.sin_addr),
+		vport, url);
+	    debug(33, 5) ("VPORT REWRITE: '%s'\n", http->uri);
 	} else {
-	    url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
-		Config.appendDomainLen + 1;
-	    http->uri = xcalloc(url_sz, 1);
-	    snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
+	    goto invalid_request;
 	}
 	http->flags.accel = 1;
-	if (Config.onoff.accel_no_pmtu_disc) {
-#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
-	    int i = IP_PMTUDISC_DONT;
-	    setsockopt(conn->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof i);
-#else
-	    static int reported = 0;
-	    if (!reported) {
-		debug(33, 1) ("Notice: httpd_accel_no_pmtu_disc not supported on your platform\n");
-		reported = 1;
-	    }
-#endif
+    } else if (conn->transparent) {
+	http->flags.accel = 1;
+    } else if (conn->port->accel) {
+	http->flags.accel = 1;
+	if (!conn->port->vhost) {
+	    url = strstr(url, "//");
+	    if (!url)
+		goto invalid_request;
+	    url = strchr(url + 2, '/');
+	    if (!url)
+		url = (char *) "/";
+	    goto accel;
 	}
     } else {
-	/* URL may be rewritten later, so make extra room */
+	/* Proxy request */
+	http->flags.accel = 0;
+    }
+    if (!http->uri) {
+	/* No special rewrites have been applied above, use the
+	 * requested url. may be rewritten later, so make extra room */
 	url_sz = strlen(url) + Config.appendDomainLen + 5;
 	http->uri = xcalloc(url_sz, 1);
 	strcpy(http->uri, url);
@@ -2962,6 +3183,13 @@
     xfree(inbuf);
     *status = 1;
     return http;
+
+  invalid_request:
+    /* This tries to back out what is done above */
+    dlinkDelete(&http->active, &ClientActiveRequests);
+    safe_free(http->uri);
+    cbdataFree(http);
+    return parseHttpRequestAbort(conn, "error:invalid-request");
 }
 
 static int
@@ -3143,7 +3371,7 @@
 	    }
 	    /* compile headers */
 	    /* we should skip request line! */
-	    if (!httpRequestParseHeader(request, prefix + req_line_sz)) {
+	    if ((http->http_ver.major >= 1) && !httpRequestParseHeader(request, prefix + req_line_sz)) {
 		debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
 		    http->uri, prefix);
 		err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
@@ -3156,6 +3384,8 @@
 		safe_free(prefix);
 		break;
 	    }
+	    if (conn->port->urlgroup)
+		request->urlgroup = xstrdup(conn->port->urlgroup);
 	    request->flags.accelerated = http->flags.accel;
 	    if (!http->flags.internal) {
 		if (internalCheck(strBuf(request->urlpath))) {
@@ -3209,6 +3439,7 @@
 		break;
 	    }
 	    http->request = requestLink(request);
+	    http->orig_request = requestLink(request);
 	    clientSetKeepaliveFlag(http);
 	    /* Do we expect a request-body? */
 	    if (request->content_length > 0) {
@@ -3257,6 +3488,7 @@
 		/* add to the client request queue */
 		for (H = &conn->chr; *H; H = &(*H)->next);
 		*H = http;
+		http->log_type = LOG_TCP_DENIED;
 		http->entry = clientCreateStoreEntry(http, METHOD_NONE, null_request_flags);
 		errorAppendEntry(http->entry, err);
 	    }
@@ -3397,8 +3629,10 @@
 	/* Invoke callback function */
 	if (valid)
 	    callback(buf, size, cbdata);
-	if (request != NULL)
+	if (request != NULL) {
 	    requestUnlink(request);	/* Linked in clientReadBody */
+	    conn->body.request = NULL;
+	}
 	debug(33, 2) ("clientProcessBody: end fd=%d size=%d body_size=%lu in.offset=%ld cb=%p req=%p\n", conn->fd, size, (unsigned long int) conn->body.size_left, (long int) conn->in.offset, callback, request);
     }
 }
@@ -3512,11 +3746,171 @@
     return 1;
 }
 
+#if IPF_TRANSPARENT
+static int
+clientNatLookup(ConnStateData * conn)
+{
+    struct natlookup natLookup;
+    static int natfd = -1;
+    int x;
+#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
+    struct ipfobj obj;
+#else
+    static int siocgnatl_cmd = SIOCGNATL & 0xff;
+#endif
+    static time_t last_reported = 0;
+
+#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
+    obj.ipfo_rev = IPFILTER_VERSION;
+    obj.ipfo_size = sizeof(natLookup);
+    obj.ipfo_ptr = &natLookup;
+    obj.ipfo_type = IPFOBJ_NATLOOKUP;
+    obj.ipfo_offset = 0;
+#endif
+
+    natLookup.nl_inport = conn->me.sin_port;
+    natLookup.nl_outport = conn->peer.sin_port;
+    natLookup.nl_inip = conn->me.sin_addr;
+    natLookup.nl_outip = conn->peer.sin_addr;
+    natLookup.nl_flags = IPN_TCP;
+    if (natfd < 0) {
+	int save_errno;
+	enter_suid();
+#ifdef IPNAT_NAME
+	natfd = open(IPNAT_NAME, O_RDONLY, 0);
+#else
+	natfd = open(IPL_NAT, O_RDONLY, 0);
+#endif
+	save_errno = errno;
+	leave_suid();
+	errno = save_errno;
+    }
+    if (natfd < 0) {
+	if (squid_curtime - last_reported > 60) {
+	    debug(50, 1) ("parseHttpRequest: NAT open failed: %s\n",
+		xstrerror());
+	    last_reported = squid_curtime;
+	}
+	return -1;
+    }
+#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
+    x = ioctl(natfd, SIOCGNATL, &obj);
+#else
+    /*
+     * IP-Filter changed the type for SIOCGNATL between
+     * 3.3 and 3.4.  It also changed the cmd value for
+     * SIOCGNATL, so at least we can detect it.  We could
+     * put something in configure and use ifdefs here, but
+     * this seems simpler.
+     */
+    if (63 == siocgnatl_cmd) {
+	struct natlookup *nlp = &natLookup;
+	x = ioctl(natfd, SIOCGNATL, &nlp);
+    } else {
+	x = ioctl(natfd, SIOCGNATL, &natLookup);
+    }
+#endif
+    if (x < 0) {
+	if (errno != ESRCH) {
+	    if (squid_curtime - last_reported > 60) {
+		debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL)\n");
+		last_reported = squid_curtime;
+	    }
+	    close(natfd);
+	    natfd = -1;
+	}
+	return -1;
+    } else {
+	int natted = conn->me.sin_addr.s_addr != natLookup.nl_realip.s_addr;
+	conn->me.sin_port = natLookup.nl_realport;
+	conn->me.sin_addr = natLookup.nl_realip;
+	if (natted)
+	    return 0;
+	else
+	    return -1;
+    }
+}
+#elif LINUX_NETFILTER
+static int
+clientNatLookup(ConnStateData * conn)
+{
+    size_t sock_sz = sizeof(conn->me);
+    struct in_addr orig_addr = conn->me.sin_addr;
+    static time_t last_reported = 0;
+    /* If the call fails the address structure will be unchanged */
+    if (getsockopt(conn->fd, SOL_IP, SO_ORIGINAL_DST, &conn->me, &sock_sz) != 0) {
+	if (squid_curtime - last_reported > 60) {
+	    debug(50, 1) ("parseHttpRequest: NF getsockopt(SO_ORIGINAL_DST) failed: %s\n", xstrerror());
+	    last_reported = squid_curtime;
+	}
+	return -1;
+    }
+    debug(33, 5) ("parseHttpRequest: addr = %s", inet_ntoa(conn->me.sin_addr));
+    if (orig_addr.s_addr != conn->me.sin_addr.s_addr)
+	return 0;
+    else
+	return -1;
+}
+#elif PF_TRANSPARENT
+static int
+clientNatLookup(ConnStateData * conn)
+{
+    struct pfioc_natlook nl;
+    static int pffd = -1;
+    static time_t last_reported = 0;
+    if (pffd < 0)
+	pffd = open("/dev/pf", O_RDWR);
+    if (pffd < 0) {
+	debug(50, 1) ("parseHttpRequest: PF open failed: %s\n",
+	    xstrerror());
+	return -1;
+    }
+    memset(&nl, 0, sizeof(struct pfioc_natlook));
+    nl.saddr.v4.s_addr = conn->peer.sin_addr.s_addr;
+    nl.sport = conn->peer.sin_port;
+    nl.daddr.v4.s_addr = conn->me.sin_addr.s_addr;
+    nl.dport = conn->me.sin_port;
+    nl.af = AF_INET;
+    nl.proto = IPPROTO_TCP;
+    nl.direction = PF_OUT;
+    if (ioctl(pffd, DIOCNATLOOK, &nl)) {
+	if (errno != ENOENT) {
+	    if (squid_curtime - last_reported > 60) {
+		debug(50, 1) ("parseHttpRequest: PF lookup failed: ioctl(DIOCNATLOOK)\n");
+		last_reported = squid_curtime;
+	    }
+	    close(pffd);
+	    pffd = -1;
+	}
+	return -1;
+    } else {
+	int natted = conn->me.sin_addr.s_addr != nt.rdaddr.v4.s_addr;
+	conn->me.sin_port = nl.rdport;
+	conn->me.sin_addr = nl.rdaddr.v4;
+	if (natted)
+	    return 0;
+	else
+	    return -1;
+    }
+}
+#else
+static int inline
+clientNatLookup(ConnStateData * conn)
+{
+    static time_t last_reported = 0;
+    if (squid_curtime - last_reported > 60) {
+	debug(33, 1) ("WARNING: transparent proxying not supported\n");
+	last_reported = squid_curtime;
+    }
+    return -1;
+}
+#endif
+
 /* Handle a new connection on HTTP socket. */
 void
 httpAccept(int sock, void *data)
 {
-    int *N = &incoming_sockets_accepted;
+    http_port_list *s = data;
     int fd = -1;
     fde *F;
     ConnStateData *connState = NULL;
@@ -3526,7 +3920,7 @@
 #if USE_IDENT
     static aclCheck_t identChecklist;
 #endif
-    commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
+    commSetSelect(sock, COMM_SELECT_READ, httpAccept, data, 0);
     while (max-- && !httpAcceptDefer(sock, NULL)) {
 	memset(&peer, '\0', sizeof(struct sockaddr_in));
 	memset(&me, '\0', sizeof(struct sockaddr_in));
@@ -3538,7 +3932,10 @@
 	}
 	F = &fd_table[fd];
 	debug(33, 4) ("httpAccept: FD %d: accepted port %d client %s:%d\n", fd, F->local_port, F->ipaddr, F->remote_port);
+	fd_note(fd, "client http connect");
 	connState = cbdataAlloc(ConnStateData);
+	connState->port = s;
+	cbdataLock(connState->port);
 	connState->peer = peer;
 	connState->log_addr = peer.sin_addr;
 	connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
@@ -3546,7 +3943,23 @@
 	connState->fd = fd;
 	connState->in.size = CLIENT_REQ_BUF_SZ;
 	connState->in.buf = memAllocate(MEM_CLIENT_REQ_BUF);
-	/* XXX account connState->in.buf */
+	if (connState->port->transparent) {
+	    if (clientNatLookup(connState) == 0) {
+		connState->transparent = 1;
+		if (Config.onoff.accel_no_pmtu_disc) {
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+		    int i = IP_PMTUDISC_DONT;
+		    setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof i);
+#else
+		    static int reported = 0;
+		    if (!reported) {
+			debug(33, 1) ("Notice: httpd_accel_no_pmtu_disc not supported on your platform\n");
+			reported = 1;
+		    }
+#endif
+		}
+	    }
+	}
 	comm_add_close_handler(fd, connStateFree, connState);
 	if (Config.onoff.log_fqdn)
 	    fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
@@ -3561,8 +3974,7 @@
 	commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
 	commSetDefer(fd, clientReadDefer, connState);
 	clientdbEstablished(peer.sin_addr, 1);
-	assert(N);
-	(*N)++;
+	incoming_sockets_accepted++;
     }
 }
 
@@ -3608,19 +4020,12 @@
     commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
 }
 
-struct _https_port_data {
-    SSL_CTX *sslContext;
-};
-typedef struct _https_port_data https_port_data;
-CBDATA_TYPE(https_port_data);
-
 /* handle a new HTTPS connection */
 static void
 httpsAccept(int sock, void *data)
 {
-    int *N = &incoming_sockets_accepted;
-    https_port_data *https_port = data;
-    SSL_CTX *sslContext = https_port->sslContext;
+    https_port_list *s = data;
+    SSL_CTX *sslContext = s->sslContext;
     int fd = -1;
     fde *F;
     ConnStateData *connState = NULL;
@@ -3632,7 +4037,7 @@
 #if USE_IDENT
     static aclCheck_t identChecklist;
 #endif
-    commSetSelect(sock, COMM_SELECT_READ, httpsAccept, https_port, 0);
+    commSetSelect(sock, COMM_SELECT_READ, httpsAccept, s, 0);
     while (max-- && !httpAcceptDefer(sock, NULL)) {
 	memset(&peer, '\0', sizeof(struct sockaddr_in));
 	memset(&me, '\0', sizeof(struct sockaddr_in));
@@ -3655,8 +4060,11 @@
 	F->write_method = &ssl_write_method;
 	debug(33, 4) ("httpsAccept: FD %d: accepted port %d client %s:%d\n", fd, F->local_port, F->ipaddr, F->remote_port);
 	debug(50, 5) ("httpsAccept: FD %d: starting SSL negotiation.\n", fd);
+	fd_note(fd, "client https connect");
 
 	connState = cbdataAlloc(ConnStateData);
+	connState->port = (http_port_list *) s;
+	cbdataLock(connState->port);
 	connState->peer = peer;
 	connState->log_addr = peer.sin_addr;
 	connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
@@ -3664,7 +4072,10 @@
 	connState->fd = fd;
 	connState->in.size = CLIENT_REQ_BUF_SZ;
 	connState->in.buf = memAllocate(MEM_CLIENT_REQ_BUF);
-	/* XXX account connState->in.buf */
+	/* transparent on SSL does not really make sense, but what the heck */
+	if (connState->port->transparent)
+	    if (clientNatLookup(connState))
+		connState->transparent = 1;
 	comm_add_close_handler(fd, connStateFree, connState);
 	if (Config.onoff.log_fqdn)
 	    fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
@@ -3679,7 +4090,7 @@
 	commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
 	commSetDefer(fd, clientReadDefer, connState);
 	clientdbEstablished(peer.sin_addr, 1);
-	(*N)++;
+	incoming_sockets_accepted++;
     }
 }
 
@@ -3812,7 +4223,7 @@
 static void
 clientHttpConnectionsOpen(void)
 {
-    sockaddr_in_list *s;
+    http_port_list *s;
     int fd;
     for (s = Config.Sockaddr.http; s; s = s->next) {
 	if (MAXHTTPPORTS == NHttpSockets) {
@@ -3831,7 +4242,7 @@
 	if (fd < 0)
 	    continue;
 	comm_listen(fd);
-	commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
+	commSetSelect(fd, COMM_SELECT_READ, httpAccept, s, 0);
 	/*
 	 * We need to set a defer handler here so that we don't
 	 * peg the CPU with select() when we hit the FD limit.
@@ -3850,33 +4261,34 @@
 clientHttpsConnectionsOpen(void)
 {
     https_port_list *s;
-    https_port_data *https_port;
     int fd;
-    for (s = Config.Sockaddr.https; s; s = s->next) {
+    for (s = Config.Sockaddr.https; s; s = (https_port_list *) s->http.next) {
 	if (MAXHTTPPORTS == NHttpSockets) {
 	    debug(1, 1) ("WARNING: You have too many 'https_port' lines.\n");
 	    debug(1, 1) ("         The limit is %d\n", MAXHTTPPORTS);
 	    continue;
 	}
 	enter_suid();
+	s->sslContext = sslCreateContext(s->cert, s->key, s->version, s->cipher, s->options);
+	if (!s->sslContext) {
+	    leave_suid();
+	    continue;
+	}
 	fd = comm_open(SOCK_STREAM,
-	    IPPROTO_TCP,
-	    s->s.sin_addr,
-	    ntohs(s->s.sin_port),
+	    0,
+	    s->http.s.sin_addr,
+	    ntohs(s->http.s.sin_port),
 	    COMM_NONBLOCKING,
 	    "HTTPS Socket");
 	leave_suid();
 	if (fd < 0)
 	    continue;
-	CBDATA_INIT_TYPE(https_port_data);
-	https_port = cbdataAlloc(https_port_data);
-	https_port->sslContext = sslCreateContext(s->cert, s->key, s->version, s->cipher, s->options);
 	comm_listen(fd);
-	commSetSelect(fd, COMM_SELECT_READ, httpsAccept, https_port, 0);
+	commSetSelect(fd, COMM_SELECT_READ, httpsAccept, s, 0);
 	commSetDefer(fd, httpAcceptDefer, NULL);
 	debug(1, 1) ("Accepting HTTPS connections at %s, port %d, FD %d.\n",
-	    inet_ntoa(s->s.sin_addr),
-	    (int) ntohs(s->s.sin_port),
+	    inet_ntoa(s->http.s.sin_addr),
+	    (int) ntohs(s->http.s.sin_port),
 	    fd);
 	HttpSockets[NHttpSockets++] = fd;
     }
Index: squid/src/enums.h
diff -u squid/src/enums.h:1.44 squid/src/enums.h:1.44.2.1
--- squid/src/enums.h:1.44	Mon May 15 16:51:55 2006
+++ squid/src/enums.h	Mon May 15 18:01:31 2006
@@ -115,6 +115,7 @@
     ACL_IDENT,
     ACL_IDENT_REGEX,
 #endif
+    ACL_TYPE,
     ACL_PROTO,
     ACL_METHOD,
     ACL_BROWSER,
@@ -140,6 +141,7 @@
     ACL_MAX_USER_IP,
     ACL_EXTERNAL,
     ACL_URLLOGIN,
+    ACL_URLGROUP,
     ACL_ENUM_MAX
 } squid_acl;
 
@@ -245,6 +247,8 @@
 #if X_ACCELERATOR_VARY
     HDR_X_ACCELERATOR_VARY,
 #endif
+    HDR_X_ERROR_URL,		/* errormap, requested URL */
+    HDR_X_ERROR_STATUS,		/* errormap, received HTTP status line */
     HDR_OTHER,
     HDR_ENUM_END
 } http_hdr_type;
@@ -312,6 +316,8 @@
     CARP,
 #endif
     ANY_OLD_PARENT,
+    USERHASH_PARENT,
+    SOURCEHASH_PARENT,
     HIER_MAX
 } hier_code;
 
@@ -568,6 +574,7 @@
     MEM_ACL_IP_DATA,
     MEM_ACL_LIST,
     MEM_ACL_NAME_LIST,
+    MEM_ACL_REQUEST_TYPE,
     MEM_AUTH_USER_T,
     MEM_AUTH_USER_HASH,
     MEM_ACL_PROXY_AUTH_MATCH,
Index: squid/src/errormap.c
diff -u /dev/null squid/src/errormap.c:1.1.30.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/errormap.c	Mon May 15 18:01:31 2006
@@ -0,0 +1,220 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section ??    Error Beautifier
+ * AUTHOR: Henrik Nordstrom
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+static const char *
+getErrorMap(const errormap * map, const http_status status, const char *squid_error, const char *aclname)
+{
+    while (map) {
+	struct error_map_entry *m;
+	for (m = map->map; m; m = m->next) {
+	    if (m->status == status)
+		return map->url;
+	    if (squid_error && strcmp(m->value, squid_error) == 0)
+		return map->url;
+	    if (aclname && strcmp(m->value, aclname) == 0)
+		return map->url;
+	}
+	map = map->next;
+    }
+    return NULL;
+}
+
+
+static int client_header_identities[] =
+{
+    HDR_USER_AGENT,
+    HDR_COOKIE,
+    HDR_X_FORWARDED_FOR,
+    HDR_VIA,
+    HDR_AUTHORIZATION,
+    HDR_ACCEPT,
+    HDR_REFERER
+};
+HttpHeaderMask client_headers;
+
+static int server_header_identities[] =
+{
+    HDR_VIA,
+    HDR_SERVER,
+    HDR_LOCATION,
+    HDR_CONTENT_LOCATION
+};
+HttpHeaderMask server_headers;
+
+typedef struct {
+    request_t *req;
+    StoreEntry *e;
+    store_client *sc;
+    ERRMAPCB *callback;
+    void *callback_data;
+    char *buf;
+} ErrorMapState;
+
+CBDATA_TYPE(ErrorMapState);
+
+static void
+errorMapFetchComplete(ErrorMapState * state)
+{
+    storeUnregister(state->sc, state->e, state);
+    state->sc = NULL;
+    storeUnlockObject(state->e);
+    state->e = NULL;
+    requestUnlink(state->req);
+    state->req = NULL;
+    memFree(state->buf, MEM_4K_BUF);
+    cbdataUnlock(state->callback_data);
+    state->callback_data = NULL;
+    cbdataFree(state);
+}
+
+static void
+errorMapFetchAbort(ErrorMapState * state)
+{
+    if (cbdataValid(state->callback_data))
+	state->callback(NULL, -1, -1, state->callback_data);
+    errorMapFetchComplete(state);
+}
+
+static void
+errorMapFetchHeaders(void *data, char *buf, ssize_t size)
+{
+    ErrorMapState *state = data;
+    size_t hdr_size;
+
+    if (EBIT_TEST(state->e->flags, ENTRY_ABORTED))
+	goto abort;
+    if (size == 0)
+	goto abort;
+    if (!cbdataValid(state->callback_data))
+	goto abort;
+
+    if ((hdr_size = headersEnd(buf, size))) {
+	http_status status;
+	/* httpReplyParse(reply, buf, hdr_size); */
+	HttpReply *reply = state->e->mem_obj->reply;
+	assert(reply);
+	status = reply->sline.status;
+	if (status != HTTP_OK)
+	    goto abort;
+	/* Send object to caller (cbdataValid verified above) */
+	state->callback(state->e, hdr_size, httpHeaderGetSize(&reply->header, HDR_CONTENT_LENGTH), state->callback_data);
+	errorMapFetchComplete(state);
+	goto done;
+    }
+    if (size >= 4096) {
+	/* Not enought space for reply headers */
+	goto abort;
+    }
+    /* Need more data */
+    storeClientCopy(state->sc, state->e, size, 0, 4096, state->buf, errorMapFetchHeaders, state);
+  done:
+    return;
+  abort:
+    errorMapFetchAbort(state);
+    return;
+}
+
+int
+errorMapStart(const errormap * map, request_t * client_req, HttpReply * reply, const char *aclname, ERRMAPCB * callback, void *callback_data)
+{
+    char squid_error[100];
+    int len = 0;
+    const char *errorUrl;
+    ErrorMapState *state;
+    const char *tmp;
+    http_status status;
+    request_t *req;
+    HttpHeaderPos hdrpos;
+    HttpHeaderEntry *hdr;
+
+    if (!client_req || !reply)
+	return 0;
+
+    status = reply->sline.status;
+
+    tmp = httpHeaderGetStr(&reply->header, HDR_X_SQUID_ERROR);
+    squid_error[0] = '\0';
+    if (tmp) {
+	xstrncpy(squid_error, tmp, sizeof(squid_error));
+	len = strcspn(squid_error, " ");
+    }
+    squid_error[len] = '\0';
+    errorUrl = getErrorMap(map, status, squid_error, aclname);
+    if (!errorUrl)
+	return 0;
+    req = urlParse(METHOD_GET, (char *) errorUrl);
+    if (!req) {
+	debug(0, 0) ("error_map: Invalid error URL '%s'\n", errorUrl);
+	return 0;
+    }
+    req->urlgroup = xstrdup("error");
+
+    state = cbdataAlloc(ErrorMapState);
+    state->req = requestLink(req);
+    state->e = storeCreateEntry(errorUrl, errorUrl, req->flags, req->method);
+    state->sc = storeClientListAdd(state->e, state);
+    state->callback = callback;
+    state->callback_data = callback_data;
+    cbdataLock(callback_data);
+
+    hdrpos = HttpHeaderInitPos;
+    while ((hdr = httpHeaderGetEntry(&client_req->header, &hdrpos)) != NULL) {
+	if (CBIT_TEST(client_headers, hdr->id))
+	    httpHeaderAddEntry(&req->header, httpHeaderEntryClone(hdr));
+    }
+    hdrpos = HttpHeaderInitPos;
+    while ((hdr = httpHeaderGetEntry(&reply->header, &hdrpos)) != NULL) {
+	if (CBIT_TEST(server_headers, hdr->id))
+	    httpHeaderAddEntry(&req->header, httpHeaderEntryClone(hdr));
+    }
+    httpHeaderPutInt(&req->header, HDR_X_ERROR_STATUS, (int) reply->sline.status);
+    httpHeaderPutStr(&req->header, HDR_X_REQUEST_URI, urlCanonical(client_req));
+
+    state->buf = memAllocate(MEM_4K_BUF);
+    fwdStart(-1, state->e, req);
+    storeClientCopy(state->sc, state->e, 0, 0, 4096, state->buf, errorMapFetchHeaders, state);
+    return 1;
+}
+
+void
+errorMapInit(void)
+{
+    CBDATA_INIT_TYPE(ErrorMapState);
+
+    httpHeaderCalcMask(&client_headers, client_header_identities, sizeof(client_header_identities) / sizeof(*client_header_identities));
+    httpHeaderCalcMask(&server_headers, server_header_identities, sizeof(server_header_identities) / sizeof(*server_header_identities));
+}
Index: squid/src/errorpage.c
diff -u squid/src/errorpage.c:1.25 squid/src/errorpage.c:1.25.2.1
--- squid/src/errorpage.c:1.25	Sun May 14 08:50:33 2006
+++ squid/src/errorpage.c	Mon May 15 18:01:31 2006
@@ -214,7 +214,7 @@
     xfree(info);
 }
 
-static int
+int
 errorPageId(const char *page_name)
 {
     int i;
@@ -248,8 +248,7 @@
     if (pageId >= ERR_NONE && pageId < ERR_MAX)		/* common case */
 	return err_type_str[pageId];
     if (pageId >= ERR_MAX && pageId - ERR_MAX < ErrorDynamicPages.count)
-	return ((ErrorDynamicPageInfo *) ErrorDynamicPages.
-	    items[pageId - ERR_MAX])->page_name;
+	return ((ErrorDynamicPageInfo *) ErrorDynamicPages.items[pageId - ERR_MAX])->page_name;
     return "ERR_UNKNOWN";	/* should not happen */
 }
 
Index: squid/src/forward.c
diff -u squid/src/forward.c:1.18 squid/src/forward.c:1.18.4.1
--- squid/src/forward.c:1.18	Fri Apr 28 04:10:52 2006
+++ squid/src/forward.c	Mon May 15 18:01:32 2006
@@ -345,7 +345,9 @@
     ErrorState *err;
     FwdServer *fs = fwdState->servers;
     const char *host;
+    const char *name;
     unsigned short port;
+    const char *domain = NULL;
     int ctimeout;
     int ftimeout = Config.Timeout.forward - (squid_curtime - fwdState->start);
     struct in_addr outgoing;
@@ -355,16 +357,14 @@
     debug(17, 3) ("fwdConnectStart: %s\n", url);
     if (fs->peer) {
 	host = fs->peer->host;
+	name = fs->peer->name;
 	port = fs->peer->http_port;
+	if (fs->peer->options.originserver)
+	    domain = fwdState->request->host;
 	ctimeout = fs->peer->connect_timeout > 0 ? fs->peer->connect_timeout
 	    : Config.Timeout.peer_connect;
-    } else if (fwdState->request->flags.accelerated &&
-	Config.Accel.single_host && Config.Accel.host) {
-	host = Config.Accel.host;
-	port = Config.Accel.port;
-	ctimeout = Config.Timeout.connect;
     } else {
-	host = fwdState->request->host;
+	host = name = fwdState->request->host;
 	port = fwdState->request->port;
 	ctimeout = Config.Timeout.connect;
     }
@@ -372,7 +372,7 @@
 	ftimeout = 5;
     if (ftimeout < ctimeout)
 	ctimeout = ftimeout;
-    if ((fd = pconnPop(host, port)) >= 0) {
+    if ((fd = pconnPop(host, port, domain)) >= 0) {
 	if (fwdCheckRetriable(fwdState)) {
 	    debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
 	    fwdState->server_fd = fd;
@@ -488,9 +488,11 @@
     if (fwdState->servers && (p = fwdState->servers->peer)) {
 	p->stats.fetches++;
 	fwdState->request->peer_login = p->login;
+	fwdState->request->peer_domain = p->domain;
 	httpStart(fwdState);
     } else {
 	fwdState->request->peer_login = NULL;
+	fwdState->request->peer_domain = NULL;
 	switch (request->protocol) {
 	case PROTO_HTTP:
 	    httpStart(fwdState);
@@ -581,6 +583,29 @@
 }
 
 void
+fwdStartPeer(peer * p, StoreEntry * e, request_t * r)
+{
+    FwdState *fwdState;
+    FwdServer *peer = NULL;
+    debug(17, 3) ("fwdStartPeer: '%s'\n", storeUrl(e));
+    e->mem_obj->request = requestLink(r);
+#if URL_CHECKSUM_DEBUG
+    assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url));
+#endif
+    fwdState = cbdataAlloc(FwdState);
+    fwdState->entry = e;
+    fwdState->client_fd = -1;
+    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);
+    peerAddFwdServer(&peer, p, DIRECT);
+    fwdStartComplete(peer, fwdState);
+}
+
+void
 fwdStart(int fd, StoreEntry * e, request_t * r)
 {
     FwdState *fwdState;
Index: squid/src/globals.h
diff -u squid/src/globals.h:1.20 squid/src/globals.h:1.20.2.1
--- squid/src/globals.h:1.20	Sun May 14 08:50:33 2006
+++ squid/src/globals.h	Mon May 15 18:01:32 2006
@@ -102,8 +102,6 @@
 extern int theOutSnmpConnection;	/* -1 */
 extern char *snmp_agentinfo;
 #endif
-extern int vhost_mode;		/* 0 */
-extern int vport_mode;		/* 0 */
 extern int n_disk_objects;	/* 0 */
 extern iostats IOStats;
 extern struct _acl_deny_info_list *DenyInfoList;	/* NULL */
Index: squid/src/http.c
diff -u squid/src/http.c:1.27 squid/src/http.c:1.27.4.1
--- squid/src/http.c:1.27	Fri Apr 28 04:10:52 2006
+++ squid/src/http.c	Mon May 15 18:01:32 2006
@@ -738,10 +738,14 @@
 #endif
 		    comm_remove_close_handler(fd, httpStateFree, httpState);
 		    fwdUnregister(fd, httpState->fwd);
-		    if (request->flags.accelerated && Config.Accel.single_host && Config.Accel.host)
-			pconnPush(fd, Config.Accel.host, Config.Accel.port);
-		    else
-			pconnPush(fd, request->host, request->port);
+		    if (httpState->peer) {
+			if (httpState->peer->options.originserver)
+			    pconnPush(fd, httpState->peer->name, httpState->peer->http_port, httpState->orig_request->host);
+			else
+			    pconnPush(fd, httpState->peer->name, httpState->peer->http_port, NULL);
+		    } else {
+			pconnPush(fd, request->host, request->port, NULL);
+		    }
 		    fwdComplete(httpState->fwd);
 		    httpState->fd = -1;
 		    httpStateFree(fd, httpState);
@@ -836,7 +840,6 @@
     const HttpHeader *hdr_in = &orig_request->header;
     int we_do_ranges;
     const HttpHeaderEntry *e;
-    String strVia;
     String strFwd;
     HttpHeaderPos pos = HttpHeaderInitPos;
     httpHeaderInit(hdr_out, hoRequest);
@@ -879,24 +882,23 @@
 	    /* Only pass on proxy authentication to peers for which
 	     * authentication forwarding is explicitly enabled
 	     */
-	    if (request->flags.proxying && orig_request->peer_login &&
-		strcmp(orig_request->peer_login, "PASS") == 0) {
+	    if (flags.proxying && orig_request->peer_login && strcmp(orig_request->peer_login, "PASS") == 0) {
 		httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
 	    }
 	    break;
 	case HDR_AUTHORIZATION:
-	    /* Pass on WWW authentication even if used locally. If this is
-	     * not wanted in an accelerator then the header can be removed
-	     * using the anonymization functions
-	     */
-	    httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
-	    /* XXX Some accelerators might want to strip the header
-	     * and regard the reply as cacheable, but authentication
-	     * is not normally enabled for accelerators without reading
-	     * the code, so there is not much use in adding logics here
-	     * without first defining the concept of having authentication
-	     * in the accelerator...
+	    /* Pass on WWW authentication.
 	     */
+	    if (!flags.originpeer) {
+		httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
+	    } else {
+		/* In accelerators, only forward authentication if enabled
+		 * (see also below for proxy->server authentication)
+		 */
+		if (orig_request->peer_login && (strcmp(orig_request->peer_login, "PASS") == 0 || strcmp(orig_request->peer_login, "PROXYPASS") == 0)) {
+		    httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
+		}
+	    }
 	    break;
 	case HDR_HOST:
 	    /*
@@ -905,7 +907,9 @@
 	     * went through our redirector and the admin configured
 	     * 'redir_rewrites_host' to be off.
 	     */
-	    if (request->flags.redirected && !Config.onoff.redir_rewrites_host)
+	    if (orig_request->peer_domain)
+		httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->peer_domain);
+	    else if (request->flags.redirected && !Config.onoff.redir_rewrites_host)
 		httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
 	    else {
 		/* use port# only if not default */
@@ -937,9 +941,13 @@
 	    if (!we_do_ranges)
 		httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
 	    break;
+	case HDR_VIA:
+	    /* If Via is disabled then forward any received header as-is */
+	    if (!Config.onoff.via)
+		httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
+	    break;
 	case HDR_PROXY_CONNECTION:
 	case HDR_CONNECTION:
-	case HDR_VIA:
 	case HDR_X_FORWARDED_FOR:
 	case HDR_CACHE_CONTROL:
 	    /* append these after the loop if needed */
@@ -951,14 +959,15 @@
     }
 
     /* append Via */
-    strVia = httpHeaderGetList(hdr_in, HDR_VIA);
-    snprintf(bbuf, BBUF_SZ, "%d.%d %s",
-	orig_request->http_ver.major,
-	orig_request->http_ver.minor, ThisCache);
-    strListAdd(&strVia, bbuf, ',');
-    httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia));
-    stringClean(&strVia);
-
+    if (Config.onoff.via) {
+	String strVia = httpHeaderGetList(hdr_in, HDR_VIA);
+	snprintf(bbuf, BBUF_SZ, "%d.%d %s",
+	    orig_request->http_ver.major,
+	    orig_request->http_ver.minor, ThisCache);
+	strListAdd(&strVia, bbuf, ',');
+	httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia));
+	stringClean(&strVia);
+    }
     /* append X-Forwarded-For */
     strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR);
     strListAdd(&strFwd,
@@ -969,8 +978,10 @@
 
     /* append Host if not there already */
     if (!httpHeaderHas(hdr_out, HDR_HOST)) {
-	/* use port# only if not default */
-	if (orig_request->port == urlDefaultPort(orig_request->protocol)) {
+	if (orig_request->peer_domain) {
+	    httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->peer_domain);
+	} else if (orig_request->port == urlDefaultPort(orig_request->protocol)) {
+	    /* use port# only if not default */
 	    httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host);
 	} else {
 	    httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d",
@@ -986,8 +997,7 @@
     }
     /* append Proxy-Authorization if configured for peer, and proxying */
     if (request->flags.proxying && orig_request->peer_login &&
-	!httpHeaderHas(hdr_out, HDR_PROXY_AUTHORIZATION) &&
-	strcmp(orig_request->peer_login, "PASS") != 0) {
+	!httpHeaderHas(hdr_out, HDR_PROXY_AUTHORIZATION)) {
 	if (*orig_request->peer_login == '*') {
 	    /* Special mode, to pass the username to the upstream cache */
 	    char loginbuf[256];
@@ -997,11 +1007,41 @@
 	    snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);
 	    httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
 		base64_encode(loginbuf));
+	} else if (strcmp(orig_request->peer_login, "PASS") == 0) {
+	    /* Nothing to do */
+	} else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) {
+	    /* Nothing to do */
 	} else {
 	    httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
 		base64_encode(orig_request->peer_login));
 	}
     }
+    /* append WWW-Authorization if configured for peer */
+    if (flags.originpeer && orig_request->peer_login &&
+	!httpHeaderHas(hdr_out, HDR_AUTHORIZATION)) {
+	if (strcmp(orig_request->peer_login, "PASS") == 0) {
+	    /* No credentials to forward.. (should have been done above if available) */
+	} else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) {
+	    /* Special mode, convert proxy authentication to WWW authentication */
+	    const char *auth = httpHeaderGetStr(hdr_in, HDR_PROXY_AUTHORIZATION);
+	    if (auth && strncasecmp(auth, "basic ", 6) == 0) {
+		httpHeaderPutStr(hdr_out, HDR_AUTHORIZATION, auth);
+	    }
+	} else if (*orig_request->peer_login == '*') {
+	    /* Special mode, to pass the username to the upstream cache */
+	    char loginbuf[256];
+	    const char *username = "-";
+	    if (orig_request->auth_user_request)
+		username = authenticateUserRequestUsername(orig_request->auth_user_request);
+	    snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);
+	    httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
+		base64_encode(loginbuf));
+	} else {
+	    /* Fixed login string */
+	    httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
+		base64_encode(orig_request->peer_login));
+	}
+    }
     /* append Cache-Control, add max-age if not there already */
     {
 	HttpHdrCc *cc = httpHeaderGetCc(hdr_in);
@@ -1084,10 +1124,15 @@
     else
 	sendHeaderDone = httpSendComplete;
 
-    if (p != NULL)
-	httpState->flags.proxying = 1;
-    else
+    if (p != NULL) {
+	if (p->options.originserver)
+	    httpState->flags.originpeer = 1;
+	else
+	    httpState->flags.proxying = 1;
+    } else {
 	httpState->flags.proxying = 0;
+	httpState->flags.originpeer = 0;
+    }
     /*
      * Is keep-alive okay for all request methods?
      */
@@ -1131,8 +1176,13 @@
     if (fwd->servers)
 	httpState->peer = fwd->servers->peer;	/* might be NULL */
     if (httpState->peer) {
+	const char *url;
+	if (httpState->peer->options.originserver)
+	    url = strBuf(orig_req->urlpath);
+	else
+	    url = storeUrl(httpState->entry);
 	proxy_req = requestCreate(orig_req->method,
-	    PROTO_NONE, storeUrl(httpState->entry));
+	    PROTO_NONE, url);
 	xstrncpy(proxy_req->host, httpState->peer->host, SQUIDHOSTNAMELEN);
 	proxy_req->port = httpState->peer->http_port;
 	proxy_req->flags = orig_req->flags;
Index: squid/src/icp_v2.c
diff -u squid/src/icp_v2.c:1.8 squid/src/icp_v2.c:1.8.2.1
--- squid/src/icp_v2.c:1.8	Mon May 15 15:50:49 2006
+++ squid/src/icp_v2.c	Mon May 15 18:01:32 2006
@@ -407,8 +407,6 @@
     int x;
     socklen_t len;
     wordlist *s;
-    if (Config2.Accel.on && !Config.onoff.accel_with_proxy)
-	return;
     if ((port = Config.Port.icp) <= 0)
 	return;
     enter_suid();
Index: squid/src/locrewrite.c
diff -u /dev/null squid/src/locrewrite.c:1.1.8.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/locrewrite.c	Mon May 15 18:01:32 2006
@@ -0,0 +1,161 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 29    Redirector
+ * AUTHOR: Henrik Nordstrom
+ * BASED ON: redirect.c by Duane Wessels
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+typedef struct {
+    void *data;
+    char *orig_url;
+    RH *handler;
+} rewriteStateData;
+
+static HLPCB locationRewriteHandleReply;
+static void locationRewriteStateFree(rewriteStateData * r);
+static helper *locrewriters = NULL;
+static OBJH locationRewriteStats;
+static int n_bypassed = 0;
+CBDATA_TYPE(rewriteStateData);
+
+static void
+locationRewriteHandleReply(void *data, char *reply)
+{
+    rewriteStateData *r = data;
+    int valid;
+    char *t;
+    debug(29, 5) ("rewriteHandleRead: {%s}\n", reply ? reply : "<NULL>");
+    if (reply) {
+	if ((t = strchr(reply, ' ')))
+	    *t = '\0';
+	if (*reply == '\0')
+	    reply = NULL;
+    }
+    valid = cbdataValid(r->data);
+    cbdataUnlock(r->data);
+    if (valid)
+	r->handler(r->data, reply);
+    locationRewriteStateFree(r);
+}
+
+static void
+locationRewriteStateFree(rewriteStateData * r)
+{
+    safe_free(r->orig_url);
+    cbdataFree(r);
+}
+
+static void
+locationRewriteStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "Redirector Statistics:\n");
+    helperStats(sentry, locrewriters);
+    if (Config.onoff.redirector_bypass)
+	storeAppendPrintf(sentry, "\nNumber of requests bypassed "
+	    "because all rewriters were busy: %d\n", n_bypassed);
+}
+
+/**** PUBLIC FUNCTIONS ****/
+
+void
+locationRewriteStart(HttpReply * rep, clientHttpRequest * http, RH * handler, void *data)
+{
+    rewriteStateData *r = NULL;
+    const char *location = httpHeaderGetStr(&rep->header, HDR_LOCATION);
+    const char *urlgroup;
+    char buf[8192];
+
+    if (http->orig_request && http->orig_request->urlgroup)
+	urlgroup = http->orig_request->urlgroup;
+    else if (http->request && http->request->urlgroup)
+	urlgroup = http->request->urlgroup;
+    else
+	urlgroup = NULL;
+
+    assert(handler);
+    if (!urlgroup || !*urlgroup)
+	urlgroup = "-";
+    debug(29, 5) ("locationRewriteStart: '%s'\n", location);
+    if (Config.Program.location_rewrite.command == NULL) {
+	handler(data, NULL);
+	return;
+    }
+    if (Config.onoff.redirector_bypass && locrewriters->stats.queue_size) {
+	/* Skip rewriter if there is one request queued */
+	n_bypassed++;
+	handler(data, NULL);
+	return;
+    }
+    r = cbdataAlloc(rewriteStateData);
+    r->orig_url = xstrdup(location);
+    r->handler = handler;
+    r->data = data;
+    cbdataLock(r->data);
+    snprintf(buf, 8192, "%s %s %s\n",
+	r->orig_url, http->uri, urlgroup);
+    helperSubmit(locrewriters, buf, locationRewriteHandleReply, r);
+}
+
+void
+locationRewriteInit(void)
+{
+    static int init = 0;
+    if (!Config.Program.location_rewrite.command)
+	return;
+    if (locrewriters == NULL)
+	locrewriters = helperCreate("location_rewriter");
+    locrewriters->cmdline = Config.Program.location_rewrite.command;
+    locrewriters->n_to_start = Config.Program.location_rewrite.children;
+    locrewriters->ipc_type = IPC_TCP_SOCKET;
+    helperOpenServers(locrewriters);
+    if (!init) {
+	cachemgrRegister("location_rewriter",
+	    "Location Rewriter Stats",
+	    locationRewriteStats, 0, 1);
+	init = 1;
+	CBDATA_INIT_TYPE(rewriteStateData);
+    }
+}
+
+void
+locationRewriteShutdown(void)
+{
+    if (!locrewriters)
+	return;
+    helperShutdown(locrewriters);
+    if (!shutting_down)
+	return;
+    helperFree(locrewriters);
+    locrewriters = NULL;
+}
Index: squid/src/main.c
diff -u squid/src/main.c:1.44 squid/src/main.c:1.44.2.1
--- squid/src/main.c:1.44	Mon May 15 08:52:05 2006
+++ squid/src/main.c	Mon May 15 18:01:32 2006
@@ -136,7 +136,14 @@
 	    opt_store_doublecheck = 1;
 	    break;
 	case 'V':
-	    vhost_mode = 1;
+	    if (Config.Sockaddr.http)
+		Config.Sockaddr.http->vhost = 1;
+#if USE_SSL
+	    else if (Config.Sockaddr.https)
+		Config.Sockaddr.https->http.vhost = 1;
+#endif
+	    else
+		fatal("No http_port specified\n");
 	    break;
 	case 'X':
 	    /* force full debugging */
@@ -310,6 +317,9 @@
 #if USE_CARP
     carpInit();
 #endif
+    peerSourceHashInit();
+    peerUserHashInit();
+    peerMonitorInit();
 }
 
 void
@@ -360,6 +370,7 @@
     idnsShutdown();
 #endif
     redirectShutdown();
+    locationRewriteShutdown();
     authenticateShutdown();
     externalAclShutdown();
     storeDirCloseSwapLogs();
@@ -387,6 +398,7 @@
     idnsInit();
 #endif
     redirectInit();
+    locationRewriteInit();
     authenticateInit(&Config.authConfig);
     externalAclInit();
 #if USE_WCCP
@@ -396,18 +408,14 @@
     wccp2Init();
 #endif
     serverConnectionsOpen();
-    if (theOutIcpConnection >= 0) {
-	if (!Config2.Accel.on || Config.onoff.accel_with_proxy)
-	    neighbors_open(theOutIcpConnection);
-	else
-	    debug(1, 1) ("ICP port disabled in httpd_accelerator mode\n");
-    }
+    neighbors_init();
     storeDirOpenSwapLogs();
     mimeInit(Config.mimeTablePathname);
     eventCleanup();
     writePidFile();		/* write PID file */
     debug(1, 1) ("Ready to serve requests.\n");
     reconfiguring = 0;
+    peerMonitorInit();
 }
 
 static void
@@ -418,6 +426,7 @@
     dnsShutdown();
 #endif
     redirectShutdown();
+    locationRewriteShutdown();
     authenticateShutdown();
     externalAclShutdown();
     _db_rotate_log();		/* cache.log */
@@ -434,6 +443,7 @@
     dnsInit();
 #endif
     redirectInit();
+    locationRewriteInit();
     authenticateInit(&Config.authConfig);
     externalAclInit();
 }
@@ -519,6 +529,8 @@
     idnsInit();
 #endif
     redirectInit();
+    locationRewriteInit();
+    errorMapInit();
     authenticateInit(&Config.authConfig);
     externalAclInit();
     useragentOpenLog();
@@ -563,12 +575,7 @@
     wccp2Init();
 #endif
     serverConnectionsOpen();
-    if (theOutIcpConnection >= 0) {
-	if (!Config2.Accel.on || Config.onoff.accel_with_proxy)
-	    neighbors_open(theOutIcpConnection);
-	else
-	    debug(1, 1) ("ICP port disabled in httpd_accelerator mode\n");
-    }
+    neighbors_init();
     if (Config.chroot_dir)
 	no_suid();
     if (!configured_once)
@@ -980,6 +987,7 @@
 #endif
     redirectShutdown();
     externalAclShutdown();
+    locationRewriteShutdown();
     icpConnectionClose();
 #if USE_HTCP
     htcpSocketClose();
Index: squid/src/mem.c
diff -u squid/src/mem.c:1.23 squid/src/mem.c:1.23.2.1
--- squid/src/mem.c:1.23	Fri May 12 15:51:56 2006
+++ squid/src/mem.c	Mon May 15 18:01:32 2006
@@ -215,6 +215,7 @@
     memDataInit(MEM_ACL_LIST, "acl_list", sizeof(acl_list), 0);
     memDataInit(MEM_ACL_NAME_LIST, "acl_name_list", sizeof(acl_name_list), 0);
     memDataInit(MEM_ACL_TIME_DATA, "acl_time_data", sizeof(acl_time_data), 0);
+    memDataInit(MEM_ACL_REQUEST_TYPE, "acl_request_type", sizeof(acl_request_type), 0);
     memDataInit(MEM_AUTH_USER_T, "auth_user_t",
 	sizeof(auth_user_t), 0);
     memDataInit(MEM_AUTH_USER_HASH, "auth_user_hash_pointer",
Index: squid/src/mib.txt
diff -u squid/src/mib.txt:1.6 squid/src/mib.txt:1.6.2.1
--- squid/src/mib.txt:1.6	Sun May 14 02:50:55 2006
+++ squid/src/mib.txt	Mon May 15 18:01:32 2006
@@ -686,7 +686,7 @@
 			  the peer caches, complete with info "
         ::= { cacheMesh 1 }
 
-	cachePeerEntry OBJECT-TYPE
+	OLDcachePeerEntry OBJECT-TYPE
 		SYNTAX CachePeerEntry
 		MAX-ACCESS not-accessible
 		STATUS current
@@ -695,7 +695,18 @@
 		INDEX 	{ cachePeerAddr }
 	::= { cachePeerTable 1 }
 
+	cachePeerEntry OBJECT-TYPE
+		SYNTAX CachePeerEntry
+		MAX-ACCESS not-accessible
+		STATUS current
+		DESCRIPTION
+			" An entry in cachePeerTable "
+		INDEX 	{ cachePeerIndex }
+	::= { cachePeerTable 2 }
+
 	CachePeerEntry ::= SEQUENCE {
+	  cachePeerIndex	Integer32,
+	  cachePeerHost		DisplayString,
 	  cachePeerName		DisplayString,
 	  cachePeerAddr  	IpAddress,
 	  cachePeerPortHttp 	Integer32 (1..65535),
@@ -817,6 +828,22 @@
 			" Number of keepalives received "
         ::= { cachePeerEntry 13 }
 
+        cachePeerIndex OBJECT-TYPE
+                SYNTAX Integer32
+                MAX-ACCESS read-only
+                STATUS current
+		DESCRIPTION
+			" Reference Index for each peer "
+        ::= { cachePeerEntry 14 }
+
+	cachePeerHost OBJECT-TYPE
+		SYNTAX DisplayString
+		MAX-ACCESS read-only
+		STATUS current
+		DESCRIPTION
+			  " The FQDN name for the peer cache "
+	::= { cachePeerEntry 15 }
+
 ---
 --- Table of cache's clients, with statistics. Children caches can be identified
 --- by non-zero number of ICP requests (unless browsers start using ICP).
Index: squid/src/neighbors.c
diff -u squid/src/neighbors.c:1.21 squid/src/neighbors.c:1.21.4.1
--- squid/src/neighbors.c:1.21	Fri Apr 28 04:10:52 2006
+++ squid/src/neighbors.c	Mon May 15 18:01:32 2006
@@ -362,7 +362,7 @@
 }
 
 void
-neighbors_open(int fd)
+neighbors_init(void)
 {
     struct sockaddr_in name;
     socklen_t len = sizeof(struct sockaddr_in);
@@ -370,25 +370,27 @@
     const char *me = getMyHostname();
     peer *this;
     peer *next;
-    memset(&name, '\0', sizeof(struct sockaddr_in));
-    if (getsockname(fd, (struct sockaddr *) &name, &len) < 0)
-	debug(15, 1) ("getsockname(%d,%p,%p) failed.\n", fd, &name, &len);
-    for (this = Config.peers; this; this = next) {
-	sockaddr_in_list *s;
-	next = this->next;
-	if (0 != strcmp(this->host, me))
-	    continue;
-	for (s = Config.Sockaddr.http; s; s = s->next) {
-	    if (this->http_port != ntohs(s->s.sin_port))
+    int fd = theInIcpConnection;
+    if (fd >= 0) {
+	memset(&name, '\0', sizeof(struct sockaddr_in));
+	if (getsockname(fd, (struct sockaddr *) &name, &len) < 0)
+	    debug(15, 1) ("getsockname(%d,%p,%p) failed.\n", fd, &name, &len);
+	for (this = Config.peers; this; this = next) {
+	    http_port_list *s;
+	    next = this->next;
+	    if (0 != strcmp(this->host, me))
 		continue;
-	    debug(15, 1) ("WARNING: Peer looks like this host\n");
-	    debug(15, 1) ("         Ignoring %s %s/%d/%d\n",
-		neighborTypeStr(this), this->host, this->http_port,
-		this->icp.port);
-	    neighborRemove(this);
+	    for (s = Config.Sockaddr.http; s; s = s->next) {
+		if (this->http_port != ntohs(s->s.sin_port))
+		    continue;
+		debug(15, 1) ("WARNING: Peer looks like this host\n");
+		debug(15, 1) ("         Ignoring %s %s/%d/%d\n",
+		    neighborTypeStr(this), this->host, this->http_port,
+		    this->icp.port);
+		neighborRemove(this);
+	    }
 	}
     }
-
     peerRefreshDNS((void *) 1);
     if (0 == echo_hdr.opcode) {
 	echo_hdr.opcode = ICP_SECHO;
@@ -405,9 +407,11 @@
     cachemgrRegister("server_list",
 	"Peer Cache Statistics",
 	neighborDumpPeers, 0, 1);
-    cachemgrRegister("non_peers",
-	"List of Unknown sites sending ICP messages",
-	neighborDumpNonPeers, 0, 1);
+    if (theInIcpConnection >= 0) {
+	cachemgrRegister("non_peers",
+	    "List of Unknown sites sending ICP messages",
+	    neighborDumpNonPeers, 0, 1);
+    }
 }
 
 int
@@ -505,9 +509,8 @@
 	    /* Neighbor is dead; ping it anyway, but don't expect a reply */
 	    /* log it once at the threshold */
 	    if (p->stats.logged_state == PEER_ALIVE) {
-		debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n",
-		    neighborTypeStr(p),
-		    p->host, p->http_port, p->icp.port);
+		debug(15, 1) ("Detected DEAD %s: %s\n",
+		    neighborTypeStr(p), p->name);
 		p->stats.logged_state = PEER_DEAD;
 	    }
 	}
@@ -681,9 +684,8 @@
 neighborAlive(peer * p, const MemObject * mem, const icp_common_t * header)
 {
     if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
-	debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n",
-	    neighborTypeStr(p),
-	    p->host, p->http_port, p->icp.port);
+	debug(15, 1) ("Detected REVIVED %s: %s\n",
+	    neighborTypeStr(p), p->name);
 	p->stats.logged_state = PEER_ALIVE;
     }
     p->stats.last_reply = squid_curtime;
@@ -714,9 +716,8 @@
 neighborAliveHtcp(peer * p, const MemObject * mem, const htcpReplyData * htcp)
 {
     if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
-	debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n",
-	    neighborTypeStr(p),
-	    p->host, p->http_port, p->icp.port);
+	debug(15, 1) ("Detected REVIVED %s: %s\n",
+	    neighborTypeStr(p), p->name);
 	p->stats.logged_state = PEER_ALIVE;
     }
     p->stats.last_reply = squid_curtime;
@@ -907,7 +908,7 @@
 {
     peer *p = NULL;
     for (p = Config.peers; p; p = p->next) {
-	if (!strcasecmp(name, p->host))
+	if (!strcasecmp(name, p->name))
 	    break;
     }
     return p;
@@ -918,7 +919,7 @@
 {
     peer *p = NULL;
     for (p = Config.peers; p; p = p->next) {
-	if (strcasecmp(name, p->host))
+	if (strcasecmp(name, p->name))
 	    continue;
 	if (port != p->http_port)
 	    continue;
@@ -934,6 +935,10 @@
 	if (!peerProbeConnect((peer *) p))
 	    return 0;
     }
+    if (p->stats.logged_state != PEER_ALIVE)
+	return 0;
+    if (p->monitor.state != PEER_ALIVE)
+	return 0;
     if (p->options.no_query)
 	return 1;
     if (p->stats.probe_start != 0 &&
@@ -963,6 +968,8 @@
     }
     aclDestroyAccessList(&p->access);
     safe_free(p->host);
+    safe_free(p->name);
+    safe_free(p->domain);
 #if USE_CACHE_DIGESTS
     if (p->digest) {
 	PeerDigest *pd = p->digest;
@@ -1033,8 +1040,9 @@
 	eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 180.0, 1);
 	return;
     }
-    for (p = Config.peers; p; p = p->next)
+    for (p = Config.peers; p; p = p->next) {
 	ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
+    }
     /* Reconfigure the peers every hour */
     eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 3600.0, 1);
 }
@@ -1049,9 +1057,8 @@
     }
     p->tcp_up--;
     if (!p->tcp_up) {
-	debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n",
-	    neighborTypeStr(p),
-	    p->host, p->http_port, p->icp.port);
+	debug(15, 1) ("Detected DEAD %s: %s\n",
+	    neighborTypeStr(p), p->name);
 	p->stats.logged_state = PEER_DEAD;
     }
 }
@@ -1068,9 +1075,9 @@
 {
     if (!p->tcp_up) {
 	debug(15, 2) ("TCP connection to %s/%d succeded\n", p->host, p->http_port);
-	debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n",
-	    neighborTypeStr(p),
-	    p->host, p->http_port, p->icp.port);
+	debug(15, 1) ("Detected REVIVED %s: %s\n",
+	    neighborTypeStr(p), p->name);
+	peerMonitorNow(p);
 	p->stats.logged_state = PEER_ALIVE;
     }
     p->tcp_up = PEER_TCP_MAGIC_COUNT;
@@ -1250,8 +1257,14 @@
 	storeAppendPrintf(sentry, " round-robin");
     if (p->options.mcast_responder)
 	storeAppendPrintf(sentry, " multicast-responder");
+    if (p->weight != 1)
+	storeAppendPrintf(sentry, " weight=%d", p->weight);
     if (p->options.closest_only)
 	storeAppendPrintf(sentry, " closest-only");
+    if (p->options.userhash)
+	storeAppendPrintf(sentry, " userhash");
+    if (p->options.sourcehash)
+	storeAppendPrintf(sentry, " sourcehash");
 #if USE_HTCP
     if (p->options.htcp)
 	storeAppendPrintf(sentry, " htcp");
@@ -1266,6 +1279,29 @@
 	storeAppendPrintf(sentry, " login=%s", p->login);
     if (p->mcast.ttl > 0)
 	storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl);
+    if (p->connect_timeout > 0)
+	storeAppendPrintf(sentry, " connect-timeout=%d", (int) p->connect_timeout);
+#if USE_CACHE_DIGESTS
+    if (p->digest_url)
+	storeAppendPrintf(sentry, " digest-url=%s", p->digest_url);
+#endif
+    if (p->options.allow_miss)
+	storeAppendPrintf(sentry, " allow-miss");
+    if (p->max_conn > 0)
+	storeAppendPrintf(sentry, " max-conn=%d", p->max_conn);
+    if (p->options.originserver)
+	storeAppendPrintf(sentry, " originserver");
+    /* name is used in the heading */
+    if (p->monitor.url)
+	storeAppendPrintf(sentry, " monitorurl=%s", p->monitor.url);
+    if (p->monitor.min > 0 || p->monitor.max > 0) {
+	if (p->monitor.max > 0)
+	    storeAppendPrintf(sentry, " monitorsize=%d:%d", p->monitor.min, p->monitor.max);
+	else
+	    storeAppendPrintf(sentry, " monitorsize=%d", p->monitor.min);
+    }
+    if (p->domain)
+	storeAppendPrintf(sentry, " forceddomain=%s", p->domain);
     storeAppendPrintf(sentry, "\n");
 }
 
@@ -1280,8 +1316,10 @@
 	storeAppendPrintf(sentry, "There are no neighbors installed.\n");
     for (e = peers; e; e = e->next) {
 	assert(e->host != NULL);
-	storeAppendPrintf(sentry, "\n%-11.11s: %s/%d/%d\n",
+	storeAppendPrintf(sentry, "\n%-11.11s: %s\n",
 	    neighborTypeStr(e),
+	    e->name);
+	storeAppendPrintf(sentry, "Host       : %s/%d/%d\n",
 	    e->host,
 	    e->http_port,
 	    e->icp.port);
@@ -1295,42 +1333,49 @@
 	    neighborUp(e) ? "Up" : "Down");
 	storeAppendPrintf(sentry, "AVG RTT    : %d msec\n", e->stats.rtt);
 	storeAppendPrintf(sentry, "OPEN CONNS : %d\n", e->stats.conn_open);
-	storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
-	    (int) (squid_curtime - e->stats.last_query));
-	storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
-	    (int) (squid_curtime - e->stats.last_reply));
-	storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
-	storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
-	    e->stats.pings_acked,
-	    percent(e->stats.pings_acked, e->stats.pings_sent));
+	if (!e->options.no_query) {
+	    storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
+		(int) (squid_curtime - e->stats.last_query));
+	    if (e->stats.last_reply > 0)
+		storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
+		    (int) (squid_curtime - e->stats.last_reply));
+	    else
+		storeAppendPrintf(sentry, "LAST REPLY : none received\n");
+	    storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
+	    storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
+		e->stats.pings_acked,
+		percent(e->stats.pings_acked, e->stats.pings_sent));
+	}
 	storeAppendPrintf(sentry, "FETCHES    : %8d %3d%%\n",
 	    e->stats.fetches,
 	    percent(e->stats.fetches, e->stats.pings_acked));
 	storeAppendPrintf(sentry, "IGNORED    : %8d %3d%%\n",
 	    e->stats.ignored_replies,
 	    percent(e->stats.ignored_replies, e->stats.pings_acked));
-	storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
+	if (!e->options.no_query) {
+	    storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
 #if USE_HTCP
-	if (e->options.htcp) {
-	    storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n",
-		e->htcp.counts[0],
-		percent(e->htcp.counts[0], e->stats.pings_acked));
-	    storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n",
-		e->htcp.counts[1],
-		percent(e->htcp.counts[1], e->stats.pings_acked));
-	} else {
+	    if (e->options.htcp) {
+		storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n",
+		    e->htcp.counts[0],
+		    percent(e->htcp.counts[0], e->stats.pings_acked));
+		storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n",
+		    e->htcp.counts[1],
+		    percent(e->htcp.counts[1], e->stats.pings_acked));
+	    } else {
 #endif
-	    for (op = ICP_INVALID; op < ICP_END; op++) {
-		if (e->icp.counts[op] == 0)
-		    continue;
-		storeAppendPrintf(sentry, "    %12.12s : %8d %3d%%\n",
-		    icp_opcode_str[op],
-		    e->icp.counts[op],
-		    percent(e->icp.counts[op], e->stats.pings_acked));
-	    }
+		for (op = ICP_INVALID; op < ICP_END; op++) {
+		    if (e->icp.counts[op] == 0)
+			continue;
+		    storeAppendPrintf(sentry, "    %12.12s : %8d %3d%%\n",
+			icp_opcode_str[op],
+			e->icp.counts[op],
+			percent(e->icp.counts[op], e->stats.pings_acked));
+		}
 #if USE_HTCP
-	}
+	    }
 #endif
+	}
 	if (e->stats.last_connect_failure) {
 	    storeAppendPrintf(sentry, "Last failed connect() at: %s\n",
 		mkhttpdlogtime(&(e->stats.last_connect_failure)));
Index: squid/src/pconn.c
diff -u squid/src/pconn.c:1.8 squid/src/pconn.c:1.8.4.1
--- squid/src/pconn.c:1.8	Fri Apr 28 04:10:53 2006
+++ squid/src/pconn.c	Mon May 15 18:01:32 2006
@@ -49,7 +49,7 @@
 
 static PF pconnRead;
 static PF pconnTimeout;
-static const char *pconnKey(const char *host, u_short port);
+static const char *pconnKey(const char *host, u_short port, const char *domain);
 static hash_table *table = NULL;
 static struct _pconn *pconnNew(const char *key);
 static void pconnDelete(struct _pconn *p);
@@ -59,10 +59,13 @@
 static MemPool *pconn_fds_pool = NULL;
 
 static const char *
-pconnKey(const char *host, u_short port)
+pconnKey(const char *host, u_short port, const char *domain)
 {
     LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN + 10);
-    snprintf(buf, SQUIDHOSTNAMELEN + 10, "%s.%d", host, (int) port);
+    if (domain)
+	snprintf(buf, SQUIDHOSTNAMELEN + 10, "%s:%d/%s", host, (int) port, domain);
+    else
+	snprintf(buf, SQUIDHOSTNAMELEN + 10, "%s:%d", host, (int) port);
     return buf;
 }
 
@@ -184,11 +187,11 @@
 }
 
 void
-pconnPush(int fd, const char *host, u_short port)
+pconnPush(int fd, const char *host, u_short port, const char *domain)
 {
     struct _pconn *p;
     int *old;
-    LOCAL_ARRAY(char, key, SQUIDHOSTNAMELEN + 10);
+    const char *key;
     LOCAL_ARRAY(char, desc, FD_DESC_SZ);
     if (fdUsageHigh()) {
 	debug(48, 3) ("pconnPush: Not many unused FDs\n");
@@ -199,7 +202,7 @@
 	return;
     }
     assert(table != NULL);
-    strcpy(key, pconnKey(host, port));
+    key = pconnKey(host, port, domain);
     p = (struct _pconn *) hash_lookup(table, key);
     if (p == NULL)
 	p = pconnNew(key);
@@ -223,14 +226,14 @@
 }
 
 int
-pconnPop(const char *host, u_short port)
+pconnPop(const char *host, u_short port, const char *domain)
 {
     struct _pconn *p;
     hash_link *hptr;
     int fd = -1;
-    LOCAL_ARRAY(char, key, SQUIDHOSTNAMELEN + 10);
+    const char *key;
     assert(table != NULL);
-    strcpy(key, pconnKey(host, port));
+    key = pconnKey(host, port, domain);
     hptr = hash_lookup(table, key);
     if (hptr != NULL) {
 	p = (struct _pconn *) hptr;
Index: squid/src/peer_monitor.c
diff -u /dev/null squid/src/peer_monitor.c:1.1.30.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/peer_monitor.c	Mon May 15 18:01:32 2006
@@ -0,0 +1,245 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section ??    Peer monitoring
+ * AUTHOR: Henrik Nordstrom
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#define DBG	1
+#include "squid.h"
+
+/* local types */
+
+struct _PeerMonitor {
+    peer *peer;
+    time_t last_probe;
+    struct {
+	request_t *req;
+	StoreEntry *e;
+	store_client *sc;
+	int size;
+	http_status status;
+	int hdr_size;
+	int offset;
+	char *buf;
+    } running;
+    char name[40];
+    char url[80];
+};
+
+CBDATA_TYPE(PeerMonitor);
+
+static void peerMonitorCompleted(PeerMonitor * pm);
+
+static void
+freePeerMonitor(void *data)
+{
+    PeerMonitor *pm = data;
+    if (cbdataValid(pm->peer))
+	pm->peer->monitor.data = NULL;
+    cbdataUnlock(pm->peer);
+    pm->peer = NULL;
+}
+
+static void
+peerMonitorFetchReply(void *data, char *buf, ssize_t size)
+{
+    PeerMonitor *pm = data;
+
+    if (size == 0 || !cbdataValid(pm->peer)) {
+	peerMonitorCompleted(pm);
+    } else {
+	pm->running.size += size;
+	pm->running.offset += size;
+	storeClientCopy(pm->running.sc, pm->running.e, pm->running.offset, pm->running.offset, 4096, buf, peerMonitorFetchReply, pm);
+    }
+}
+
+static void
+peerMonitorFetchReplyHeaders(void *data, char *buf, ssize_t size)
+{
+    PeerMonitor *pm = data;
+    size_t hdr_size;
+
+    if (EBIT_TEST(pm->running.e->flags, ENTRY_ABORTED))
+	goto completed;
+    if (size <= 0)
+	goto completed;
+    if (!cbdataValid(pm->peer))
+	goto completed;
+
+    if ((hdr_size = headersEnd(buf, size))) {
+	http_status status;
+	HttpReply *reply = pm->running.e->mem_obj->reply;
+	assert(reply);
+	/* httpReplyParse(reply, buf, hdr_size); */
+	status = reply->sline.status;
+	pm->running.status = status;
+	if (status != HTTP_OK)
+	    goto completed;
+	pm->running.size = size - hdr_size;
+	pm->running.offset = size;
+	storeClientCopy(pm->running.sc, pm->running.e, pm->running.offset, pm->running.offset, 4096, buf, peerMonitorFetchReply, pm);
+    } else {
+	/* need more data, do we have space? */
+	if (size >= 4096)
+	    goto completed;
+	else
+	    storeClientCopy(pm->running.sc, pm->running.e, size, 0, 4096, buf, peerMonitorFetchReplyHeaders, pm);
+    }
+    return;
+
+  completed:
+    /* We are fully done with this monitoring request. Clean up */
+    peerMonitorCompleted(pm);
+    return;
+}
+
+static void
+peerMonitorRequest(void *data)
+{
+    PeerMonitor *pm = data;
+    char *url = pm->url;
+    request_t *req;
+
+    if (!cbdataValid(pm->peer)) {
+	cbdataFree(pm);
+	return;
+    }
+    req = urlParse(METHOD_GET, url);
+    if (!req) {
+	debug(DBG, 1) ("peerMonitorRequest: Failed to parse URL '%s' for cache_peer %s\n", url, pm->peer->name);
+	cbdataFree(pm);
+	return;
+    }
+    pm->last_probe = squid_curtime;
+
+    httpHeaderPutStr(&req->header, HDR_ACCEPT, "*/*");
+    httpHeaderPutStr(&req->header, HDR_USER_AGENT, full_appname_string);
+    if (pm->peer->login)
+	xstrncpy(req->login, pm->peer->login, MAX_LOGIN_SZ);
+    pm->running.req = requestLink(req);
+    pm->running.e = storeCreateEntry(url, url, req->flags, req->method);
+    pm->running.sc = storeClientListAdd(pm->running.e, pm);
+    pm->running.buf = memAllocate(MEM_4K_BUF);
+    fwdStartPeer(pm->peer, pm->running.e, pm->running.req);
+    storeClientCopy(pm->running.sc, pm->running.e, 0, 0, 4096, pm->running.buf, peerMonitorFetchReplyHeaders, pm);
+    return;
+}
+
+static void
+peerMonitorCompleted(PeerMonitor * pm)
+{
+    int state = PEER_ALIVE;
+    peer *p = pm->peer;
+    storeUnregister(pm->running.sc, pm->running.e, pm);
+    storeUnlockObject(pm->running.e);
+    requestUnlink(pm->running.req);
+    memFree(pm->running.buf, MEM_4K_BUF);
+    if (!cbdataValid(pm->peer)) {
+	cbdataFree(pm);
+	return;
+    }
+    /* Figure out if the response was OK or not */
+    if (pm->running.status != HTTP_OK) {
+	debug(DBG, 1) ("peerMonitor %s: Failed, status != 200 (%d)\n",
+	    p->name, pm->running.status);
+	state = PEER_DEAD;
+    } else if (pm->running.size < p->monitor.min) {
+	debug(DBG, 1) ("peerMonitor %s: Failed, reply size %d < min %d\n",
+	    p->name, pm->running.size, p->monitor.min);
+	state = PEER_DEAD;
+    } else if (pm->running.size > p->monitor.max && p->monitor.max > 0) {
+	debug(DBG, 1) ("peerMonitor %s: Failed, reply size %d > max %d\n",
+	    p->name, pm->running.size, p->monitor.max);
+	state = PEER_DEAD;
+    } else {
+	debug(DBG, 2) ("peerMonitor %s: OK\n", p->name);
+    }
+    p->monitor.state = state;
+    if (state != p->stats.logged_state) {
+	switch (state) {
+	case PEER_ALIVE:
+	    debug(DBG, 1) ("Detected REVIVED %s: %s\n",
+		neighborTypeStr(p), p->name);
+	    break;
+	case PEER_DEAD:
+	    debug(DBG, 1) ("Detected DEAD %s: %s\n",
+		neighborTypeStr(p), p->name);
+	    break;
+	}
+	p->stats.logged_state = state;
+    }
+    memset(&pm->running, 0, sizeof(pm->running));
+    eventAdd(pm->name, peerMonitorRequest, pm, (double) (pm->last_probe + pm->peer->monitor.interval - current_dtime), 1);
+}
+
+static void
+peerMonitorStart(peer * peer)
+{
+    PeerMonitor *pm;
+    char *url = peer->monitor.url;
+    if (!url || !*url)
+	return;
+    if (!peer->monitor.interval)
+	return;
+    CBDATA_INIT_TYPE_FREECB(PeerMonitor, freePeerMonitor);
+    pm = cbdataAlloc(PeerMonitor);
+    snprintf(pm->name, sizeof(pm->name), "monitor %s", peer->name);
+    pm->peer = peer;
+    peer->monitor.data = pm;
+    cbdataLock(pm->peer);
+    if (*url == '/') {
+	snprintf(pm->url, sizeof(pm->url), "http://%s:%d%s",
+	    pm->peer->host, pm->peer->http_port, url);
+    } else {
+	xstrncpy(pm->url, url, sizeof(pm->url));
+    }
+    peerMonitorRequest(pm);
+}
+
+void
+peerMonitorNow(peer * peer)
+{
+    PeerMonitor *pm = peer->monitor.data;
+    if (pm && !pm->running.req) {
+	eventDelete(peerMonitorRequest, pm);
+	peerMonitorRequest(pm);
+    }
+}
+
+void
+peerMonitorInit(void)
+{
+    peer *p;
+    for (p = Config.peers; p; p = p->next)
+	peerMonitorStart(p);
+}
Index: squid/src/peer_select.c
diff -u squid/src/peer_select.c:1.18 squid/src/peer_select.c:1.18.2.1
--- squid/src/peer_select.c:1.18	Mon May 15 16:51:56 2006
+++ squid/src/peer_select.c	Mon May 15 18:01:32 2006
@@ -59,6 +59,8 @@
     "CARP",
 #endif
     "ANY_PARENT",
+    "USERHASH_PARENT",
+    "SOURCEHASH_PARENT",
     "INVALID CODE"
 };
 
@@ -90,7 +92,6 @@
 static void peerGetSomeDirect(ps_state *);
 static void peerGetSomeParent(ps_state *);
 static void peerGetAllParents(ps_state *);
-static void peerAddFwdServer(FwdServer **, peer *, hier_code);
 
 static void
 peerSelectStateFree(ps_state * psstate)
@@ -443,6 +444,10 @@
 	return;
     if ((p = getDefaultParent(request))) {
 	code = DEFAULT_PARENT;
+    } else if ((p = peerUserHashSelectParent(request))) {
+	code = USERHASH_PARENT;
+    } else if ((p = peerSourceHashSelectParent(request))) {
+	code = SOURCEHASH_PARENT;
     } else if ((p = getRoundRobinParent(request))) {
 	code = ROUNDROBIN_PARENT;
     } else if ((p = getFirstUpParent(request))) {
@@ -644,7 +649,7 @@
 	debug(44, 1) ("peerHandlePingReply: unknown protocol_t %d\n", (int) proto);
 }
 
-static void
+void
 peerAddFwdServer(FwdServer ** FS, peer * p, hier_code code)
 {
     FwdServer *fs = memAllocate(MEM_FWD_SERVER);
Index: squid/src/peer_sourcehash.c
diff -u /dev/null squid/src/peer_sourcehash.c:1.1.18.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/peer_sourcehash.c	Mon May 15 18:01:32 2006
@@ -0,0 +1,162 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 44    Peer user hash based selection
+ * AUTHOR: Henrik Nordstrom
+ * BASED ON: carp.c by Eric Stern
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (((sizeof(unsigned long)*8)-(n)))))
+
+static int n_sourcehash_peers = 0;
+static peer **sourcehash_peers = NULL;
+
+static int
+peerSortWeight(const void *a, const void *b)
+{
+    const peer *const *p1 = a, *const *p2 = b;
+    return (*p1)->weight - (*p2)->weight;
+}
+
+void
+peerSourceHashInit(void)
+{
+    int W = 0;
+    int K;
+    int k;
+    double P_last, X_last, Xn;
+    peer *p;
+    peer **P;
+    char *t;
+    /* Clean up */
+    for (k = 0; k < n_sourcehash_peers; k++) {
+	cbdataUnlock(sourcehash_peers[k]);
+    }
+    safe_free(sourcehash_peers);
+    n_sourcehash_peers = 0;
+    /* find out which peers we have */
+    for (p = Config.peers; p; p = p->next) {
+	if (!p->options.sourcehash)
+	    continue;
+	assert(p->type == PEER_PARENT);
+	if (p->weight == 0)
+	    continue;
+	n_sourcehash_peers++;
+	W += p->weight;
+    }
+    if (n_sourcehash_peers == 0)
+	return;
+    sourcehash_peers = xcalloc(n_sourcehash_peers, sizeof(*sourcehash_peers));
+    /* Build a list of the found peers and calculate hashes and load factors */
+    for (P = sourcehash_peers, p = Config.peers; p; p = p->next) {
+	if (!p->options.sourcehash)
+	    continue;
+	if (p->weight == 0)
+	    continue;
+	/* calculate this peers hash */
+	p->sourcehash.hash = 0;
+	for (t = p->host; *t != 0; t++)
+	    p->sourcehash.hash += ROTATE_LEFT(p->sourcehash.hash, 19) + (unsigned int) *t;
+	p->sourcehash.hash += p->sourcehash.hash * 0x62531965;
+	p->sourcehash.hash = ROTATE_LEFT(p->sourcehash.hash, 21);
+	/* and load factor */
+	p->sourcehash.load_factor = ((double) p->weight) / (double) W;
+	if (floor(p->sourcehash.load_factor * 1000.0) == 0.0)
+	    p->sourcehash.load_factor = 0.0;
+	/* add it to our list of peers */
+	*P++ = p;
+	cbdataLock(p);
+    }
+    /* Sort our list on weight */
+    qsort(sourcehash_peers, n_sourcehash_peers, sizeof(*sourcehash_peers), peerSortWeight);
+    /* Calculate the load factor multipliers X_k
+     *
+     * X_1 = pow ((K*p_1), (1/K))
+     * X_k = ([K-k+1] * [P_k - P_{k-1}])/(X_1 * X_2 * ... * X_{k-1})
+     * X_k += pow ((X_{k-1}, {K-k+1})
+     * X_k = pow (X_k, {1/(K-k+1)})
+     * simplified to have X_1 part of the loop
+     */
+    K = n_sourcehash_peers;
+    P_last = 0.0;		/* Empty P_0 */
+    Xn = 1.0;			/* Empty starting point of X_1 * X_2 * ... * X_{x-1} */
+    X_last = 0.0;		/* Empty X_0, nullifies the first pow statement */
+    for (k = 1; k <= K; k++) {
+	double Kk1 = (double) (K - k + 1);
+	p = sourcehash_peers[k - 1];
+	p->sourcehash.load_multiplier = (Kk1 * (p->sourcehash.load_factor - P_last)) / Xn;
+	p->sourcehash.load_multiplier += pow(X_last, Kk1);
+	p->sourcehash.load_multiplier = pow(p->sourcehash.load_multiplier, 1.0 / Kk1);
+	Xn *= p->sourcehash.load_multiplier;
+	X_last = p->sourcehash.load_multiplier;
+	P_last = p->sourcehash.load_factor;
+    }
+}
+
+peer *
+peerSourceHashSelectParent(request_t * request)
+{
+    int k;
+    const char *c;
+    peer *p = NULL;
+    peer *tp;
+    unsigned int user_hash = 0;
+    unsigned int combined_hash;
+    double score;
+    double high_score = 0;
+    char *key = NULL;
+
+    key = inet_ntoa(request->client_addr);
+
+    /* calculate hash key */
+    debug(39, 2) ("peerSourceHashSelectParent: Calculating hash for %s\n", key);
+    for (c = key; *c != 0; c++)
+	user_hash += ROTATE_LEFT(user_hash, 19) + *c;
+    /* select peer */
+    for (k = 0; k < n_sourcehash_peers; k++) {
+	tp = sourcehash_peers[k];
+	combined_hash = (user_hash ^ tp->sourcehash.hash);
+	combined_hash += combined_hash * 0x62531965;
+	combined_hash = ROTATE_LEFT(combined_hash, 21);
+	score = combined_hash * tp->sourcehash.load_multiplier;
+	debug(39, 3) ("peerSourceHashSelectParent: %s combined_hash %u score %.0f\n",
+	    tp->host, combined_hash, score);
+	if ((score > high_score) && peerHTTPOkay(tp, request)) {
+	    p = tp;
+	    high_score = score;
+	}
+    }
+    if (p)
+	debug(39, 2) ("peerSourceHashSelectParent: selected %s\n", p->host);
+    return p;
+}
Index: squid/src/peer_userhash.c
diff -u /dev/null squid/src/peer_userhash.c:1.1.30.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/peer_userhash.c	Mon May 15 18:01:32 2006
@@ -0,0 +1,165 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 44    Peer user hash based selection
+ * AUTHOR: Henrik Nordstrom
+ * BASED ON: carp.c by Eric Stern
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (((sizeof(unsigned long)*8)-(n)))))
+
+static int n_userhash_peers = 0;
+static peer **userhash_peers = NULL;
+
+static int
+peerSortWeight(const void *a, const void *b)
+{
+    const peer *const *p1 = a, *const *p2 = b;
+    return (*p1)->weight - (*p2)->weight;
+}
+
+void
+peerUserHashInit(void)
+{
+    int W = 0;
+    int K;
+    int k;
+    double P_last, X_last, Xn;
+    peer *p;
+    peer **P;
+    char *t;
+    /* Clean up */
+    for (k = 0; k < n_userhash_peers; k++) {
+	cbdataUnlock(userhash_peers[k]);
+    }
+    safe_free(userhash_peers);
+    n_userhash_peers = 0;
+    /* find out which peers we have */
+    for (p = Config.peers; p; p = p->next) {
+	if (!p->options.userhash)
+	    continue;
+	assert(p->type == PEER_PARENT);
+	if (p->weight == 0)
+	    continue;
+	n_userhash_peers++;
+	W += p->weight;
+    }
+    if (n_userhash_peers == 0)
+	return;
+    userhash_peers = xcalloc(n_userhash_peers, sizeof(*userhash_peers));
+    /* Build a list of the found peers and calculate hashes and load factors */
+    for (P = userhash_peers, p = Config.peers; p; p = p->next) {
+	if (!p->options.userhash)
+	    continue;
+	if (p->weight == 0)
+	    continue;
+	/* calculate this peers hash */
+	p->userhash.hash = 0;
+	for (t = p->host; *t != 0; t++)
+	    p->userhash.hash += ROTATE_LEFT(p->userhash.hash, 19) + (unsigned int) *t;
+	p->userhash.hash += p->userhash.hash * 0x62531965;
+	p->userhash.hash = ROTATE_LEFT(p->userhash.hash, 21);
+	/* and load factor */
+	p->userhash.load_factor = ((double) p->weight) / (double) W;
+	if (floor(p->userhash.load_factor * 1000.0) == 0.0)
+	    p->userhash.load_factor = 0.0;
+	/* add it to our list of peers */
+	*P++ = p;
+	cbdataLock(p);
+    }
+    /* Sort our list on weight */
+    qsort(userhash_peers, n_userhash_peers, sizeof(*userhash_peers), peerSortWeight);
+    /* Calculate the load factor multipliers X_k
+     *
+     * X_1 = pow ((K*p_1), (1/K))
+     * X_k = ([K-k+1] * [P_k - P_{k-1}])/(X_1 * X_2 * ... * X_{k-1})
+     * X_k += pow ((X_{k-1}, {K-k+1})
+     * X_k = pow (X_k, {1/(K-k+1)})
+     * simplified to have X_1 part of the loop
+     */
+    K = n_userhash_peers;
+    P_last = 0.0;		/* Empty P_0 */
+    Xn = 1.0;			/* Empty starting point of X_1 * X_2 * ... * X_{x-1} */
+    X_last = 0.0;		/* Empty X_0, nullifies the first pow statement */
+    for (k = 1; k <= K; k++) {
+	double Kk1 = (double) (K - k + 1);
+	p = userhash_peers[k - 1];
+	p->userhash.load_multiplier = (Kk1 * (p->userhash.load_factor - P_last)) / Xn;
+	p->userhash.load_multiplier += pow(X_last, Kk1);
+	p->userhash.load_multiplier = pow(p->userhash.load_multiplier, 1.0 / Kk1);
+	Xn *= p->userhash.load_multiplier;
+	X_last = p->userhash.load_multiplier;
+	P_last = p->userhash.load_factor;
+    }
+}
+
+peer *
+peerUserHashSelectParent(request_t * request)
+{
+    int k;
+    const char *c;
+    peer *p = NULL;
+    peer *tp;
+    unsigned int user_hash = 0;
+    unsigned int combined_hash;
+    double score;
+    double high_score = 0;
+    char *key = NULL;
+
+    if (request->auth_user_request)
+	key = authenticateUserRequestUsername(request->auth_user_request);
+    if (!key)
+	return NULL;
+
+    /* calculate hash key */
+    debug(39, 2) ("peerUserHashSelectParent: Calculating hash for %s\n", key);
+    for (c = key; *c != 0; c++)
+	user_hash += ROTATE_LEFT(user_hash, 19) + *c;
+    /* select peer */
+    for (k = 0; k < n_userhash_peers; k++) {
+	tp = userhash_peers[k];
+	combined_hash = (user_hash ^ tp->userhash.hash);
+	combined_hash += combined_hash * 0x62531965;
+	combined_hash = ROTATE_LEFT(combined_hash, 21);
+	score = combined_hash * tp->userhash.load_multiplier;
+	debug(39, 3) ("peerUserHashSelectParent: %s combined_hash %u score %.0f\n",
+	    tp->host, combined_hash, score);
+	if ((score > high_score) && peerHTTPOkay(tp, request)) {
+	    p = tp;
+	    high_score = score;
+	}
+    }
+    if (p)
+	debug(39, 2) ("peerUserHashSelectParent: selected %s\n", p->host);
+    return p;
+}
Index: squid/src/protos.h
diff -u squid/src/protos.h:1.72 squid/src/protos.h:1.72.2.1
--- squid/src/protos.h:1.72	Mon May 15 15:50:49 2006
+++ squid/src/protos.h	Mon May 15 18:01:32 2006
@@ -661,7 +661,7 @@
 extern void neighborAddAcl(const char *, const char *);
 extern void neighborsUdpAck(const cache_key *, icp_common_t *, const struct sockaddr_in *);
 extern void neighborAdd(const char *, const char *, int, int, int, int, int);
-extern void neighbors_open(int);
+extern void neighbors_init(void);
 extern peer *peerFindByName(const char *);
 extern peer *peerFindByNameAndPort(const char *, unsigned short);
 extern peer *getDefaultParent(request_t * request);
@@ -684,6 +684,7 @@
 #if USE_HTCP
 extern void neighborsHtcpReply(const cache_key *, htcpReplyData *, const struct sockaddr_in *);
 #endif
+extern void peerAddFwdServer(FwdServer ** FS, peer * p, hier_code code);
 
 extern void netdbInit(void);
 extern void netdbHandlePingReply(const struct sockaddr_in *from, int hops, int rtt);
@@ -716,6 +717,7 @@
 
 /* forward.c */
 extern void fwdStart(int, StoreEntry *, request_t *);
+extern void fwdStartPeer(peer *, StoreEntry *, request_t *);
 extern DEFER fwdCheckDeferRead;
 extern void fwdFail(FwdState *, ErrorState *);
 extern void fwdUnregister(int fd, FwdState *);
@@ -737,6 +739,10 @@
 extern void redirectInit(void);
 extern void redirectShutdown(void);
 
+extern void locationRewriteStart(HttpReply *, clientHttpRequest *, RH *, void *);
+extern void locationRewriteInit(void);
+extern void locationRewriteShutdown(void);
+
 /* auth_modules.c */
 extern void authSchemeSetup(void);
 
@@ -1142,9 +1148,10 @@
 extern void errorStateFree(ErrorState * err);
 extern int errorReservePageId(const char *page_name);
 extern ErrorState *errorCon(err_type type, http_status);
+extern int errorPageId(const char *page_name);
 
-extern void pconnPush(int, const char *host, u_short port);
-extern int pconnPop(const char *host, u_short port);
+extern void pconnPush(int, const char *host, u_short port, const char *domain);
+extern int pconnPop(const char *host, u_short port, const char *domain);
 extern void pconnInit(void);
 
 extern int asnMatchIp(void *, struct in_addr);
@@ -1240,6 +1247,11 @@
 extern peer *carpSelectParent(request_t *);
 #endif
 
+extern void peerUserHashInit(void);
+extern peer *peerUserHashSelectParent(request_t *);
+extern void peerSourceHashInit(void);
+extern peer *peerSourceHashSelectParent(request_t *);
+
 #if DELAY_POOLS
 extern void delayPoolsInit(void);
 extern void delayInitDelayData(unsigned short pools);
@@ -1361,4 +1373,12 @@
 extern void dump_wccp2_service_info(StoreEntry * e, const char *label, void *v);
 #endif
 
+/* peer_monitor.c */
+extern void peerMonitorInit(void);
+extern void peerMonitorNow(peer *);
+
+/* errormap.c */
+extern void errorMapInit(void);
+extern int errorMapStart(const errormap * map, request_t * req, HttpReply * reply, const char *aclname, ERRMAPCB * callback, void *data);
+
 #endif /* SQUID_PROTOS_H */
Index: squid/src/redirect.c
diff -u squid/src/redirect.c:1.11 squid/src/redirect.c:1.11.4.1
--- squid/src/redirect.c:1.11	Fri Apr 28 04:10:53 2006
+++ squid/src/redirect.c	Mon May 15 18:01:32 2006
@@ -96,6 +96,7 @@
     ConnStateData *conn = http->conn;
     redirectStateData *r = NULL;
     const char *fqdn;
+    char *urlgroup = conn->port->urlgroup;
     char buf[8192];
     assert(http);
     assert(handler);
@@ -122,12 +123,13 @@
     cbdataLock(r->data);
     if ((fqdn = fqdncache_gethostbyaddr(r->client_addr, 0)) == NULL)
 	fqdn = dash_str;
-    snprintf(buf, 8192, "%s %s/%s %s %s\n",
+    snprintf(buf, 8192, "%s %s/%s %s %s %s\n",
 	r->orig_url,
 	inet_ntoa(r->client_addr),
 	fqdn,
 	r->client_ident[0] ? rfc1738_escape(r->client_ident) : dash_str,
-	r->method_s);
+	r->method_s,
+	urlgroup ? urlgroup : "-");
     helperSubmit(redirectors, buf, redirectHandleReply, r);
 }
 
@@ -135,17 +137,17 @@
 redirectInit(void)
 {
     static int init = 0;
-    if (!Config.Program.redirect)
+    if (!Config.Program.url_rewrite.command)
 	return;
     if (redirectors == NULL)
-	redirectors = helperCreate("redirector");
-    redirectors->cmdline = Config.Program.redirect;
-    redirectors->n_to_start = Config.redirectChildren;
+	redirectors = helperCreate("url_rewriter");
+    redirectors->cmdline = Config.Program.url_rewrite.command;
+    redirectors->n_to_start = Config.Program.url_rewrite.children;
     redirectors->ipc_type = IPC_TCP_SOCKET;
     helperOpenServers(redirectors);
     if (!init) {
-	cachemgrRegister("redirector",
-	    "URL Redirector Stats",
+	cachemgrRegister("url_rewriter",
+	    "URL Rewriter Stats",
 	    redirectStats, 0, 1);
 	init = 1;
 	CBDATA_INIT_TYPE(redirectStateData);
Index: squid/src/snmp_agent.c
diff -u squid/src/snmp_agent.c:1.11 squid/src/snmp_agent.c:1.11.2.1
--- squid/src/snmp_agent.c:1.11	Sun May 14 02:50:55 2006
+++ squid/src/snmp_agent.c	Mon May 15 18:01:32 2006
@@ -153,21 +153,46 @@
 {
     variable_list *Answer = NULL;
     struct in_addr *laddr;
+    int loop, index = 0;
     char *cp = NULL;
     peer *p = NULL;
     int cnt = 0;
     debug(49, 5) ("snmp_meshPtblFn: peer %d requested!\n", Var->name[LEN_SQ_MESH + 3]);
     *ErrP = SNMP_ERR_NOERROR;
-    laddr = oid2addr(&Var->name[LEN_SQ_MESH + 3]);
-    for (p = Config.peers; p != NULL; p = p->next, cnt++)
-	if (p->in_addr.sin_addr.s_addr == laddr->s_addr)
-	    break;
+    switch (Var->name[LEN_SQ_MESH + 1]) {
+    case 1:
+	laddr = oid2addr(&Var->name[LEN_SQ_MESH + 3]);
+	for (p = Config.peers; p != NULL; p = p->next, cnt++) {
+	    index++;
+	    if (p->in_addr.sin_addr.s_addr == laddr->s_addr)
+		break;
+	}
+	break;
+    case 2:
+	index = Var->name[LEN_SQ_MESH + 3];
+	loop = 1;
+	p = Config.peers;
+	while (loop != index && p != NULL) {
+	    loop++;
+	    p = p->next;
+	}
+	break;
+    default:
+	break;
+    }
     if (p == NULL) {
 	*ErrP = SNMP_ERR_NOSUCHNAME;
 	return NULL;
     }
     switch (Var->name[LEN_SQ_MESH + 2]) {
     case MESH_PTBL_NAME:
+	cp = p->name;
+	Answer = snmp_var_new(Var->name, Var->name_length);
+	Answer->type = ASN_OCTET_STR;
+	Answer->val_len = strlen(cp);
+	Answer->val.string = (u_char *) xstrdup(cp);
+	break;
+    case MESH_PTBL_HOST:
 	cp = p->host;
 	Answer = snmp_var_new(Var->name, Var->name_length);
 	Answer->type = ASN_OCTET_STR;
@@ -234,6 +259,11 @@
 	    p->stats.n_keepalives_recv,
 	    SMI_COUNTER32);
 	break;
+    case MESH_PTBL_INDEX:
+	Answer = snmp_var_new_integer(Var->name, Var->name_length,
+	    index,
+	    ASN_INTEGER);
+	break;
     default:
 	*ErrP = SNMP_ERR_NOSUCHNAME;
 	break;
Index: squid/src/snmp_core.c
diff -u squid/src/snmp_core.c:1.12 squid/src/snmp_core.c:1.12.2.1
--- squid/src/snmp_core.c:1.12	Sun May 14 02:50:55 2006
+++ squid/src/snmp_core.c	Mon May 15 18:01:32 2006
@@ -65,6 +65,7 @@
 static oid *static_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
 static oid *time_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
 static oid *peer_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
+static oid *peer_InstIndex(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
 static oid *client_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
 static void snmpDecodePacket(snmp_request_t * rq);
 static void snmpConstructReponse(snmp_request_t * rq);
@@ -282,9 +283,9 @@
 				    snmpAddNode(snmpCreateOid(LEN_SQ_MESH, SQ_MESH),
 					LEN_SQ_MESH, NULL, NULL, 2,
 					snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 1, SQ_MESH, 1),
-					    LEN_SQ_MESH + 1, NULL, NULL, 1,
+					    LEN_SQ_MESH + 1, NULL, NULL, 2,
 					    snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 2, SQ_MESH, 1, 1),
-						LEN_SQ_MESH + 2, NULL, NULL, 13,
+						LEN_SQ_MESH + 2, NULL, NULL, 14,
 						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 1),
 						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
 						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 2),
@@ -310,7 +311,41 @@
 						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 12),
 						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
 						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 13),
-						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0))),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 1, 15),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_Inst, 0)),
+					    snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 2, SQ_MESH, 1, 2),
+						LEN_SQ_MESH + 2, NULL, NULL, 15,
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 1),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 2),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 3),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 4),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 5),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 6),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 7),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 8),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 9),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 10),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 11),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 12),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 13),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 14),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0),
+						snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 3, SQ_MESH, 1, 2, 15),
+						    LEN_SQ_MESH + 3, snmp_meshPtblFn, peer_InstIndex, 0))),
 					snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 1, SQ_MESH, 2),
 					    LEN_SQ_MESH + 1, NULL, NULL, 1,
 					    snmpAddNode(snmpCreateOid(LEN_SQ_MESH + 2, SQ_MESH, 2, 1),
@@ -766,19 +801,17 @@
 {
     oid *instance = NULL;
     u_char *cp = NULL;
-    peer *peers = Config.peers;
+    peer *peerptr = Config.peers;
+    peer *peerptr2;
     struct in_addr *laddr = NULL;
     char *host_addr = NULL, *current_addr = NULL, *last_addr = NULL;
 
-    if (peers == NULL) {
-	current = current->parent->parent->parent->leaves[1];
-	while ((current) && (!current->parsefunction))
-	    current = current->leaves[0];
-	instance = client_Inst(current->name, len, current, Fn);
+    if (peerptr == NULL) {
+	/* Do nothing */
     } else if (*len <= current->len) {
 	instance = xmalloc(sizeof(name) * (*len + 4));
 	xmemcpy(instance, name, (sizeof(name) * *len));
-	cp = (u_char *) & (peers->in_addr.sin_addr.s_addr);
+	cp = (u_char *) & (peerptr->in_addr.sin_addr.s_addr);
 	instance[*len] = *cp++;
 	instance[*len + 1] = *cp++;
 	instance[*len + 2] = *cp++;
@@ -787,33 +820,74 @@
     } else {
 	laddr = oid2addr(&name[*len - 4]);
 	host_addr = inet_ntoa(*laddr);
-	last_addr = xmalloc(strlen(host_addr));
-	strncpy(last_addr, host_addr, strlen(host_addr));
-	current_addr = inet_ntoa(peers->in_addr.sin_addr);
-	while ((peers) && (strncmp(last_addr, current_addr, strlen(current_addr)))) {
-	    if (peers->next) {
-		peers = peers->next;
-		current_addr = inet_ntoa(peers->in_addr.sin_addr);
-	    } else {
-		peers = NULL;
+	last_addr = xstrdup(host_addr);
+      skip_duplicate:
+	current_addr = inet_ntoa(peerptr->in_addr.sin_addr);
+	while (peerptr && strcmp(last_addr, current_addr) != 0) {
+	    peerptr = peerptr->next;
+	    if (peerptr)
+		current_addr = inet_ntoa(peerptr->in_addr.sin_addr);
+	}
+
+	/* Find the next peer */
+	if (peerptr)
+	    peerptr = peerptr->next;
+
+	/* watch out for duplicate addresses */
+	for (peerptr2 = Config.peers; peerptr && peerptr2 != peerptr; peerptr2 = peerptr2->next) {
+	    if (peerptr2->in_addr.sin_addr.s_addr == peerptr->in_addr.sin_addr.s_addr) {
+		/* ouch.. there are more than one peer on this IP. Skip the second one */
+		peerptr = peerptr->next;
+		if (peerptr)
+		    goto skip_duplicate;
 	    }
 	}
+
 	xfree(last_addr);
-	if (peers) {
-	    if (peers->next) {
-		peers = peers->next;
-		instance = xmalloc(sizeof(name) * (*len));
-		xmemcpy(instance, name, (sizeof(name) * *len));
-		cp = (u_char *) & (peers->in_addr.sin_addr.s_addr);
-		instance[*len - 4] = *cp++;
-		instance[*len - 3] = *cp++;
-		instance[*len - 2] = *cp++;
-		instance[*len - 1] = *cp++;
-	    } else {
-		return (instance);
-	    }
-	} else {
-	    return (instance);
+
+	if (peerptr) {
+	    instance = xmalloc(sizeof(name) * (*len));
+	    xmemcpy(instance, name, (sizeof(name) * *len));
+	    cp = (u_char *) & (peerptr->in_addr.sin_addr.s_addr);
+	    instance[*len - 4] = *cp++;
+	    instance[*len - 3] = *cp++;
+	    instance[*len - 2] = *cp++;
+	    instance[*len - 1] = *cp++;
+	}
+    }
+    *Fn = current->parsefunction;
+    return (instance);
+}
+
+static oid *
+peer_InstIndex(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
+{
+    oid *instance = NULL;
+
+    if (!Config.peers) {
+	/* Do nothing */
+    } else if (*len <= current->len) {
+	instance = xmalloc(sizeof(name) * (*len + 1));
+	xmemcpy(instance, name, (sizeof(name) * *len));
+	instance[*len] = 1;
+	*len += 1;
+    } else {
+	int identifier, loop = 1;	/* our index starts at 1 */
+	peer *p = Config.peers;
+	identifier = name[*len - 1];
+
+	/* We want the next one... */
+	identifier += 1;
+
+	/* Make sure it exists */
+	while ((identifier != loop) && (p != NULL)) {
+	    loop++;
+	    p = p->next;
+	}
+	if (p != NULL) {
+	    instance = xmalloc(sizeof(name) * (*len));
+	    xmemcpy(instance, name, (sizeof(name) * *len));
+	    instance[*len - 1] = loop;
 	}
     }
     *Fn = current->parsefunction;
Index: squid/src/store_key_md5.c
diff -u squid/src/store_key_md5.c:1.6 squid/src/store_key_md5.c:1.6.4.1
--- squid/src/store_key_md5.c:1.6	Fri Apr 13 17:31:02 2001
+++ squid/src/store_key_md5.c	Mon May 15 18:01:32 2006
@@ -138,6 +138,10 @@
     MD5Update(&M, (unsigned char *) url, strlen(url));
     if (request->vary_headers)
 	MD5Update(&M, (unsigned char *) request->vary_headers, strlen(request->vary_headers));
+    if (request->urlgroup) {
+	MD5Update(&M, (unsigned char *) " ", 1);
+	MD5Update(&M, (unsigned char *) request->urlgroup, strlen(request->urlgroup));
+    }
     MD5Final(digest, &M);
     return digest;
 }
Index: squid/src/structs.h
diff -u squid/src/structs.h:1.78 squid/src/structs.h:1.78.2.1
--- squid/src/structs.h:1.78	Mon May 15 16:51:56 2006
+++ squid/src/structs.h	Mon May 15 18:01:32 2006
@@ -336,20 +336,38 @@
     relist *next;
 };
 
+struct _acl_request_type {
+    unsigned int accelerated:1;
+    unsigned int internal:1;
+};
+
 struct _sockaddr_in_list {
     struct sockaddr_in s;
     sockaddr_in_list *next;
 };
 
+struct _http_port_list {
+    http_port_list *next;
+    struct sockaddr_in s;
+    char *protocol;		/* protocol name */
+    char *name;			/* visible name */
+    char *defaultsite;		/* default web site */
+    char *urlgroup;		/* default urlgroup */
+    unsigned int transparent:1;	/* transparent proxy */
+    unsigned int accel:1;	/* HTTP accelerator */
+    unsigned int vhost:1;	/* uses host header */
+    unsigned int vport:1;	/* virtual port support */
+};
+
 #if USE_SSL
 struct _https_port_list {
-    https_port_list *next;
-    struct sockaddr_in s;
+    http_port_list http;	/* must be first */
     char *cert;
     char *key;
     int version;
     char *cipher;
     char *options;
+    SSL_CTX *sslContext;
 };
 
 #endif
@@ -443,7 +461,7 @@
 #endif
     } Port;
     struct {
-	sockaddr_in_list *http;
+	http_port_list *http;
 #if USE_SSL
 	https_port_list *https;
 #endif
@@ -501,7 +519,14 @@
 #if USE_DNSSERVERS
 	char *dnsserver;
 #endif
-	wordlist *redirect;
+	struct {
+	    wordlist *command;
+	    int children;
+	} url_rewrite;
+	struct {
+	    wordlist *command;
+	    int children;
+	} location_rewrite;
 #if USE_ICMP
 	char *pinger;
 #endif
@@ -513,15 +538,9 @@
 #if USE_DNSSERVERS
     int dnsChildren;
 #endif
-    int redirectChildren;
     time_t authenticateGCInterval;
     time_t authenticateTTL;
     time_t authenticateIpTTL;
-    struct {
-	int single_host;
-	char *host;
-	u_short port;
-    } Accel;
     char *appendDomain;
     int appendDomainLen;
     char *debugOptions;
@@ -595,7 +614,6 @@
 	int log_mime_hdrs;
 	int log_fqdn;
 	int announce;
-	int accel_with_proxy;
 	int mem_pools;
 	int test_reachability;
 	int half_closed_clients;
@@ -623,14 +641,15 @@
 	int detect_broken_server_pconns;
 	int balance_on_multiple_ip;
 	int relaxed_header_parser;
-	int accel_uses_host_header;
 	int accel_no_pmtu_disc;
 	int global_internal_static;
 	int httpd_suppress_version_string;
+	int via;
     } onoff;
     acl *aclList;
     struct {
 	acl_access *http;
+	acl_access *http2;
 	acl_access *icp;
 	acl_access *miss;
 	acl_access *NeverDirect;
@@ -645,7 +664,8 @@
 #if USE_IDENT
 	acl_access *identLookup;
 #endif
-	acl_access *redirector;
+	acl_access *url_rewrite;
+	acl_access *location_rewrite;
 	acl_access *reply;
 	acl_address *outgoing_address;
 	acl_tos *outgoing_tos;
@@ -730,14 +750,11 @@
     char *store_dir_select_algorithm;
     int sleep_after_fork;	/* microseconds */
     external_acl *externalAclHelperList;
+    errormap *errorMapList;
 };
 
 struct _SquidConfig2 {
     struct {
-	char *prefix;
-	int on;
-    } Accel;
-    struct {
 	int enable_purge;
     } onoff;
     uid_t effectiveUserID;
@@ -996,6 +1013,7 @@
     unsigned int keepalive_broken:1;
     unsigned int abuse_detected:1;
     unsigned int request_sent:1;
+    unsigned int originpeer:1;
 };
 
 struct _HttpStateData {
@@ -1083,6 +1101,7 @@
 struct _clientHttpRequest {
     ConnStateData *conn;
     request_t *request;		/* Parsed URL ... */
+    request_t *orig_request;	/* Parsed URL ... */
     store_client *sc;		/* The store_client we're using */
     store_client *old_sc;	/* ... for entry to be validated */
     char *uri;
@@ -1156,6 +1175,8 @@
 	int n;
 	time_t until;
     } defer;
+    http_port_list *port;
+    int transparent;
 };
 
 struct _ipcache_addrs {
@@ -1259,6 +1280,7 @@
 #endif
 
 struct _peer {
+    char *name;
     char *host;
     peer_t type;
     struct sockaddr_in in_addr;
@@ -1310,6 +1332,9 @@
 	unsigned int no_delay:1;
 #endif
 	unsigned int allow_miss:1;
+	unsigned int originserver:1;
+	unsigned int userhash:1;
+	unsigned int sourcehash:1;
     } options;
     int weight;
     struct {
@@ -1341,9 +1366,29 @@
 	float load_factor;
     } carp;
 #endif
+    struct {
+	unsigned int hash;
+	double load_multiplier;
+	double load_factor;
+    } userhash;
+    struct {
+	unsigned int hash;
+	double load_multiplier;
+	double load_factor;
+    } sourcehash;
     char *login;		/* Proxy authorization */
     time_t connect_timeout;
     int max_conn;
+    struct {
+	char *url;
+	ssize_t min, max;
+	int interval;
+	int timeout;
+	int state;
+	time_t last;
+	PeerMonitor *data;
+    } monitor;
+    char *domain;		/* Forced domain */
 };
 
 struct _net_db_name {
@@ -1635,7 +1680,7 @@
     unsigned int hierarchical:1;
     unsigned int loopdetect:1;
     unsigned int proxy_keepalive:1;
-    unsigned int proxying:1;
+    unsigned int proxying:1;	/* this should be killed, also in httpstateflags */
     unsigned int refresh:1;
     unsigned int redirected:1;
     unsigned int need_validation:1;
@@ -1702,6 +1747,8 @@
     char *peer_login;		/* Configured peer login:password */
     time_t lastmod;		/* Used on refreshes */
     const char *vary_headers;	/* Used when varying entities are detected. Changes how the store key is calculated */
+    char *urlgroup;		/* urlgroup, returned by redirectors */
+    char *peer_domain;		/* Configured peer forceddomain */
     BODY_HANDLER *body_reader;
     void *body_reader_data;
 };
@@ -2263,4 +2310,15 @@
     void (*dump) (StoreEntry * e, const char *option, SwapDir * sd);
 };
 
+struct error_map_entry {
+    struct error_map_entry *next;
+    char *value;
+    int status;
+};
+struct _errormap {
+    errormap *next;
+    char *url;
+    struct error_map_entry *map;
+};
+
 #endif /* SQUID_STRUCTS_H */
Index: squid/src/typedefs.h
diff -u squid/src/typedefs.h:1.31 squid/src/typedefs.h:1.31.2.1
--- squid/src/typedefs.h:1.31	Mon May 15 15:50:49 2006
+++ squid/src/typedefs.h	Mon May 15 18:01:32 2006
@@ -92,6 +92,7 @@
 typedef struct _acl_user_data acl_user_data;
 typedef struct _acl_user_ip_data acl_user_ip_data;
 typedef struct _acl_arp_data acl_arp_data;
+typedef struct _acl_request_type acl_request_type;
 typedef struct _acl acl;
 typedef struct _acl_snmp_comm acl_snmp_comm;
 typedef struct _acl_list acl_list;
@@ -105,6 +106,7 @@
 typedef struct _ushortlist ushortlist;
 typedef struct _relist relist;
 typedef struct _sockaddr_in_list sockaddr_in_list;
+typedef struct _http_port_list http_port_list;
 typedef struct _https_port_list https_port_list;
 typedef struct _SquidConfig SquidConfig;
 typedef struct _SquidConfig2 SquidConfig2;
@@ -217,6 +219,8 @@
 typedef struct _RemovalPurgeWalker RemovalPurgeWalker;
 typedef struct _RemovalPolicyNode RemovalPolicyNode;
 typedef struct _RemovalPolicySettings RemovalPolicySettings;
+typedef struct _errormap errormap;
+typedef struct _PeerMonitor PeerMonitor;
 
 typedef struct _http_version_t http_version_t;
 
@@ -389,4 +393,6 @@
 typedef struct _external_acl external_acl;
 typedef struct _external_acl_entry external_acl_entry;
 
+typedef void ERRMAPCB(StoreEntry *, int body_offset, squid_off_t content_length, void *data);
+
 #endif /* SQUID_TYPEDEFS_H */