diff -u -r -N squid-3.1.0.10/ChangeLog squid-3.1.0.11/ChangeLog
--- squid-3.1.0.10/ChangeLog	2009-07-18 16:16:09.000000000 +1200
+++ squid-3.1.0.11/ChangeLog	2009-07-19 17:11:05.000000000 +1200
@@ -1,3 +1,17 @@
+Changes to squid-3.1.0.11 (19 Jul 2009):
+
+	- Bug 2087: Support adaptation sets and chains
+	- Bug 2459: dns error message broken when error handling delayed
+	- Support ICAP Retry
+	- Support ICAP retries based on the ICAP responses status code
+	- Support logging ICAP
+	- Support logging total DNS wait time
+	- Support logging response times of adaptation transactions
+	- General logging enhancements
+	- Dynamically form chains based on ICAP X-Next-Services header
+	- Support cross-transactional ICAP header exchange
+	- ... and much adaptation polish and improvements
+
 Changes to squid-3.1.0.10 (18 Jul 2009):
 
 	- Bug 2680: Regression Crash after rotate with no helpers running
diff -u -r -N squid-3.1.0.10/configure squid-3.1.0.11/configure
--- squid-3.1.0.10/configure	2009-07-18 16:17:06.000000000 +1200
+++ squid-3.1.0.11/configure	2009-07-19 17:11:50.000000000 +1200
@@ -1,7 +1,7 @@
 #! /bin/sh
 # From configure.in Revision: 1.497 .
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.62 for Squid Web Proxy 3.1.0.10.
+# Generated by GNU Autoconf 2.62 for Squid Web Proxy 3.1.0.11.
 #
 # Report bugs to <http://www.squid-cache.org/bugs/>.
 #
@@ -751,8 +751,8 @@
 # Identity of this package.
 PACKAGE_NAME='Squid Web Proxy'
 PACKAGE_TARNAME='squid'
-PACKAGE_VERSION='3.1.0.10'
-PACKAGE_STRING='Squid Web Proxy 3.1.0.10'
+PACKAGE_VERSION='3.1.0.11'
+PACKAGE_STRING='Squid Web Proxy 3.1.0.11'
 PACKAGE_BUGREPORT='http://www.squid-cache.org/bugs/'
 
 ac_unique_file="src/main.cc"
@@ -1678,7 +1678,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures Squid Web Proxy 3.1.0.10 to adapt to many kinds of systems.
+\`configure' configures Squid Web Proxy 3.1.0.11 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1748,7 +1748,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Squid Web Proxy 3.1.0.10:";;
+     short | recursive ) echo "Configuration of Squid Web Proxy 3.1.0.11:";;
    esac
   cat <<\_ACEOF
 
@@ -2079,7 +2079,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Squid Web Proxy configure 3.1.0.10
+Squid Web Proxy configure 3.1.0.11
 generated by GNU Autoconf 2.62
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -2093,7 +2093,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by Squid Web Proxy $as_me 3.1.0.10, which was
+It was created by Squid Web Proxy $as_me 3.1.0.11, which was
 generated by GNU Autoconf 2.62.  Invocation command line was
 
   $ $0 $@
@@ -2811,7 +2811,7 @@
 
 # Define the identity of the package.
  PACKAGE='squid'
- VERSION='3.1.0.10'
+ VERSION='3.1.0.11'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -49693,7 +49693,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by Squid Web Proxy $as_me 3.1.0.10, which was
+This file was extended by Squid Web Proxy $as_me 3.1.0.11, which was
 generated by GNU Autoconf 2.62.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -49746,7 +49746,7 @@
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_version="\\
-Squid Web Proxy config.status 3.1.0.10
+Squid Web Proxy config.status 3.1.0.11
 configured by $0, generated by GNU Autoconf 2.62,
   with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
diff -u -r -N squid-3.1.0.10/configure.in squid-3.1.0.11/configure.in
--- squid-3.1.0.10/configure.in	2009-07-18 16:17:06.000000000 +1200
+++ squid-3.1.0.11/configure.in	2009-07-19 17:11:50.000000000 +1200
@@ -2,7 +2,7 @@
 dnl
 dnl  $Id$
 dnl
-AC_INIT(Squid Web Proxy, 3.1.0.10, http://www.squid-cache.org/bugs/, squid)
+AC_INIT(Squid Web Proxy, 3.1.0.11, http://www.squid-cache.org/bugs/, squid)
 AC_PREREQ(2.52)
 AM_CONFIG_HEADER(include/autoconf.h)
 AC_CONFIG_AUX_DIR(cfgaux)
diff -u -r -N squid-3.1.0.10/include/Array.h squid-3.1.0.11/include/Array.h
--- squid-3.1.0.10/include/Array.h	2009-07-18 16:16:12.000000000 +1200
+++ squid-3.1.0.11/include/Array.h	2009-07-19 17:11:08.000000000 +1200
@@ -109,6 +109,7 @@
     iterator end();
     const_iterator end () const;
     E& operator [] (unsigned i);
+    const E& operator [] (unsigned i) const;
 
     /* Do not change these, until the entry C struct is removed */
     size_t capacity;
@@ -346,6 +347,14 @@
     return items[i];
 }
 
+template<class E>
+const E &
+Vector<E>::operator [] (unsigned i) const
+{
+    assert (size() > i);
+    return items[i];
+}
+
 template<class C>
 VectorIteratorBase<C>::VectorIteratorBase() : pos(0), theVector(NULL)
 {}
diff -u -r -N squid-3.1.0.10/include/version.h squid-3.1.0.11/include/version.h
--- squid-3.1.0.10/include/version.h	2009-07-18 16:17:06.000000000 +1200
+++ squid-3.1.0.11/include/version.h	2009-07-19 17:11:50.000000000 +1200
@@ -9,7 +9,7 @@
  */
 
 #ifndef SQUID_RELEASE_TIME
-#define SQUID_RELEASE_TIME 1247890568
+#define SQUID_RELEASE_TIME 1247980265
 #endif
 
 #ifndef APP_SHORTNAME
diff -u -r -N squid-3.1.0.10/src/access_log.cc squid-3.1.0.11/src/access_log.cc
--- squid-3.1.0.10/src/access_log.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/access_log.cc	2009-07-19 17:11:12.000000000 +1200
@@ -106,7 +106,16 @@
 static void fvdbRegisterWithCacheManager();
 #endif
 
-static int LogfileStatus = LOG_DISABLE;
+int LogfileStatus = LOG_DISABLE;
+
+#if USE_ADAPTATION
+bool alLogformatHasAdaptToken = false;
+#endif
+
+#if ICAP_CLIENT
+bool alLogformatHasIcapToken = false;
+#endif
+
 #define LOG_BUF_SZ (MAX_URL<<2)
 
 static const char c2x[] =
@@ -333,6 +342,10 @@
     LFT_TIME_GMT,
     LFT_TIME_TO_HANDLE_REQUEST,
 
+    LFT_PEER_RESPONSE_TIME,
+    LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME,
+    LFT_DNS_WAIT_TIME,
+
     LFT_REQUEST_HEADER,
     LFT_REQUEST_HEADER_ELEM,
     LFT_REQUEST_ALL_HEADERS,
@@ -348,7 +361,9 @@
     /*LFT_USER_SCHEME, */
     LFT_USER_EXTERNAL,
 
-    LFT_HTTP_CODE,
+    LFT_HTTP_SENT_STATUS_CODE_OLD_30,
+    LFT_HTTP_SENT_STATUS_CODE,
+    LFT_HTTP_RECEIVED_STATUS_CODE,
     /*LFT_HTTP_STATUS, */
 
     LFT_SQUID_STATUS,
@@ -365,7 +380,7 @@
 
     LFT_REQUEST_SIZE_TOTAL,
     /*LFT_REQUEST_SIZE_LINE, */
-    /*LFT_REQUEST_SIZE_HEADERS, */
+    LFT_REQUEST_SIZE_HEADERS,
     /*LFT_REQUEST_SIZE_BODY, */
     /*LFT_REQUEST_SIZE_BODY_NO_TE, */
 
@@ -373,7 +388,7 @@
     LFT_REPLY_HIGHOFFSET,
     LFT_REPLY_OBJECTSIZE,
     /*LFT_REPLY_SIZE_LINE, */
-    /*LFT_REPLY_SIZE_HEADERS, */
+    LFT_REPLY_SIZE_HEADERS,
     /*LFT_REPLY_SIZE_BODY, */
     /*LFT_REPLY_SIZE_BODY_NO_TE, */
 
@@ -381,6 +396,39 @@
     LFT_IO_SIZE_TOTAL,
     LFT_EXT_LOG,
 
+#if USE_ADAPTATION
+    LTF_ADAPTATION_SUM_XACT_TIMES,
+    LTF_ADAPTATION_ALL_XACT_TIMES,
+#endif
+
+#if ICAP_CLIENT    
+
+    LFT_ICAP_TOTAL_TIME,
+    LFT_ICAP_LAST_MATCHED_HEADER,
+    LFT_ICAP_LAST_MATCHED_HEADER_ELEM,
+    LFT_ICAP_LAST_MATCHED_ALL_HEADERS,
+
+    LFT_ICAP_ADDR,
+    LFT_ICAP_SERV_NAME,
+    LFT_ICAP_REQUEST_URI,
+    LFT_ICAP_REQUEST_METHOD,
+    LFT_ICAP_BYTES_SENT,
+    LFT_ICAP_BYTES_READ,
+    
+    LFT_ICAP_REQ_HEADER,
+    LFT_ICAP_REQ_HEADER_ELEM,
+    LFT_ICAP_REQ_ALL_HEADERS,
+    
+    LFT_ICAP_REP_HEADER,
+    LFT_ICAP_REP_HEADER_ELEM,
+    LFT_ICAP_REP_ALL_HEADERS,
+    
+    LFT_ICAP_TR_RESPONSE_TIME,
+    LFT_ICAP_IO_TIME,
+    LFT_ICAP_OUTCOME,
+    LFT_ICAP_STATUS_CODE,
+#endif
+
     LFT_PERCENT			/* special string cases for escaped chars */
 } logformat_bcode_t;
 
@@ -447,6 +495,10 @@
     {"tg", LFT_TIME_GMT},
     {"tr", LFT_TIME_TO_HANDLE_REQUEST},
 
+    {"<pt", LFT_PEER_RESPONSE_TIME},
+    {"<tt", LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME},
+    {"dt", LFT_DNS_WAIT_TIME},
+
     {">h", LFT_REQUEST_HEADER},
     {">h", LFT_REQUEST_ALL_HEADERS},
     {"<h", LFT_REPLY_HEADER},
@@ -459,7 +511,9 @@
     {"ui", LFT_USER_IDENT},
     {"ue", LFT_USER_EXTERNAL},
 
-    {"Hs", LFT_HTTP_CODE},
+    {"Hs", LFT_HTTP_SENT_STATUS_CODE_OLD_30},
+    {">Hs", LFT_HTTP_SENT_STATUS_CODE},
+    {"<Hs", LFT_HTTP_RECEIVED_STATUS_CODE},
     /*{ "Ht", LFT_HTTP_STATUS }, */
 
     {"Ss", LFT_SQUID_STATUS},
@@ -477,7 +531,7 @@
 
     { ">st", LFT_REQUEST_SIZE_TOTAL },
     /*{ ">sl", LFT_REQUEST_SIZE_LINE }, * / / * the request line "GET ... " */
-    /*{ ">sh", LFT_REQUEST_SIZE_HEADERS }, */
+    { ">sh", LFT_REQUEST_SIZE_HEADERS },
     /*{ ">sb", LFT_REQUEST_SIZE_BODY }, */
     /*{ ">sB", LFT_REQUEST_SIZE_BODY_NO_TE }, */
 
@@ -485,7 +539,7 @@
     {"<sH", LFT_REPLY_HIGHOFFSET},
     {"<sS", LFT_REPLY_OBJECTSIZE},
     /*{ "<sl", LFT_REPLY_SIZE_LINE }, * /   / * the reply line (protocol, code, text) */
-    /*{ "<sh", LFT_REPLY_SIZE_HEADERS }, */
+    { "<sh", LFT_REPLY_SIZE_HEADERS },
     /*{ "<sb", LFT_REPLY_SIZE_BODY }, */
     /*{ "<sB", LFT_REPLY_SIZE_BODY_NO_TE }, */
 
@@ -495,6 +549,31 @@
 
     {"%", LFT_PERCENT},
 
+#if USE_ADAPTATION
+    {"adapt::all_trs", LTF_ADAPTATION_ALL_XACT_TIMES},
+    {"adapt::sum_trs", LTF_ADAPTATION_SUM_XACT_TIMES},
+#endif
+
+#if ICAP_CLIENT
+    {"icap::tt", LFT_ICAP_TOTAL_TIME},
+    {"icap::<last_h", LFT_ICAP_LAST_MATCHED_HEADER},
+
+    {"icap::<A",  LFT_ICAP_ADDR},
+    {"icap::<service_name",  LFT_ICAP_SERV_NAME},
+    {"icap::ru",  LFT_ICAP_REQUEST_URI},
+    {"icap::rm",  LFT_ICAP_REQUEST_METHOD},
+    {"icap::>st",  LFT_ICAP_BYTES_SENT},
+    {"icap::<st",  LFT_ICAP_BYTES_READ},
+
+    {"icap::>h",  LFT_ICAP_REQ_HEADER},
+    {"icap::<h",  LFT_ICAP_REP_HEADER},
+
+    {"icap::tr",  LFT_ICAP_TR_RESPONSE_TIME},
+    {"icap::tio",  LFT_ICAP_IO_TIME},
+    {"icap::to",  LFT_ICAP_OUTCOME},
+    {"icap::Hs",  LFT_ICAP_STATUS_CODE},
+#endif
+
     {NULL, LFT_NONE}		/* this must be last */
 };
 
@@ -533,12 +612,16 @@
             break;
 
         case LFT_CLIENT_IP_ADDRESS:
-            if (!out) {
+            if (al->cache.caddr.IsNoAddr()) // e.g., ICAP OPTIONS lack client
+                out = "-";
+            else
                 out = al->cache.caddr.NtoA(tmp,1024);
-            }
             break;
 
         case LFT_CLIENT_FQDN:
+            if (al->cache.caddr.IsAnyAddr()) // e.g., ICAP OPTIONS lack client
+                out = "-";
+            else
             out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
             if (!out) {
                 out = al->cache.caddr.NtoA(tmp,1024);
@@ -620,6 +703,31 @@
             doint = 1;
             break;
 
+    case LFT_PEER_RESPONSE_TIME:
+            if (al->hier.peer_response_time < 0) {
+                out = "-";
+            } else {
+                outoff = al->hier.peer_response_time;
+                dooff = 1;
+            }
+            break;
+
+        case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME:
+            if (al->hier.total_response_time < 0) {
+                out = "-";
+            } else {
+                outoff = al->hier.total_response_time;
+                dooff = 1;
+            }
+            break;
+
+        case LFT_DNS_WAIT_TIME:
+            if (al->request && al->request->dnsWait >= 0) {
+                outint = al->request->dnsWait;
+                doint = 1;
+            }
+            break;
+
         case LFT_REQUEST_HEADER:
 
             if (al->request)
@@ -641,6 +749,175 @@
 
             break;
 
+#if USE_ADAPTATION
+        case LTF_ADAPTATION_SUM_XACT_TIMES:
+            if (al->request) {
+                Adaptation::History::Pointer ah = al->request->adaptHistory();
+                if (ah != NULL)
+                    ah->sumLogString(fmt->data.string, sb);
+                out = sb.termedBuf();
+            }
+            break;
+
+        case LTF_ADAPTATION_ALL_XACT_TIMES:
+            if (al->request) {
+                Adaptation::History::Pointer ah = al->request->adaptHistory();
+                if (ah != NULL)
+                    ah->allLogString(fmt->data.string, sb);
+                out = sb.termedBuf();
+            }
+            break;
+#endif
+
+#if ICAP_CLIENT            
+        case LFT_ICAP_LAST_MATCHED_HEADER:
+            if (al->request) {
+                Adaptation::Icap::History::Pointer ih = al->request->icapHistory();
+                if (ih != NULL)
+                    sb = ih->mergeOfIcapHeaders.getByName(fmt->data.header.header);
+            }
+
+            out = sb.termedBuf();
+
+            quote = 1;
+
+            break;
+
+        case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
+            if (al->request) {
+                Adaptation::Icap::History::Pointer ih = al->request->icapHistory();
+                if (ih != NULL)
+                    sb = ih->mergeOfIcapHeaders.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+            }
+
+            out = sb.termedBuf();
+
+            quote = 1;
+
+            break;
+
+        case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
+            out = al->headers.icap;
+
+            quote = 1;
+
+            break;
+
+        case LFT_ICAP_ADDR:
+            if (!out)
+        out = al->icap.hostAddr.NtoA(tmp,1024);
+            break;
+
+        case LFT_ICAP_SERV_NAME:
+            out = al->icap.serviceName.termedBuf();
+            break;
+
+        case LFT_ICAP_REQUEST_URI:
+            out = al->icap.reqUri.termedBuf();
+            break;
+
+        case LFT_ICAP_REQUEST_METHOD:
+            out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
+            break;
+
+        case LFT_ICAP_BYTES_SENT:
+            outint = al->icap.bytesSent;
+            doint = 1;
+            break;
+
+        case LFT_ICAP_BYTES_READ:
+            outint = al->icap.bytesRead;
+            doint = 1;
+            break;
+
+        case LFT_ICAP_REQ_HEADER:
+            if (NULL != al->icap.request) {
+                sb = al->icap.request->header.getByName(fmt->data.header.header);
+                out = sb.termedBuf();
+                quote = 1;
+            }
+            break;
+
+        case LFT_ICAP_REQ_HEADER_ELEM:
+            if (al->request)
+                sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+
+            out = sb.termedBuf();
+
+            quote = 1;
+
+            break;
+
+        case LFT_ICAP_REQ_ALL_HEADERS:
+            if (al->icap.request) {
+                HttpHeaderPos pos = HttpHeaderInitPos;
+                while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
+                    sb.append(e->name);
+                    sb.append(": ");
+                    sb.append(e->value);
+                    sb.append("\r\n");
+                }
+                out = sb.termedBuf();
+                quote = 1;
+            }
+            break;
+
+        case LFT_ICAP_REP_HEADER:
+            if (NULL != al->icap.reply) {
+                sb = al->icap.reply->header.getByName(fmt->data.header.header);
+                out = sb.termedBuf();
+                quote = 1;
+            }
+            break;
+
+        case LFT_ICAP_REP_HEADER_ELEM:
+            if (NULL != al->icap.reply)
+                sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+
+            out = sb.termedBuf();
+
+            quote = 1;
+
+            break;
+
+        case LFT_ICAP_REP_ALL_HEADERS: 
+            if (al->icap.reply) {
+                HttpHeaderPos pos = HttpHeaderInitPos;
+                while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
+                    sb.append(e->name);
+                    sb.append(": ");
+                    sb.append(e->value);
+                    sb.append("\r\n");
+                }
+                out = sb.termedBuf();
+                quote = 1;
+            }
+            break;
+
+        case LFT_ICAP_TR_RESPONSE_TIME:
+            outint = al->icap.trTime;
+            doint = 1;
+            break;
+
+        case LFT_ICAP_IO_TIME:
+            outint = al->icap.ioTime;
+            doint = 1;
+            break;
+
+        case LFT_ICAP_STATUS_CODE:
+            outint = al->icap.resStatus;
+            doint  = 1;
+            break;
+
+        case LFT_ICAP_OUTCOME:
+            out = al->icap.outcome;
+            break;
+
+        case LFT_ICAP_TOTAL_TIME:
+            outint = al->icap.processingTime;
+            doint = 1;
+            break;
+#endif
         case LFT_REQUEST_HEADER_ELEM:
             if (al->request)
                 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
@@ -719,13 +996,25 @@
             /* case LFT_USER_REALM: */
             /* case LFT_USER_SCHEME: */
 
-        case LFT_HTTP_CODE:
+        // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
+        // but compiler complains if ommited
+        case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
+        case LFT_HTTP_SENT_STATUS_CODE:
             outint = al->http.code;
 
             doint = 1;
 
             break;
 
+        case LFT_HTTP_RECEIVED_STATUS_CODE:
+            if(al->hier.peer_reply_status == HTTP_STATUS_NONE) {
+                out = "-";
+            }
+            else {
+                outint = al->hier.peer_reply_status;
+                doint = 1;
+            }
+            break;
             /* case LFT_HTTP_STATUS:
              *           out = statusline->text;
              *     quote = 1;
@@ -780,7 +1069,10 @@
             break;
 
             /*case LFT_REQUEST_SIZE_LINE: */
-            /*case LFT_REQUEST_SIZE_HEADERS: */
+        case LFT_REQUEST_SIZE_HEADERS:
+            outoff = al->cache.requestHeadersSize;
+            dooff =1;
+            break;
             /*case LFT_REQUEST_SIZE_BODY: */
             /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
 
@@ -804,7 +1096,9 @@
             break;
 
             /*case LFT_REPLY_SIZE_LINE: */
-            /*case LFT_REPLY_SIZE_HEADERS: */
+        case LFT_REPLY_SIZE_HEADERS:
+            outint = al->cache.replyHeadersSize;
+            doint = 1;
             /*case LFT_REPLY_SIZE_BODY: */
             /*case LFT_REPLY_SIZE_BODY_NO_TE: */
 
@@ -1022,6 +1316,15 @@
             cur++;
     }
 
+    // For upward compatibility, assume "http::" prefix as default prefix 
+    // for all log access formating codes, except those starting
+    // from "icap::", "adapt::" and "%"
+    if (strncmp(cur,"http::", 6) == 0 &&
+        strncmp(cur+6, "icap::", 6) != 0  &&
+        strncmp(cur+6, "adapt::", 12) != 0 && *(cur+6) != '%' ) {
+        cur += 6;
+    }
+
     lt->type = LFT_NONE;
 
     for (lte = logformat_token_table; lte->config != NULL; lte++) {
@@ -1046,6 +1349,14 @@
 
     switch (lt->type) {
 
+#if ICAP_CLIENT    
+    case LFT_ICAP_LAST_MATCHED_HEADER:
+
+    case LFT_ICAP_REQ_HEADER:
+
+    case LFT_ICAP_REP_HEADER:
+#endif
+
     case LFT_REQUEST_HEADER:
 
     case LFT_REPLY_HEADER:
@@ -1064,16 +1375,30 @@
 
                 lt->data.header.element = cp;
 
-                lt->type = (lt->type == LFT_REQUEST_HEADER) ?
-                           LFT_REQUEST_HEADER_ELEM :
-                           LFT_REPLY_HEADER_ELEM;
+                switch(lt->type) {
+                case LFT_REQUEST_HEADER: lt->type = LFT_REQUEST_HEADER_ELEM; break;
+                case LFT_REPLY_HEADER: lt->type = LFT_REPLY_HEADER_ELEM; break;
+#if ICAP_CLIENT
+                case LFT_ICAP_LAST_MATCHED_HEADER: lt->type = LFT_ICAP_LAST_MATCHED_HEADER_ELEM; break;
+                case LFT_ICAP_REQ_HEADER: lt->type = LFT_ICAP_REQ_HEADER_ELEM; break; 
+                case LFT_ICAP_REP_HEADER: lt->type = LFT_ICAP_REP_HEADER_ELEM; break;
+#endif
+                default:break;
+                }
             }
 
             lt->data.header.header = header;
         } else {
-            lt->type = (lt->type == LFT_REQUEST_HEADER) ?
-                       LFT_REQUEST_ALL_HEADERS :
-                       LFT_REPLY_ALL_HEADERS;
+            switch(lt->type) {
+            case LFT_REQUEST_HEADER: lt->type = LFT_REQUEST_ALL_HEADERS; break;
+            case LFT_REPLY_HEADER: lt->type = LFT_REPLY_ALL_HEADERS; break;
+#if ICAP_CLIENT
+            case LFT_ICAP_LAST_MATCHED_HEADER: lt->type = LFT_ICAP_LAST_MATCHED_ALL_HEADERS; break;
+            case LFT_ICAP_REQ_HEADER: lt->type = LFT_ICAP_REQ_ALL_HEADERS; break; 
+            case LFT_ICAP_REP_HEADER: lt->type = LFT_ICAP_REP_ALL_HEADERS; break;
+#endif
+            default:break;
+            }
             Config.onoff.log_mime_hdrs = 1;
         }
 
@@ -1099,6 +1424,10 @@
 
         break;
 
+    case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
+        debugs(46, 0, "WARNING: the \"Hs\" formating code is deprecated use the \">Hs\" instead");
+	lt->type = LFT_HTTP_SENT_STATUS_CODE;
+        break;
     default:
         break;
     }
@@ -1160,7 +1489,11 @@
 
                 case LFT_STRING:
                     break;
-
+#if ICAP_CLIENT
+                case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
+                case LFT_ICAP_REQ_HEADER_ELEM:
+                case LFT_ICAP_REP_HEADER_ELEM:
+#endif
                 case LFT_REQUEST_HEADER_ELEM:
 
                 case LFT_REPLY_HEADER_ELEM:
@@ -1172,9 +1505,16 @@
 
                     arg = argbuf;
 
-                    type = (type == LFT_REQUEST_HEADER_ELEM) ?
-                           LFT_REQUEST_HEADER :
-                           LFT_REPLY_HEADER;
+                    switch(type) {
+                    case LFT_REQUEST_HEADER_ELEM: type = LFT_REQUEST_HEADER_ELEM; break;
+                    case LFT_REPLY_HEADER_ELEM: type = LFT_REPLY_HEADER_ELEM; break;
+#if ICAP_CLIENT
+                    case LFT_ICAP_LAST_MATCHED_HEADER_ELEM: type = LFT_ICAP_LAST_MATCHED_HEADER; break;
+                    case LFT_ICAP_REQ_HEADER_ELEM: type = LFT_ICAP_REQ_HEADER; break; 
+                    case LFT_ICAP_REP_HEADER_ELEM: type = LFT_ICAP_REP_HEADER; break;
+#endif
+                    default:break;
+                    }
 
                     break;
 
@@ -1182,9 +1522,22 @@
 
                 case LFT_REPLY_ALL_HEADERS:
 
-                    type = (type == LFT_REQUEST_ALL_HEADERS) ?
-                           LFT_REQUEST_HEADER :
-                           LFT_REPLY_HEADER;
+#if ICAP_CLIENT
+                case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
+                case LFT_ICAP_REQ_ALL_HEADERS:
+                case LFT_ICAP_REP_ALL_HEADERS:
+#endif
+
+                    switch(type) {
+                    case LFT_REQUEST_ALL_HEADERS: type = LFT_REQUEST_HEADER; break;
+                    case LFT_REPLY_ALL_HEADERS: type = LFT_REPLY_HEADER; break;
+#if ICAP_CLIENT
+                    case LFT_ICAP_LAST_MATCHED_ALL_HEADERS: type = LFT_ICAP_LAST_MATCHED_HEADER; break;
+                    case LFT_ICAP_REQ_ALL_HEADERS: type = LFT_ICAP_REQ_HEADER; break; 
+                    case LFT_ICAP_REP_ALL_HEADERS: type = LFT_ICAP_REP_HEADER; break;
+#endif
+                    default:break;
+                    }
 
                     break;
 
@@ -1235,7 +1588,7 @@
                     storeAppendPrintf(entry, "{%s}", arg);
 
                 for (te = logformat_token_table; te->config != NULL; te++) {
-                    if (te->token_type == t->type) {
+                    if (te->token_type == type) {
                         storeAppendPrintf(entry, "%s", te->config);
                         break;
                     }
@@ -1387,13 +1740,61 @@
 
 }
 
-void
-accessLogLog(AccessLogEntry * al, ACLChecklist * checklist)
+#if ICAP_CLIENT
+static void
+accessLogICAPSquid(AccessLogEntry * al, Logfile * logfile)
 {
-    customlog *log;
+    const char *client = NULL;
+    const char *user = NULL;
+    char tmp[MAX_IPSTRLEN], clientbuf[MAX_IPSTRLEN];
 
-    if (LogfileStatus != LOG_ENABLE)
-        return;
+    if (al->cache.caddr.IsAnyAddr()) { // ICAP OPTIONS xactions lack client
+        client = "-";
+    } else {
+        if (Config.onoff.log_fqdn)
+            client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
+        if (!client)
+            client = al->cache.caddr.NtoA(clientbuf, MAX_IPSTRLEN);
+    }
+
+    user = accessLogFormatName(al->cache.authuser);
+
+    if (!user)
+        user = accessLogFormatName(al->cache.extuser);
+
+#if USE_SSL
+
+    if (!user)
+        user = accessLogFormatName(al->cache.ssluser);
+
+#endif
+
+    if (!user)
+        user = accessLogFormatName(al->cache.rfc931);
+
+    if (user && !*user)
+        safe_free(user);
+
+        logfilePrintf(logfile, "%9ld.%03d %6d %s -/%03d %"PRId64" %s %s %s -/%s -\n",
+                      (long int) current_time.tv_sec,
+                      (int) current_time.tv_usec / 1000,
+
+                      al->icap.trTime,
+                      client,
+
+                      al->icap.resStatus,
+                      al->icap.bytesRead,
+                      Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod),
+                      al->icap.reqUri.termedBuf(),
+                      user ? user : dash_str,
+                      al->icap.hostAddr.NtoA(tmp, MAX_IPSTRLEN));
+    safe_free(user);
+}
+#endif
+
+void 
+accessLogLogTo(customlog* log, AccessLogEntry * al, ACLChecklist * checklist)
+{
 
     if (al->url == NULL)
         al->url = dash_str;
@@ -1411,7 +1812,7 @@
     if (al->hier.host[0] == '\0')
         xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
 
-    for (log = Config.Log.accesslogs; log; log = log->next) {
+    for (; log; log = log->next) {
         if (checklist && log->aclList && !checklist->matchAclListFast(log->aclList))
             continue;
 
@@ -1441,6 +1842,13 @@
 
             break;
 
+#if ICAP_CLIENT
+        case CLF_ICAP_SQUID:
+            accessLogICAPSquid(al, log->logfile);
+
+            break;
+#endif
+
         case CLF_NONE:
             goto last;
 
@@ -1458,7 +1866,15 @@
 
 last:
     (void)0; /* NULL statement for label */
+}
 
+void
+accessLogLog(AccessLogEntry * al, ACLChecklist * checklist)
+{
+    if (LogfileStatus != LOG_ENABLE)
+        return;
+    
+    accessLogLogTo(Config.Log.accesslogs, al, checklist);
 #if MULTICAST_MISS_STREAM
 
     if (al->cache.code != LOG_TCP_MISS)
@@ -1534,7 +1950,10 @@
         code(HIER_NONE),
         cd_lookup(LOOKUP_NONE),
         n_choices(0),
-        n_ichoices(0)
+        n_ichoices(0),
+        peer_reply_status(HTTP_STATUS_NONE),
+        peer_response_time(-1),
+        total_response_time(-1)
 {
     memset(host, '\0', SQUIDHOSTNAMELEN);
     memset(cd_host, '\0', SQUIDHOSTNAMELEN);
@@ -1544,6 +1963,12 @@
 
     store_complete_stop.tv_sec =0;
     store_complete_stop.tv_usec =0;
+
+    peer_http_request_sent.tv_sec = 0;
+    peer_http_request_sent.tv_usec = 0;
+
+    first_conn_start.tv_sec = 0;
+    first_conn_start.tv_usec = 0;
 }
 
 void
@@ -1580,6 +2005,28 @@
         log->logfile = logfileOpen(log->filename, MAX_URL << 1, 1);
 
         LogfileStatus = LOG_ENABLE;
+
+#if USE_ADAPTATION || ICAP_CLIENT
+        alLogformatHasAdaptToken = false;
+        alLogformatHasIcapToken = false;
+        for (logformat_token * curr_token = (log->logFormat?log->logFormat->format:NULL); curr_token; curr_token = curr_token->next)
+        {
+#if USE_ADAPTATION
+            if (curr_token->type == LTF_ADAPTATION_SUM_XACT_TIMES ||
+                curr_token->type == LTF_ADAPTATION_ALL_XACT_TIMES) {
+                alLogformatHasAdaptToken = true;
+            }
+#endif    
+#if ICAP_CLIENT
+            if (curr_token->type == LFT_ICAP_LAST_MATCHED_HEADER ||
+                curr_token->type == LFT_ICAP_LAST_MATCHED_HEADER_ELEM ||
+                curr_token->type == LFT_ICAP_LAST_MATCHED_ALL_HEADERS)
+            {
+                alLogformatHasIcapToken = true;
+            }
+#endif
+        }
+#endif
     }
 
 #if HEADERS_LOG
@@ -1848,11 +2295,20 @@
 accessLogFreeMemory(AccessLogEntry * aLogEntry)
 {
     safe_free(aLogEntry->headers.request);
+
+#if ICAP_CLIENT
+    safe_free(aLogEntry->headers.icap);
+#endif
+
     safe_free(aLogEntry->headers.reply);
     safe_free(aLogEntry->cache.authuser);
 
     HTTPMSGUNLOCK(aLogEntry->reply);
     HTTPMSGUNLOCK(aLogEntry->request);
+#if ICAP_CLIENT
+    HTTPMSGUNLOCK(aLogEntry->icap.reply);
+    HTTPMSGUNLOCK(aLogEntry->icap.request);
+#endif
 }
 
 int
diff -u -r -N squid-3.1.0.10/src/AccessLogEntry.h squid-3.1.0.11/src/AccessLogEntry.h
--- squid-3.1.0.10/src/AccessLogEntry.h	2009-07-18 16:16:15.000000000 +1200
+++ squid-3.1.0.11/src/AccessLogEntry.h	2009-07-19 17:11:08.000000000 +1200
@@ -35,6 +35,9 @@
 #include "HierarchyLogEntry.h"
 #include "ip/IpAddress.h"
 #include "HttpRequestMethod.h"
+#if ICAP_CLIENT
+#include "adaptation/icap/Elements.h"
+#endif
 
 /* forward decls */
 class HttpReply;
@@ -84,6 +87,8 @@
         CacheDetails() : caddr(),
                 requestSize(0),
                 replySize(0),
+                requestHeadersSize(0),
+                replyHeadersSize(0),
                 highOffset(0),
                 objectSize(0),
                 code (LOG_TAG_NONE),
@@ -100,6 +105,8 @@
         IpAddress caddr;
         int64_t requestSize;
         int64_t replySize;
+        int requestHeadersSize; ///< received, including request line
+        int replyHeadersSize; ///< sent, including status line
         int64_t highOffset;
         int64_t objectSize;
         log_type code;
@@ -118,9 +125,17 @@
     {
 
     public:
-        Headers() : request(NULL), reply(NULL) {}
+        Headers() : request(NULL),
+#if ICAP_CLIENT
+                    icap(NULL),
+#endif
+                    reply(NULL) {}
 
         char *request;
+
+#if ICAP_CLIENT
+        char * icap;    ///< last matching ICAP response header.
+#endif
         char *reply;
     } headers;
 
@@ -137,6 +152,41 @@
     HierarchyLogEntry hier;
     HttpReply *reply;
     HttpRequest *request;
+
+#if ICAP_CLIENT
+    /** \brief This subclass holds log info for ICAP part of request
+     *  \todo Inner class declarations should be moved outside
+     */
+    class IcapLogEntry {
+    public:
+    IcapLogEntry():request(NULL),reply(NULL),outcome(Adaptation::Icap::xoUnknown),trTime(0),ioTime(0),resStatus(HTTP_STATUS_NONE){}
+
+        IpAddress hostAddr; ///< ICAP server IP address
+        String serviceName;        ///< ICAP service name
+        String reqUri;             ///< ICAP Request-URI
+        Adaptation::Icap::ICAP::Method reqMethod; ///< ICAP request method
+        int64_t bytesSent;       ///< number of bytes sent to ICAP server so far
+        int64_t bytesRead;       ///< number of bytes read from ICAP server so far
+        HttpRequest* request;    ///< ICAP request
+        HttpReply* reply;        ///< ICAP reply
+
+        Adaptation::Icap::XactOutcome outcome; ///< final transaction status
+        /** \brief Transaction response time.
+         * The timer starts when the ICAP transaction
+         *  is created and stops when the result of the transaction is logged
+         */
+        int trTime;
+        /** \brief Transaction I/O time.
+         * The timer starts when the first ICAP request
+         * byte is scheduled for sending and stops when the lastbyte of the 
+         * ICAP response is received.
+         */
+        int ioTime;
+        http_status resStatus;   ///< ICAP response status code
+        int processingTime;      ///< total ICAP processing time in milliseconds
+    }
+    icap;
+#endif
 };
 
 class ACLChecklist;
@@ -144,6 +194,7 @@
 class logformat_token;
 
 /* Should be in 'AccessLog.h' as the driver */
+extern void accessLogLogTo(customlog* log, AccessLogEntry* al, ACLChecklist* checklist = NULL);
 extern void accessLogLog(AccessLogEntry *, ACLChecklist * checklist);
 extern void accessLogRotate(void);
 extern void accessLogClose(void);
diff -u -r -N squid-3.1.0.10/src/acl/DestinationDomain.cc squid-3.1.0.11/src/acl/DestinationDomain.cc
--- squid-3.1.0.10/src/acl/DestinationDomain.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/acl/DestinationDomain.cc	2009-07-19 17:11:12.000000000 +1200
@@ -58,14 +58,15 @@
 }
 
 void
-DestinationDomainLookup::LookupDone(const char *fqdn, void *data)
+DestinationDomainLookup::LookupDone(const char *fqdn, const DnsLookupDetails &details, void *data)
 {
-    ACLChecklist *checklist = (ACLChecklist *)data;
+    ACLFilledChecklist *checklist = Filled((ACLChecklist*)data);
     assert (checklist->asyncState() == DestinationDomainLookup::Instance());
 
     checklist->asyncInProgress(false);
     checklist->changeState (ACLChecklist::NullState::Instance());
-    Filled(checklist)->markDestinationDomainChecked();
+    checklist->markDestinationDomainChecked();
+    checklist->request->recordLookup(details);
     checklist->check();
 }
 
diff -u -r -N squid-3.1.0.10/src/acl/DestinationDomain.h squid-3.1.0.11/src/acl/DestinationDomain.h
--- squid-3.1.0.10/src/acl/DestinationDomain.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/acl/DestinationDomain.h	2009-07-19 17:11:12.000000000 +1200
@@ -73,7 +73,7 @@
 
 private:
     static DestinationDomainLookup instance_;
-    static void LookupDone(const char *, void *);
+    static void LookupDone(const char *, const DnsLookupDetails &, void *);
 };
 
 /// \ingroup ACLAPI
diff -u -r -N squid-3.1.0.10/src/acl/DestinationIp.cc squid-3.1.0.11/src/acl/DestinationIp.cc
--- squid-3.1.0.10/src/acl/DestinationIp.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/acl/DestinationIp.cc	2009-07-19 17:11:12.000000000 +1200
@@ -86,11 +86,12 @@
 }
 
 void
-DestinationIPLookup::LookupDone(const ipcache_addrs * ia, void *data)
+DestinationIPLookup::LookupDone(const ipcache_addrs *, const DnsLookupDetails &details, void *data)
 {
-    ACLChecklist *checklist = (ACLChecklist *)data;
+    ACLFilledChecklist *checklist = Filled((ACLChecklist*)data);
     assert (checklist->asyncState() == DestinationIPLookup::Instance());
-    Filled(checklist)->request->flags.destinationIPLookupCompleted();
+    checklist->request->flags.destinationIPLookupCompleted();
+    checklist->request->recordLookup(details);
     checklist->asyncInProgress(false);
     checklist->changeState (ACLChecklist::NullState::Instance());
     checklist->check();
diff -u -r -N squid-3.1.0.10/src/acl/SourceDomain.cc squid-3.1.0.11/src/acl/SourceDomain.cc
--- squid-3.1.0.10/src/acl/SourceDomain.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/acl/SourceDomain.cc	2009-07-19 17:11:12.000000000 +1200
@@ -39,6 +39,7 @@
 #include "acl/Checklist.h"
 #include "acl/RegexData.h"
 #include "acl/DomainData.h"
+#include "HttpRequest.h"
 
 SourceDomainLookup SourceDomainLookup::instance_;
 
@@ -56,14 +57,15 @@
 }
 
 void
-SourceDomainLookup::LookupDone(const char *fqdn, void *data)
+SourceDomainLookup::LookupDone(const char *fqdn, const DnsLookupDetails &details, void *data)
 {
-    ACLChecklist *checklist = (ACLChecklist *)data;
+    ACLFilledChecklist *checklist = Filled((ACLChecklist*)data);
     assert (checklist->asyncState() == SourceDomainLookup::Instance());
 
     checklist->asyncInProgress(false);
     checklist->changeState (ACLChecklist::NullState::Instance());
-    Filled(checklist)->markSourceDomainChecked();
+    checklist->markSourceDomainChecked();
+    checklist->request->recordLookup(details);
     checklist->check();
 }
 
diff -u -r -N squid-3.1.0.10/src/acl/SourceDomain.h squid-3.1.0.11/src/acl/SourceDomain.h
--- squid-3.1.0.10/src/acl/SourceDomain.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/acl/SourceDomain.h	2009-07-19 17:11:12.000000000 +1200
@@ -67,7 +67,7 @@
 
 private:
     static SourceDomainLookup instance_;
-    static void LookupDone(const char *, void *);
+    static void LookupDone(const char *, const DnsLookupDetails &, void *);
 };
 
 class ACLSourceDomain
diff -u -r -N squid-3.1.0.10/src/adaptation/AccessCheck.cc squid-3.1.0.11/src/adaptation/AccessCheck.cc
--- squid-3.1.0.10/src/adaptation/AccessCheck.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/AccessCheck.cc	2009-07-19 17:11:12.000000000 +1200
@@ -23,51 +23,50 @@
 
     if (Config::Enabled) {
         // the new check will call the callback and delete self, eventually
-        AccessCheck *check = new AccessCheck(method, vp, req, rep, cb, cbdata);
-        check->check();
-        return true;
+        return AsyncStart(new AccessCheck(
+            ServiceFilter(method, vp, req, rep), cb, cbdata));
     }
 
     debugs(83, 3, HERE << "adaptation off, skipping");
     return false;
 }
 
-Adaptation::AccessCheck::AccessCheck(Method aMethod,
-                                     VectPoint aPoint,
-                                     HttpRequest *aReq,
-                                     HttpReply *aRep,
+Adaptation::AccessCheck::AccessCheck(const ServiceFilter &aFilter,
                                      AccessCheckCallback *aCallback,
-                                     void *aCallbackData): AsyncJob("AccessCheck"), done(FALSE)
-{
-    // TODO: assign these at creation time
-
-    method = aMethod;
-    point = aPoint;
-
-    req = HTTPMSGLOCK(aReq);
-    rep = aRep ? HTTPMSGLOCK(aRep) : NULL;
-
-    callback = aCallback;
-
-    callback_data = cbdataReference(aCallbackData);
+                                     void *aCallbackData):
+    AsyncJob("AccessCheck"), filter(aFilter),
+    callback(aCallback),
+    callback_data(cbdataReference(aCallbackData)),
+    acl_checklist(NULL)
+{
+#if ICAP_CLIENT
+    Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
+    if (h != NULL)
+        h->start("ACL");
+#endif
 
-    acl_checklist = NULL;
-
-    debugs(93, 5, HERE << "AccessCheck constructed for " << methodStr(method) << " " << vectPointStr(point));
+    debugs(93, 5, HERE << "AccessCheck constructed for " <<
+        methodStr(filter.method) << " " << vectPointStr(filter.point));
 }
 
 Adaptation::AccessCheck::~AccessCheck()
 {
-    HTTPMSGUNLOCK(req);
-    HTTPMSGUNLOCK(rep);
+#if ICAP_CLIENT
+    Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
+    if (h != NULL)
+        h->stop("ACL");
+#endif
     if (callback_data)
         cbdataReferenceDone(callback_data);
 }
 
-/*
- * Walk the access rules list and find all classes that have at least
- * one service with matching method and vectoring point.
- */
+void
+Adaptation::AccessCheck::start() {
+	AsyncJob::start();
+	check();
+}
+
+/// Walk the access rules list to find rules with applicable service groups
 void
 Adaptation::AccessCheck::check()
 {
@@ -75,17 +74,9 @@
 
     typedef AccessRules::iterator ARI;
     for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
-
-        /*
-         * We only find the first matching service because we only need
-         * one matching service to justify ACL-checking a class.  We might
-         * use other services belonging to the class if the first service
-         * turns out to be unusable for some reason.
-         */
         AccessRule *r = *i;
-        ServicePointer service = findBestService(*r, false);
-        if (service != NULL) {
-            debugs(93, 5, HERE << "check: rule '" << r->id << "' has candidate service '" << service->cfg().key << "'");
+        if (isCandidate(*r)) {
+            debugs(93, 5, HERE << "check: rule '" << r->id << "' is a candidate");
             candidates += r->id;
         }
     }
@@ -107,8 +98,8 @@
         if (AccessRule *r = FindRule(topCandidate())) {
             /* BUG 2526: what to do when r->acl is empty?? */
             // XXX: we do not have access to conn->rfc931 here.
-            acl_checklist = new ACLFilledChecklist(r->acl, req, dash_str);
-            acl_checklist->reply = rep ? HTTPMSGLOCK(rep) : NULL;
+            acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str);
+            acl_checklist->reply = filter.reply ? HTTPMSGLOCK(filter.reply) : NULL;
             acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
             return;
         }
@@ -116,9 +107,9 @@
         candidates.shift(); // the rule apparently went away (reconfigure)
     }
 
-    // when there are no canidates, fake answer 1
     debugs(93, 4, HERE << "NO candidates left");
-    noteAnswer(1);
+    callBack(NULL);
+    Must(done());
 }
 
 void
@@ -134,130 +125,75 @@
     ac->noteAnswer(answer==ACCESS_ALLOWED);
 }
 
+/// process the results of the ACL check
 void
 Adaptation::AccessCheck::noteAnswer(int answer)
 {
-    debugs(93, 5, HERE << "AccessCheck::noteAnswer " << answer);
-    if (candidates.size())
-        debugs(93, 5, HERE << "was checking rule" << topCandidate());
-
-    if (!answer) {
-        candidates.shift(); // the rule did not match
-        checkCandidates();
-        return;
+    Must(!candidates.empty()); // the candidate we were checking must be there
+    debugs(93,5, HERE << topCandidate() << " answer=" << answer);
+
+    if (answer) { // the rule matched
+        ServiceGroupPointer g = topGroup();
+        if (g != NULL) { // the corresponding group found
+            callBack(g);
+            Must(done());
+            return;
+        }
     }
 
-    /*
-     * We use an event here to break deep function call sequences
-     */
-    // XXX: use AsyncCall for callback and remove
-    CallJobHere(93, 5, this, Adaptation::AccessCheck::do_callback);
+    // no match or the group disappeared during reconfiguration
+    candidates.shift();
+    checkCandidates();
 }
 
+/// call back with a possibly nil group; the job ends here because all failures
+/// at this point are fatal to the access check process
 void
-Adaptation::AccessCheck::do_callback()
+Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g)
 {
-    debugs(93, 3, HERE);
-
-    if (candidates.size())
-        debugs(93, 3, HERE << "was checking rule" << topCandidate());
+    debugs(93,3, HERE << g);
 
     void *validated_cbdata;
-    if (!cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
-        debugs(93,3,HERE << "do_callback: callback_data became invalid, skipping");
-        return;
+    if (cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
+        callback(g, validated_cbdata);
     }
+    mustStop("done"); // called back or will never be able to call back
+}
 
-    ServicePointer service = NULL;
+Adaptation::ServiceGroupPointer
+Adaptation::AccessCheck::topGroup() const
+{
+    ServiceGroupPointer g;
     if (candidates.size()) {
         if (AccessRule *r = FindRule(topCandidate())) {
-            service = findBestService(*r, true);
-            if (service != NULL)
-                debugs(93,3,HERE << "do_callback: with service " << service->cfg().uri);
-            else
-                debugs(93,3,HERE << "do_callback: no service for rule" << r->id);
+            g = FindGroup(r->groupId);
+            debugs(93,5, HERE << "top group for " << r->id << " is " << g);
         } else {
-            debugs(93,3,HERE << "do_callback: no rule" << topCandidate());
+            debugs(93,5, HERE << "no rule for " << topCandidate());
         }
-        candidates.shift(); // done with topCandidate()
     } else {
-        debugs(93,3,HERE << "do_callback: no candidate rules");
+        debugs(93,5, HERE << "no candidates"); // should not happen
     }
 
-    callback(service, validated_cbdata);
-    done = TRUE;
+    return g;
 }
 
-Adaptation::ServicePointer
-Adaptation::AccessCheck::findBestService(AccessRule &r, bool preferUp)
+/** Returns true iff the rule's service group will be used after ACL matches.
+    Used to detect rules worth ACl-checking. */
+bool
+Adaptation::AccessCheck::isCandidate(AccessRule &r)
 {
+    debugs(93,7,HERE << "checking candidacy of " << r.id << ", group " <<
+        r.groupId);
 
-    const char *what = preferUp ? "up " : "";
-    debugs(93,7,HERE << "looking for the first matching " <<
-           what << "service in group " << r.groupId);
-
-    ServicePointer secondBest;
-
-    ServiceGroup *g = FindGroup(r.groupId);
+    ServiceGroupPointer g = FindGroup(r.groupId);
 
     if (!g) {
-        debugs(93,5,HERE << "lost " << r.groupId << " group in rule" << r.id);
-        return ServicePointer();
-    }
-
-    ServiceGroup::Loop loop(g->initialServices());
-    typedef ServiceGroup::iterator SGI;
-    for (SGI i = loop.begin; i != loop.end; ++i) {
-
-        ServicePointer service = FindService(*i);
-
-        if (!service)
-            continue;
-
-        if (method != service->cfg().method)
-            continue;
-
-        if (point != service->cfg().point)
-            continue;
-
-        // sending a message to a broken service is likely to cause errors
-        if (service->cfg().bypass && service->broken())
-            continue;
-
-        if (service->up()) {
-            // sending a message to a service that does not want it is useless
-            // note that we cannot check wantsUrl for service that is not "up"
-            // note that even essential services are skipped on unwanted URLs!
-            if (!service->wantsUrl(req->urlpath))
-                continue;
-        } else {
-            if (!secondBest)
-                secondBest = service;
-            if (preferUp) {
-                // the caller asked for an "up" service and we can bypass this one
-                if (service->cfg().bypass)
-                    continue;
-                debugs(93,5,HERE << "cannot skip an essential down service");
-                what = "down-but-essential ";
-            }
-        }
-
-        debugs(93,5,HERE << "found first matching " <<
-               what << "service for " << r.groupId << " group in rule" << r.id <<
-               ": " << service->cfg().key);
-
-        return service;
-    }
-
-    if (secondBest != NULL) {
-        what = "down ";
-        debugs(93,5,HERE << "found first matching " <<
-               what << "service for " << r.groupId << " group in rule" << r.id <<
-               ": " << secondBest->cfg().key);
-        return secondBest;
+        debugs(93,7,HERE << "lost " << r.groupId << " group in rule" << r.id);
+        return false;
     }
 
-    debugs(93,5,HERE << "found no matching " <<
-           what << "services for " << r.groupId << " group in rule" << r.id);
-    return ServicePointer();
+    const bool wants = g->wants(filter);
+    debugs(93,7,HERE << r.groupId << (wants ? " wants" : " ignores"));
+    return wants;
 }
diff -u -r -N squid-3.1.0.10/src/adaptation/AccessCheck.h squid-3.1.0.11/src/adaptation/AccessCheck.h
--- squid-3.1.0.10/src/adaptation/AccessCheck.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/AccessCheck.h	2009-07-19 17:11:12.000000000 +1200
@@ -4,6 +4,7 @@
 #include "base/AsyncJob.h"
 #include "adaptation/Elements.h"
 #include "adaptation/forward.h"
+#include "adaptation/ServiceFilter.h"
 
 class HttpRequest;
 class HttpReply;
@@ -18,7 +19,7 @@
 class AccessCheck: public virtual AsyncJob
 {
 public:
-    typedef void AccessCheckCallback(ServicePointer match, void *data);
+    typedef void AccessCheckCallback(ServiceGroupPointer group, void *data);
 
     // use this to start async ACL checks; returns true if started
     static bool Start(Method method, VectPoint vp, HttpRequest *req,
@@ -26,14 +27,11 @@
 
 protected:
     // use Start to start adaptation checks
-    AccessCheck(Method, VectPoint, HttpRequest *, HttpReply *, AccessCheckCallback *, void *);
+    AccessCheck(const ServiceFilter &aFilter, AccessCheckCallback *, void *);
     ~AccessCheck();
 
 private:
-    Method method;
-    VectPoint point;
-    HttpRequest *req;
-    HttpReply *rep;
+    const ServiceFilter filter;
     AccessCheckCallback *callback;
     void *callback_data;
     ACLFilledChecklist *acl_checklist;
@@ -41,23 +39,23 @@
     typedef int Candidate;
     typedef Vector<Candidate> Candidates;
     Candidates candidates;
-    Candidate topCandidate() { return *candidates.begin(); }
+    Candidate topCandidate() const { return *candidates.begin(); }
+    ServiceGroupPointer topGroup() const; // may return nil
 
-    void do_callback();
-    ServicePointer findBestService(AccessRule &r, bool preferUp);
-    bool done;
+    void callBack(const ServiceGroupPointer &g);
+    bool isCandidate(AccessRule &r);
 
 public:
-    void check();
     void checkCandidates();
     static void AccessCheckCallbackWrapper(int, void*);
-#if 0
-    static EVH AccessCheckCallbackEvent;
-#endif
     void noteAnswer(int answer);
 
-//AsyncJob virtual methods
-    virtual bool doneAll() const { return AsyncJob::doneAll() && done;}
+protected:
+    // AsyncJob API
+    virtual void start();
+    virtual bool doneAll() const { return false; } /// not done until mustStop
+
+    void check();
 
 private:
     CBDATA_CLASS2(AccessCheck);
diff -u -r -N squid-3.1.0.10/src/adaptation/AccessRule.cc squid-3.1.0.11/src/adaptation/AccessRule.cc
--- squid-3.1.0.10/src/adaptation/AccessRule.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/AccessRule.cc	2009-07-19 17:11:12.000000000 +1200
@@ -32,7 +32,7 @@
         debugs(93,7, HERE << "no service group: " << groupId);
         // try to add a one-service group
         if (FindService(groupId) != NULL) {
-            ServiceGroup *g = new SingleService(groupId);
+            ServiceGroupPointer g = new SingleService(groupId);
             g->finalize(); // explicit groups were finalized before rules
             AllGroups().push_back(g);
         }
@@ -44,7 +44,7 @@
     }
 }
 
-Adaptation::ServiceGroup *
+Adaptation::ServiceGroupPointer
 Adaptation::AccessRule::group()
 {
     return FindGroup(groupId);
diff -u -r -N squid-3.1.0.10/src/adaptation/AccessRule.h squid-3.1.0.11/src/adaptation/AccessRule.h
--- squid-3.1.0.10/src/adaptation/AccessRule.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/AccessRule.h	2009-07-19 17:11:12.000000000 +1200
@@ -22,7 +22,7 @@
     void finalize();
 
     // service group consisting of one or more services
-    ServiceGroup *group();
+    ServiceGroupPointer group();
 
 public:
     typedef int Id;
diff -u -r -N squid-3.1.0.10/src/adaptation/Config.cc squid-3.1.0.11/src/adaptation/Config.cc
--- squid-3.1.0.10/src/adaptation/Config.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Config.cc	2009-07-19 17:11:12.000000000 +1200
@@ -41,15 +41,21 @@
 #include "adaptation/Service.h"
 #include "adaptation/AccessRule.h"
 #include "adaptation/ServiceGroups.h"
+#include "adaptation/History.h"
 
 
 bool Adaptation::Config::Enabled = false;
+char *Adaptation::Config::masterx_shared_name = NULL;
+int Adaptation::Config::service_iteration_limit = 16;
 
 void
 Adaptation::Config::parseService()
 {
     ServiceConfig *cfg = new ServiceConfig;
-    cfg->parse();
+    if (!cfg->parse()) {
+        fatalf("%s:%d: malformed adaptation service configuration",
+            cfg_filename, config_lineno);
+    }
     serviceConfigs.push_back(cfg);
 }
 
@@ -125,22 +131,34 @@
 void
 Adaptation::Config::ParseServiceSet()
 {
-    ServiceSet *g = new ServiceSet();
+    Adaptation::Config::ParseServiceGroup(new ServiceSet);
+}
+
+void
+Adaptation::Config::ParseServiceChain()
+{
+    Adaptation::Config::ParseServiceGroup(new ServiceChain);
+}
+
+void
+Adaptation::Config::ParseServiceGroup(ServiceGroupPointer g)
+{
+    assert(g != NULL);
     g->parse();
     AllGroups().push_back(g);
 }
 
 void
-Adaptation::Config::FreeServiceSet()
+Adaptation::Config::FreeServiceGroups()
 {
     while (!AllGroups().empty()) {
-        delete AllGroups().back();
+        // groups are refcounted so we do not explicitly delete them
         AllGroups().pop_back();
     }
 }
 
 void
-Adaptation::Config::DumpServiceSet(StoreEntry *entry, const char *name)
+Adaptation::Config::DumpServiceGroups(StoreEntry *entry, const char *name)
 {
     typedef Groups::iterator GI;
     for (GI i = AllGroups().begin(); i != AllGroups().end(); ++i)
@@ -191,7 +209,7 @@
 Adaptation::Config::~Config()
 {
     FreeAccess();
-    FreeServiceSet();
+    FreeServiceGroups();
 
     // invalidate each service so that it can be deleted when refcount=0
     while (!AllServices().empty()) {
diff -u -r -N squid-3.1.0.10/src/adaptation/Config.h squid-3.1.0.11/src/adaptation/Config.h
--- squid-3.1.0.10/src/adaptation/Config.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Config.h	2009-07-19 17:11:12.000000000 +1200
@@ -3,34 +3,22 @@
 
 #include "event.h"
 #include "base/AsyncCall.h"
+#include "adaptation/forward.h"
 #include "adaptation/Elements.h"
 
 class acl_access;
 class ConfigParser;
 
-template <class C>
-class RefCount;
-
 namespace Adaptation
 {
 
-class Service;
-class ServiceConfig;
-class Class;
-
-typedef RefCount<Service> ServicePointer;
-
-class ServiceGroup;
-class AccessRule;
-
 class Config
 {
 public:
     static void Finalize(bool enable);
 
     static void ParseServiceSet(void);
-    static void FreeServiceSet(void);
-    static void DumpServiceSet(StoreEntry *, const char *);
+    static void ParseServiceChain(void);
 
     static void ParseAccess(ConfigParser &parser);
     static void FreeAccess(void);
@@ -41,6 +29,11 @@
 public:
     static bool Enabled; // true if at least one adaptation mechanism is
 
+    // these are global squid.conf options, documented elsewhere
+    static char *masterx_shared_name; // global TODO: do we need TheConfig?
+    static int service_iteration_limit;
+    // Options below are accessed via Icap::TheConfig or Ecap::TheConfig
+    // TODO: move ICAP-specific options to Icap::Config and add TheConfig
     int onoff;
     int send_client_ip;
     int send_client_username;
@@ -56,7 +49,6 @@
     void freeService(void);
     void dumpService(StoreEntry *, const char *) const;
     ServicePointer findService(const String&);
-    Class * findClass(const String& key);
 
     virtual void finalize();
 
@@ -65,6 +57,10 @@
     Config &operator =(const Config &); // unsupported
 
     virtual ServicePointer createService(const ServiceConfig &cfg) = 0;
+
+    static void ParseServiceGroup(ServiceGroupPointer group);
+    static void FreeServiceGroups(void);
+    static void DumpServiceGroups(StoreEntry *, const char *);
 };
 
 } // namespace Adaptation
diff -u -r -N squid-3.1.0.10/src/adaptation/ecap/XactionRep.cc squid-3.1.0.11/src/adaptation/ecap/XactionRep.cc
--- squid-3.1.0.10/src/adaptation/ecap/XactionRep.cc	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ecap/XactionRep.cc	2009-07-19 17:11:13.000000000 +1200
@@ -5,6 +5,7 @@
 #include "TextException.h"
 #include "HttpRequest.h"
 #include "HttpReply.h"
+#include "SquidTime.h"
 #include "adaptation/ecap/XactionRep.h"
 
 CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep);
@@ -14,9 +15,12 @@
         HttpMsg *virginHeader, HttpRequest *virginCause,
         const Adaptation::ServicePointer &aService):
         AsyncJob("Adaptation::Ecap::XactionRep"),
-        Adaptation::Initiate("Adaptation::Ecap::XactionRep", anInitiator, aService),
+        Adaptation::Initiate("Adaptation::Ecap::XactionRep", anInitiator),
+        theService(aService),
         theVirginRep(virginHeader), theCauseRep(NULL),
-        proxyingVb(opUndecided), proxyingAb(opUndecided), canAccessVb(false),
+        proxyingVb(opUndecided), proxyingAb(opUndecided),
+        adaptHistoryId(-1),
+		canAccessVb(false),
         abProductionFinished(false), abProductionAtEnd(false)
 {
     if (virginCause)
@@ -38,6 +42,13 @@
     theMaster = x;
 }
 
+Adaptation::Service &
+Adaptation::Ecap::XactionRep::service()
+{
+    Must(theService != NULL);
+    return *theService;
+}
+
 void
 Adaptation::Ecap::XactionRep::start()
 {
@@ -48,6 +59,15 @@
     else
         proxyingVb = opNever;
 
+    const HttpRequest *request = dynamic_cast<const HttpRequest*> (theCauseRep ?
+        theCauseRep->raw().header : theVirginRep.raw().header);
+    Must(request);
+    Adaptation::History::Pointer ah = request->adaptLogHistory();
+    if (ah != NULL) { 
+        // retrying=false because ecap never retries transactions
+        adaptHistoryId = ah->recordXactStart(service().cfg().key, current_time, false);
+    }
+
     theMaster->start();
 }
 
@@ -74,6 +94,14 @@
     }
 
     terminateMaster();
+
+    const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ?
+        theCauseRep->raw().header : theVirginRep.raw().header);
+    Must(request);
+    Adaptation::History::Pointer ah = request->adaptLogHistory();
+    if (ah != NULL && adaptHistoryId >= 0)
+        ah->recordXactFinish(adaptHistoryId);
+
     Adaptation::Initiate::swanSong();
 }
 
diff -u -r -N squid-3.1.0.10/src/adaptation/ecap/XactionRep.h squid-3.1.0.11/src/adaptation/ecap/XactionRep.h
--- squid-3.1.0.10/src/adaptation/ecap/XactionRep.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ecap/XactionRep.h	2009-07-19 17:11:13.000000000 +1200
@@ -8,9 +8,9 @@
 
 #include "BodyPipe.h"
 #include "adaptation/Initiate.h"
-#include "adaptation/Service.h"
 #include "adaptation/Message.h"
 #include "adaptation/ecap/MessageRep.h"
+#include "adaptation/ecap/ServiceRep.h"
 #include <libecap/common/forward.h>
 #include <libecap/common/memory.h>
 #include <libecap/host/xaction.h>
@@ -72,6 +72,8 @@
     virtual const char *status() const;
 
 protected:
+    Service &service();
+
     Adaptation::Message &answer();
 
     void dropVirgin(const char *reason);
@@ -82,6 +84,7 @@
 
 private:
     AdapterXaction theMaster; // the actual adaptation xaction we represent
+    Adaptation::ServicePointer theService; ///< xaction's adaptation service
 
     MessageRep theVirginRep;
     MessageRep *theCauseRep;
@@ -92,6 +95,7 @@
     typedef enum { opUndecided, opOn, opComplete, opNever } OperationState;
     OperationState proxyingVb; // delivering virgin body from core to adapter
     OperationState proxyingAb; // delivering adapted body from adapter to core
+    int adaptHistoryId;        ///< adaptation history slot reservation
     bool canAccessVb;          // virgin BodyPipe content is accessible
     bool abProductionFinished; // whether adapter has finished producing ab
     bool abProductionAtEnd;    // whether adapter produced a complete ab
diff -u -r -N squid-3.1.0.10/src/adaptation/forward.h squid-3.1.0.11/src/adaptation/forward.h
--- squid-3.1.0.10/src/adaptation/forward.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/forward.h	2009-07-19 17:11:13.000000000 +1200
@@ -22,9 +22,12 @@
 class AccessCheck;
 class AccessRule;
 class ServiceGroup;
+class ServicePlan;
+class ServiceFilter;
 class Message;
 
 typedef RefCount<Service> ServicePointer;
+typedef RefCount<ServiceGroup> ServiceGroupPointer;
 
 } // namespace Adaptation
 
diff -u -r -N squid-3.1.0.10/src/adaptation/History.cc squid-3.1.0.11/src/adaptation/History.cc
--- squid-3.1.0.10/src/adaptation/History.cc	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/History.cc	2009-07-19 17:11:12.000000000 +1200
@@ -0,0 +1,138 @@
+#include "config.h"
+#include "globals.h"
+#include "TextException.h"
+#include "SquidTime.h"
+#include "HttpRequest.h" /* for alLogformatHasAdaptToken */
+#include "adaptation/Config.h"
+#include "adaptation/History.h"
+
+/// impossible services value to identify unset theNextServices
+const static String TheNullServices(",null,");
+
+Adaptation::History::Entry::Entry(const String &sid, const timeval &when):
+    service(sid), start(when), theRptm(-1), retried(false)
+{
+}
+
+Adaptation::History::Entry::Entry():
+    start(current_time), theRptm(-1), retried(false)
+{
+}
+
+void Adaptation::History::Entry::stop()
+{
+    // theRptm may already be set if the access log entry has already been made
+    (void)rptm(); // will cache result in theRptm if not set already
+}
+
+int Adaptation::History::Entry::rptm()
+{
+    if (theRptm < 0)
+        theRptm = tvSubMsec(start, current_time);
+    return theRptm;
+}
+
+
+Adaptation::History::History(): theNextServices(TheNullServices) {
+}
+
+int Adaptation::History::recordXactStart(const String &sid, const timeval &when, bool retrying)
+{
+    if (retrying) {
+        Must(!theEntries.empty()); // or there would be nothing to retry
+        theEntries.back().retried = true;
+    }
+    theEntries.push_back(Adaptation::History::Entry(sid, when));
+    return theEntries.size() - 1; // record position becomes history ID
+}
+
+void Adaptation::History::recordXactFinish(int hid)
+{
+    Must(0 <= hid && hid < static_cast<int>(theEntries.size()));
+    theEntries[hid].stop();
+}
+
+void Adaptation::History::allLogString(const char *serviceId, String &s)
+{
+    s="";
+    bool prevWasRetried = false;
+    // XXX: Fix Vector<> so that we can use const_iterator here
+    typedef Adaptation::History::Entries::iterator ECI;
+    for (ECI i = theEntries.begin(); i != theEntries.end(); ++i) {
+        // TODO: here and below, optimize service ID comparison?
+        if (!serviceId || i->service == serviceId) {
+            if (s.size() > 0) // not the first logged time, must delimit
+                s.append(prevWasRetried ? "+" : ",");
+
+            char buf[64];
+            snprintf(buf, sizeof(buf), "%d", i->rptm());
+            s.append(buf);
+
+            // continue; we may have two identical services (e.g., for retries)
+        }
+        prevWasRetried = i->retried;
+    }
+}
+
+void Adaptation::History::sumLogString(const char *serviceId, String &s)
+{
+    s="";
+    int retriedRptm = 0; // sum of rptm times of retried transactions
+    typedef Adaptation::History::Entries::iterator ECI;
+    for (ECI i = theEntries.begin(); i != theEntries.end(); ++i) {
+        if (i->retried) { // do not log retried xact but accumulate their time
+            retriedRptm += i->rptm();
+        } else
+        if (!serviceId || i->service == serviceId) {
+            if (s.size() > 0) // not the first logged time, must delimit
+                s.append(",");
+
+            char buf[64];
+            snprintf(buf, sizeof(buf), "%d", retriedRptm + i->rptm());
+            s.append(buf);
+
+            // continue; we may have two identical services (e.g., for retries)
+        }
+
+        if (!i->retried)
+            retriedRptm = 0;
+    }
+
+    // the last transaction is never retried or it would not be the last
+    Must(!retriedRptm); 
+}
+
+void Adaptation::History::updateXxRecord(const char *name, const String &value)
+{
+    theXxName = name;
+    theXxValue = value;
+}
+
+bool Adaptation::History::getXxRecord(String &name, String &value) const
+{
+    if (theXxName.size() <= 0)
+       return false;
+
+    name = theXxName;
+    value = theXxValue;
+    return true;
+}
+
+void Adaptation::History::updateNextServices(const String &services)
+{
+    if (theNextServices != TheNullServices)
+       debugs(93,3, HERE << "old services: " << theNextServices);
+    debugs(93,3, HERE << "new services: " << services);
+    Must(services != TheNullServices);
+    theNextServices = services;
+}
+
+bool Adaptation::History::extractNextServices(String &value)
+{
+    if (theNextServices == TheNullServices)
+       return false;
+
+    value = theNextServices;
+    theNextServices = TheNullServices; // prevents resetting the plan twice
+    return true;
+}
diff -u -r -N squid-3.1.0.10/src/adaptation/History.h squid-3.1.0.11/src/adaptation/History.h
--- squid-3.1.0.10/src/adaptation/History.h	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/History.h	2009-07-19 17:11:12.000000000 +1200
@@ -0,0 +1,74 @@
+#ifndef SQUID_ADAPT_HISTORY_H
+#define SQUID_ADAPT_HISTORY_H
+
+#include "RefCount.h"
+#include "Array.h"
+#include "SquidString.h"
+
+namespace Adaptation {
+
+
+/// collects information about adaptations related to a master transaction
+class History: public RefCountable {
+public:
+    typedef RefCount<Adaptation::History> Pointer;
+
+    History();
+
+    /// record the start of a xact, return xact history ID
+    int recordXactStart(const String &serviceId, const timeval &when, bool retrying);
+
+    /// record the end of a xact identified by its history ID
+    void recordXactFinish(int hid);
+
+    /// dump individual xaction times to a string
+    void allLogString(const char *serviceId, String &buf);
+
+    /// dump xaction times, merging retried and retry times together
+    void sumLogString(const char *serviceId, String &buf);
+
+    /// sets or resets a cross-transactional database record
+    void updateXxRecord(const char *name, const String &value);
+
+    /// returns true and fills the record fields iff there is a db record
+    bool getXxRecord(String &name, String &value) const;
+
+    /// sets or resets next services for the Adaptation::Iterator to notice
+    void updateNextServices(const String &services);
+
+    /// returns true, fills the value, and resets iff next services were set
+    bool extractNextServices(String &value);
+
+private:
+    /// single Xaction stats (i.e., a historical record entry)
+    class Entry {
+    public:
+        Entry(const String &serviceId, const timeval &when);
+        Entry(); // required by Vector<>
+
+        void stop(); ///< updates stats on transaction end
+        int rptm(); ///< returns response time [msec], calculates it if needed
+
+        String service; ///< adaptation service ID
+        timeval start; ///< when the xaction was started
+
+    private:
+        int theRptm; ///< calculated and cached response time value in msec
+
+    public:
+        bool retried; ///< whether the xaction was replaced by another
+    };
+    
+    typedef Vector<Entry> Entries;  
+    Entries theEntries; ///< historical record, in the order of xact starts
+
+    // theXx* will become a map<string,string>, but we only support one record
+    String theXxName; ///< name part of the cross-transactional database record
+    String theXxValue; ///< value part of the cross-xactional database record
+
+    String theNextServices; ///< services Adaptation::Iterator must use next
+};
+
+} // namespace Adaptation
+
+#endif
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Config.cc squid-3.1.0.11/src/adaptation/icap/Config.cc
--- squid-3.1.0.10/src/adaptation/icap/Config.cc	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Config.cc	2009-07-19 17:11:13.000000000 +1200
@@ -47,7 +47,7 @@
 
 Adaptation::Icap::Config::Config(): preview_enable(0), preview_size(0),
         connect_timeout_raw(0), io_timeout_raw(0), reuse_connections(0),
-        client_username_header(NULL), client_username_encode(0)
+        client_username_header(NULL), client_username_encode(0), repeat(NULL)
 {
 }
 
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Config.h squid-3.1.0.11/src/adaptation/icap/Config.h
--- squid-3.1.0.10/src/adaptation/icap/Config.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Config.h	2009-07-19 17:11:13.000000000 +1200
@@ -41,13 +41,12 @@
 #include "adaptation/Config.h"
 #include "adaptation/icap/ServiceRep.h"
 
+class acl_access;
 
 namespace Adaptation
 {
 namespace Icap {
 
-class acl_access;
-
 class ConfigParser;
 
 class Config: public Adaptation::Config
@@ -62,6 +61,8 @@
     int reuse_connections;
     char* client_username_header;
     int client_username_encode;
+    acl_access *repeat; ///< icap_retry ACL in squid.conf
+    int repeat_limit; ///< icap_retry_limit in squid.conf
 
     Config();
     ~Config();
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Elements.cc squid-3.1.0.11/src/adaptation/icap/Elements.cc
--- squid-3.1.0.10/src/adaptation/icap/Elements.cc	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Elements.cc	2009-07-19 17:11:13.000000000 +1200
@@ -2,3 +2,15 @@
 #include "adaptation/icap/Elements.h"
 
 // TODO: remove this file?
+namespace Adaptation {
+namespace Icap {
+
+const XactOutcome xoUnknown = "ICAP_ERR_UNKNOWN";
+const XactOutcome xoError = "ICAP_ERR_OTHER";
+const XactOutcome xoOpt = "ICAP_OPT";
+const XactOutcome xoEcho = "ICAP_ECHO";
+const XactOutcome xoModified = "ICAP_MOD";
+const XactOutcome xoSatisfied = "ICAP_SAT";
+
+} // namespace Icap
+} // namespace Adaptation
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Elements.h squid-3.1.0.11/src/adaptation/icap/Elements.h
--- squid-3.1.0.10/src/adaptation/icap/Elements.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Elements.h	2009-07-19 17:11:13.000000000 +1200
@@ -43,6 +43,7 @@
 {
 namespace Icap {
 
+//TODO: remove the ICAP namespace
 namespace ICAP {
 using Adaptation::Method;
 using Adaptation::methodNone;
@@ -59,6 +60,13 @@
 using Adaptation::vectPointStr;
 }
 
+typedef const char *XactOutcome; ///< transaction result for logging
+extern const XactOutcome xoUnknown; ///< initial value: outcome was not set
+extern const XactOutcome xoError; ///< all kinds of transaction errors
+extern const XactOutcome xoOpt; ///< OPTION transaction
+extern const XactOutcome xoEcho; ///< preserved virgin message (ICAP 204)
+extern const XactOutcome xoModified; ///< replaced virgin msg with adapted
+extern const XactOutcome xoSatisfied; ///< request satisfaction
 
 } // namespace Icap
 } // namespace Adaptation
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/History.cc squid-3.1.0.11/src/adaptation/icap/History.cc
--- squid-3.1.0.10/src/adaptation/icap/History.cc	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/History.cc	2009-07-19 17:11:13.000000000 +1200
@@ -0,0 +1,103 @@
+#include "adaptation/icap/History.h"
+#include "squid.h"
+#include "globals.h"
+#include "SquidTime.h"
+
+Adaptation::Icap::History::History(): mergeOfIcapHeaders(hoRequest),
+    lastIcapHeader(hoRequest), logType(LOG_TAG_NONE), req_sz(0),
+    pastTime(0), concurrencyLevel(0)
+{
+}
+
+Adaptation::Icap::History::History(const Adaptation::Icap::History& ih)
+{
+    assign(ih);
+}
+
+Adaptation::Icap::History::~History()
+{
+    mergeOfIcapHeaders.clean();
+    lastIcapHeader.clean();
+    rfc931.clean();
+#if USE_SSL
+    ssluser.clean();
+#endif 
+    log_uri.clean();
+}
+
+void Adaptation::Icap::History::assign(const Adaptation::Icap::History& ih)
+{
+    mergeOfIcapHeaders.clean();
+    mergeOfIcapHeaders.update(&ih.mergeOfIcapHeaders, NULL);
+    lastIcapHeader.clean();
+    lastIcapHeader.update(&ih.lastIcapHeader, NULL);
+    rfc931 = ih.rfc931;
+
+#if USE_SSL
+    ssluser = ih.ssluser;
+#endif
+
+    logType = ih.logType;
+    log_uri = ih.log_uri;
+    req_sz = ih.req_sz;
+    pastTime = ih.pastTime;
+    currentStart = ih.currentStart;
+    concurrencyLevel = ih.concurrencyLevel;
+    debugs(93,7, HERE << this << " = " << &ih);
+}
+
+Adaptation::Icap::History& Adaptation::Icap::History::operator=(const History& ih)
+{
+    if (this != &ih)
+        assign(ih);
+    return *this;
+}
+
+void Adaptation::Icap::History::setIcapLastHeader(const HttpHeader * lih)
+{
+    lastIcapHeader.clean();
+    lastIcapHeader.update(lih, NULL);
+}
+
+void Adaptation::Icap::History::mergeIcapHeaders(const HttpHeader * lih)
+{
+    mergeOfIcapHeaders.update(lih, NULL);
+    mergeOfIcapHeaders.compact();
+}
+
+void Adaptation::Icap::History::start(const char *context) 
+{
+    if (!concurrencyLevel++)
+        currentStart = current_time;
+
+    debugs(93,4, HERE << "start " << context << " level=" << concurrencyLevel
+        << " time=" << pastTime << ' ' << this);
+}
+
+void Adaptation::Icap::History::stop(const char *context) 
+{
+    if (!concurrencyLevel) {
+        debugs(93,1, HERE << "Internal error: poor history accounting " << this);
+        return;
+    }
+
+    const int current = currentTime();
+    debugs(93,4, HERE << "stop " << context << " level=" << concurrencyLevel <<
+        " time=" << pastTime << '+' << current << ' ' << this);
+    
+    if (!--concurrencyLevel)
+        pastTime += current;
+}
+
+int Adaptation::Icap::History::processingTime() const
+{
+    const int total = pastTime + currentTime();
+    debugs(93,7, HERE << " current total: " << total << ' ' << this);
+    return total;
+}
+
+int Adaptation::Icap::History::currentTime() const
+{
+    return concurrencyLevel > 0 ?
+        max(0, tvSubMsec(currentStart, current_time)) : 0;
+}
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/History.h squid-3.1.0.11/src/adaptation/icap/History.h
--- squid-3.1.0.10/src/adaptation/icap/History.h	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/History.h	2009-07-19 17:11:13.000000000 +1200
@@ -0,0 +1,60 @@
+#ifndef SQUID_ICAPHISTORY_H
+#define SQUID_ICAPHISTORY_H
+
+#include "RefCount.h"
+#include "HttpHeader.h"
+#include "enums.h"
+
+namespace Adaptation {
+namespace Icap {
+
+/// collects information about ICAP processing related to an HTTP transaction
+class History: public RefCountable {
+public:
+    typedef RefCount<History> Pointer;
+
+    History();
+    History(const History& ih);
+
+    ~History();
+
+    History& operator=(const History& ih);
+
+    ///store the last reply header from ICAP server
+    void setIcapLastHeader(const HttpHeader * lih);
+    ///merge new header and stored one
+    void mergeIcapHeaders(const HttpHeader * lih);
+
+    /// record the start of an ICAP processing interval
+    void start(const char *context);
+    /// note the end of an ICAP processing interval
+    void stop(const char *context);
+
+    /// returns the total time of all ICAP processing intervals
+    int processingTime() const;
+
+    HttpHeader mergeOfIcapHeaders; ///< Merge of REQMOD and RESPMOD responses. If both responses contain the header, the one from the last response will be used
+    HttpHeader lastIcapHeader; ///< Last received reply from ICAP server
+    String rfc931; ///< the username from ident
+#if USE_SSL
+    String ssluser; ///< the username from SSL
+#endif
+    log_type logType; ///< the squid request status (TCP_MISS etc)
+
+    String log_uri; ///< the request uri
+    size_t req_sz; ///< the request size
+
+private:
+    void assign(const History &);
+
+    int currentTime() const; ///< time since current start or zero
+
+    timeval currentStart; ///< when the current processing interval started
+    int pastTime;         ///< sum of closed processing interval durations
+    int concurrencyLevel; ///< number of concurrent processing threads
+};
+
+} // namespace Icap
+} // namespace Adaptation
+
+#endif /*SQUID_HISTORY_H*/
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/icap_log.cc squid-3.1.0.11/src/adaptation/icap/icap_log.cc
--- squid-3.1.0.10/src/adaptation/icap/icap_log.cc	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/icap_log.cc	2009-07-19 17:11:13.000000000 +1200
@@ -0,0 +1,52 @@
+#include "squid.h"
+#include "icap_log.h"
+#include "AccessLogEntry.h"
+
+int IcapLogfileStatus = LOG_DISABLE;
+
+void
+icapLogOpen()
+{
+    customlog *log;
+
+    for (log = Config.Log.icaplogs; log; log = log->next) {
+        if (log->type == CLF_NONE)
+            continue;
+
+        if (log->type == CLF_AUTO)
+            log->type = CLF_ICAP_SQUID;
+
+        log->logfile = logfileOpen(log->filename, MAX_URL << 1, 1);
+
+        IcapLogfileStatus = LOG_ENABLE;
+    }
+}
+
+void 
+icapLogClose()
+{
+    customlog *log;
+
+    for (log = Config.Log.icaplogs; log; log = log->next) {
+        if (log->logfile) {
+            logfileClose(log->logfile);
+            log->logfile = NULL;
+        }
+    }
+}
+
+void
+icapLogRotate()
+{
+    for (customlog* log = Config.Log.icaplogs; log; log = log->next) {
+        if (log->logfile) {
+            logfileRotate(log->logfile);
+        }
+    }
+}
+
+void icapLogLog(AccessLogEntry *al, ACLChecklist * checklist)
+{
+    if (IcapLogfileStatus == LOG_ENABLE)
+        accessLogLogTo(Config.Log.icaplogs, al, checklist);
+}
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/icap_log.h squid-3.1.0.11/src/adaptation/icap/icap_log.h
--- squid-3.1.0.10/src/adaptation/icap/icap_log.h	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/icap_log.h	2009-07-19 17:11:13.000000000 +1200
@@ -0,0 +1,14 @@
+#ifndef ICAP_LOG_H_
+#define ICAP_LOG_H_
+
+class AccessLogEntry;
+class ACLChecklist;
+
+void icapLogClose();
+void icapLogOpen();
+void icapLogRotate();
+void icapLogLog(AccessLogEntry *al, ACLChecklist * checklist);
+
+extern int IcapLogfileStatus;
+
+#endif /*ICAP_LOG_H_*/
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Launcher.cc squid-3.1.0.11/src/adaptation/icap/Launcher.cc
--- squid-3.1.0.10/src/adaptation/icap/Launcher.cc	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Launcher.cc	2009-07-19 17:11:13.000000000 +1200
@@ -4,17 +4,21 @@
 
 #include "squid.h"
 #include "TextException.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "acl/FilledChecklist.h"
 #include "HttpMsg.h"
 #include "adaptation/icap/Launcher.h"
 #include "adaptation/icap/Xaction.h"
 #include "adaptation/icap/ServiceRep.h"
+#include "adaptation/icap/Config.h"
 
 
 Adaptation::Icap::Launcher::Launcher(const char *aTypeName,
                                      Adaptation::Initiator *anInitiator, Adaptation::ServicePointer &aService):
         AsyncJob(aTypeName),
-        Adaptation::Initiate(aTypeName, anInitiator, aService),
-        theXaction(0), theLaunches(0)
+        Adaptation::Initiate(aTypeName, anInitiator),
+        theService(aService), theXaction(0), theLaunches(0)
 {
 }
 
@@ -28,17 +32,20 @@
     Adaptation::Initiate::start();
 
     Must(theInitiator);
-    launchXaction(false);
+    launchXaction("first");
 }
 
-void Adaptation::Icap::Launcher::launchXaction(bool final)
+void Adaptation::Icap::Launcher::launchXaction(const char *xkind)
 {
     Must(!theXaction);
     ++theLaunches;
-    debugs(93,4, HERE << "launching xaction #" << theLaunches);
+    debugs(93,4, HERE << "launching " << xkind << " xaction #" << theLaunches);
     Adaptation::Icap::Xaction *x = createXaction();
-    if (final)
+    x->attempts = theLaunches;
+    if (theLaunches > 1)
         x->disableRetries();
+    if (theLaunches >= TheConfig.repeat_limit)
+        x->disableRepeats("over icap_retry_limit");
     theXaction = initiateAdaptation(x);
     Must(theXaction);
 }
@@ -60,19 +67,33 @@
 
 }
 
+// XXX: this call is unused by ICAPXaction in favor of ICAPLauncher::noteXactAbort
 void Adaptation::Icap::Launcher::noteAdaptationQueryAbort(bool final)
 {
+    debugs(93,5, HERE << "launches: " << theLaunches << "; final: " << final);
     clearAdaptation(theXaction);
+       
+    Must(done()); // swanSong will notify the initiator
+}
 
-    // TODO: add more checks from FwdState::checkRetry()?
-    if (!final && theLaunches < 2 && !shutting_down) {
-        launchXaction(true);
-    } else {
-        debugs(93,3, HERE << "cannot retry the failed ICAP xaction; tries: " <<
-               theLaunches << "; final: " << final);
-        Must(done()); // swanSong will notify the initiator
-    }
+void Adaptation::Icap::Launcher::noteXactAbort(XactAbortInfo &info)
+{
+    debugs(93,5, HERE << "theXaction:" << theXaction << " launches: " << theLaunches);
 
+     // TODO: add more checks from FwdState::checkRetry()?
+    if (canRetry(info)) {
+        clearAdaptation(theXaction);
+        launchXaction("retry");
+    } 
+    else if (canRepeat(info)) {
+        clearAdaptation(theXaction);
+        launchXaction("repeat");
+    } else {
+        debugs(93,3, HERE << "cannot retry or repeat a failed transaction");
+        clearAdaptation(theXaction);
+        tellQueryAborted(false); // caller decides based on bypass, consumption
+        Must(done());
+    }   
 }
 
 bool Adaptation::Icap::Launcher::doneAll() const
@@ -83,10 +104,67 @@
 void Adaptation::Icap::Launcher::swanSong()
 {
     if (theInitiator)
-        tellQueryAborted(!service().cfg().bypass);
+        tellQueryAborted(true); // always final here because abnormal
 
     if (theXaction)
         clearAdaptation(theXaction);
 
     Adaptation::Initiate::swanSong();
 }
+
+bool Adaptation::Icap::Launcher::canRetry(Adaptation::Icap::XactAbortInfo &info) const
+{
+    // We do not check and can exceed zero repeat limit when retrying.
+    // This is by design as the limit does not apply to pconn retrying.
+    return !shutting_down && info.isRetriable;
+}
+
+bool Adaptation::Icap::Launcher::canRepeat(Adaptation::Icap::XactAbortInfo &info) const
+{
+    debugs(93,9, HERE << shutting_down);
+    if (theLaunches >= TheConfig.repeat_limit || shutting_down)
+        return false;
+
+    debugs(93,9, HERE << info.isRepeatable); // TODO: update and use status()
+    if (!info.isRepeatable)
+        return false;
+
+    debugs(93,9, HERE << info.icapReply);
+    if (!info.icapReply) // did not get to read an ICAP reply; a timeout?
+        return true;
+       
+    debugs(93,9, HERE << info.icapReply->sline.status);
+    if (!info.icapReply->sline.status) // failed to parse the reply; I/O err
+        return true;
+    
+    ACLFilledChecklist *cl =
+        new ACLFilledChecklist(TheConfig.repeat, info.icapRequest, dash_str);
+    cl->reply = HTTPMSGLOCK(info.icapReply);
+    
+    const bool result = cl->fastCheck();
+    delete cl;
+    return result;
+}
+
+/* ICAPXactAbortInfo */
+
+Adaptation::Icap::XactAbortInfo::XactAbortInfo(HttpRequest *anIcapRequest,
+    HttpReply *anIcapReply, bool beRetriable, bool beRepeatable):
+    icapRequest(anIcapRequest ? HTTPMSGLOCK(anIcapRequest) : NULL),
+    icapReply(anIcapReply ? HTTPMSGLOCK(anIcapReply) : NULL),
+    isRetriable(beRetriable), isRepeatable(beRepeatable)
+{
+}
+
+Adaptation::Icap::XactAbortInfo::XactAbortInfo(const Adaptation::Icap::XactAbortInfo &i):
+    icapRequest(i.icapRequest ? HTTPMSGLOCK(i.icapRequest) : NULL),
+    icapReply(i.icapReply ? HTTPMSGLOCK(i.icapReply) : NULL),
+    isRetriable(i.isRetriable), isRepeatable(i.isRepeatable)
+{
+}
+
+Adaptation::Icap::XactAbortInfo::~XactAbortInfo()
+{
+    HTTPMSGUNLOCK(icapRequest);
+    HTTPMSGUNLOCK(icapReply);
+}
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Launcher.h squid-3.1.0.11/src/adaptation/icap/Launcher.h
--- squid-3.1.0.10/src/adaptation/icap/Launcher.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Launcher.h	2009-07-19 17:11:13.000000000 +1200
@@ -64,6 +64,7 @@
 namespace Icap {
 
 class Xaction;
+class XactAbortInfo;
 
 // Note: Initiate must be the first parent for cbdata to work. We use
 // a temporary InitaitorHolder/toCbdata hacks and do not call cbdata
@@ -79,6 +80,11 @@
 
     // Adaptation::Initiator: asynchronous communication with the current transaction
     virtual void noteAdaptationAnswer(HttpMsg *message);
+    virtual void noteXactAbort(XactAbortInfo &info);
+
+private:
+    bool canRetry(XactAbortInfo &info) const; //< true if can retry in the case of persistent connection failures
+    bool canRepeat(XactAbortInfo &info) const; //< true if can repeat in the case of no or unsatisfactory response 
     virtual void noteAdaptationQueryAbort(bool final);
 
 protected:
@@ -90,14 +96,58 @@
     // creates the right ICAP transaction using stored configuration params
     virtual Xaction *createXaction() = 0;
 
-    void launchXaction(bool final);
+    void launchXaction(const char *xkind);
 
-    Adaptation::Initiate *theXaction; // current ICAP transaction
+    Adaptation::ServicePointer theService; ///< ICAP service for all launches
+    Adaptation::Initiate *theXaction; ///< current ICAP transaction
     int theLaunches; // the number of transaction launches
 };
 
+/// helper class to pass information about aborted ICAP requests to 
+/// the Adaptation::Icap::Launcher class
+class XactAbortInfo {
+public:
+    XactAbortInfo(HttpRequest *anIcapRequest, HttpReply *anIcapReply,
+                  bool beRetriable, bool beRepeatable);
+    XactAbortInfo(const XactAbortInfo &);
+    ~XactAbortInfo();
+
+    HttpRequest *icapRequest;
+    HttpReply *icapReply;
+    bool isRetriable;
+    bool isRepeatable;
+    
+private:
+    XactAbortInfo &operator =(const XactAbortInfo &); // undefined
+};
+
+/* required by UnaryMemFunT */
+inline std::ostream &operator << (std::ostream &os, Adaptation::Icap::XactAbortInfo info)
+{
+    // Nothing, it is unused
+    return os;
+}
+
+/// A Dialer class used to schedule the Adaptation::Icap::Launcher::noteXactAbort call
+class XactAbortCall: public UnaryMemFunT<Adaptation::Icap::Launcher, Adaptation::Icap::XactAbortInfo> {
+public:
+    typedef void (Adaptation::Icap::Launcher::*DialMethod)(Adaptation::Icap::XactAbortInfo &);
+    XactAbortCall(Adaptation::Icap::Launcher *launcer, DialMethod aMethod, 
+                  const Adaptation::Icap::XactAbortInfo &info):
+    UnaryMemFunT<Adaptation::Icap::Launcher, Adaptation::Icap::XactAbortInfo>(launcer, NULL, info),
+       dialMethod(aMethod)
+    {}
+    virtual void print(std::ostream &os) const {  os << '(' << "retriable:" << arg1.isRetriable << ", repeatable:" << arg1.isRepeatable << ')'; }
+
+public:
+    DialMethod dialMethod;
+    
+protected:
+    virtual void doDial() { (object->*dialMethod)(arg1); }
+};
 
 } // namespace Icap
 } // namespace Adaptation
 
+
 #endif /* SQUID_ICAPLAUNCHER_H */
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Makefile.am squid-3.1.0.11/src/adaptation/icap/Makefile.am
--- squid-3.1.0.10/src/adaptation/icap/Makefile.am	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Makefile.am	2009-07-19 17:11:13.000000000 +1200
@@ -22,4 +22,8 @@
 	Xaction.cc \
 	Xaction.h \
 	ModXact.cc \
-	ModXact.h
+	ModXact.h \
+	icap_log.cc \
+	icap_log.h \
+	History.cc \
+	History.h
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Makefile.in squid-3.1.0.11/src/adaptation/icap/Makefile.in
--- squid-3.1.0.10/src/adaptation/icap/Makefile.in	2009-07-18 16:16:48.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Makefile.in	2009-07-19 17:11:34.000000000 +1200
@@ -47,7 +47,8 @@
 LTLIBRARIES = $(noinst_LTLIBRARIES)
 libicap_la_LIBADD =
 am_libicap_la_OBJECTS = Client.lo Config.lo Elements.lo Options.lo \
-	ServiceRep.lo Launcher.lo OptXact.lo Xaction.lo ModXact.lo
+	ServiceRep.lo Launcher.lo OptXact.lo Xaction.lo ModXact.lo \
+	icap_log.lo History.lo
 libicap_la_OBJECTS = $(am_libicap_la_OBJECTS)
 DEFAULT_INCLUDES = 
 depcomp = $(SHELL) $(top_srcdir)/cfgaux/depcomp
@@ -279,7 +280,11 @@
 	Xaction.cc \
 	Xaction.h \
 	ModXact.cc \
-	ModXact.h
+	ModXact.h \
+	icap_log.cc \
+	icap_log.h \
+	History.cc \
+	History.h
 
 all: all-am
 
@@ -342,12 +347,14 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Client.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Config.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Elements.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/History.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Launcher.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModXact.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OptXact.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Options.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServiceRep.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Xaction.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icap_log.Plo@am__quote@
 
 .cc.o:
 @am__fastdepCXX_TRUE@	$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/ModXact.cc squid-3.1.0.11/src/adaptation/icap/ModXact.cc
--- squid-3.1.0.10/src/adaptation/icap/ModXact.cc	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/ModXact.cc	2009-07-19 17:11:13.000000000 +1200
@@ -17,6 +17,9 @@
 #include "auth/UserRequest.h"
 #include "adaptation/icap/Config.h"
 #include "SquidTime.h"
+#include "AccessLogEntry.h"
+#include "adaptation/icap/History.h"
+#include "adaptation/History.h"
 
 // flow and terminology:
 //     HTTP| --> receive --> encode --> write --> |network
@@ -41,10 +44,12 @@
                                    HttpRequest *virginCause, Adaptation::Icap::ServiceRep::Pointer &aService):
         AsyncJob("Adaptation::Icap::ModXact"),
         Adaptation::Icap::Xaction("Adaptation::Icap::ModXact", anInitiator, aService),
-        icapReply(NULL),
         virginConsumed(0),
         bodyParser(NULL),
-        canStartBypass(false) // too early
+        canStartBypass(false), // too early
+        protectGroupBypass(true),
+        replyBodySize(0),
+        adaptHistoryId(-1)
 {
     assert(virginHeader);
 
@@ -58,8 +63,8 @@
     // encoding
     // nothing to do because we are using temporary buffers
 
-    // parsing
-    icapReply = new HttpReply;
+    // parsing; TODO: do not set until we parse, see ICAPOptXact
+    icapReply = HTTPMSGLOCK(new HttpReply);
     icapReply->protoPrefix = "ICAP/"; // TODO: make an IcapReply class?
 
     debugs(93,7, HERE << "initialized." << status());
@@ -70,6 +75,11 @@
 {
     Adaptation::Icap::Xaction::start();
 
+    // reserve an adaptation history slot (attempts are known at this time)
+    Adaptation::History::Pointer ah = virginRequest().adaptLogHistory();
+    if (ah != NULL)
+        adaptHistoryId = ah->recordXactStart(service().cfg().key, icap_tr_start, attempts > 1);
+
     estimateVirginBody(); // before virgin disappears!
 
     canStartBypass = service().cfg().bypass;
@@ -101,6 +111,7 @@
         startWriting();
     } else {
         disableRetries();
+        disableRepeats("ICAP service is unusable");
         throw TexcHere("ICAP service is unusable");
     }
 }
@@ -131,6 +142,7 @@
 
     // write headers
     state.writing = State::writingHeaders;
+    icap_tio_start = current_time;
     scheduleWrite(requestBuf);
 }
 
@@ -304,6 +316,14 @@
     buf.append(ICAP::crlf, 2); // chunk-terminating CRLF
 }
 
+const HttpRequest &Adaptation::Icap::ModXact::virginRequest() const
+{
+    const HttpRequest *request = virgin.cause ?
+        virgin.cause : dynamic_cast<const HttpRequest*>(virgin.header);
+    Must(request);
+    return *request;
+}
+
 // did the activity reached the end of the virgin body?
 bool Adaptation::Icap::ModXact::virginBodyEndReached(const Adaptation::Icap::VirginBodyAct &act) const
 {
@@ -336,7 +356,8 @@
 
 void Adaptation::Icap::ModXact::virginConsume()
 {
-    debugs(93, 9, HERE << "consumption guards: " << !virgin.body_pipe << isRetriable);
+    debugs(93, 9, HERE << "consumption guards: " << !virgin.body_pipe << isRetriable <<
+           isRepeatable << canStartBypass << protectGroupBypass);
 
     if (!virgin.body_pipe)
         return; // nothing to consume
@@ -345,11 +366,12 @@
         return; // do not consume if we may have to retry later
 
     BodyPipe &bp = *virgin.body_pipe;
+    const bool wantToPostpone = isRepeatable || canStartBypass || protectGroupBypass;
 
     // Why > 2? HttpState does not use the last bytes in the buffer
     // because delayAwareRead() is arguably broken. See
     // HttpStateData::maybeReadVirginBody for more details.
-    if (canStartBypass && bp.buf().spaceSize() > 2) {
+    if (wantToPostpone && bp.buf().spaceSize() > 2) {
         // Postponing may increase memory footprint and slow the HTTP side
         // down. Not postponing may increase the number of ICAP errors
         // if the ICAP service fails. We may also use "potential" space to
@@ -381,7 +403,8 @@
         bp.consume(size);
         virginConsumed += size;
         Must(!isRetriable); // or we should not be consuming
-        disableBypass("consumed content");
+        disableRepeats("consumed content");
+        disableBypass("consumed content", true);
     }
 }
 
@@ -477,6 +500,7 @@
 void Adaptation::Icap::ModXact::handleCommRead(size_t)
 {
     Must(!state.doneParsing());
+    icap_tio_finish = current_time;
     parseMore();
     readMore();
 }
@@ -498,8 +522,9 @@
         debugs(93,5, HERE << "echoed " << size << " out of " << sizeMax <<
                " bytes");
         virginBodySending.progress(size);
+        disableRepeats("echoed content");
+        disableBypass("echoed content", true);
         virginConsume();
-        disableBypass("echoed content");
     }
 
     if (virginBodyEndReached(virginBodySending)) {
@@ -521,8 +546,10 @@
 // stop (or do not start) sending adapted message body
 void Adaptation::Icap::ModXact::stopSending(bool nicely)
 {
+    debugs(93, 7, HERE << "Enter stop sending ");
     if (doneSending())
         return;
+    debugs(93, 7, HERE << "Proceed with stop sending ");
 
     if (state.sending != State::sendingUndecided) {
         debugs(93, 7, HERE << "will no longer send" << status());
@@ -584,9 +611,10 @@
 
 void Adaptation::Icap::ModXact::bypassFailure()
 {
-    disableBypass("already started to bypass");
+    disableBypass("already started to bypass", false);
 
     Must(!isRetriable); // or we should not be bypassing
+    // TODO: should the same be enforced for isRepeatable? Check icap_repeat??
 
     prepEchoing();
 
@@ -605,12 +633,16 @@
     }
 }
 
-void Adaptation::Icap::ModXact::disableBypass(const char *reason)
+void Adaptation::Icap::ModXact::disableBypass(const char *reason, bool includingGroupBypass)
 {
     if (canStartBypass) {
         debugs(93,7, HERE << "will never start bypass because " << reason);
         canStartBypass = false;
     }
+    if (protectGroupBypass && includingGroupBypass) {
+        debugs(93,7, HERE << "not protecting group bypass because " << reason);
+        protectGroupBypass = false;
+    }
 }
 
 
@@ -623,8 +655,11 @@
 
     if (gotEncapsulated("res-hdr")) {
         adapted.setHeader(new HttpReply);
+        setOutcome(service().cfg().method == ICAP::methodReqmod ?
+            xoSatisfied : xoModified);
     } else if (gotEncapsulated("req-hdr")) {
         adapted.setHeader(new HttpRequest);
+        setOutcome(xoModified);
     } else
         throw TexcHere("Neither res-hdr nor req-hdr in maybeAllocateHttpMsg()");
 }
@@ -654,7 +689,8 @@
 // called after parsing all headers or when bypassing an exception
 void Adaptation::Icap::ModXact::startSending()
 {
-    disableBypass("sent headers");
+    disableRepeats("sent headers");
+    disableBypass("sent headers", true);
     sendAnswer(adapted.header);
 
     if (state.sending == State::sendingVirgin)
@@ -700,6 +736,40 @@
         break;
     }
 
+    const HttpRequest *request = dynamic_cast<HttpRequest*>(adapted.header);
+    if (!request)
+        request = &virginRequest();
+
+    // update the cross-transactional database if needed (all status codes!)
+    if (const char *xxName = Adaptation::Config::masterx_shared_name) {
+        Adaptation::History::Pointer ah = request->adaptHistory(true);
+        if (ah != NULL) {
+            const String val = icapReply->header.getByName(xxName);
+            if (val.size() > 0) // XXX: HttpHeader lacks empty value detection
+                ah->updateXxRecord(xxName, val);
+        }
+    }
+
+    // update the adaptation plan if needed (all status codes!)
+    if (service().cfg().routing) {
+        String services;
+        if (icapReply->header.getList(HDR_X_NEXT_SERVICES, &services)) {
+            Adaptation::History::Pointer ah = request->adaptHistory(true);
+            if (ah != NULL)
+                ah->updateNextServices(services);
+        }
+    } // TODO: else warn (occasionally!) if we got HDR_X_NEXT_SERVICES
+
+    // We need to store received ICAP headers for <icapLastHeader logformat option.
+    // If we already have stored headers from previous ICAP transaction related to this
+    // request, old headers will be replaced with the new one.
+    
+    Adaptation::Icap::History::Pointer h = request->icapHistory();
+    if (h != NULL) {
+        h->mergeIcapHeaders(&icapReply->header);
+        h->setIcapLastHeader(&icapReply->header);
+    }
+
     // handle100Continue() manages state.writing on its own.
     // Non-100 status means the server needs no postPreview data from us.
     if (state.writing == State::writingPaused)
@@ -762,12 +832,15 @@
 // We actually start sending (echoig or not) in startSending.
 void Adaptation::Icap::ModXact::prepEchoing()
 {
-    disableBypass("preparing to echo content");
+    disableRepeats("preparing to echo content");
+    disableBypass("preparing to echo content", true);
+    setOutcome(xoEcho);
 
     // We want to clone the HTTP message, but we do not want
     // to copy some non-HTTP state parts that HttpMsg kids carry in them.
     // Thus, we cannot use a smart pointer, copy constructor, or equivalent.
     // Instead, we simply write the HTTP message and "clone" it by parsing.
+    // TODO: use HttpMsg::clone()!
 
     HttpMsg *oldHead = virgin.header;
     debugs(93, 7, HERE << "cloning virgin message " << oldHead);
@@ -781,8 +854,10 @@
     // allocate the adapted message and copy metainfo
     Must(!adapted.header);
     HttpMsg *newHead = NULL;
-    if (dynamic_cast<const HttpRequest*>(oldHead)) {
+    if (const HttpRequest *oldR = dynamic_cast<const HttpRequest*>(oldHead)) {
         HttpRequest *newR = new HttpRequest;
+        newR->canonical = oldR->canonical ?
+            xstrdup(oldR->canonical) : NULL; // parse() does not set it
         newHead = newR;
     } else if (dynamic_cast<const HttpReply*>(oldHead)) {
         HttpReply *newRep = new HttpReply;
@@ -879,6 +954,9 @@
         return false;
     }
 
+    if (HttpRequest *r = dynamic_cast<HttpRequest*>(head))
+        urlCanonical(r); // parse does not set HttpRequest::canonical
+
     debugs(93, 5, HERE << "parse success, consume " << head->hdr_sz << " bytes, return true");
     readBuf.consume(head->hdr_sz);
     return true;
@@ -913,10 +991,14 @@
 
     debugs(93, 5, HERE << "have " << readBuf.contentSize() << " body bytes after " <<
            "parse; parsed all: " << parsed);
+    replyBodySize += adapted.body_pipe->buf().contentSize();
 
     // TODO: expose BodyPipe::putSize() to make this check simpler and clearer
-    if (adapted.body_pipe->buf().contentSize() > 0) // parsed something sometime
-        disableBypass("sent adapted content");
+    // TODO: do we really need this if we disable when sending headers?
+    if (adapted.body_pipe->buf().contentSize() > 0) { // parsed something sometime
+        disableRepeats("sent adapted content");
+        disableBypass("sent adapted content", true);
+    }
 
     if (parsed) {
         stopParsing();
@@ -1014,14 +1096,74 @@
     stopWriting(false);
     stopSending(false);
 
-    if (icapReply) {
-        delete icapReply;
-        icapReply = NULL;
-    }
+    // update adaptation history if start was called and we reserved a slot
+    Adaptation::History::Pointer ah = virginRequest().adaptLogHistory();
+    if (ah != NULL && adaptHistoryId >= 0)
+        ah->recordXactFinish(adaptHistoryId);
 
     Adaptation::Icap::Xaction::swanSong();
 }
 
+void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry *);
+
+void Adaptation::Icap::ModXact::finalizeLogInfo()
+{
+    HttpRequest * request_ = NULL;
+    HttpReply * reply_ = NULL;
+    if(!(request_ = dynamic_cast<HttpRequest*>(adapted.header)))
+    {
+        request_ = (virgin.cause? virgin.cause: dynamic_cast<HttpRequest*>(virgin.header));
+        reply_ = dynamic_cast<HttpReply*>(adapted.header);
+    }
+
+    Adaptation::Icap::History::Pointer h = request_->icapHistory();
+     Must(h != NULL); // ICAPXaction::maybeLog calls only if there is a log
+     al.icp.opcode = ICP_INVALID;
+     al.url = h->log_uri.termedBuf();
+     const Adaptation::Icap::ServiceRep  &s = service();
+     al.icap.reqMethod = s.cfg().method;
+
+     al.cache.caddr = request_->client_addr;
+
+     al.request = HTTPMSGLOCK(request_);
+     if(reply_)
+         al.reply = HTTPMSGLOCK(reply_);
+     else
+         al.reply = NULL;
+
+     if (h->rfc931.size())
+         al.cache.rfc931 = h->rfc931.termedBuf();
+
+#if USE_SSL
+     if (h->ssluser.size())
+         al.cache.ssluser = h->ssluser.termedBuf();
+#endif
+     al.cache.code = h->logType;
+     al.cache.requestSize = h->req_sz;
+     if (reply_) {
+         al.http.code = reply_->sline.status;
+         al.http.content_type = reply_->content_type.termedBuf();
+         al.cache.replySize = replyBodySize + reply_->hdr_sz;
+         al.cache.highOffset = replyBodySize;
+         //don't set al.cache.objectSize because it hasn't exist yet
+
+         Packer p;
+         MemBuf mb;
+
+         mb.init();
+         packerToMemInit(&p, &mb);
+
+         reply_->header.packInto(&p);
+         al.headers.reply = xstrdup(mb.buf);
+
+         packerClean(&p);
+         mb.clean();
+     }
+     prepareLogWithRequestDetails(request_, &al);
+     Xaction::finalizeLogInfo();
+}
+
+
 void Adaptation::Icap::ModXact::makeRequestHeaders(MemBuf &buf)
 {
     char ntoabuf[MAX_IPSTRLEN];
@@ -1048,6 +1190,21 @@
         buf.Printf("Proxy-Authorization: " SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(vh));
     }
 
+    const HttpRequest *request = &virginRequest();
+
+    // share the cross-transactional database records if needed
+    if (Adaptation::Config::masterx_shared_name) {
+        Adaptation::History::Pointer ah = request->adaptHistory(true);
+        if (ah != NULL) {
+            String name, value;
+            if (ah->getXxRecord(name, value)) {
+                buf.Printf(SQUIDSTRINGPH ": " SQUIDSTRINGPH "\r\n",  
+                    SQUIDSTRINGPRINT(name), SQUIDSTRINGPRINT(value));
+            }
+        }
+    }
+    
+
     buf.Printf("Encapsulated: ");
 
     MemBuf httpBuf;
@@ -1057,10 +1214,6 @@
     // build HTTP request header, if any
     ICAP::Method m = s.method;
 
-    const HttpRequest *request = virgin.cause ?
-                                 virgin.cause :
-                                 dynamic_cast<const HttpRequest*>(virgin.header);
-
     // to simplify, we could assume that request is always available
 
     String urlPath;
@@ -1113,6 +1266,9 @@
 
     buf.append(ICAP::crlf, 2); // terminate ICAP header
 
+    // fill icapRequest for logging
+    Must(icapRequest->parseCharBuf(buf.content(), buf.contentSize()));
+
     // start ICAP request body with encapsulated HTTP headers
     buf.append(httpBuf.content(), httpBuf.contentSize());
 
@@ -1141,7 +1297,8 @@
 
     if (const HttpRequest* old_request = dynamic_cast<const HttpRequest*>(head)) {
         HttpRequest* new_request = new HttpRequest;
-        urlParse(old_request->method, old_request->canonical,new_request);
+        assert(old_request->canonical);
+        urlParse(old_request->method, old_request->canonical, new_request);
         new_request->http_ver = old_request->http_ver;
         headClone = new_request;
     } else if (const HttpReply *old_reply = dynamic_cast<const HttpReply*>(head)) {
@@ -1186,10 +1343,7 @@
         return;
     }
 
-    const HttpRequest *request = virgin.cause ?
-                                 virgin.cause :
-                                 dynamic_cast<const HttpRequest*>(virgin.header);
-    const String urlPath = request ? request->urlpath : String();
+    const String urlPath = virginRequest().urlpath;
     size_t wantedSize;
     if (!service().wantsPreview(urlPath, wantedSize)) {
         debugs(93, 5, HERE << "should not offer preview for " << urlPath);
@@ -1309,6 +1463,9 @@
 
     if (canStartBypass)
         buf.append("Y", 1);
+
+    if (protectGroupBypass)
+        buf.append("G", 1);
 }
 
 void Adaptation::Icap::ModXact::fillDoneStatus(MemBuf &buf) const
@@ -1531,6 +1688,7 @@
 {
     virgin.setHeader(virginHeader);
     virgin.setCause(virginCause);
+    updateHistory(true);
 }
 
 Adaptation::Icap::Xaction *Adaptation::Icap::ModXactLauncher::createXaction()
@@ -1540,3 +1698,25 @@
     Must(s != NULL);
     return new Adaptation::Icap::ModXact(this, virgin.header, virgin.cause, s);
 }
+
+void Adaptation::Icap::ModXactLauncher::swanSong() {
+    debugs(93, 5, HERE << "swan sings");
+    updateHistory(false);
+    Adaptation::Icap::Launcher::swanSong();
+}
+
+void Adaptation::Icap::ModXactLauncher::updateHistory(bool start) {
+     HttpRequest *r = virgin.cause ?
+         virgin.cause : dynamic_cast<HttpRequest*>(virgin.header);
+
+     // r should never be NULL but we play safe; TODO: add Should()
+     if (r) {
+         Adaptation::Icap::History::Pointer h = r->icapHistory();
+         if (h != NULL) {
+             if (start)
+                 h->start("ICAPModXactLauncher");
+             else
+                 h->stop("ICAPModXactLauncher");
+         }
+     }
+}
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/ModXact.h squid-3.1.0.11/src/adaptation/icap/ModXact.h
--- squid-3.1.0.10/src/adaptation/icap/ModXact.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/ModXact.h	2009-07-19 17:11:13.000000000 +1200
@@ -167,6 +167,9 @@
 private:
     virtual void start();
 
+    /// locates the request, either as a cause or as a virgin message itself
+    const HttpRequest &virginRequest() const; // Must always be available
+
     void estimateVirginBody();
     void makeAdaptedBodyPipe(const char *what);
 
@@ -226,7 +229,7 @@
     void bypassFailure();
 
     void startSending();
-    void disableBypass(const char *reason);
+    void disableBypass(const char *reason, bool includeGroupBypass);
 
     void prepEchoing();
     void echoMore();
@@ -250,8 +253,7 @@
     bool gotEncapsulated(const char *section) const;
     void checkConsuming();
 
-
-    HttpReply *icapReply;
+    virtual void finalizeLogInfo();
 
     SizedEstimate virginBody;
     VirginBodyAct virginBodyWriting; // virgin body writing state
@@ -262,6 +264,11 @@
     ChunkedCodingParser *bodyParser; // ICAP response body parser
 
     bool canStartBypass; // enables bypass of transaction failures
+    bool protectGroupBypass; // protects ServiceGroup-wide bypass of failures
+
+    uint64_t replyBodySize; ///< dechunked ICAP reply body size
+
+    int adaptHistoryId; ///< adaptation history slot reservation
 
     class State
     {
@@ -319,6 +326,11 @@
 protected:
     virtual Xaction *createXaction();
 
+    virtual void swanSong();
+
+    /// starts or stops transaction accounting in ICAP history
+    void updateHistory(bool start);
+
     InOut virgin;
 
 private:
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/OptXact.cc squid-3.1.0.11/src/adaptation/icap/OptXact.cc
--- squid-3.1.0.10/src/adaptation/icap/OptXact.cc	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/OptXact.cc	2009-07-19 17:11:13.000000000 +1200
@@ -9,6 +9,8 @@
 #include "adaptation/icap/OptXact.h"
 #include "adaptation/icap/Options.h"
 #include "TextException.h"
+#include "SquidTime.h"
+#include "HttpRequest.h"
 
 CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, OptXact);
 CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, OptXactLauncher);
@@ -36,7 +38,7 @@
     makeRequest(requestBuf);
     debugs(93, 9, HERE << "request " << status() << ":\n" <<
            (requestBuf.terminate(), requestBuf.content()));
-
+    icap_tio_start = current_time;
     scheduleWrite(requestBuf);
 }
 
@@ -48,6 +50,10 @@
     const String host = s.cfg().host;
     buf.Printf("Host: " SQUIDSTRINGPH ":%d\r\n", SQUIDSTRINGPRINT(host), s.cfg().port);
     buf.append(ICAP::crlf, 2);
+
+    // XXX: HttpRequest cannot fully parse ICAP Request-Line
+    http_status status;
+    Must(icapRequest->parse(&buf, true, &status) > 0);
 }
 
 void Adaptation::Icap::OptXact::handleCommWrote(size_t size)
@@ -60,7 +66,10 @@
 void Adaptation::Icap::OptXact::handleCommRead(size_t)
 {
     if (HttpMsg *r = parseResponse()) {
+        icap_tio_finish = current_time;
+        setOutcome(xoOpt);
         sendAnswer(r);
+        icapReply = HTTPMSGLOCK(dynamic_cast<HttpReply*>(r));
         Must(done()); // there should be nothing else to do
         return;
     }
@@ -74,11 +83,11 @@
            status());
     debugs(93, 5, HERE << "\n" << readBuf.content());
 
-    HttpReply *r = new HttpReply;
+    HttpReply *r = HTTPMSGLOCK(new HttpReply);
     r->protoPrefix = "ICAP/"; // TODO: make an IcapReply class?
 
     if (!parseHttpMsg(r)) { // throws on errors
-        delete r;
+        HTTPMSGUNLOCK(r);
         return 0;
     }
 
@@ -88,6 +97,18 @@
     return r;
 }
 
+void Adaptation::Icap::OptXact::swanSong()
+{
+    Adaptation::Icap::Xaction::swanSong();
+}
+
+void Adaptation::Icap::OptXact::finalizeLogInfo()
+{
+    //    al.cache.caddr = 0;
+    al.icap.reqMethod = Adaptation::methodOptions;
+    Adaptation::Icap::Xaction::finalizeLogInfo();
+}
+
 /* Adaptation::Icap::OptXactLauncher */
 
 Adaptation::Icap::OptXactLauncher::OptXactLauncher(Adaptation::Initiator *anInitiator, Adaptation::ServicePointer aService):
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/OptXact.h squid-3.1.0.11/src/adaptation/icap/OptXact.h
--- squid-3.1.0.10/src/adaptation/icap/OptXact.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/OptXact.h	2009-07-19 17:11:13.000000000 +1200
@@ -63,7 +63,10 @@
 
     void startReading();
 
+    virtual void swanSong();
+
 private:
+    virtual void finalizeLogInfo();
     CBDATA_CLASS2(OptXact);
 };
 
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Xaction.cc squid-3.1.0.11/src/adaptation/icap/Xaction.cc
--- squid-3.1.0.10/src/adaptation/icap/Xaction.cc	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Xaction.cc	2009-07-19 17:11:13.000000000 +1200
@@ -7,10 +7,16 @@
 #include "CommCalls.h"
 #include "HttpMsg.h"
 #include "adaptation/icap/Xaction.h"
+#include "adaptation/icap/Launcher.h"
 #include "adaptation/icap/Config.h"
 #include "TextException.h"
 #include "pconn.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "acl/FilledChecklist.h"
+#include "icap_log.h"
 #include "fde.h"
+#include "SquidTime.h"
 
 static PconnPool *icapPconnPool = new PconnPool("ICAP Servers");
 
@@ -19,40 +25,55 @@
 
 Adaptation::Icap::Xaction::Xaction(const char *aTypeName, Adaptation::Initiator *anInitiator, Adaptation::Icap::ServiceRep::Pointer &aService):
         AsyncJob(aTypeName),
-        Adaptation::Initiate(aTypeName, anInitiator, aService.getRaw()),
+        Adaptation::Initiate(aTypeName, anInitiator),
+        icapRequest(NULL),
+        icapReply(NULL),
+        attempts(0),
         connection(-1),
+        theService(aService),
         commBuf(NULL), commBufSize(0),
         commEof(false),
         reuseConnection(true),
         isRetriable(true),
+        isRepeatable(true),
         ignoreLastWrite(false),
         connector(NULL), reader(NULL), writer(NULL), closer(NULL)
 {
     debugs(93,3, typeName << " constructed, this=" << this <<
            " [icapx" << id << ']'); // we should not call virtual status() here
+    icapRequest = HTTPMSGLOCK(new HttpRequest);
+    icap_tr_start = current_time;
 }
 
 Adaptation::Icap::Xaction::~Xaction()
 {
     debugs(93,3, typeName << " destructed, this=" << this <<
            " [icapx" << id << ']'); // we should not call virtual status() here
+    HTTPMSGUNLOCK(icapRequest);
+    HTTPMSGUNLOCK(icapReply);
 }
 
 Adaptation::Icap::ServiceRep &
 Adaptation::Icap::Xaction::service()
 {
-    Adaptation::Icap::ServiceRep *s = dynamic_cast<Adaptation::Icap::ServiceRep*>(&Initiate::service());
-    Must(s);
-    return *s;
+    Must(theService != NULL);
+    return *theService;
 }
 
 void Adaptation::Icap::Xaction::disableRetries()
 {
-    debugs(93,5, typeName << (isRetriable ? " becomes" : " remains") <<
-           " final" << status());
+    debugs(93,5, typeName << (isRetriable ? " from now on" : " still") <<
+           " cannot be retried " << status());
     isRetriable = false;
 }
 
+void Adaptation::Icap::Xaction::disableRepeats(const char *reason)
+{
+    debugs(93,5, typeName << (isRepeatable ? " from now on" : " still") <<
+           " cannot be repeated because " << reason << status());
+    isRepeatable = false;
+}
+
 void Adaptation::Icap::Xaction::start()
 {
     Adaptation::Initiate::start();
@@ -220,6 +241,7 @@
         debugs(93, 7, HERE << "ignoring last write; status: " << io.flag);
     } else {
         Must(io.flag == COMM_OK);
+        al.icap.bytesSent += io.size;
         updateTimeout();
         handleCommWrote(io.size);
     }
@@ -256,6 +278,13 @@
     mustStop("ICAP service connection externally closed");
 }
 
+void Adaptation::Icap::Xaction::callException(const std::exception  &e)
+{
+    setOutcome(xoError);
+    Adaptation::Initiate::callException(e);
+}
+
+
 void Adaptation::Icap::Xaction::callEnd()
 {
     if (doneWithIo()) {
@@ -317,6 +346,8 @@
     Must(io.flag == COMM_OK);
     Must(io.size >= 0);
 
+    al.icap.bytesRead+=io.size;
+
     updateTimeout();
 
     debugs(93, 3, HERE << "read " << io.size << " bytes");
@@ -397,6 +428,17 @@
 
 }
 
+void Adaptation::Icap::Xaction::setOutcome(const Adaptation::Icap::XactOutcome &xo)
+{
+    if (al.icap.outcome != xoUnknown) {
+        debugs(93, 3, HERE << "Warning: reseting outcome: from " <<
+            al.icap.outcome << " to " << xo);
+    } else {
+        debugs(93, 4, HERE << xo);
+    }
+    al.icap.outcome = xo;
+}
+
 // This 'last chance' method is called before a 'done' transaction is deleted.
 // It is wrong to call virtual methods from a destructor. Besides, this call
 // indicates that the transaction will terminate as planned.
@@ -413,11 +455,56 @@
         memFreeBuf(commBufSize, commBuf);
 
     if (theInitiator)
-        tellQueryAborted(!isRetriable);
+        tellQueryAborted();
+
+    maybeLog();
 
     Adaptation::Initiate::swanSong();
 }
 
+void Adaptation::Icap::Xaction::tellQueryAborted() {
+    Adaptation::Icap::Launcher *l = dynamic_cast<Adaptation::Icap::Launcher*>(theInitiator.ptr());
+    Adaptation::Icap::XactAbortInfo abortInfo(icapRequest, icapReply, retriable(), repeatable());
+    CallJob(91, 5, __FILE__, __LINE__, 
+            "Adaptation::Icap::Launcher::noteXactAbort",
+            XactAbortCall(l, &Adaptation::Icap::Launcher::noteXactAbort, abortInfo) );
+    clearInitiator();
+}
+
+
+void Adaptation::Icap::Xaction::maybeLog() {
+    if(IcapLogfileStatus == LOG_ENABLE)
+    {
+        ACLChecklist *checklist = new ACLFilledChecklist(::Config.accessList.icap, al.request, dash_str);
+        if (!::Config.accessList.icap || checklist->fastCheck()) {
+            finalizeLogInfo();
+            icapLogLog(&al, checklist);
+        }
+        accessLogFreeMemory(&al);
+        delete checklist;
+    }
+}
+
+void Adaptation::Icap::Xaction::finalizeLogInfo()
+{
+    //prepare log data
+    al.icp.opcode = ICP_INVALID;
+    
+    const Adaptation::Icap::ServiceRep &s = service();
+    al.icap.hostAddr = s.cfg().host.termedBuf();
+    al.icap.serviceName = s.cfg().key;
+    al.icap.reqUri = s.cfg().uri;
+    
+    al.icap.ioTime = tvSubMsec(icap_tio_start, icap_tio_finish);
+    al.icap.trTime = tvSubMsec(icap_tr_start, current_time);
+
+    al.icap.request = HTTPMSGLOCK(icapRequest);
+    if (icapReply) {
+        al.icap.reply = HTTPMSGLOCK(icapReply);
+        al.icap.resStatus = icapReply->sline.status;
+    }
+}
+
 // returns a temporary string depicting transaction status, for debugging
 const char *Adaptation::Icap::Xaction::status() const
 {
diff -u -r -N squid-3.1.0.10/src/adaptation/icap/Xaction.h squid-3.1.0.11/src/adaptation/icap/Xaction.h
--- squid-3.1.0.10/src/adaptation/icap/Xaction.h	2009-07-18 16:16:23.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/icap/Xaction.h	2009-07-19 17:11:13.000000000 +1200
@@ -39,6 +39,7 @@
 #include "MemBuf.h"
 #include "adaptation/icap/ServiceRep.h"
 #include "adaptation/Initiate.h"
+#include "AccessLogEntry.h"
 
 class HttpMsg;
 class CommConnectCbParams;
@@ -65,6 +66,9 @@
     virtual ~Xaction();
 
     void disableRetries();
+    void disableRepeats(const char *reason);
+    bool retriable() const { return isRetriable; }
+    bool repeatable() const { return isRepeatable; }
 
     // comm handler wrappers, treat as private
     void noteCommConnected(const CommConnectCbParams &io);
@@ -73,6 +77,13 @@
     void noteCommTimedout(const CommTimeoutCbParams &io);
     void noteCommClosed(const CommCloseCbParams &io);
 
+    // TODO: create these only when actually sending/receiving
+    HttpRequest *icapRequest; ///< sent (or at least created) ICAP request
+    HttpReply *icapReply; ///< received ICAP reply, if any
+
+    /// the number of times we tried to get to the service, including this time
+    int attempts;
+
 protected:
     virtual void start();
     virtual void noteInitiatorAborted(); // TODO: move to Adaptation::Initiate
@@ -113,13 +124,23 @@
     // useful for debugging
     virtual bool fillVirginHttpHeader(MemBuf&) const;
 
-    // custom end-of-call checks
+    // custom exception handling and end-of-call checks
+    virtual void callException(const std::exception  &e);
     virtual void callEnd();
 
+    // logging
+    void setOutcome(const XactOutcome &xo);
+    virtual void finalizeLogInfo();
+
     ServiceRep &service();
 
+private:
+    void tellQueryAborted();
+    void maybeLog();
+
 protected:
     int connection;     // FD of the ICAP server connection
+    Adaptation::Icap::ServiceRep::Pointer theService;
 
     /*
      * We have two read buffers.   We would prefer to read directly
@@ -137,7 +158,8 @@
     size_t commBufSize;
     bool commEof;
     bool reuseConnection;
-    bool isRetriable;
+    bool isRetriable;  ///< can retry on persistent connection failures
+    bool isRepeatable; ///< can repeat if no or unsatisfactory response
     bool ignoreLastWrite;
 
     const char *stopReason;
@@ -148,6 +170,12 @@
     AsyncCall::Pointer writer;
     AsyncCall::Pointer closer;
 
+    AccessLogEntry al;
+
+    timeval icap_tr_start;     /*time when the ICAP transaction was created */
+    timeval icap_tio_start;    /*time when the first ICAP request byte was scheduled for sending*/
+    timeval icap_tio_finish;   /*time when the last byte of the ICAP responsewas received*/
+
 private:
     //CBDATA_CLASS2(Xaction);
 };
diff -u -r -N squid-3.1.0.10/src/adaptation/Initiate.cc squid-3.1.0.11/src/adaptation/Initiate.cc
--- squid-3.1.0.10/src/adaptation/Initiate.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Initiate.cc	2009-07-19 17:11:12.000000000 +1200
@@ -4,7 +4,6 @@
 
 #include "squid.h"
 #include "HttpMsg.h"
-#include "adaptation/Service.h"
 #include "adaptation/Initiator.h"
 #include "adaptation/Initiate.h"
 
@@ -30,11 +29,9 @@
 
 /* Initiate */
 
-Adaptation::Initiate::Initiate(const char *aTypeName,
-                               Initiator *anInitiator, ServicePointer aService):
-        AsyncJob(aTypeName), theInitiator(anInitiator), theService(aService)
+Adaptation::Initiate::Initiate(const char *aTypeName, Initiator *anInitiator):
+    AsyncJob(aTypeName), theInitiator(anInitiator)
 {
-    assert(theService != NULL);
     assert(theInitiator);
 }
 
@@ -84,13 +81,6 @@
     clearInitiator();
 }
 
-Adaptation::Service &
-Adaptation::Initiate::service()
-{
-    assert(theService != NULL);
-    return *theService;
-}
-
 const char *Adaptation::Initiate::status() const
 {
     return AsyncJob::status(); // for now
diff -u -r -N squid-3.1.0.10/src/adaptation/Initiate.h squid-3.1.0.11/src/adaptation/Initiate.h
--- squid-3.1.0.10/src/adaptation/Initiate.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Initiate.h	2009-07-19 17:11:12.000000000 +1200
@@ -32,6 +32,7 @@
 
     bool isThere(); // we have a valid initiator pointer
     Initiator *ptr(); // asserts isThere()
+    void *theCbdata() { return cbdata;}
 
 private:
     InitiatorHolder &operator =(const InitiatorHolder &anInitiator);
@@ -55,15 +56,13 @@
 {
 
 public:
-    Initiate(const char *aTypeName, Initiator *anInitiator, ServicePointer aService);
+    Initiate(const char *aTypeName, Initiator *anInitiator);
     virtual ~Initiate();
 
     // communication with the initiator
     virtual void noteInitiatorAborted() = 0;
 
 protected:
-    Service &service();
-
     void sendAnswer(HttpMsg *msg); // send to the initiator
     void tellQueryAborted(bool final); // tell initiator
     void clearInitiator(); // used by noteInitiatorAborted; TODO: make private
@@ -73,7 +72,10 @@
     virtual const char *status() const; // for debugging
 
     InitiatorHolder theInitiator;
-    ServicePointer theService;
+
+private:
+    Initiate(const Initiate &); // no definition
+    Initiate &operator =(const Initiate &); // no definition
 };
 
 } // namespace Adaptation
diff -u -r -N squid-3.1.0.10/src/adaptation/Iterator.cc squid-3.1.0.11/src/adaptation/Iterator.cc
--- squid-3.1.0.10/src/adaptation/Iterator.cc	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Iterator.cc	2009-07-19 17:11:12.000000000 +1200
@@ -0,0 +1,211 @@
+/*
+ * DEBUG: section 93  Adaptation
+ */
+
+#include "squid.h"
+#include "TextException.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "HttpMsg.h"
+#include "adaptation/Config.h"
+#include "adaptation/Iterator.h"
+#include "adaptation/Service.h"
+#include "adaptation/ServiceFilter.h"
+#include "adaptation/ServiceGroups.h"
+
+
+Adaptation::Iterator::Iterator(Adaptation::Initiator *anInitiator,
+    HttpMsg *aMsg, HttpRequest *aCause,
+    const ServiceGroupPointer &aGroup):
+    AsyncJob("Iterator"),
+    Adaptation::Initiate("Iterator", anInitiator),
+    theGroup(aGroup),
+    theMsg(HTTPMSGLOCK(aMsg)),
+    theCause(aCause ? HTTPMSGLOCK(aCause) : NULL),
+    theLauncher(0),
+    iterations(0),
+    adapted(false)
+{
+}
+
+Adaptation::Iterator::~Iterator()
+{
+    assert(!theLauncher);
+    HTTPMSGUNLOCK(theMsg);
+    HTTPMSGUNLOCK(theCause);
+}
+
+void Adaptation::Iterator::start()
+{
+    Adaptation::Initiate::start();
+
+    thePlan = ServicePlan(theGroup, filter());
+    step();
+}
+
+void Adaptation::Iterator::step()
+{
+    ++iterations;
+    debugs(93,5, HERE << '#' << iterations << " plan: " << thePlan);
+
+    Must(!theLauncher);
+
+    if (thePlan.exhausted()) { // nothing more to do
+        sendAnswer(theMsg);
+        Must(done());
+        return;
+    }
+
+    if (iterations > Adaptation::Config::service_iteration_limit) {
+        debugs(93,DBG_CRITICAL, "Adaptation iterations limit (" <<
+            Adaptation::Config::service_iteration_limit << ") exceeded:\n" <<
+            "\tPossible service loop with " <<
+            theGroup->kind << " " << theGroup->id << ", plan=" << thePlan);
+        throw TexcHere("too many adaptations");
+    }
+
+    ServicePointer service = thePlan.current();
+    Must(service != NULL);
+    debugs(93,5, HERE << "using adaptation service: " << service->cfg().key);
+
+    theLauncher = initiateAdaptation(
+        service->makeXactLauncher(this, theMsg, theCause));
+    Must(theLauncher);
+    Must(!done());
+}
+
+void Adaptation::Iterator::noteAdaptationAnswer(HttpMsg *aMsg)
+{
+    // set theCause if we switched to request satisfaction mode
+    if (!theCause) { // probably sent a request message
+        if (dynamic_cast<HttpReply*>(aMsg)) { // we got a response message
+            if (HttpRequest *cause = dynamic_cast<HttpRequest*>(theMsg)) {
+                // definately sent request, now use it as the cause
+                theCause = cause; // moving the lock
+                theMsg = 0;
+                debugs(93,3, HERE << "in request satisfaction mode");
+            }
+        }
+    }
+
+    Must(aMsg);
+    HTTPMSGUNLOCK(theMsg);
+    theMsg = HTTPMSGLOCK(aMsg);
+    adapted = true;
+
+    clearAdaptation(theLauncher);
+    if (!updatePlan(true)) // do not immediatelly advance the new plan
+        thePlan.next(filter());
+    step();
+}
+
+void Adaptation::Iterator::noteInitiatorAborted()
+{
+    announceInitiatorAbort(theLauncher); // propogate to the transaction
+    clearInitiator();
+    mustStop("initiator gone");
+}
+
+void Adaptation::Iterator::noteAdaptationQueryAbort(bool final)
+{
+    debugs(93,5, HERE << "final: " << final << " plan: " << thePlan);
+    clearAdaptation(theLauncher);
+    updatePlan(false);
+
+    // can we replace the failed service (group-level bypass)?
+    const bool srcIntact = !theMsg->body_pipe || 
+        !theMsg->body_pipe->consumedSize();
+    // can we ignore the failure (compute while thePlan is not exhausted)?
+    Must(!thePlan.exhausted());
+    const bool canIgnore = thePlan.current()->cfg().bypass;
+    debugs(85,5, HERE << "flags: " << srcIntact << canIgnore << adapted);
+
+    if (srcIntact) {
+        if (thePlan.replacement(filter()) != NULL) {
+            debugs(93,3, HERE << "trying a replacement service");
+            step();
+            return;
+        }
+    }
+
+    if (canIgnore && srcIntact && adapted) {
+        debugs(85,3, HERE << "responding with older adapted msg");
+        sendAnswer(theMsg);
+        mustStop("sent older adapted msg");
+        return;
+    }
+
+    // caller may recover if we can ignore the error and virgin msg is intact
+    const bool useVirgin = canIgnore && !adapted && srcIntact;
+    tellQueryAborted(!useVirgin);
+    mustStop("group failure");
+}
+
+bool Adaptation::Iterator::doneAll() const
+{
+    return Adaptation::Initiate::doneAll() && thePlan.exhausted();
+}
+
+void Adaptation::Iterator::swanSong()
+{
+    if (theInitiator)
+        tellQueryAborted(true); // abnormal condition that should not happen
+
+    if (theLauncher)
+        clearAdaptation(theLauncher);
+
+    Adaptation::Initiate::swanSong();
+}
+
+bool Adaptation::Iterator::updatePlan(bool adopt)
+{
+    HttpRequest *r = theCause ? theCause : dynamic_cast<HttpRequest*>(theMsg);
+    Must(r);
+
+    Adaptation::History::Pointer ah = r->adaptHistory();
+    if (!ah)
+        return false; // the feature is not enabled or is not triggered
+
+    String services;
+    if (!ah->extractNextServices(services)) { // clears history
+        debugs(85,9, HERE << "no service-proposed plan received");
+        return false; // the service did not provide a new plan
+    }
+
+    if (!adopt) {
+        debugs(85,3, HERE << "rejecting service-proposed plan");
+        return false;
+    }
+  
+    debugs(85,3, HERE << "retiring old plan: " << thePlan);
+    theGroup = new DynamicServiceChain(services, theGroup); // refcounted
+    thePlan = ServicePlan(theGroup, filter());
+    debugs(85,3, HERE << "adopted service-proposed plan: " << thePlan);
+    return true;
+}
+
+Adaptation::ServiceFilter Adaptation::Iterator::filter() const
+{
+    // the method may differ from theGroup->method due to request satisfaction
+    Method method = methodNone;
+    // temporary variables, no locking needed
+    HttpRequest *req = NULL;
+    HttpReply *rep = NULL;
+
+    if (HttpRequest *r = dynamic_cast<HttpRequest*>(theMsg)) {
+        method = methodReqmod;
+        req = r;
+        rep = NULL;
+    } else
+    if (HttpReply *r = dynamic_cast<HttpReply*>(theMsg)) {
+        method = methodRespmod;
+        req = theCause;
+        rep = r;
+    } else {
+        Must(false); // should not happen
+    }
+
+    return ServiceFilter(method, theGroup->point, req, rep);
+}
+
+CBDATA_NAMESPACED_CLASS_INIT(Adaptation, Iterator);
diff -u -r -N squid-3.1.0.10/src/adaptation/Iterator.h squid-3.1.0.11/src/adaptation/Iterator.h
--- squid-3.1.0.10/src/adaptation/Iterator.h	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Iterator.h	2009-07-19 17:11:13.000000000 +1200
@@ -0,0 +1,65 @@
+#ifndef SQUID_ADAPTATION__ITERATOR_H
+#define SQUID_ADAPTATION__ITERATOR_H
+
+#include "adaptation/Initiator.h"
+#include "adaptation/Initiate.h"
+#include "adaptation/ServiceGroups.h"
+
+namespace Adaptation
+{
+
+/* Iterator is started by client or server Initiators. It iterates services
+   in a given group, starting transaction launcher for each service, according
+   to the service plan. Service plans support adaptation sets and chains.
+
+   Note: Initiate must be the first parent for cbdata to work. We use
+   a temporary InitiatorHolder/toCbdata hacks and do not call cbdata
+   operations on the initiator directly. 
+*/
+
+/// iterates services in ServiceGroup, starting adaptation launchers
+class Iterator: public Initiate, public Initiator
+{
+public:
+    Iterator(Adaptation::Initiator *anInitiator, 
+        HttpMsg *virginHeader, HttpRequest *virginCause,
+        const Adaptation::ServiceGroupPointer &aGroup);
+    virtual ~Iterator();
+
+    // Adaptation::Initiate: asynchronous communication with the initiator
+    void noteInitiatorAborted();
+
+    // Adaptation::Initiator: asynchronous communication with the current launcher
+    virtual void noteAdaptationAnswer(HttpMsg *message);
+    virtual void noteAdaptationQueryAbort(bool final);
+
+protected:
+    // Adaptation::Initiate API implementation
+    virtual void start();
+    virtual bool doneAll() const;
+    virtual void swanSong();
+
+    /// launches adaptation for the service selected by the plan
+    void step();
+
+    /// replace the current group and plan with service-proposed ones if needed
+    bool updatePlan(bool adopt); // returns true iff the plan was replaced
+
+    /// creates service filter for the current step
+    ServiceFilter filter() const;
+
+    ServiceGroupPointer theGroup; ///< the service group we are iterating
+    ServicePlan thePlan; ///< which services to use and in what order
+    HttpMsg *theMsg; ///< the message being adapted (virgin for each step)
+    HttpRequest *theCause; ///< the cause of the original virgin message
+    Adaptation::Initiate *theLauncher; ///< current transaction launcher
+    int iterations; ///< number of steps initiated
+    bool adapted; ///< whether the virgin message has been replaced
+
+    CBDATA_CLASS2(Iterator);
+};
+
+} // namespace Adaptation
+
+
+#endif /* SQUID_ADAPTATION__ITERATOR_H */
diff -u -r -N squid-3.1.0.10/src/adaptation/Makefile.am squid-3.1.0.11/src/adaptation/Makefile.am
--- squid-3.1.0.10/src/adaptation/Makefile.am	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Makefile.am	2009-07-19 17:11:13.000000000 +1200
@@ -29,6 +29,8 @@
 	Initiate.h \
 	Initiator.cc \
 	Initiator.h \
+	Iterator.cc \
+	Iterator.h \
 	Message.cc \
 	Message.h \
 	Service.cc \
@@ -36,7 +38,11 @@
 	ServiceConfig.cc \
 	ServiceConfig.h \
 	ServiceGroups.cc \
-	ServiceGroups.h
+	ServiceGroups.h \
+	ServiceFilter.cc \
+	ServiceFilter.h \
+	History.cc \
+	History.h 
 
 # add libraries for specific adaptation schemes
 libadaptation_la_LIBADD = @ECAP_LIBS@ @ECAPLIB@ @ICAP_LIBS@
diff -u -r -N squid-3.1.0.10/src/adaptation/Makefile.in squid-3.1.0.11/src/adaptation/Makefile.in
--- squid-3.1.0.10/src/adaptation/Makefile.in	2009-07-18 16:16:48.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Makefile.in	2009-07-19 17:11:34.000000000 +1200
@@ -48,8 +48,9 @@
 CONFIG_CLEAN_FILES =
 LTLIBRARIES = $(noinst_LTLIBRARIES)
 am_libadaptation_la_OBJECTS = AccessCheck.lo AccessRule.lo Config.lo \
-	Elements.lo Initiate.lo Initiator.lo Message.lo Service.lo \
-	ServiceConfig.lo ServiceGroups.lo
+	Elements.lo Initiate.lo Initiator.lo Iterator.lo Message.lo \
+	Service.lo ServiceConfig.lo ServiceGroups.lo ServiceFilter.lo \
+	History.lo
 libadaptation_la_OBJECTS = $(am_libadaptation_la_OBJECTS)
 DEFAULT_INCLUDES = 
 depcomp = $(SHELL) $(top_srcdir)/cfgaux/depcomp
@@ -287,6 +288,8 @@
 	Initiate.h \
 	Initiator.cc \
 	Initiator.h \
+	Iterator.cc \
+	Iterator.h \
 	Message.cc \
 	Message.h \
 	Service.cc \
@@ -294,7 +297,11 @@
 	ServiceConfig.cc \
 	ServiceConfig.h \
 	ServiceGroups.cc \
-	ServiceGroups.h
+	ServiceGroups.h \
+	ServiceFilter.cc \
+	ServiceFilter.h \
+	History.cc \
+	History.h 
 
 
 # add libraries for specific adaptation schemes
@@ -362,11 +369,14 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AccessRule.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Config.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Elements.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/History.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Initiate.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Initiator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Iterator.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Message.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Service.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServiceConfig.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServiceFilter.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServiceGroups.Plo@am__quote@
 
 .cc.o:
diff -u -r -N squid-3.1.0.10/src/adaptation/Service.cc squid-3.1.0.11/src/adaptation/Service.cc
--- squid-3.1.0.10/src/adaptation/Service.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Service.cc	2009-07-19 17:11:13.000000000 +1200
@@ -3,6 +3,8 @@
  */
 
 #include "squid.h"
+#include "HttpRequest.h"
+#include "adaptation/ServiceFilter.h"
 #include "adaptation/Service.h"
 
 Adaptation::Service::Service(const ServiceConfig &aConfig): theConfig(aConfig)
@@ -23,6 +25,32 @@
     return probed() && !up();
 }
 
+bool
+Adaptation::Service::wants(const ServiceFilter &filter) const
+{
+    if (cfg().method != filter.method)
+        return false;
+
+    if (cfg().point != filter.point)
+        return false;
+
+    // sending a message to a broken service is likely to cause errors
+    if (cfg().bypass && broken())
+        return false;
+
+    if (up()) {
+        // Sending a message to a service that does not want it is useless.
+        // note that we cannot check wantsUrl for service that is not "up"
+        // note that even essential services are skipped on unwanted URLs!
+        return wantsUrl(filter.request->urlpath);
+    }
+
+    // The service is down and is either not bypassable or not probed due
+    // to the bypass && broken() test above. Thus, we want to use it!
+    return true;
+}
+
+
 Adaptation::Services &
 Adaptation::AllServices()
 {
diff -u -r -N squid-3.1.0.10/src/adaptation/ServiceConfig.cc squid-3.1.0.11/src/adaptation/ServiceConfig.cc
--- squid-3.1.0.10/src/adaptation/ServiceConfig.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ServiceConfig.cc	2009-07-19 17:11:13.000000000 +1200
@@ -7,7 +7,8 @@
 #include "adaptation/ServiceConfig.h"
 
 Adaptation::ServiceConfig::ServiceConfig():
-        port(-1), method(methodNone), point(pointNone), bypass(false)
+    port(-1), method(methodNone), point(pointNone),
+    bypass(false), routing(false)
 {}
 
 const char *
@@ -59,23 +60,85 @@
 
     ConfigParser::ParseString(&key);
     ConfigParser::ParseString(&method_point);
-    ConfigParser::ParseBool(&bypass);
-    ConfigParser::ParseString(&uri);
-
-    debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
-           key << " " << method_point << " " << bypass);
-
     method = parseMethod(method_point);
     point = parseVectPoint(method_point);
 
-    debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
-           "service_configConfig is " << methodStr() << "_" << vectPointStr());
+    // reset optional parameters in case we are reconfiguring
+    bypass = routing = false;
+
+    // handle optional service name=value parameters
+    const char *lastOption = NULL;
+    while (char *option = strtok(NULL, w_space)) {
+        if (strcmp(option, "0") == 0) { // backward compatibility
+            bypass = false;
+            continue;
+        }
+        if (strcmp(option, "1") == 0) { // backward compatibility
+            bypass = true;
+            continue;
+        }
+
+        const char *name = option;
+        char *value = strstr(option, "=");
+        if (!value) {
+            lastOption = option;
+            break;
+        }
+        *value = '\0'; // terminate option name
+        ++value; // skip '='
+
+        // TODO: warn if option is set twice?
+        bool grokked = false;
+        if (strcmp(name, "bypass") == 0)
+            grokked = grokBool(bypass, name, value);
+        else
+        if (strcmp(name, "routing") == 0)
+            grokked = grokBool(routing, name, value);
+        else {
+            debugs(3, 0, cfg_filename << ':' << config_lineno << ": " <<
+                "unknown adaptation service option: " << name << '=' << value);
+        }
+        if (!grokked)
+            return false;
+    }
+
+    // what is left must be the service URI
+    if (!grokUri(lastOption))
+        return false;
+
+    // there should be nothing else left
+    if (const char *tail = strtok(NULL, w_space)) {
+        debugs(3, 0, cfg_filename << ':' << config_lineno << ": " <<
+            "garbage after adaptation service URI: " << tail);
+        return false;
+    }
 
+    debugs(3,5, cfg_filename << ':' << config_lineno << ": " <<
+        "adaptation_service " << key << ' ' <<
+            methodStr() << "_" << vectPointStr() << ' ' <<
+            bypass << routing << ' ' <<
+            uri);
+
+    return true;
+}
+
+bool
+Adaptation::ServiceConfig::grokUri(const char *value)
+{
     // TODO: find core code that parses URLs and extracts various parts
 
+    if (!value || !*value) {
+        debugs(3, 0, HERE << cfg_filename << ':' << config_lineno << ": " <<
+               "empty adaptation service URI");
+        return false;
+    }
+
+    uri = value;
+
     // extract scheme and use it as the service_configConfig protocol
     const char *schemeSuffix = "://";
-    if (const String::size_type schemeEnd=uri.find(schemeSuffix))
+    const String::size_type schemeEnd = uri.find(schemeSuffix);
+    if (schemeEnd != String::npos)
         protocol=uri.substr(0,schemeEnd);
 
     debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
@@ -138,10 +201,22 @@
     }
 
     resource.limitInit(s, len + 1);
+    return true;
+}
+
 
-    if ((bypass != 0) && (bypass != 1)) {
+bool
+Adaptation::ServiceConfig::grokBool(bool &var, const char *name, const char *value)
+{
+    if (!strcmp(value, "0") || !strcmp(value, "off"))
+        var = false;
+    else
+    if (!strcmp(value, "1") || !strcmp(value, "on"))
+        var = true;
+    else {
         debugs(3, 0, HERE << cfg_filename << ':' << config_lineno << ": " <<
-               "wrong bypass value; 0 or 1 expected: " << bypass);
+               "wrong value for boolean " << name << "; " <<
+               "'0', '1', 'on', or 'off' expected but got: " << value);
         return false;
     }
 
diff -u -r -N squid-3.1.0.10/src/adaptation/ServiceConfig.h squid-3.1.0.11/src/adaptation/ServiceConfig.h
--- squid-3.1.0.10/src/adaptation/ServiceConfig.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ServiceConfig.h	2009-07-19 17:11:13.000000000 +1200
@@ -32,10 +32,15 @@
     Method method;   // what is being adapted (REQMOD vs RESPMOD)
     VectPoint point; // where the adaptation happens (pre- or post-cache)
     bool bypass;
+    bool routing; ///< whether this service may determine the next service(s)
 
 protected:
     Method parseMethod(const char *buf) const;
     VectPoint parseVectPoint(const char *buf) const;
+ 
+    /// interpret parsed values
+    bool grokBool(bool &var, const char *name, const char *value);
+    bool grokUri(const char *value);
 };
 
 } // namespace Adaptation
diff -u -r -N squid-3.1.0.10/src/adaptation/ServiceFilter.cc squid-3.1.0.11/src/adaptation/ServiceFilter.cc
--- squid-3.1.0.10/src/adaptation/ServiceFilter.cc	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ServiceFilter.cc	2009-07-19 17:11:13.000000000 +1200
@@ -0,0 +1,40 @@
+#include "squid.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "adaptation/ServiceFilter.h"
+
+
+Adaptation::ServiceFilter::ServiceFilter(Method aMethod, VectPoint aPoint,
+HttpRequest *aReq, HttpReply *aRep): method(aMethod), point(aPoint),
+    request(HTTPMSGLOCK(aReq)),
+    reply(aRep ? HTTPMSGLOCK(aRep) : NULL)
+{
+    // a lot of code assumes that there is always a virgin request or cause
+    assert(request);
+}
+
+Adaptation::ServiceFilter::ServiceFilter(const ServiceFilter &f):
+    method(f.method), point(f.point),
+    request(HTTPMSGLOCK(f.request)),
+    reply(f.reply ? HTTPMSGLOCK(f.reply) : NULL)
+{
+}
+
+Adaptation::ServiceFilter::~ServiceFilter()
+{
+    HTTPMSGUNLOCK(request);
+    HTTPMSGUNLOCK(reply);
+}
+
+Adaptation::ServiceFilter &Adaptation::ServiceFilter::operator =(const ServiceFilter &f)
+{
+    if (this != &f) {
+        method = f.method;
+        point = f.point;
+        HTTPMSGUNLOCK(request);
+        HTTPMSGUNLOCK(reply);
+        request = HTTPMSGLOCK(f.request);
+        reply = f.reply ? HTTPMSGLOCK(f.reply) : NULL;
+    }
+    return *this;
+}
diff -u -r -N squid-3.1.0.10/src/adaptation/ServiceFilter.h squid-3.1.0.11/src/adaptation/ServiceFilter.h
--- squid-3.1.0.10/src/adaptation/ServiceFilter.h	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ServiceFilter.h	2009-07-19 17:11:13.000000000 +1200
@@ -0,0 +1,31 @@
+#ifndef SQUID_ADAPTATION__SERVICE_FILTER_H
+#define SQUID_ADAPTATION__SERVICE_FILTER_H
+
+#include "adaptation/Elements.h"
+
+class HttpRequest;
+class HttpReply;
+
+namespace Adaptation
+{
+
+/// information used to search for adaptation services
+class ServiceFilter
+{
+public:
+    ServiceFilter(Method, VectPoint, HttpRequest *, HttpReply *); // locks
+    ServiceFilter(const ServiceFilter &f);
+    ~ServiceFilter(); // unlocks
+
+    ServiceFilter &operator =(const ServiceFilter &f);
+
+public:
+    Method method; ///< adaptation direction
+    VectPoint point; ///< adaptation location
+    HttpRequest *request; ///< HTTP request being adapted or cause; may be nil
+    HttpReply *reply; ///< HTTP response being adapted; may be nil
+};
+
+} // namespace Adaptation
+
+#endif /* SQUID_ADAPTATION__SERVICE_FILTER_H */
diff -u -r -N squid-3.1.0.10/src/adaptation/ServiceGroups.cc squid-3.1.0.11/src/adaptation/ServiceGroups.cc
--- squid-3.1.0.10/src/adaptation/ServiceGroups.cc	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ServiceGroups.cc	2009-07-19 17:11:13.000000000 +1200
@@ -5,10 +5,14 @@
 #include "adaptation/Config.h"
 #include "adaptation/AccessRule.h"
 #include "adaptation/Service.h"
+#include "adaptation/ServiceFilter.h"
 #include "adaptation/ServiceGroups.h"
 
+#define ServiceGroup ServiceGroup
 
-Adaptation::ServiceGroup::ServiceGroup(const String &aKind): kind(aKind)
+Adaptation::ServiceGroup::ServiceGroup(const String &aKind, bool allSame):
+    kind(aKind), method(methodNone), point(pointNone),
+    allServicesSame(allSame)
 {
 }
 
@@ -28,57 +32,252 @@
     wordlistDestroy(&names);
 }
 
+// Note: configuration code aside, this method is called by DynamicServiceChain
 void
 Adaptation::ServiceGroup::finalize()
 {
-    for (iterator i = services.begin(); i != services.end(); ++i) {
-        const String &id = *i;
-        // TODO: fail on failures
-        if (!FindService(id))
-            debugs(93,0, "ERROR: Unknown adaptation name: " << id);
+    // 1) warn if services have different methods or vectoring point
+    // 2) warn if all-same services have different bypass status
+    // 3) warn if there are seemingly identical services in the group
+    // TODO: optimize by remembering ServicePointers rather than IDs
+
+    String baselineKey;
+    bool baselineBypass = false;
+    for (Pos pos = 0; has(pos); ++pos) {
+        // TODO: quit on all errors
+        const String &sid = services[pos];
+        ServicePointer service = at(pos);
+        if (service != NULL) {
+            if (method == methodNone) { 
+                // optimization: cache values that should be the same
+                method = service->cfg().method;
+                point = service->cfg().point;
+            } else {
+                if (method != service->cfg().method)
+                    finalizeMsg("Inconsistent service method for", sid, true);
+                if (point != service->cfg().point)
+                    finalizeMsg("Inconsistent vectoring point for", sid, true);
+            }
+
+            checkUniqueness(pos);
+
+            if (allServicesSame) { 
+                if (!baselineKey.size()) {
+                    baselineKey = service->cfg().key;
+                    baselineBypass = service->cfg().bypass;
+                } else
+                if (baselineBypass != service->cfg().bypass) {
+                    debugs(93,0, "WARNING: Inconsistent bypass in " << kind <<
+                        ' ' << id << " may produce surprising results: " <<
+                        baselineKey << " vs. " << sid);
+                }
+            }
+        } else { 
+            finalizeMsg("ERROR: Unknown adaptation name", sid, true);
+        }
     }
     debugs(93,7, HERE << "finalized " << kind << ": " << id);
 }
 
-/* ServiceSet */
+/// checks that the service name or URI is not repeated later in the group
+void
+Adaptation::ServiceGroup::checkUniqueness(const Pos checkedPos) const
+{
+    ServicePointer checkedService = at(checkedPos);
+    if (!checkedService) // should not happen but be robust
+        return;
+
+    for (Pos p = checkedPos + 1; has(p); ++p) {
+        ServicePointer s = at(p);
+        if (s != NULL && s->cfg().key == checkedService->cfg().key)
+            finalizeMsg("duplicate service name", s->cfg().key, false);
+        else
+        if (s != NULL && s->cfg().uri == checkedService->cfg().uri)
+            finalizeMsg("duplicate service URI", s->cfg().uri, false);
+    }
+}
 
-Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set")
+/// emits a formatted warning or error message at the appropriate dbg level
+void
+Adaptation::ServiceGroup::finalizeMsg(const char *msg, const String &culprit,
+    bool error) const
 {
+    const int level = error ? DBG_CRITICAL : DBG_IMPORTANT;
+    const char *pfx = error ? "ERROR: " : "WARNING: ";
+    debugs(93,level, pfx << msg << ' ' << culprit << " in " << kind << " '" <<
+        id << "'");
+}
+
+Adaptation::ServicePointer Adaptation::ServiceGroup::at(const Pos pos) const {
+    return FindService(services[pos]);
 }
 
-Adaptation::ServiceGroup::Loop Adaptation::ServiceSet::initialServices()
+/// \todo: optimize to cut search short instead of looking for the best svc
+bool
+Adaptation::ServiceGroup::wants(const ServiceFilter &filter) const
 {
-    return Loop(services.begin(), services.end());
+    Pos pos = 0;
+    return findService(filter, pos);
 }
 
-#if FUTURE_OPTIMIZATION
-void
-Adaptation::ServiceSet::finalize()
+bool
+Adaptation::ServiceGroup::findService(const ServiceFilter &filter, Pos &pos) const
 {
-    ServiceGroup::finalize();
+    if (method != filter.method || point != filter.point) {
+        debugs(93,5,HERE << id << " serves another location");
+        return false; // assume other services have the same wrong location
+    }
 
-    for (wordlist *iter = service_names; iter; iter = iter->next) {
-        ServicePointer match = Config::FindService(iter->id);
-        if (match != NULL)
-            services += match;
+    // find the next interested service, skipping problematic ones if possible
+    bool foundEssential = false;
+    Pos essPos = 0;
+    for (; has(pos); ++pos) {
+        debugs(93,9,HERE << id << " checks service at " << pos);
+        ServicePointer service = at(pos);
+
+        if (!service)
+            continue; // the service was lost due to reconfiguration
+
+        if (!service->wants(filter))
+            continue; // the service is not interested
+
+        if (service->up() || !service->probed()) {
+            debugs(93,9,HERE << id << " has matching service at " << pos);
+            return true;
+        }
+
+        if (service->cfg().bypass) { // we can safely ignore bypassable downers
+            debugs(93,9,HERE << id << " has bypassable service at " << pos);
+            continue;
+        }
+
+        if (!allServicesSame) { // cannot skip (i.e., find best) service
+            debugs(93,9,HERE << id << " has essential service at " << pos);
+            return true;
+        }
+
+        if (!foundEssential) {
+            debugs(93,9,HERE << id << " searches for best essential service from " << pos);
+            foundEssential = true;
+            essPos = pos;
+        }
     }
+
+    if (foundEssential) {
+        debugs(93,9,HERE << id << " has best essential service at " << essPos);
+        pos = essPos;
+        return true;
+    }
+
+    debugs(93,5,HERE << id << " has no matching services");
+    return false;
+}
+
+bool
+Adaptation::ServiceGroup::findReplacement(const ServiceFilter &filter, Pos &pos) const
+{
+    return allServicesSame && findService(filter, pos);
+}
+
+bool
+Adaptation::ServiceGroup::findLink(const ServiceFilter &filter, Pos &pos) const
+{
+    return !allServicesSame && findService(filter, pos);
+}
+
+
+/* ServiceSet */
+
+Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set", true)
+{
 }
-#endif
 
 
 /* SingleService */
 
 Adaptation::SingleService::SingleService(const String &aServiceId):
-        ServiceGroup("single-service group")
+        ServiceGroup("single-service group", false)
 {
     id = aServiceId;
     services.push_back(aServiceId);
 }
 
-Adaptation::ServiceGroup::Loop
-Adaptation::SingleService::initialServices()
+
+/* ServiceChain */
+
+Adaptation::ServiceChain::ServiceChain(): ServiceGroup("adaptation chain", false)
+{
+}
+
+
+/* ServiceChain */
+
+Adaptation::DynamicServiceChain::DynamicServiceChain(const String &ids,
+    const ServiceGroupPointer prev)
+{
+    kind = "dynamic adaptation chain"; // TODO: optimize by using String const
+    id = ids; // use services ids as the dynamic group ID
+
+    // initialize cache to improve consistency checks in finalize()
+    if (prev != NULL) {
+        method = prev->method;
+        point = prev->point;
+    }
+
+    // populate services storage with supplied service ids
+    const char *item = NULL;
+    int ilen = 0;
+    const char *pos = NULL;
+    while (strListGetItem(&ids, ',', &item, &ilen, &pos))
+        services.push_back(item);
+
+    finalize(); // will report [dynamic] config errors
+}
+
+/* ServicePlan */
+
+Adaptation::ServicePlan::ServicePlan(): pos(0), atEof(true)
 {
-    return Loop(services.begin(), services.end()); // there should be only one
+}
+
+Adaptation::ServicePlan::ServicePlan(const ServiceGroupPointer &g,
+    const ServiceFilter &filter):
+    group(g), pos(0), atEof(!g || !g->has(pos))
+{
+    // this will find the first service because starting pos is zero
+    if (!atEof && !group->findService(filter, pos))
+        atEof = true;
+}
+
+Adaptation::ServicePointer
+Adaptation::ServicePlan::current() const
+{
+    // may return NULL even if not atEof
+    return atEof ? Adaptation::ServicePointer() : group->at(pos);
+}
+
+Adaptation::ServicePointer
+Adaptation::ServicePlan::replacement(const ServiceFilter &filter) {
+    if (!atEof && !group->findReplacement(filter, ++pos))
+        atEof = true;
+    return current();
+}
+
+Adaptation::ServicePointer
+Adaptation::ServicePlan::next(const ServiceFilter &filter) {
+    if (!atEof && !group->findLink(filter, ++pos))
+        atEof = true;
+    return current();
+}
+
+std::ostream &
+Adaptation::ServicePlan::print(std::ostream &os) const
+{
+    if (!group)
+        return os << "[nil]";
+
+    return os << group->id << '[' << pos << ".." << group->services.size() <<
+        (atEof ? ".]" : "]");
 }
 
 
@@ -91,7 +290,7 @@
     return TheGroups;
 }
 
-Adaptation::ServiceGroup *
+Adaptation::ServiceGroupPointer
 Adaptation::FindGroup(const ServiceGroup::Id &id)
 {
     typedef Groups::iterator GI;
diff -u -r -N squid-3.1.0.10/src/adaptation/ServiceGroups.h squid-3.1.0.11/src/adaptation/ServiceGroups.h
--- squid-3.1.0.10/src/adaptation/ServiceGroups.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/ServiceGroups.h	2009-07-19 17:11:13.000000000 +1200
@@ -3,6 +3,8 @@
 
 #include "SquidString.h"
 #include "Array.h"
+#include "RefCount.h"
+#include "adaptation/Elements.h"
 #include "adaptation/forward.h"
 
 namespace Adaptation
@@ -10,36 +12,55 @@
 
 // Interface for grouping adaptation services together.
 // Specific groups differ in how the first and the next services are selected
-class ServiceGroup
+class ServiceGroup: public RefCountable
 {
 public:
+    typedef RefCount<ServiceGroup> Pointer;
+
     typedef Vector<String> Store;
-    typedef Store::iterator iterator;
     typedef String Id;
-
-    // Information sufficient to iterate services stored in the group,
-    // grouped together to simplify initial/sequentialServices interfaces.
-    // The iterators point back to
-    struct Loop {
-        Loop(const iterator &b, const iterator &e): begin(b), end(e) {}
-        iterator begin;
-        iterator end;
-    };
+    typedef unsigned int Pos; // Vector<>::poistion_type
+    friend class ServicePlan;
 
 public:
-    ServiceGroup(const String &aKind);
+    ServiceGroup(const String &aKind, bool areAllServicesSame);
     virtual ~ServiceGroup();
 
     virtual void parse();
     virtual void finalize(); // called after all are parsed
 
-    virtual Loop initialServices() = 0;
-    // TODO: virtual Loop sequentialServices() = 0;
+    bool wants(const ServiceFilter &filter) const;
+
+protected:
+    ///< whether this group has a service at the specified pos
+    bool has(const Pos pos) const {
+        // does not check that the service at pos still exists
+        return pos < services.size(); // unsigned pos is never negative
+    }
+
+    /// these methods control group iteration; used by ServicePlan
+
+    /// find next to try after failure, starting with pos
+    bool findReplacement(const ServiceFilter &filter, Pos &pos) const;
+    /// find next to link after success, starting with pos
+    bool findLink(const ServiceFilter &filter, Pos &pos) const;
+
+private:
+    ServicePointer at(const Pos pos) const;
+    bool findService(const ServiceFilter &filter, Pos &pos) const;
+
+    void checkUniqueness(const Pos checkedPos) const;
+    void finalizeMsg(const char *msg, const String &culprit, bool error) const;
 
 public:
     String kind;
     Id id;
     Store services;
+
+    Method method; /// based on the first added service
+    VectPoint point; /// based on the first added service
+
+    const bool allServicesSame; // whether we can freely substitute services
 };
 
 // a group of equivalent services; one service per set is usually used
@@ -47,7 +68,10 @@
 {
 public:
     ServiceSet();
-    virtual Loop initialServices();
+
+protected:
+    virtual bool replace(Pos &pos) const { return has(++pos); }
+    virtual bool advance(Pos &pos) const { return false; }
 };
 
 // corner case: a group consisting of one service
@@ -55,16 +79,66 @@
 {
 public:
     SingleService(const String &aServiceKey);
-    virtual Loop initialServices();
+
+protected:
+    virtual bool replace(Pos &pos) const { return false; } 
+    virtual bool advance(Pos &pos) const { return false; }
 };
 
-// TODO: a group of services that must be used one after another
-// class ServiceChain: public ServiceGroup
+/// a group of services that must be used one after another
+class ServiceChain: public ServiceGroup
+{
+public:
+    ServiceChain();
+
+protected:
+    virtual bool replace(Pos &pos) const { return false; } 
+    virtual bool advance(Pos &pos) const { return has(++pos); }
+};
 
+/// a temporary service chain built upon another service request
+class DynamicServiceChain: public ServiceChain
+{
+public:
+    DynamicServiceChain(const String &srvcs, const ServiceGroupPointer prev);
+};
+
+
+/** iterates services stored in a group; iteration is not linear because we
+    need to both replace failed services and advance to the next chain link */
+class ServicePlan {
+public:
+    typedef unsigned int Pos; // Vector<>::poistion_type
+
+public:
+    ServicePlan();
+    explicit ServicePlan(const ServiceGroupPointer &g, const ServiceFilter &filter);
+
+    ///< true iff there are no more services planned
+    bool exhausted() const { return atEof; }
+
+    /// returns nil if the plan is complete
+    ServicePointer current() const; ///< current service
+    ServicePointer replacement(const ServiceFilter &filter); ///< next to try after failure
+    ServicePointer next(const ServiceFilter &filter); ///< next in chain after success
+
+    std::ostream &print(std::ostream &os) const;
+
+private:
+    ServiceGroupPointer group; ///< the group we are iterating
+    Pos pos; ///< current service position within the group
+    bool atEof; ///< cached information for better performance
+};
+
+inline
+std::ostream &operator <<(std::ostream &os, const ServicePlan &p)
+{
+    return p.print(os);
+}
 
-typedef Vector<Adaptation::ServiceGroup*> Groups;
+typedef Vector<ServiceGroupPointer> Groups;
 extern Groups &AllGroups();
-extern ServiceGroup *FindGroup(const ServiceGroup::Id &id);
+extern ServiceGroupPointer FindGroup(const ServiceGroup::Id &id);
 
 
 } // namespace Adaptation
diff -u -r -N squid-3.1.0.10/src/adaptation/Service.h squid-3.1.0.11/src/adaptation/Service.h
--- squid-3.1.0.10/src/adaptation/Service.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/adaptation/Service.h	2009-07-19 17:11:13.000000000 +1200
@@ -39,6 +39,8 @@
     typedef void Callback(void *data, Pointer &service);
     void callWhenReady(Callback *cb, void *data);
 
+    bool wants(const ServiceFilter &filter) const;
+
     // the methods below can only be called on an up() service
     virtual bool wantsUrl(const String &urlPath) const = 0;
 
diff -u -r -N squid-3.1.0.10/src/cache_cf.cc squid-3.1.0.11/src/cache_cf.cc
--- squid-3.1.0.10/src/cache_cf.cc	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/cache_cf.cc	2009-07-19 17:11:13.000000000 +1200
@@ -65,6 +65,7 @@
 #include "adaptation/Config.h"
 
 static void parse_adaptation_service_set_type();
+static void parse_adaptation_service_chain_type();
 static void parse_adaptation_access_type();
 
 #endif
@@ -3432,6 +3433,10 @@
         cl->type = CLF_SQUID;
     } else if (strcmp(logdef_name, "common") == 0) {
         cl->type = CLF_COMMON;
+#if ICAP_CLIENT
+    } else if (strcmp(logdef_name, "icap_squid") == 0) {
+        cl->type = CLF_ICAP_SQUID;
+#endif
     } else {
         debugs(3, 0, "Log format '" << logdef_name << "' is not defined");
         self_destruct();
@@ -3484,7 +3489,11 @@
         case CLF_COMMON:
             storeAppendPrintf(entry, "%s squid", log->filename);
             break;
-
+#if ICAP_CLIENT
+        case CLF_ICAP_SQUID:
+            storeAppendPrintf(entry, "%s icap_squid", log->filename);
+            break;
+#endif
         case CLF_AUTO:
 
             if (log->aclList)
@@ -3545,6 +3554,12 @@
 }
 
 static void
+parse_adaptation_service_chain_type()
+{
+    Adaptation::Config::ParseServiceChain();
+}
+
+static void
 parse_adaptation_access_type()
 {
     Adaptation::Config::ParseAccess(LegacyParser);
diff -u -r -N squid-3.1.0.10/src/cf.data.depend squid-3.1.0.11/src/cf.data.depend
--- squid-3.1.0.10/src/cf.data.depend	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/cf.data.depend	2009-07-19 17:11:13.000000000 +1200
@@ -25,8 +25,9 @@
 http_header_replace
 http_port_list
 https_port_list
-adaptation_access_type	adaptation_service_set acl icap_service icap_class
-adaptation_service_set_type	icap_service
+adaptation_access_type	adaptation_service_set adaptation_service_chain acl icap_service icap_class
+adaptation_service_set_type	icap_service ecap_service
+adaptation_service_chain_type	icap_service ecap_service
 icap_access_type	icap_class acl
 icap_class_type		icap_service
 icap_service_type
diff -u -r -N squid-3.1.0.10/src/cf.data.pre squid-3.1.0.11/src/cf.data.pre
--- squid-3.1.0.10/src/cf.data.pre	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/cf.data.pre	2009-07-19 17:11:13.000000000 +1200
@@ -2365,6 +2365,7 @@
 
 	Format codes:
 
+		%	a literal % character
 		>a	Client source IP address
 		>A	Client FQDN
 		>p	Client source port
@@ -2374,42 +2375,96 @@
 		ts	Seconds since epoch
 		tu	subsecond time (milliseconds)
 		tl	Local time. Optional strftime format argument
-			default %d/%b/%Y:%H:%M:%S %z
+				default %d/%b/%Y:%H:%M:%S %z
 		tg	GMT time. Optional strftime format argument
-			default %d/%b/%Y:%H:%M:%S %z
+				default %d/%b/%Y:%H:%M:%S %z
 		tr	Response time (milliseconds)
-		>h	Request header. Optional header name argument
-			on the format header[:[separator]element]
-		<h	Reply header. Optional header name argument
-			as for >h
-		un	User name
-		ul	User name from authentication
-		ui	User name from ident
-		us	User name from SSL
-		ue	User name from external acl helper
-		Hs	HTTP status code
-		Ss	Squid request status (TCP_MISS etc)
-		Sh	Squid hierarchy status (DEFAULT_PARENT etc)
-		mt	MIME content type
-		rm	Request method (GET/POST etc)
-		ru	Request URL
-		rp	Request URL-Path excluding hostname
-		rv	Request protocol version
-		et	Tag returned by external acl
-		ea	Log string returned by external acl
-		<st	Reply size including HTTP headers
-		>st	Request size including HTTP headers
-		st	Request+Reply size including HTTP headers
-		<sH	Reply high offset sent
-		<sS	Upstream object size
-		%	a literal % character
+		dt	Total time spent making DNS lookups (milliseconds)
+
+	HTTP cache related format codes:
+
+		[http::]>h	Request header. Optional header name argument
+				on the format header[:[separator]element]
+		[http::]<h	Reply header. Optional header name argument
+				as for >h
+		[http::]un	User name
+		[http::]ul	User name from authentication
+		[http::]ui	User name from ident
+		[http::]us	User name from SSL
+		[http::]ue	User name from external acl helper
+		[http::]>Hs	HTTP status code sent to the client
+		[http::]<Hs	HTTP status code received from the next hop
+		[http::]Ss	Squid request status (TCP_MISS etc)
+		[http::]Sh	Squid hierarchy status (DEFAULT_PARENT etc)
+		[http::]mt	MIME content type
+		[http::]rm	Request method (GET/POST etc)
+		[http::]ru	Request URL
+		[http::]rp	Request URL-Path excluding hostname
+		[http::]rv	Request protocol version
+		[http::]et	Tag returned by external acl
+		[http::]ea	Log string returned by external acl
+		[http::]<st	Sent reply size including HTTP headers
+		[http::]>st	Received request size including HTTP headers. In the
+				case of chunked requests the chunked encoding metadata
+				are not included
+		[http::]>sh	Received HTTP request headers size
+		[http::]<sh	Sent HTTP reply headers size
+		[http::]st	Request+Reply size including HTTP headers
+		[http::]<sH	Reply high offset sent
+		[http::]<sS	Upstream object size
+		[http::]<pt	Peer response time in milliseconds. The timer starts
+				when the last request byte is sent to the next hop
+				and stops when the last response byte is received.
+		[http::]<tt	Total server-side time in milliseconds. The timer 
+				starts with the first connect request (or write I/O)
+				sent to the first selected peer. The timer stops
+				with the last I/O with the last peer.
+
+	If ICAP is enabled, the following two codes become available (as
+	well as ICAP log codes documented with the icap_log option):
+
+		icap::tt        Total ICAP processing time for the HTTP
+				transaction. The timer ticks when ICAP
+				ACLs are checked and when ICAP
+				transaction is in progress.
+
+		icap::<last_h	The header of the last ICAP response
+				related to the HTTP transaction. Like
+				<h, accepts an optional header name
+				argument.  Will not change semantics
+				when multiple ICAP transactions per HTTP
+				transaction are supported.
+
+	If adaptation is enabled the following two codes become available:
+
+		adapt::sum_trs Summed adaptation transaction response
+				times recorded as a comma-separated list in
+				the order of transaction start time. Each time
+				value is recorded as an integer number,
+				representing response time of one or more
+				adaptation (ICAP or eCAP) transaction in
+				milliseconds.  When a failed transaction is
+				being retried or repeated, its time is not
+				logged individually but added to the
+				replacement (next) transaction. See also:
+				adapt::all_trs.
+
+		adapt::all_trs All adaptation transaction response times.
+				Same as adaptation_strs but response times of
+				individual transactions are never added
+				together. Instead, all transaction response
+				times are recorded individually.
+
+	You can prefix adapt::*_trs format codes with adaptation
+	service name in curly braces to record response time(s) specific
+	to that service. For example: %{my_service}adapt::sum_trs
 
 	The default formats available (which do not need re-defining) are:
 
-logformat squid %ts.%03tu %6tr %>a %Ss/%03Hs %<st %rm %ru %un %Sh/%<A %mt
-logformat squidmime %ts.%03tu %6tr %>a %Ss/%03Hs %<st %rm %ru %un %Sh/%<A %mt [%>h] [%<h]
-logformat common %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %Hs %<st %Ss:%Sh
-logformat combined %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
+logformat squid %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %un %Sh/%<A %mt
+logformat squidmime %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %un %Sh/%<A %mt [%>h] [%<h]
+logformat common %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st %Ss:%Sh
+logformat combined %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
 DOC_END
 
 NAME: access_log cache_access_log
@@ -2444,6 +2499,88 @@
 		access_log @DEFAULT_ACCESS_LOG@ squid
 DOC_END
 
+NAME: icap_log
+TYPE: access_log
+IFDEF: ICAP_CLIENT
+LOC: Config.Log.icaplogs
+DEFAULT: none
+DOC_START
+	ICAP log files record ICAP transaction summaries, one line per
+	transaction.
+
+	The icap_log option format is:
+	icap_log <filepath> [<logformat name> [acl acl ...]]
+	icap_log none [acl acl ...]]
+	
+	Please see access_log option documentation for details. The two
+	kinds of logs share the overall configuration approach and many
+	features.
+
+	ICAP processing of a single HTTP message or transaction may
+	require multiple ICAP transactions.  In such cases, multiple
+	ICAP transaction log lines will correspond to a single access
+	log line.
+
+	ICAP log uses logformat codes that make sense for an ICAP
+	transaction. Header-related codes are applied to the HTTP header
+	embedded in an ICAP server response, with the following caveats:
+	For REQMOD, there is no HTTP response header unless the ICAP
+	server performed request satisfaction. For RESPMOD, the HTTP
+	request header is the header sent to the ICAP server. For
+	OPTIONS, there are no HTTP headers.
+
+	The following format codes are also available for ICAP logs:
+
+		icap::<A	ICAP server IP address. Similar to <A.
+
+		icap::<service_name	ICAP service name from the icap_service
+				option in Squid configuration file.
+
+		icap::ru	ICAP Request-URI. Similar to ru.
+
+		icap::rm	ICAP request method (REQMOD, RESPMOD, or 
+				OPTIONS). Similar to existing rm.
+
+		icap::>st	Bytes sent to the ICAP server (TCP payload
+				only; i.e., what Squid writes to the socket).
+
+		icap::<st	Bytes received from the ICAP server (TCP
+				payload only; i.e., what Squid reads from
+				the socket).
+
+		icap::tr 	Transaction response time (in
+				milliseconds).  The timer starts when
+				the ICAP transaction is created and
+				stops when the transaction is completed.
+				Similar to tr.
+
+		icap::tio	Transaction I/O time (in milliseconds). The
+				timer starts when the first ICAP request
+				byte is scheduled for sending. The timers
+				stops when the last byte of the ICAP response
+				is received.
+
+		icap::to 	Transaction outcome: ICAP_ERR* for all
+				transaction errors, ICAP_OPT for OPTION
+				transactions, ICAP_ECHO for 204
+				responses, ICAP_MOD for message
+				modification, and ICAP_SAT for request
+				satisfaction. Similar to Ss.
+
+		icap::Hs	ICAP response status code. Similar to Hs.
+
+		icap::>h	ICAP request header(s). Similar to >h.
+
+		icap::<h	ICAP response header(s). Similar to <h.
+
+	The default ICAP log format, which can be used without an explicit
+	definition, is called icap_squid:
+
+logformat icap_squid %ts.%03tu %6icap::tr %>a %icap::to/%03icap::Hs %icap::<size %icap::rm %icap::ru% %un -/%icap::<A -
+
+	See also: logformat, log_icap, and %icap::<last_h 
+DOC_END
+
 NAME: log_access
 TYPE: acl_access
 LOC: Config.accessList.log
@@ -2458,6 +2595,16 @@
 	See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
 DOC_END
 
+NAME: log_icap
+TYPE: acl_access
+IFDEF: ICAP_CLIENT
+LOC: Config.accessList.icap
+DEFAULT: none
+DOC_START
+	This options allows you to control which requests get logged
+	to icap.log. See the icap_log directive for ICAP log details.
+DOC_END
+
 NAME: cache_store_log
 TYPE: string
 DEFAULT: none
@@ -3335,6 +3482,33 @@
 	be no limit imposed.
 DOC_END
 
+NAME: chunked_request_body_max_size
+COMMENT: (bytes)
+TYPE: b_int64_t
+DEFAULT: 64 KB
+LOC: Config.maxChunkedRequestBodySize
+DOC_START
+	A broken or confused HTTP/1.1 client may send a chunked HTTP
+	request to Squid. Squid does not have full support for that
+	feature yet. To cope with such requests, Squid buffers the
+	entire request and then dechunks request body to create a
+	plain HTTP/1.0 request with a known content length. The plain
+	request is then used by the rest of Squid code as usual.
+
+	The option value specifies the maximum size of the buffer used
+	to hold the request before the conversion. If the chunked
+	request size exceeds the specified limit, the conversion
+	fails, and the client receives an "unsupported request" error,
+	as if dechunking was disabled.
+
+	Dechunking is enabled by default. To disable conversion of
+	chunked requests, set the maximum to zero.
+
+	Request dechunking feature and this option in particular are a
+	temporary hack. When chunking requests and responses are fully
+	supported, there will be no need to buffer a chunked request.
+DOC_END
+
 NAME: broken_posts
 IFDEF: HTTP_VIOLATIONS
 TYPE: acl_access
@@ -5462,27 +5636,63 @@
 LOC: Adaptation::Icap::TheConfig
 DEFAULT: none
 DOC_START
-	Defines a single ICAP service
+	Defines a single ICAP service using the following format:
 
-	icap_service servicename vectoring_point bypass service_url
+	icap_service service_name vectoring_point [options] service_url
 
-	vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache
+	service_name: ID
+		an opaque identifier which must be unique in squid.conf
+
+	vectoring_point: reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache
 		This specifies at which point of transaction processing the
 		ICAP service should be activated. *_postcache vectoring points
 		are not yet supported.
-	bypass = 1|0
-		If set to 1, the ICAP service is treated as optional. If the
-		service cannot be reached or malfunctions, Squid will try to
-		ignore any errors and process the message as if the service
-		was not enabled. No all ICAP errors can be bypassed.
-		If set to 0, the ICAP service is treated as essential and all
-		ICAP errors will result in an error page returned to the
-		HTTP client.
-	service_url = icap://servername:port/service
+
+	service_url: icap://servername:port/servicepath
+		ICAP server and service location.
+
+	ICAP does not allow a single service to handle both REQMOD and RESPMOD
+	transactions. Squid does not enforce that requirement. You can specify
+	services with the same service_url and different vectoring_points. You
+	can even specify multiple identical services as long as their
+	service_names differ.
+
+
+	Service options are separated by white space. ICAP services support
+	the following name=value options:
+
+	bypass=on|off|1|0
+		If set to 'on' or '1', the ICAP service is treated as
+		optional. If the service cannot be reached or malfunctions,
+		Squid will try to ignore any errors and process the message as
+		if the service was not enabled. No all ICAP errors can be
+		bypassed.  If set to 0, the ICAP service is treated as
+		essential and all ICAP errors will result in an error page
+		returned to the HTTP client.
+
+		Bypass is off by default: services are treated as essential.
+
+	routing=on|off|1|0
+		If set to 'on' or '1', the ICAP service is allowed to
+		dynamically change the current message adaptation plan by
+		returning a chain of services to be used next. The services
+		are specified using the X-Next-Services ICAP response header
+		value, formatted as a comma-separated list of service names.
+		Each named service should be configured in squid.conf and
+		should have the same method and vectoring point as the current
+		ICAP transaction.  Services violating these rules are ignored.
+		An empty X-Next-Services value results in an empty plan which
+		ends the current adaptation. 
+
+		Routing is not allowed by default: the ICAP X-Next-Services
+		response header is ignored.
+
+	Older icap_service format without optional named parameters is
+	deprecated but supported for backward compatibility.
 
 Example:
-icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod
-icap_service service_2 respmod_precache 0 icap://icap2.mydomain.net:1344/respmod
+icap_service svcBlocker reqmod_precache bypass=0 icap://icap1.mydomain.net:1344/reqmod
+icap_service svcLogger reqmod_precache routing=on icap://icap2.mydomain.net:1344/respmod
 DOC_END
 
 NAME: icap_class
@@ -5491,15 +5701,13 @@
 LOC: none
 DEFAULT: none
 DOC_START
-	This depricated option was documented to define an ICAP service
+	This deprecated option was documented to define an ICAP service
 	chain, even though it actually defined a set of similar, redundant
 	services, and the chains were not supported. 
 
 	To define a set of redundant services, please use the
-	adaptation_service_set directive.
-
-	If you need adaptation service chains, patches or sponsorship
-	is welcome.
+	adaptation_service_set directive. For service chains, use
+	adaptation_service_chain.
 DOC_END
 
 NAME: icap_access
@@ -5508,7 +5716,7 @@
 LOC: none
 DEFAULT: none
 DOC_START
-        This option is depricated. Please use adaptation_access, which
+	This option is deprecated. Please use adaptation_access, which
 	has the same ICAP functionality, but comes with better
 	documentation, and eCAP support.
 DOC_END
@@ -5581,24 +5789,81 @@
 DEFAULT: none
 DOC_START
 
-	Defines a named adaptation service set. The set is populated in
-	the order of adaptation_service_set directives in this file.
-	When adaptation ACLs are processed, the first and only the first
-	applicable adaptation service from the set will be used. Thus,
-	the set should group similar, redundant services, rather than a
-	chain of complementary services.
-
-	If you have a single adaptation service, you do not need to
-	define a set containing it because adaptation_access accepts
-	service names.
+	Configures an ordered set of similar, redundant services. This is
+	useful when hot standby or backup adaptation servers are available.
+
+	    adaptation_service_set set_name service_name1 service_name2 ...
+
+ 	The named services are used in the set declaration order. The first
+	applicable adaptation service from the set is used first. The next
+	applicable service is tried if and only if the transaction with the
+	previous service fails and the message waiting to be adapted is still
+	intact.
+
+	When adaptation starts, broken services are ignored as if they were
+	not a part of the set. A broken service is a down optional service.
+
+	The services in a set must be attached to the same vectoring point
+	(e.g., pre-cache) and use the same adaptation method (e.g., REQMOD).
+
+	If all services in a set are optional then adaptation failures are
+	bypassable. If all services in the set are essential, then a
+	transaction failure with one service may still be retried using
+	another service from the set, but when all services fail, the master
+	transaction fails as well.
+
+	A set may contain a mix of optional and essential services, but that
+	is likely to lead to surprising results because broken services become
+	ignored (see above), making previously bypassable failures fatal.
+	Technically, it is the bypassability of the last failed service that
+	matters.
 
-	See also: adaptation_access
+	See also: adaptation_access adaptation_service_chain
 
 Example:
 adaptation_service_set svcBlocker urlFilterPrimary urlFilterBackup
 adaptation service_set svcLogger loggerLocal loggerRemote
 DOC_END
 
+NAME: adaptation_service_chain
+TYPE: adaptation_service_chain_type
+IFDEF: USE_ADAPTATION
+LOC: none
+DEFAULT: none
+DOC_START
+
+	Configures a list of complementary services that will be applied
+	one-by-one, forming an adaptation chain or pipeline. This is useful
+	when Squid must perform different adaptations on the same message.
+
+	    adaptation_service_chain chain_name service_name1 svc_name2 ...
+
+ 	The named services are used in the chain declaration order. The first
+	applicable adaptation service from the chain is used first. The next
+	applicable service is applied to the successful adaptation results of
+	the previous service in the chain.
+
+	When adaptation starts, broken services are ignored as if they were
+	not a part of the chain. A broken service is a down optional service.
+
+	Request satisfaction terminates the adaptation chain because Squid
+	does not currently allow declaration of RESPMOD services at the
+	"reqmod_precache" vectoring point (see icap_service or ecap_service).
+
+	The services in a chain must be attached to the same vectoring point
+	(e.g., pre-cache) and use the same adaptation method (e.g., REQMOD).
+
+	A chain may contain a mix of optional and essential services. If an
+	essential adaptation fails (or the failure cannot be bypassed for
+	other reasons), the master transaction fails. Otherwise, the failure
+	is bypassed as if the failed adaptation service was not in the chain.
+
+	See also: adaptation_access adaptation_service_set
+
+Example:
+adaptation_service_chain svcRequest requestLogger urlFilter leakDetector
+DOC_END
+
 NAME: adaptation_access
 TYPE: adaptation_access_type
 IFDEF: USE_ADAPTATION
@@ -5638,6 +5903,89 @@
 adaptation_access service_1 allow all
 DOC_END
 
+NAME: adaptation_service_iteration_limit
+TYPE: int
+IFDEF: USE_ADAPTATION
+LOC: Adaptation::Config::service_iteration_limit
+DEFAULT: 16
+DOC_START
+	Limits the number of iterations allowed when applying adaptation
+	services to a message. If your longest adaptation set or chain
+	may have more than 16 services, increase the limit beyond its
+	default value of 16. If detecting infinite iteration loops sooner
+	is critical, make the iteration limit match the actual number
+	of services in your longest adaptation set or chain.
+
+	Infinite adaptation loops are most likely with routing services.
+
+	See also: icap_service routing=1
+DOC_END
+
+NAME: adaptation_masterx_shared_names
+TYPE: string
+IFDEF: USE_ADAPTATION
+LOC: Adaptation::Config::masterx_shared_name
+DEFAULT: none
+DOC_START
+	For each master transaction (i.e., the HTTP request and response
+	sequence, including all related ICAP and eCAP exchanges), Squid
+	maintains a table of metadata. The table entries are (name, value)
+	pairs shared among eCAP and ICAP exchanges. The table is destroyed
+	with the master transaction.
+
+	This option specifies the table entry names that Squid must accept
+	from and forward to the adaptation transactions.
+
+	An ICAP REQMOD or RESPMOD transaction may set an entry in the 
+	shared table by returning an ICAP header field with a name 
+	specified in adaptation_masterx_shared_names. Squid will store 
+	and forward that ICAP header field to subsequent ICAP 
+	transactions within the same master transaction scope.
+
+	Only one shared entry name is supported at this time.
+
+Example:
+# share authentication information among ICAP services
+adaptation_masterx_shared_names X-Subscriber-ID
+DOC_END
+
+NAME: icap_retry
+TYPE: acl_access
+IFDEF: ICAP_CLIENT
+LOC: Adaptation::Icap::TheConfig.repeat
+DEFAULT: none
+DEFAULT_IF_NONE: deny all
+DOC_START
+	This ACL determines which retriable ICAP transactions are
+	retried. Transactions that received a complete ICAP response
+	and did not have to consume or produce HTTP bodies to receive
+	that response are usually retriable.
+
+	icap_retry allow|deny [!]aclname ...
+
+	Squid automatically retries some ICAP I/O timeouts and errors
+	due to persistent connection race conditions.
+
+	See also: icap_retry_limit
+DOC_END
+
+NAME: icap_retry_limit
+TYPE: int
+IFDEF: ICAP_CLIENT
+LOC: Adaptation::Icap::TheConfig.repeat_limit
+DEFAULT: 0
+DOC_START
+	Limits the number of retries allowed. When set to zero (default),
+	no retries are allowed.
+
+	Communication errors due to persistent connection race
+	conditions are unavoidable, automatically retried, and do not
+	count against this limit.
+
+	See also: icap_retry
+DOC_END
+
+
 COMMENT_START
  DNS OPTIONS
  -----------------------------------------------------------------------------
diff -u -r -N squid-3.1.0.10/src/ClientRequestContext.h squid-3.1.0.11/src/ClientRequestContext.h
--- squid-3.1.0.10/src/ClientRequestContext.h	2009-07-18 16:16:15.000000000 +1200
+++ squid-3.1.0.11/src/ClientRequestContext.h	2009-07-19 17:11:08.000000000 +1200
@@ -34,7 +34,7 @@
 #if USE_ADAPTATION
 
     void adaptationAccessCheck();
-    void adaptationAclCheckDone(Adaptation::ServicePointer service);
+    void adaptationAclCheckDone(Adaptation::ServiceGroupPointer g);
 #endif
 
     ClientHttpRequest *http;
diff -u -r -N squid-3.1.0.10/src/client_side.cc squid-3.1.0.11/src/client_side.cc
--- squid-3.1.0.10/src/client_side.cc	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/client_side.cc	2009-07-19 17:11:13.000000000 +1200
@@ -103,6 +103,7 @@
 #include "ClientRequestContext.h"
 #include "MemBuf.h"
 #include "SquidTime.h"
+#include "ChunkedCodingParser.h"
 
 #if LINGERING_CLOSE
 #define comm_close comm_lingering_close
@@ -154,7 +155,7 @@
 static void clientUpdateStatCounters(log_type logType);
 static void clientUpdateHierCounters(HierarchyLogEntry *);
 static bool clientPingHasFinished(ping_data const *aPing);
-static void clientPrepareLogWithRequestDetails(HttpRequest *, AccessLogEntry *);
+void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry *);
 #ifndef PURIFY
 static int connIsUsable(ConnStateData * conn);
 #endif
@@ -444,11 +445,14 @@
 }
 
 void
-clientPrepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry * aLogEntry)
+prepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry * aLogEntry)
 {
     assert(request);
     assert(aLogEntry);
 
+#if ICAP_CLIENT   
+    Adaptation::Icap::History::Pointer ih = request->icapHistory();
+#endif
     if (Config.onoff.log_mime_hdrs) {
         Packer p;
         MemBuf mb;
@@ -456,14 +460,31 @@
         packerToMemInit(&p, &mb);
         request->header.packInto(&p);
         aLogEntry->headers.request = xstrdup(mb.buf);
+
+#if ICAP_CLIENT
+        packerClean(&p);
+        mb.reset();
+        packerToMemInit(&p, &mb);
+
+        if (ih != NULL)
+            ih->lastIcapHeader.packInto(&p);
+        aLogEntry->headers.icap = xstrdup(mb.buf);
+#endif
+
         packerClean(&p);
         mb.clean();
     }
 
+#if ICAP_CLIENT
+    if (ih != NULL)
+        aLogEntry->icap.processingTime = ih->processingTime();
+#endif
+
     aLogEntry->http.method = request->method;
     aLogEntry->http.version = request->http_ver;
     aLogEntry->hier = request->hier;
-    aLogEntry->cache.requestSize += request->content_length;
+    if (request->content_length > 0) // negative when no body or unknown length
+        aLogEntry->cache.requestSize += request->content_length;
     aLogEntry->cache.extuser = request->extacl_user.termedBuf();
 
     if (request->auth_user_request) {
@@ -502,8 +523,10 @@
         if (getConn() != NULL) al.cache.caddr = getConn()->log_addr;
 
         al.cache.requestSize = req_sz;
+        al.cache.requestHeadersSize = req_sz;
 
         al.cache.replySize = out.size;
+        al.cache.replyHeadersSize = out.headers_sz;
 
         al.cache.highOffset = out.offset;
 
@@ -512,7 +535,7 @@
         al.cache.msec = tvSubMsec(start_time, current_time);
 
         if (request)
-            clientPrepareLogWithRequestDetails(request, &al);
+            prepareLogWithRequestDetails(request, &al);
 
         if (getConn() != NULL && getConn()->rfc931[0])
             al.cache.rfc931 = getConn()->rfc931;
@@ -1873,6 +1896,18 @@
     }
 }
 
+// Temporary hack helper: determine whether the request is chunked, expensive
+static bool
+isChunkedRequest(const HttpParser *hp) {
+    HttpRequest request;
+    if (!request.parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp)))
+	return false;
+
+    return request.header.has(HDR_TRANSFER_ENCODING) &&
+	request.header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',');
+}
+
+
 /**
  *  parseHttpRequest()
  *
@@ -1885,7 +1920,6 @@
 static ClientSocketContext *
 parseHttpRequest(ConnStateData *conn, HttpParser *hp, HttpRequestMethod * method_p, HttpVersion *http_ver)
 {
-    char *url = NULL;
     char *req_hdr = NULL;
     char *end;
     size_t req_sz;
@@ -1952,17 +1986,6 @@
         return parseHttpRequestAbort(conn, "error:unsupported-request-method");
     }
 
-    /* set url */
-    /*
-     * XXX this should eventually not use a malloc'ed buffer; the transformation code
-     * below needs to be modified to not expect a mutable nul-terminated string.
-     */
-    url = (char *)xmalloc(hp->u_end - hp->u_start + 16);
-
-    memcpy(url, hp->buf + hp->u_start, hp->u_end - hp->u_start + 1);
-
-    url[hp->u_end - hp->u_start + 1] = '\0';
-
     /*
      * Process headers after request line
      * TODO: Use httpRequestParse here.
@@ -1982,7 +2005,6 @@
      */
     if ( squid_strnstr(req_hdr, "\r\r\n", req_sz) ) {
         debugs(33, 1, "WARNING: suspicious HTTP request contains double CR");
-        xfree(url);
         return parseHttpRequestAbort(conn, "error:double-CR");
     }
 
@@ -1990,6 +2012,35 @@
            (int) HttpParserRequestLen(hp) << ", req_line_sz = " <<
            HttpParserReqSz(hp));
 
+    // Temporary hack: We might receive a chunked body from a broken HTTP/1.1
+    // client that sends chunked requests to HTTP/1.0 Squid. If the request
+    // might have a chunked body, parse the headers early to look for the
+    // "Transfer-Encoding: chunked" header. If we find it, wait until the
+    // entire body is available so that we can set the content length and
+    // forward the request without chunks. The primary reason for this is
+    // to avoid forwarding a chunked request because the server side lacks
+    // logic to determine when it is valid to do so.
+    // FUTURE_CODE_TO_SUPPORT_CHUNKED_REQUESTS below will replace this hack.
+    if (hp->v_min == 1 && hp->v_maj == 1 && // broken client, may send chunks
+        Config.maxChunkedRequestBodySize > 0 && // configured to dechunk
+        (*method_p == METHOD_PUT || *method_p == METHOD_POST)) {
+
+        // check only once per request because isChunkedRequest is expensive
+        if (conn->in.dechunkingState == ConnStateData::chunkUnknown) {
+            if (isChunkedRequest(hp))
+                conn->startDechunkingRequest(hp);
+            else
+                conn->in.dechunkingState = ConnStateData::chunkNone;
+        }
+
+        if (conn->in.dechunkingState == ConnStateData::chunkParsing) {
+            if (conn->parseRequestChunks(hp)) // parses newly read chunks
+                return NULL; // wait for more data
+            debugs(33, 5, HERE << "Got complete chunked request or err.");
+            assert(conn->in.dechunkingState != ConnStateData::chunkParsing);
+        }
+    }
+
     /* Ok, all headers are received */
     http = new ClientHttpRequest(conn);
 
@@ -2006,6 +2057,17 @@
 
     debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp->buf) + hp->hdr_start);
 
+    /* set url */
+    /*
+     * XXX this should eventually not use a malloc'ed buffer; the transformation code
+     * below needs to be modified to not expect a mutable nul-terminated string.
+     */
+    char *url = (char *)xmalloc(hp->u_end - hp->u_start + 16);
+
+    memcpy(url, hp->buf + hp->u_start, hp->u_end - hp->u_start + 1);
+
+    url[hp->u_end - hp->u_start + 1] = '\0';
+
 #if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
 
     if ((t = strchr(url, '#')))	/* remove HTML anchors */
@@ -2157,6 +2219,11 @@
 int
 connKeepReadingIncompleteRequest(ConnStateData * conn)
 {
+    // when we read chunked requests, the entire body is buffered
+    // XXX: this check ignores header size and its limits.
+    if (conn->in.dechunkingState == ConnStateData::chunkParsing)
+        return conn->in.notYetUsed < Config.maxChunkedRequestBodySize;
+
     return conn->in.notYetUsed >= Config.maxRequestHeaderSize ? 0 : 1;
 }
 
@@ -2166,8 +2233,13 @@
     ClientSocketContext *context = parseHttpRequestAbort(conn, "error:request-too-large");
     clientStreamNode *node = context->getClientReplyContext();
     assert(!connKeepReadingIncompleteRequest(conn));
-    debugs(33, 1, "Request header is too large (" << conn->in.notYetUsed << " bytes)");
-    debugs(33, 1, "Config 'request_header_max_size'= " << Config.maxRequestHeaderSize << " bytes.");
+    if (conn->in.dechunkingState == ConnStateData::chunkParsing) {
+        debugs(33, 1, "Chunked request is too large (" << conn->in.notYetUsed << " bytes)");
+        debugs(33, 1, "Config 'chunked_request_body_max_size'= " << Config.maxChunkedRequestBodySize << " bytes.");
+    } else {
+        debugs(33, 1, "Request header is too large (" << conn->in.notYetUsed << " bytes)");
+        debugs(33, 1, "Config 'request_header_max_size'= " << Config.maxRequestHeaderSize << " bytes.");
+    }
     clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
     assert (repContext);
     repContext->setReplyToError(ERR_TOO_BIG,
@@ -2213,6 +2285,9 @@
     ClientHttpRequest *http = context->http;
     HttpRequest *request = NULL;
     bool notedUseOfBuffer = false;
+    bool tePresent = false;
+    bool deChunked = false;
+    bool unsupportedTe = false;
 
     /* We have an initial client stream in place should it be needed */
     /* setup our private context */
@@ -2313,8 +2388,17 @@
     request->my_addr = conn->me;
     request->http_ver = http_ver;
 
-    if (!urlCheckRequest(request) ||
-            request->header.has(HDR_TRANSFER_ENCODING)) {
+    tePresent = request->header.has(HDR_TRANSFER_ENCODING);
+    deChunked = conn->in.dechunkingState == ConnStateData::chunkReady;
+    if (deChunked) {
+        assert(tePresent);
+        request->setContentLength(conn->in.dechunked.contentSize());
+        request->header.delById(HDR_TRANSFER_ENCODING);
+        conn->finishDechunkingRequest(hp);
+    }
+
+    unsupportedTe = tePresent && !deChunked;
+    if (!urlCheckRequest(request) || unsupportedTe) {
         clientStreamNode *node = context->getClientReplyContext();
         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
         assert (repContext);
@@ -2648,13 +2732,74 @@
 {
     assert(bodyPipe != NULL);
 
-    if (const size_t putSize = bodyPipe->putMoreData(in.buf, in.notYetUsed))
-        connNoteUseOfBuffer(this, putSize);
+    size_t putSize = 0;    
 
-    if (!bodyPipe->mayNeedMoreData()) {
-        // BodyPipe will clear us automagically when we produced everything
-        bodyPipe = NULL;
+#if FUTURE_CODE_TO_SUPPORT_CHUNKED_REQUESTS
+   // The code below works, in principle, but we cannot do dechunking 
+   // on-the-fly because that would mean sending chunked requests to
+   // the next hop. Squid lacks logic to determine which servers can
+   // receive chunk requests. Squid v3.0 code cannot even handle chunked
+   // responses which we may encourage by sending chunked requests.
+   // The error generation code probably needs more work.
+    if (in.bodyParser) { // chunked body
+        debugs(33,5, HERE << "handling chunked request body for FD " << fd);
+        bool malformedChunks = false;
+
+        MemBuf raw; // ChunkedCodingParser only works with MemBufs
+        raw.init(in.notYetUsed, in.notYetUsed);
+        raw.append(in.buf, in.notYetUsed);
+        try { // the parser will throw on errors
+            const mb_size_t wasContentSize = raw.contentSize();
+            BodyPipeCheckout bpc(*bodyPipe);
+            const bool parsed = in.bodyParser->parse(&raw, &bpc.buf);
+            bpc.checkIn();
+            putSize = wasContentSize - raw.contentSize();
 
+            if (parsed) {
+                stopProducingFor(bodyPipe, true); // this makes bodySize known
+            } else {
+                // parser needy state must imply body pipe needy state
+                if (in.bodyParser->needsMoreData() &&
+                    !bodyPipe->mayNeedMoreData())
+                    malformedChunks = true;
+                // XXX: if bodyParser->needsMoreSpace, how can we guarantee it?
+            }
+        } catch (...) { // XXX: be more specific
+            malformedChunks = true;
+        }
+
+        if (malformedChunks) {
+            if (bodyPipe != NULL)
+                stopProducingFor(bodyPipe, false);
+
+            ClientSocketContext::Pointer context = getCurrentContext();
+            if (!context->http->out.offset) {
+                clientStreamNode *node = context->getClientReplyContext();
+                clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+                assert (repContext);
+                repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST,
+                    METHOD_NONE, NULL, &peer.sin_addr,
+                    NULL, NULL, NULL);
+                context->pullData();
+            }
+            flags.readMoreRequests = false;
+            return; // XXX: is that sufficient to generate an error?
+        }
+    } else // identity encoding 
+#endif
+    {
+        debugs(33,5, HERE << "handling plain request body for FD " << fd);
+        putSize = bodyPipe->putMoreData(in.buf, in.notYetUsed);
+        if (!bodyPipe->mayNeedMoreData()) {
+            // BodyPipe will clear us automagically when we produced everything
+            bodyPipe = NULL;
+        }
+    }
+
+    if (putSize > 0)
+        connNoteUseOfBuffer(this, putSize);
+
+    if (!bodyPipe) {
         debugs(33,5, HERE << "produced entire request body for FD " << fd);
 
         if (closing()) {
@@ -3435,19 +3580,121 @@
     bodyPipe->enableAutoConsumption();
 }
 
+// initialize dechunking state
+void
+ConnStateData::startDechunkingRequest(HttpParser *hp)
+{
+    debugs(33, 5, HERE << "start dechunking at " << HttpParserRequestLen(hp));
+    assert(in.dechunkingState == chunkUnknown);
+    assert(!in.bodyParser);
+    in.bodyParser = new ChunkedCodingParser;
+    in.chunkedSeen = HttpParserRequestLen(hp); // skip headers when dechunking
+    in.chunked.init();  // TODO: should we have a smaller-than-default limit?
+    in.dechunked.init();
+    in.dechunkingState = chunkParsing;
+}
+
+// put parsed content into input buffer and clean up
+void
+ConnStateData::finishDechunkingRequest(HttpParser *hp)
+{
+    debugs(33, 5, HERE << "finish dechunking; content: " << in.dechunked.contentSize());
+
+    assert(in.dechunkingState == chunkReady);
+    assert(in.bodyParser); 
+    delete in.bodyParser;
+    in.bodyParser = NULL;
+
+    const mb_size_t headerSize = HttpParserRequestLen(hp);
+
+    // dechunking cannot make data bigger
+    assert(headerSize + in.dechunked.contentSize() + in.chunked.contentSize()
+        <= static_cast<mb_size_t>(in.notYetUsed));
+    assert(in.notYetUsed <= in.allocatedSize);
+
+    // copy dechunked content
+    char *end = in.buf + headerSize;
+    xmemmove(end, in.dechunked.content(), in.dechunked.contentSize());
+    end += in.dechunked.contentSize();
+
+    // copy post-chunks leftovers, if any, caused by request pipelining?
+    if (in.chunked.contentSize()) {
+        xmemmove(end, in.chunked.content(), in.chunked.contentSize());
+        end += in.chunked.contentSize();
+    }
+
+    in.notYetUsed = end - in.buf;
+
+    in.chunked.clean();
+    in.dechunked.clean();
+    in.dechunkingState = chunkUnknown;
+}
+
+// parse newly read request chunks and buffer them for finishDechunkingRequest
+// returns true iff needs more data
+bool
+ConnStateData::parseRequestChunks(HttpParser *)
+{
+    debugs(33,5, HERE << "parsing chunked request body at " <<
+        in.chunkedSeen << " < " << in.notYetUsed);
+    assert(in.bodyParser);
+    assert(in.dechunkingState == chunkParsing);
+
+    assert(in.chunkedSeen <= in.notYetUsed);
+    const mb_size_t fresh = in.notYetUsed - in.chunkedSeen;
+
+    // be safe: count some chunked coding metadata towards the total body size
+    if (fresh + in.dechunked.contentSize() > Config.maxChunkedRequestBodySize) {
+        debugs(33,3, HERE << "chunked body (" << fresh << " + " <<
+            in.dechunked.contentSize() << " may exceed " <<
+            "chunked_request_body_max_size=" <<
+            Config.maxChunkedRequestBodySize);
+        in.dechunkingState = chunkError;
+        return false;
+    }
+        
+    if (fresh > in.chunked.potentialSpaceSize()) {
+        // should not happen if Config.maxChunkedRequestBodySize is reasonable
+        debugs(33,1, HERE << "request_body_max_size exceeds chunked buffer " <<
+            "size: " << fresh << " + " << in.chunked.contentSize() << " > " <<
+            in.chunked.potentialSpaceSize() << " with " <<
+            "chunked_request_body_max_size=" <<
+            Config.maxChunkedRequestBodySize);
+        in.dechunkingState = chunkError;
+        return false;
+    }
+    in.chunked.append(in.buf + in.chunkedSeen, fresh);
+    in.chunkedSeen += fresh;
+
+    try { // the parser will throw on errors
+        if (in.bodyParser->parse(&in.chunked, &in.dechunked))
+            in.dechunkingState = chunkReady; // successfully parsed all chunks
+        else
+            return true; // need more, keep the same state
+    } catch (...) {
+        debugs(33,3, HERE << "chunk parsing error");
+        in.dechunkingState = chunkError;
+    }
+    return false; // error, unsupported, or done
+}
+
 char *
 ConnStateData::In::addressToReadInto() const
 {
     return buf + notYetUsed;
 }
 
-ConnStateData::In::In() : buf (NULL), notYetUsed (0), allocatedSize (0)
+ConnStateData::In::In() : bodyParser(NULL),
+    buf (NULL), notYetUsed (0), allocatedSize (0),
+    dechunkingState(ConnStateData::chunkUnknown)
 {}
 
 ConnStateData::In::~In()
 {
     if (allocatedSize)
         memFreeBuf(allocatedSize, buf);
+    if (bodyParser)
+        delete bodyParser; // TODO: pool
 }
 
 /* This is a comm call normally scheduled by comm_close() */
diff -u -r -N squid-3.1.0.10/src/client_side.h squid-3.1.0.11/src/client_side.h
--- squid-3.1.0.10/src/client_side.h	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/client_side.h	2009-07-19 17:11:13.000000000 +1200
@@ -48,6 +48,9 @@
 
 class AuthUserRequest;
 
+class ChunkedCodingParser;
+class HttpParser;
+
 template <class T>
 class Range;
 
@@ -144,13 +147,22 @@
 
     int fd;
 
+    /// chunk buffering and parsing algorithm state
+    typedef enum { chunkUnknown, chunkNone, chunkParsing, chunkReady, chunkError } DechunkingState;
+
     struct In {
         In();
         ~In();
         char *addressToReadInto() const;
+
+	ChunkedCodingParser *bodyParser; ///< parses chunked request body
+	MemBuf chunked; ///< contains unparsed raw (chunked) body data
+	MemBuf dechunked; ///< accumulates parsed (dechunked) content
         char *buf;
         size_t notYetUsed;
         size_t allocatedSize;
+	size_t chunkedSeen; ///< size of processed or ignored raw read data
+	DechunkingState dechunkingState; ///< request dechunking state
     } in;
 
     int64_t bodySizeLeft();
@@ -253,6 +265,10 @@
     bool switchedToHttps() const { return false; }
 #endif
 
+    void startDechunkingRequest(HttpParser *hp);
+    bool parseRequestChunks(HttpParser *hp);
+    void finishDechunkingRequest(HttpParser *hp);
+
 private:
     int connReadWasError(comm_err_t flag, int size, int xerrno);
     int connFinishedWithConn(int size);
diff -u -r -N squid-3.1.0.10/src/client_side_request.cc squid-3.1.0.11/src/client_side_request.cc
--- squid-3.1.0.10/src/client_side_request.cc	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/client_side_request.cc	2009-07-19 17:11:13.000000000 +1200
@@ -59,12 +59,19 @@
 #include "SquidTime.h"
 #include "wordlist.h"
 #include "inet_pton.h"
+#include "fde.h"
 
 #if USE_ADAPTATION
 #include "adaptation/AccessCheck.h"
+#include "adaptation/Iterator.h"
 #include "adaptation/Service.h"
-static void adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data);
+#if ICAP_CLIENT
+#include "adaptation/icap/History.h"
 #endif
+//static void adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data);
+#endif
+
+
 
 #if LINGERING_CLOSE
 #define comm_close comm_lingering_close
@@ -613,34 +620,45 @@
 
 #if USE_ADAPTATION
 static void
-adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data)
+adaptationAclCheckDoneWrapper(Adaptation::ServiceGroupPointer g, void *data)
 {
     ClientRequestContext *calloutContext = (ClientRequestContext *)data;
 
     if (!calloutContext->httpStateIsValid())
         return;
 
-    calloutContext->adaptationAclCheckDone(service);
+    calloutContext->adaptationAclCheckDone(g);
 }
 
 void
-ClientRequestContext::adaptationAclCheckDone(Adaptation::ServicePointer service)
+ClientRequestContext::adaptationAclCheckDone(Adaptation::ServiceGroupPointer g)
 {
     debugs(93,3,HERE << this << " adaptationAclCheckDone called");
     assert(http);
 
-    if (http->startAdaptation(service))
-        return;
+#if ICAP_CLIENT                                                                
+    Adaptation::Icap::History::Pointer ih = http->request->icapHistory();
+    if(ih != NULL)
+    {                                                                          
+        if (http->getConn() != NULL)
+        {
+            ih->rfc931 = http->getConn()->rfc931;
+#if USE_SSL                              
+            ih->ssluser = sslGetUserEmail(fd_table[http->getConn()->fd].ssl);
+#endif  
+        }
+        ih->log_uri = http->log_uri;
+        ih->req_sz = http->req_sz;
+    }
+#endif
 
-    if (!service || service->cfg().bypass) {
-        // handle ICAP start failure when no service was selected
-        // or where the selected service was optional
+    if (!g) {
+        debugs(85,3, HERE << "no adaptation needed");
         http->doCallouts();
         return;
     }
 
-    // handle start failure for an essential ICAP service
-    http->handleAdaptationFailure();
+    http->startAdaptation(g);
 }
 
 #endif
@@ -1301,6 +1319,12 @@
 
     debugs(83, 3, HERE << "calling processRequest()");
     processRequest();
+
+#if ICAP_CLIENT
+    Adaptation::Icap::History::Pointer ih = request->icapHistory();
+    if (ih != NULL)
+        ih->logType = logType;
+#endif
 }
 
 #ifndef _USE_INLINE_
@@ -1308,29 +1332,19 @@
 #endif
 
 #if USE_ADAPTATION
-/*
- * Initiate an ICAP transaction.  Return false on errors.
- * The caller must handle errors.
- */
-bool
-ClientHttpRequest::startAdaptation(Adaptation::ServicePointer service)
+/// Initiate an asynchronous adaptation transaction which will call us back.
+void
+ClientHttpRequest::startAdaptation(const Adaptation::ServiceGroupPointer &g)
 {
-    debugs(85, 3, HERE << this << " ClientHttpRequest::startAdaptation() called");
-    if (!service) {
-        debugs(85, 3, "ClientHttpRequest::startAdaptation fails: lack of service");
-        return false;
-    }
-    if (service->broken()) {
-        debugs(85, 3, "ClientHttpRequest::startAdaptation fails: broken service");
-        return false;
-    }
-
+    debugs(85, 3, HERE << "adaptation needed for " << this);
     assert(!virginHeadSource);
     assert(!adaptedBodySource);
-    virginHeadSource = initiateAdaptation(service->makeXactLauncher(
-                                              this, request, NULL));
+    virginHeadSource = initiateAdaptation(
+        new Adaptation::Iterator(this, request, NULL, g));
 
-    return virginHeadSource != NULL;
+    // we could try to guess whether we can bypass this adaptation 
+    // initiation failure, but it should not really happen
+    assert(virginHeadSource != NULL); // Must, really
 }
 
 void
diff -u -r -N squid-3.1.0.10/src/client_side_request.h squid-3.1.0.11/src/client_side_request.h
--- squid-3.1.0.10/src/client_side_request.h	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/client_side_request.h	2009-07-19 17:11:13.000000000 +1200
@@ -159,7 +159,7 @@
 #if USE_ADAPTATION
 
 public:
-    bool startAdaptation(Adaptation::ServicePointer);
+    void startAdaptation(const Adaptation::ServiceGroupPointer &g);
 
     // private but exposed for ClientRequestContext
     void handleAdaptationFailure(bool bypassable = false);
diff -u -r -N squid-3.1.0.10/src/CommCalls.cc squid-3.1.0.11/src/CommCalls.cc
--- squid-3.1.0.10/src/CommCalls.cc	2009-07-18 16:16:15.000000000 +1200
+++ squid-3.1.0.11/src/CommCalls.cc	2009-07-19 17:11:08.000000000 +1200
@@ -67,6 +67,13 @@
     return true; // now we are in sync and can handle the call
 }
 
+void
+CommConnectCbParams::print(std::ostream &os) const
+{
+    CommCommonCbParams::print(os);
+    os << ", " << dns;
+}
+
 /* CommIoCbParams */
 
 CommIoCbParams::CommIoCbParams(void *aData): CommCommonCbParams(aData),
@@ -150,7 +157,7 @@
 void
 CommConnectCbPtrFun::dial()
 {
-    handler(params.fd, params.flag, params.xerrno, params.data);
+    handler(params.fd, params.dns, params.flag, params.xerrno, params.data);
 }
 
 void
diff -u -r -N squid-3.1.0.10/src/CommCalls.h squid-3.1.0.11/src/CommCalls.h
--- squid-3.1.0.10/src/CommCalls.h	2009-07-18 16:16:15.000000000 +1200
+++ squid-3.1.0.11/src/CommCalls.h	2009-07-19 17:11:08.000000000 +1200
@@ -8,6 +8,7 @@
 
 #include "comm.h"
 #include "ConnectionDetail.h"
+#include "DnsLookupDetails.h"
 #include "base/AsyncCall.h"
 #include "base/AsyncJobCalls.h"
 
@@ -79,6 +80,11 @@
     CommConnectCbParams(void *aData);
 
     bool syncWithComm(); // see CommCommonCbParams::syncWithComm
+
+    void print(std::ostream &os) const;
+
+public:
+    DnsLookupDetails dns;
 };
 
 // read/write (I/O) parameters
diff -u -r -N squid-3.1.0.10/src/comm.cc squid-3.1.0.11/src/comm.cc
--- squid-3.1.0.10/src/comm.cc	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/comm.cc	2009-07-19 17:11:13.000000000 +1200
@@ -209,6 +209,7 @@
     IpAddress default_addr;
     // NP: CANNOT store the default addr:port together as it gets set/reset differently.
 
+    DnsLookupDetails dns; ///< host lookup details
     IpAddress S;
     AsyncCall::Pointer callback;
 
@@ -876,19 +877,13 @@
 }
 
 static void
-commConnectDnsHandle(const ipcache_addrs * ia, void *data)
+commConnectDnsHandle(const ipcache_addrs *ia, const DnsLookupDetails &details, void *data)
 {
     ConnectStateData *cs = (ConnectStateData *)data;
+    cs->dns = details;
 
     if (ia == NULL) {
         debugs(5, 3, "commConnectDnsHandle: Unknown host: " << cs->host);
-
-        if (!dns_error_message) {
-            dns_error_message = "Unknown DNS error";
-            debugs(5, 1, "commConnectDnsHandle: Bad dns_error_message");
-        }
-
-        assert(dns_error_message != NULL);
         cs->callCallback(COMM_ERR_DNS, 0);
         return;
     }
@@ -918,6 +913,7 @@
     typedef CommConnectCbParams Params;
     Params &params = GetCommParams<Params>(callback);
     params.fd = fd;
+    params.dns = dns;
     params.flag = status;
     params.xerrno = xerrno;
     ScheduleCallHere(callback);
diff -u -r -N squid-3.1.0.10/src/comm.h squid-3.1.0.11/src/comm.h
--- squid-3.1.0.10/src/comm.h	2009-07-18 16:16:24.000000000 +1200
+++ squid-3.1.0.11/src/comm.h	2009-07-19 17:11:13.000000000 +1200
@@ -27,7 +27,8 @@
 #endif
 } comm_err_t;
 
-typedef void CNCB(int fd, comm_err_t status, int xerrno, void *data);
+class DnsLookupDetails;
+typedef void CNCB(int fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data);
 
 typedef void IOCB(int fd, char *, size_t size, comm_err_t flag, int xerrno, void *data);
 
diff -u -r -N squid-3.1.0.10/src/dns_internal.cc squid-3.1.0.11/src/dns_internal.cc
--- squid-3.1.0.10/src/dns_internal.cc	2009-07-18 16:16:25.000000000 +1200
+++ squid-3.1.0.11/src/dns_internal.cc	2009-07-19 17:11:13.000000000 +1200
@@ -699,7 +699,7 @@
 }
 
 static void
-idnsInitVCConnected(int fd, comm_err_t status, int xerrno, void *data)
+idnsInitVCConnected(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
 {
     nsvc * vc = (nsvc *)data;
 
diff -u -r -N squid-3.1.0.10/src/DnsLookupDetails.cc squid-3.1.0.11/src/DnsLookupDetails.cc
--- squid-3.1.0.10/src/DnsLookupDetails.cc	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/DnsLookupDetails.cc	2009-07-19 17:11:09.000000000 +1200
@@ -0,0 +1,25 @@
+/*
+ * DEBUG: section 78    DNS lookups
+ */
+
+#include "config.h"
+#include "DnsLookupDetails.h"
+
+DnsLookupDetails::DnsLookupDetails(): wait(-1)
+{
+}
+
+DnsLookupDetails::DnsLookupDetails(const String &e, int w):
+    error(e), wait(w)
+{
+}
+
+std::ostream &
+DnsLookupDetails::print(std::ostream &os) const
+{
+    if (wait > 0)
+        os << "lookup_wait=" << wait;
+    if (error.size())
+        os << " lookup_err=" << error;
+    return os;
+}
diff -u -r -N squid-3.1.0.10/src/DnsLookupDetails.h squid-3.1.0.11/src/DnsLookupDetails.h
--- squid-3.1.0.10/src/DnsLookupDetails.h	1970-01-01 12:00:00.000000000 +1200
+++ squid-3.1.0.11/src/DnsLookupDetails.h	2009-07-19 17:11:09.000000000 +1200
@@ -0,0 +1,31 @@
+/*
+ * DEBUG: section 78    DNS lookups
+ */
+
+#ifndef SQUID_DNS_LOOKUP_DETAILS_H
+#define SQUID_DNS_LOOKUP_DETAILS_H
+
+#include "typedefs.h"
+#include "SquidString.h"
+
+/// encapsulates DNS lookup results
+class DnsLookupDetails
+{
+public:
+    DnsLookupDetails(); ///< no error, no lookup delay (i.e., no lookup)
+    DnsLookupDetails(const String &error, int wait);
+
+    std::ostream &print(std::ostream &os) const;
+
+public:
+    String error; ///< error message for unsuccessdul lookups; empty otherwise
+    int wait; ///< msecs spent waiting for the lookup (if any) or -1 (if none)
+};
+
+inline
+std::ostream &operator << (std::ostream &os, const DnsLookupDetails &dns)
+{
+    return dns.print(os);
+}
+
+#endif
diff -u -r -N squid-3.1.0.10/src/enums.h squid-3.1.0.11/src/enums.h
--- squid-3.1.0.10/src/enums.h	2009-07-18 16:16:25.000000000 +1200
+++ squid-3.1.0.11/src/enums.h	2009-07-19 17:11:13.000000000 +1200
@@ -533,6 +533,9 @@
     CLF_CUSTOM,
     CLF_SQUID,
     CLF_COMMON,
+#if ICAP_CLIENT
+    CLF_ICAP_SQUID,
+#endif
     CLF_NONE
 } customlog_type;
 
diff -u -r -N squid-3.1.0.10/src/errorpage.cc squid-3.1.0.11/src/errorpage.cc
--- squid-3.1.0.10/src/errorpage.cc	2009-07-18 16:16:25.000000000 +1200
+++ squid-3.1.0.11/src/errorpage.cc	2009-07-19 17:11:13.000000000 +1200
@@ -496,7 +496,6 @@
     HTTPMSGUNLOCK(err->request);
     safe_free(err->redirect_url);
     safe_free(err->url);
-    safe_free(err->dnsserver_msg);
     safe_free(err->request_hdrs);
     wordlistDestroy(&err->ftp.server_msg);
     safe_free(err->ftp.request);
@@ -536,9 +535,8 @@
     if (auth_user_request->denyMessage())
         str.Printf("Auth ErrMsg: %s\r\n", auth_user_request->denyMessage());
 
-    if (dnsserver_msg) {
-        str.Printf("DNS Server ErrMsg: %s\r\n", dnsserver_msg);
-    }
+    if (dnsError.size() > 0)
+        str.Printf("DNS ErrMsg: %s\r\n", dnsError.termedBuf());
 
     /* - TimeStamp */
     str.Printf("TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
@@ -819,8 +817,8 @@
         break;
 
     case 'z':
-        if (dnsserver_msg)
-            p = dnsserver_msg;
+        if (dnsError.size() > 0)
+            p = dnsError.termedBuf();
         else
             p = "[unknown]";
 
diff -u -r -N squid-3.1.0.10/src/errorpage.h squid-3.1.0.11/src/errorpage.h
--- squid-3.1.0.10/src/errorpage.h	2009-07-18 16:16:25.000000000 +1200
+++ squid-3.1.0.11/src/errorpage.h	2009-07-19 17:11:13.000000000 +1200
@@ -120,7 +120,7 @@
     char *url;
     int xerrno;
     u_short port;
-    char *dnsserver_msg;
+    String dnsError; ///< DNS lookup error message
     time_t ttl;
 
     IpAddress src_addr;
diff -u -r -N squid-3.1.0.10/src/forward.cc squid-3.1.0.11/src/forward.cc
--- squid-3.1.0.10/src/forward.cc	2009-07-18 16:16:26.000000000 +1200
+++ squid-3.1.0.11/src/forward.cc	2009-07-19 17:11:14.000000000 +1200
@@ -375,10 +375,10 @@
 #endif
 
 static void
-fwdConnectDoneWrapper(int server_fd, comm_err_t status, int xerrno, void *data)
+fwdConnectDoneWrapper(int server_fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
 {
     FwdState *fwd = (FwdState *) data;
-    fwd->connectDone(server_fd, status, xerrno);
+    fwd->connectDone(server_fd, dns, status, xerrno);
 }
 
 static void
@@ -675,11 +675,13 @@
 #endif
 
 void
-FwdState::connectDone(int aServerFD, comm_err_t status, int xerrno)
+FwdState::connectDone(int aServerFD, const DnsLookupDetails &dns, comm_err_t status, int xerrno)
 {
     FwdServer *fs = servers;
     assert(server_fd == aServerFD);
 
+    request->recordLookup(dns);
+
     if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
         updateHierarchyInfo();
 
@@ -697,7 +699,7 @@
 
         ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
 
-        anErr->dnsserver_msg = xstrdup(dns_error_message_safe());
+        anErr->dnsError = dns.error;
 
         fail(anErr);
 
@@ -778,6 +780,9 @@
     assert(server_fd == -1);
     debugs(17, 3, "fwdConnectStart: " << url);
 
+    if (n_tries == 0) // first attempt
+        request->hier.first_conn_start = current_time;
+
     if (fs->_peer) {
         ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
                    : Config.Timeout.peer_connect;
@@ -813,7 +818,7 @@
                 request->flags.auth = 1;
             comm_add_close_handler(fd, fwdServerClosedWrapper, this);
             updateHierarchyInfo();
-            connectDone(fd, COMM_OK, 0);
+            connectDone(fd, DnsLookupDetails(), COMM_OK, 0);
             return;
         }
         /* Failure. Fall back on next path */
diff -u -r -N squid-3.1.0.10/src/forward.h squid-3.1.0.11/src/forward.h
--- squid-3.1.0.10/src/forward.h	2009-07-18 16:16:26.000000000 +1200
+++ squid-3.1.0.11/src/forward.h	2009-07-19 17:11:14.000000000 +1200
@@ -37,7 +37,7 @@
     bool reforwardableStatus(http_status s);
     void serverClosed(int fd);
     void connectStart();
-    void connectDone(int server_fd, comm_err_t status, int xerrno);
+    void connectDone(int server_fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno);
     void connectTimeout(int fd);
     void initiateSSL();
     void negotiateSSL(int fd);
diff -u -r -N squid-3.1.0.10/src/fqdncache.cc squid-3.1.0.11/src/fqdncache.cc
--- squid-3.1.0.10/src/fqdncache.cc	2009-07-18 16:16:26.000000000 +1200
+++ squid-3.1.0.11/src/fqdncache.cc	2009-07-19 17:11:14.000000000 +1200
@@ -83,9 +83,6 @@
 /// \ingroup FQDNCacheInternal
 #define FQDN_HIGH_WATER      95
 
-/// \ingroup FQDNCacheAPI
-typedef struct _fqdncache_entry fqdncache_entry;
-
 /**
  \ingroup FQDNCacheAPI
  * The data structure used for storing name-address mappings
@@ -93,7 +90,8 @@
  * where structures of type fqdncache_entry whose most
  * interesting members are:
  */
-struct _fqdncache_entry {
+class fqdncache_entry {
+public:
     hash_link hash;		/* must be first */
     time_t lastref;
     time_t expires;
@@ -111,6 +109,8 @@
         unsigned int negcached:1;
         unsigned int fromhosts:1;
     } flags;
+
+    int age() const; ///< time passed since request_time or -1 if unknown
 };
 
 /// \ingroup FQDNCacheInternal
@@ -134,7 +134,7 @@
 #endif
 static void fqdncacheRelease(fqdncache_entry *);
 static fqdncache_entry *fqdncacheCreateEntry(const char *name);
-static void fqdncacheCallback(fqdncache_entry *);
+static void fqdncacheCallback(fqdncache_entry *, int wait);
 static fqdncache_entry *fqdncache_get(const char *);
 static FQDNH dummy_handler;
 static int fqdncacheExpiredEntry(const fqdncache_entry *);
@@ -152,6 +152,13 @@
 /// \ingroup FQDNCacheInternal
 static long fqdncache_high = 200;
 
+int
+fqdncache_entry::age() const
+{
+    return request_time.tv_sec ? tvSubMsec(request_time, current_time) : -1;
+}
+
+
 /**
  \ingroup FQDNCacheInternal
  * Removes the given fqdncache entry
@@ -304,7 +311,7 @@
  * Walks down the pending list, calling handlers
  */
 static void
-fqdncacheCallback(fqdncache_entry * f)
+fqdncacheCallback(fqdncache_entry * f, int wait)
 {
     FQDNH *callback;
     void *cbdata;
@@ -320,8 +327,8 @@
     f->handler = NULL;
 
     if (cbdataReferenceValidDone(f->handlerData, &cbdata)) {
-        dns_error_message = f->error_message;
-        callback(f->name_count ? f->names[0] : NULL, cbdata);
+        const DnsLookupDetails details(f->error_message, wait);
+        callback(f->name_count ? f->names[0] : NULL, details, cbdata);
     }
 
     fqdncacheUnlockEntry(f);
@@ -490,8 +497,8 @@
     fqdncache_entry *f;
     static_cast<generic_cbdata *>(data)->unwrap(&f);
     n = ++FqdncacheStats.replies;
-    statHistCount(&statCounter.dns.svc_time,
-                  tvSubMsec(f->request_time, current_time));
+    const int age = f->age();
+    statHistCount(&statCounter.dns.svc_time, age);
 #if USE_DNSSERVERS
 
     fqdncacheParse(f, reply);
@@ -502,7 +509,7 @@
 
     fqdncacheAddEntry(f);
 
-    fqdncacheCallback(f);
+    fqdncacheCallback(f, age);
 }
 
 /**
@@ -528,8 +535,8 @@
 
     if (name[0] == '\0') {
         debugs(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!");
-        dns_error_message = "Invalid hostname";
-        handler(NULL, handlerData);
+        const DnsLookupDetails details("Invalid hostname", -1); // error, no lookup
+        handler(NULL, details, handlerData);
         return;
     }
 
@@ -555,7 +562,7 @@
 
         f->handlerData = cbdataReference(handlerData);
 
-        fqdncacheCallback(f);
+        fqdncacheCallback(f, -1); // no lookup
 
         return;
     }
@@ -654,16 +661,16 @@
         f = NULL;
     } else if (f->flags.negcached) {
         FqdncacheStats.negative_hits++;
-        dns_error_message = f->error_message;
+        // ignore f->error_message: the caller just checks FQDN cache presence
         return NULL;
     } else {
         FqdncacheStats.hits++;
         f->lastref = squid_curtime;
-        dns_error_message = f->error_message;
+        // ignore f->error_message: the caller just checks FQDN cache presence
         return f->names[0];
     }
 
-    dns_error_message = NULL;
+    /* no entry [any more] */
 
     FqdncacheStats.misses++;
 
@@ -732,7 +739,7 @@
 
 /// \ingroup FQDNCacheInternal
 static void
-dummy_handler(const char *bufnotused, void *datanotused)
+dummy_handler(const char *, const DnsLookupDetails &, void *)
 {
     return;
 }
@@ -932,14 +939,3 @@
 }
 
 #endif /*SQUID_SNMP */
-
-/// XXX: a hack to work around the missing DNS error info
-// see http://www.squid-cache.org/bugs/show_bug.cgi?id=2459
-const char *
-dns_error_message_safe()
-{
-    if (dns_error_message)
-        return dns_error_message;
-    debugs(35,DBG_IMPORTANT, "Internal error: lost DNS error info");
-    return "lost DNS error";
-}
diff -u -r -N squid-3.1.0.10/src/ftp.cc squid-3.1.0.11/src/ftp.cc
--- squid-3.1.0.10/src/ftp.cc	2009-07-18 16:16:26.000000000 +1200
+++ squid-3.1.0.11/src/ftp.cc	2009-07-19 17:11:14.000000000 +1200
@@ -2787,10 +2787,11 @@
 }
 
 void
-FtpStateData::ftpPasvCallback(int fd, comm_err_t status, int xerrno, void *data)
+FtpStateData::ftpPasvCallback(int fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
 {
     FtpStateData *ftpState = (FtpStateData *)data;
     debugs(9, 3, HERE);
+    ftpState->request->recordLookup(dns);
 
     if (status != COMM_OK) {
         debugs(9, 2, HERE << "Failed to connect. Retrying without PASV.");
diff -u -r -N squid-3.1.0.10/src/globals.h squid-3.1.0.11/src/globals.h
--- squid-3.1.0.10/src/globals.h	2009-07-18 16:16:26.000000000 +1200
+++ squid-3.1.0.11/src/globals.h	2009-07-19 17:11:14.000000000 +1200
@@ -55,7 +55,6 @@
 //MOVED:structs.h    extern SquidConfig2 Config2;
     extern char *ConfigFile;	/* NULL */
     extern char *IcpOpcodeStr[];
-    extern const char *dns_error_message;	/* NULL */
     extern const char *log_tags[];
     extern char tmp_error_buf[ERROR_BUF_SZ];
     extern char ThisCache[RFC2181_MAXHOSTNAMELEN << 1];
diff -u -r -N squid-3.1.0.10/src/HierarchyLogEntry.h squid-3.1.0.11/src/HierarchyLogEntry.h
--- squid-3.1.0.10/src/HierarchyLogEntry.h	2009-07-18 16:16:17.000000000 +1200
+++ squid-3.1.0.11/src/HierarchyLogEntry.h	2009-07-19 17:11:09.000000000 +1200
@@ -56,6 +56,12 @@
     struct timeval peer_select_start;
 
     struct timeval store_complete_stop;
+    
+    http_status peer_reply_status; ///< last HTTP status code received
+    timeval peer_http_request_sent; ///< last peer finished writing req
+    int64_t peer_response_time; ///< last peer response delay
+    timeval first_conn_start; ///< first connection use among all peers
+    int64_t total_response_time; ///< cumulative for all peers
 };
 
 extern void hierarchyNote(HierarchyLogEntry *, hier_code, const char *);
diff -u -r -N squid-3.1.0.10/src/http.cc squid-3.1.0.11/src/http.cc
--- squid-3.1.0.10/src/http.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/http.cc	2009-07-19 17:11:14.000000000 +1200
@@ -87,6 +87,10 @@
     readBuf->init(4096, SQUID_TCP_SO_RCVBUF);
     orig_request = HTTPMSGLOCK(fwd->request);
 
+    // reset peer response time stats for %<pt
+    orig_request->hier.peer_http_request_sent.tv_sec = 0;
+    orig_request->hier.peer_http_request_sent.tv_usec = 0;
+
     if (fwd->servers)
         _peer = fwd->servers->_peer;         /* might be NULL */
 
@@ -732,6 +736,8 @@
      * Parse the header and remove all referenced headers
      */
 
+    orig_request->hier.peer_reply_status = newrep->sline.status;
+
     ctx_exit(ctx);
 
 }
@@ -1064,6 +1070,11 @@
             clen >>= 1;
 
         IOStats.Http.read_hist[bin]++;
+
+        // update peer response time stats (%<pt)
+        const timeval &sent = orig_request->hier.peer_http_request_sent;
+        orig_request->hier.peer_response_time =
+            sent.tv_sec ? tvSubMsec(sent, current_time) : -1;
     }
 
     /** \par
@@ -1411,6 +1422,8 @@
     commSetTimeout(fd, Config.Timeout.read, timeoutCall);
 
     flags.request_sent = 1;
+    
+    orig_request->hier.peer_http_request_sent = current_time;
 }
 
 // Close the HTTP server connection. Used by serverComplete().
diff -u -r -N squid-3.1.0.10/src/HttpHeader.cc squid-3.1.0.11/src/HttpHeader.cc
--- squid-3.1.0.10/src/HttpHeader.cc	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/HttpHeader.cc	2009-07-19 17:11:09.000000000 +1200
@@ -142,6 +142,9 @@
 #if X_ACCELERATOR_VARY
     {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr},
 #endif
+#if USE_ADAPTATION
+    {"X-Next-Services", HDR_X_NEXT_SERVICES, ftStr},
+#endif
     {"Surrogate-Capability", HDR_SURROGATE_CAPABILITY, ftStr},
     {"Surrogate-Control", HDR_SURROGATE_CONTROL, ftPSc},
     {"Front-End-Https", HDR_FRONT_END_HTTPS, ftStr},
@@ -187,6 +190,9 @@
 #if X_ACCELERATOR_VARY
     HDR_X_ACCELERATOR_VARY,
 #endif
+#if USE_ADAPTATION
+    HDR_X_NEXT_SERVICES,
+#endif
     HDR_SURROGATE_CAPABILITY,
     HDR_SURROGATE_CONTROL,
     HDR_X_FORWARDED_FOR
@@ -222,6 +228,9 @@
 #if X_ACCELERATOR_VARY
     HDR_X_ACCELERATOR_VARY,
 #endif
+#if USE_ADAPTATION
+    HDR_X_NEXT_SERVICES,
+#endif
     HDR_X_SQUID_ERROR,
     HDR_SURROGATE_CONTROL
 };
diff -u -r -N squid-3.1.0.10/src/HttpHeader.h squid-3.1.0.11/src/HttpHeader.h
--- squid-3.1.0.10/src/HttpHeader.h	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/HttpHeader.h	2009-07-19 17:11:09.000000000 +1200
@@ -123,6 +123,9 @@
 #if X_ACCELERATOR_VARY
     HDR_X_ACCELERATOR_VARY,
 #endif
+#if USE_ADAPTATION
+    HDR_X_NEXT_SERVICES,
+#endif
     HDR_SURROGATE_CAPABILITY,
     HDR_SURROGATE_CONTROL,
     HDR_FRONT_END_HTTPS,
diff -u -r -N squid-3.1.0.10/src/HttpMsg.cc squid-3.1.0.11/src/HttpMsg.cc
--- squid-3.1.0.10/src/HttpMsg.cc	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/HttpMsg.cc	2009-07-19 17:11:09.000000000 +1200
@@ -309,6 +309,14 @@
     return -1;
 }
 
+void
+HttpMsg::setContentLength(int64_t clen)
+{
+    header.delById(HDR_CONTENT_LENGTH); // if any
+    header.putInt64(HDR_CONTENT_LENGTH, clen);
+    content_length = clen;
+}
+
 /* returns true if connection should be "persistent"
  * after processing this message */
 int
diff -u -r -N squid-3.1.0.10/src/HttpMsg.h squid-3.1.0.11/src/HttpMsg.h
--- squid-3.1.0.10/src/HttpMsg.h	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/HttpMsg.h	2009-07-19 17:11:09.000000000 +1200
@@ -58,6 +58,9 @@
     ///< produce a message copy, except for a few connection-specific settings
     virtual HttpMsg *clone() const = 0; ///< \todo rename: not a true copy?
 
+    /// [re]sets Content-Length header and cached value
+    void setContentLength(int64_t clen);
+
 public:
     HttpVersion http_ver;
 
@@ -109,7 +112,8 @@
 };
 
 /* Temporary parsing state; might turn into the replacement parser later on */
-struct _HttpParser {
+class HttpParser {
+public:
     char state;
     const char *buf;
     int bufsiz;
@@ -120,7 +124,6 @@
     int v_start, v_end;
     int v_maj, v_min;
 };
-typedef struct _HttpParser HttpParser;
 
 extern void HttpParserInit(HttpParser *, const char *buf, int len);
 extern int HttpParserParseReqLine(HttpParser *hp);
diff -u -r -N squid-3.1.0.10/src/HttpRequest.cc squid-3.1.0.11/src/HttpRequest.cc
--- squid-3.1.0.10/src/HttpRequest.cc	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/HttpRequest.cc	2009-07-19 17:11:09.000000000 +1200
@@ -40,6 +40,9 @@
 #include "HttpHeaderRange.h"
 #include "MemBuf.h"
 #include "Store.h"
+#if ICAP_CLIENT
+#include "adaptation/icap/icap_log.h"
+#endif
 
 HttpRequest::HttpRequest() : HttpMsg(hoRequest)
 {
@@ -87,6 +90,7 @@
     my_addr.SetEmpty();
     body_pipe = NULL;
     // hier
+    dnsWait = -1;
     errType = ERR_NONE;
     peer_login = NULL;		// not allocated/deallocated by this class
     peer_domain = NULL;		// not allocated/deallocated by this class
@@ -99,6 +103,12 @@
 #if FOLLOW_X_FORWARDED_FOR
     indirect_client_addr.SetEmpty();
 #endif /* FOLLOW_X_FORWARDED_FOR */
+#if USE_ADAPTATION
+    adaptHistory_ = NULL;
+#endif
+#if ICAP_CLIENT
+    icapHistory_ = NULL;
+#endif
 }
 
 void
@@ -138,6 +148,13 @@
     extacl_passwd.clean();
 
     extacl_log.clean();
+
+#if USE_ADAPTATION
+    adaptHistory_ = NULL;
+#endif
+#if ICAP_CLIENT
+    icapHistory_ = NULL;
+#endif
 }
 
 void
@@ -163,25 +180,14 @@
     strncpy(copy->host, host, sizeof(host)); // SQUIDHOSTNAMELEN
     copy->host_addr = host_addr;
 
-    if (auth_user_request) {
-        copy->auth_user_request = auth_user_request;
-        AUTHUSERREQUESTLOCK(copy->auth_user_request, "HttpRequest::clone");
-    }
-
     copy->port = port;
     // urlPath handled in ctor
     copy->canonical = canonical ? xstrdup(canonical) : NULL;
 
-    // This may be too conservative for the 204 No Content case
-    // may eventually need cloneNullAdaptationImmune() for that.
-    copy->flags = flags.cloneAdaptationImmune();
-
     copy->range = range ? new HttpHdrRange(*range) : NULL;
     copy->ims = ims;
     copy->imslen = imslen;
     copy->max_forwards = max_forwards;
-    copy->client_addr = client_addr;
-    copy->my_addr = my_addr;
     copy->hier = hier; // Is it safe to copy? Should we?
 
     copy->errType = errType;
@@ -197,6 +203,8 @@
     copy->extacl_passwd = extacl_passwd;
     copy->extacl_log = extacl_log;
 
+    assert(copy->inheritProperties(this));
+
     return copy;
 }
 
@@ -356,6 +364,44 @@
     reset_tcp = 0;
 }
 
+#if ICAP_CLIENT
+Adaptation::Icap::History::Pointer 
+HttpRequest::icapHistory() const
+{
+    if (!icapHistory_) {
+        if ((LogfileStatus == LOG_ENABLE && alLogformatHasIcapToken) ||
+            IcapLogfileStatus == LOG_ENABLE) {
+            icapHistory_ = new Adaptation::Icap::History();
+            debugs(93,4, HERE << "made " << icapHistory_ << " for " << this);
+        }
+    }
+
+    return icapHistory_;
+}
+#endif
+
+#if USE_ADAPTATION
+Adaptation::History::Pointer 
+HttpRequest::adaptHistory(bool createIfNone) const
+{
+    if (!adaptHistory_ && createIfNone) {
+        adaptHistory_ = new Adaptation::History();
+        debugs(93,4, HERE << "made " << adaptHistory_ << " for " << this);
+    }
+
+    return adaptHistory_;
+}
+
+Adaptation::History::Pointer 
+HttpRequest::adaptLogHistory() const
+{
+    const bool loggingNeedsHistory = (LogfileStatus == LOG_ENABLE) &&
+        alLogformatHasAdaptToken; // TODO: make global to remove this method?
+    return HttpRequest::adaptHistory(loggingNeedsHistory);
+}
+
+#endif
+
 bool
 HttpRequest::multipartRangeRequest() const
 {
@@ -509,6 +555,15 @@
     client_addr = aReq->client_addr;
     my_addr = aReq->my_addr;
 
+    dnsWait = aReq->dnsWait;
+
+#if USE_ADAPTATION
+    adaptHistory_ = aReq->adaptHistory();
+#endif
+#if ICAP_CLIENT
+    icapHistory_ = aReq->icapHistory();
+#endif
+
     // This may be too conservative for the 204 No Content case
     // may eventually need cloneNullAdaptationImmune() for that.
     flags = aReq->flags.cloneAdaptationImmune();
@@ -523,3 +578,13 @@
     }
     return true;
 }
+
+void HttpRequest::recordLookup(const DnsLookupDetails &dns)
+{
+    if (dns.wait >= 0) { // known delay
+        if (dnsWait >= 0) // have recorded DNS wait before
+            dnsWait += dns.wait;
+        else
+            dnsWait = dns.wait;
+    }
+}
diff -u -r -N squid-3.1.0.10/src/HttpRequest.h squid-3.1.0.11/src/HttpRequest.h
--- squid-3.1.0.10/src/HttpRequest.h	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/HttpRequest.h	2009-07-19 17:11:09.000000000 +1200
@@ -38,13 +38,28 @@
 #include "client_side.h"
 #include "HierarchyLogEntry.h"
 #include "HttpRequestMethod.h"
+#if USE_ADAPTATION
+#include "adaptation/History.h"
+#endif
+#if ICAP_CLIENT
+#include "adaptation/icap/History.h"
+#endif
 
 /*  Http Request */
 //DEAD?: extern int httpRequestHdrAllowedByName(http_hdr_type id);
 extern void httpRequestPack(void *obj, Packer *p);
 
+// TODO: Move these three to access_log.h or AccessLogEntry.h
+#if USE_ADAPTATION
+extern bool alLogformatHasAdaptToken;
+#endif
+#if ICAP_CLIENT
+extern bool alLogformatHasIcapToken;
+#endif
+extern int LogfileStatus;
 
 class HttpHdrRange;
+class DnsLookupDetails;
 
 class HttpRequest: public HttpMsg
 {
@@ -84,6 +99,19 @@
     };
     inline const char* GetHost(void) const { return host; };
 
+#if USE_ADAPTATION
+    /// Returns possibly nil history, creating it if adapt. logging is enabled
+    Adaptation::History::Pointer adaptLogHistory() const;
+    /// Returns possibly nil history, creating it if requested
+    Adaptation::History::Pointer adaptHistory(bool createIfNone = false) const;
+#endif
+#if ICAP_CLIENT
+    /// Returns possibly nil history, creating it if icap logging is enabled
+    Adaptation::Icap::History::Pointer icapHistory() const;
+#endif
+
+    void recordLookup(const DnsLookupDetails &detail);
+
 protected:
     void clean();
 
@@ -103,6 +131,13 @@
      */
     ConnStateData *pinned_connection;
 
+#if USE_ADAPTATION
+    mutable Adaptation::History::Pointer adaptHistory_; ///< per-HTTP transaction info
+#endif
+#if ICAP_CLIENT
+    mutable Adaptation::Icap::History::Pointer icapHistory_; ///< per-HTTP transaction info
+#endif
+
 public:
     IpAddress host_addr;
 
@@ -134,6 +169,8 @@
 
     HierarchyLogEntry hier;
 
+    int dnsWait; ///< sum of DNS lookup delays in milliseconds, for %dt
+
     err_type errType;
 
     char *peer_login;		/* Configured peer login:password */
diff -u -r -N squid-3.1.0.10/src/icmp/net_db.cc squid-3.1.0.11/src/icmp/net_db.cc
--- squid-3.1.0.10/src/icmp/net_db.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/icmp/net_db.cc	2009-07-19 17:11:14.000000000 +1200
@@ -271,7 +271,7 @@
 }
 
 static void
-netdbSendPing(const ipcache_addrs * ia, void *data)
+netdbSendPing(const ipcache_addrs *ia, const DnsLookupDetails &, void *data)
 {
     IpAddress addr;
     char *hostname = NULL;
diff -u -r -N squid-3.1.0.10/src/ident/Ident.cc squid-3.1.0.11/src/ident/Ident.cc
--- squid-3.1.0.10/src/ident/Ident.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/ident/Ident.cc	2009-07-19 17:11:14.000000000 +1200
@@ -120,7 +120,7 @@
 }
 
 static void
-Ident::ConnectDone(int fd, comm_err_t status, int xerrno, void *data)
+Ident::ConnectDone(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
 {
     IdentStateData *state = (IdentStateData *)data;
     IdentClient *c;
diff -u -r -N squid-3.1.0.10/src/ipcache.cc squid-3.1.0.11/src/ipcache.cc
--- squid-3.1.0.10/src/ipcache.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/ipcache.cc	2009-07-19 17:11:14.000000000 +1200
@@ -77,9 +77,6 @@
  * the ipcache_high threshold.
  */
 
-/// \ingroup IPCacheAPI
-typedef struct _ipcache_entry ipcache_entry;
-
 /**
  \ingroup IPCacheAPI
  *
@@ -88,7 +85,8 @@
  * where structures of type ipcache_entry whose most
  * interesting members are:
  */
-struct _ipcache_entry {
+class ipcache_entry {
+public:
     hash_link hash;		/* must be first */
     time_t lastref;
     time_t expires;
@@ -108,6 +106,8 @@
         unsigned int negcached:1;
         unsigned int fromhosts:1;
     } flags;
+
+    int age() const; ///< time passed since request_time or -1 if unknown
 };
 
 /// \ingroup IPCacheInternal
@@ -161,6 +161,14 @@
 extern int _dns_ttl_;
 #endif
 
+int
+ipcache_entry::age() const
+{
+    return request_time.tv_sec ? tvSubMsec(request_time, current_time) : -1;
+}
+
+
+
 /**
  \ingroup IPCacheInternal
  *
@@ -324,7 +332,7 @@
  * walks down the pending list, calling handlers
  */
 static void
-ipcacheCallback(ipcache_entry * i)
+ipcacheCallback(ipcache_entry *i, int wait)
 {
     IPH *callback = i->handler;
     void *cbdata = NULL;
@@ -340,8 +348,8 @@
     i->handler = NULL;
 
     if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
-        dns_error_message = i->error_message;
-        callback(i->addrs.count ? &i->addrs : NULL, cbdata);
+        const DnsLookupDetails details(i->error_message, wait);
+        callback((i->addrs.count ? &i->addrs : NULL), details, cbdata);
     }
 
     ipcacheUnlockEntry(i);
@@ -646,8 +654,9 @@
     ipcache_entry *i;
     static_cast<generic_cbdata *>(data)->unwrap(&i);
     IpcacheStats.replies++;
-    statHistCount(&statCounter.dns.svc_time,
-                  tvSubMsec(i->request_time, current_time));
+    const int age = i->age();
+    statHistCount(&statCounter.dns.svc_time, age);
+                  
 #if USE_DNSSERVERS
 
     done = ipcacheParse(i, reply);
@@ -661,7 +670,7 @@
 
     {
         ipcacheAddEntry(i);
-        ipcacheCallback(i);
+        ipcacheCallback(i, age);
     }
 }
 
@@ -694,16 +703,16 @@
     if (name == NULL || name[0] == '\0') {
         debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
         IpcacheStats.invalid++;
-        dns_error_message = "Invalid hostname";
-        handler(NULL, handlerData);
+        const DnsLookupDetails details("Invalid hostname", -1); // error, no lookup
+        handler(NULL, details, handlerData);
         return;
     }
 
     if ((addrs = ipcacheCheckNumeric(name))) {
         debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name << "' (already numeric)");
-        dns_error_message = NULL;
         IpcacheStats.numeric_hits++;
-        handler(addrs, handlerData);
+        const DnsLookupDetails details(NULL, -1); // no error, no lookup
+        handler(addrs, details, handlerData);
         return;
     }
 
@@ -729,7 +738,7 @@
 
         i->handlerData = cbdataReference(handlerData);
 
-        ipcacheCallback(i);
+        ipcacheCallback(i, -1); // no lookup
 
         return;
     }
@@ -823,16 +832,16 @@
         i = NULL;
     } else if (i->flags.negcached) {
         IpcacheStats.negative_hits++;
-        dns_error_message = i->error_message;
+        // ignore i->error_message: the caller just checks IP cache presence
         return NULL;
     } else {
         IpcacheStats.hits++;
         i->lastref = squid_curtime;
-        dns_error_message = i->error_message;
+        // ignore i->error_message: the caller just checks IP cache presence
         return &i->addrs;
     }
 
-    dns_error_message = NULL;
+    /* no entry [any more] */
 
     if ((addrs = ipcacheCheckNumeric(name))) {
         IpcacheStats.numeric_hits++;
@@ -1064,7 +1073,7 @@
 /// \ingroup IPCacheInternal
 /// Callback.
 static void
-ipcacheHandleCnameRecurse(const ipcache_addrs *addrs, void *cbdata)
+ipcacheHandleCnameRecurse(const ipcache_addrs *addrs, const DnsLookupDetails &, void *cbdata)
 {
 #if DNS_CNAME
     ipcache_entry *i = NULL;
@@ -1159,7 +1168,7 @@
     /* finish the lookup we were doing on parent when we got side-tracked for CNAME loop */
     if (i->cname_wait == 0) {
         ipcacheAddEntry(i);
-        ipcacheCallback(i);
+        ipcacheCallback(i, i->age()); // age since i creation, includes CNAMEs
     }
     // else still more CNAME to be found.
 #endif /* DNS_CNAME */
diff -u -r -N squid-3.1.0.10/src/main.cc squid-3.1.0.11/src/main.cc
--- squid-3.1.0.10/src/main.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/main.cc	2009-07-19 17:11:14.000000000 +1200
@@ -34,6 +34,9 @@
 
 #include "squid.h"
 #include "AccessLogEntry.h"
+#if ICAP_CLIENT
+#include "adaptation/icap/icap_log.h"
+#endif
 #include "auth/Gadgets.h"
 #include "ConfigParser.h"
 #include "errorpage.h"
@@ -695,6 +698,9 @@
     storeDirCloseSwapLogs();
     storeLogClose();
     accessLogClose();
+#if ICAP_CLIENT
+    icapLogClose();
+#endif
     useragentLogClose();
     refererCloseLog();
 
@@ -726,6 +732,9 @@
     parseEtcHosts();
     errorInitialize();		/* reload error pages */
     accessLogInit();
+#if ICAP_CLIENT
+    icapLogOpen();
+#endif
     storeLogOpen();
     useragentOpenLog();
     refererOpenLog();
@@ -790,6 +799,9 @@
     accessLogRotate();		/* access.log */
     useragentRotateLog();	/* useragent.log */
     refererRotateLog();		/* referer.log */
+#if ICAP_CLIENT
+    icapLogRotate();               /*icap.log*/
+#endif
 #if WIP_FWD_LOG
 
     fwdLogRotate();
@@ -951,6 +963,10 @@
 
     accessLogInit();
 
+#if ICAP_CLIENT    
+    icapLogOpen();
+#endif
+
 #if USE_IDENT
     Ident::Init();
 #endif
diff -u -r -N squid-3.1.0.10/src/Makefile.am squid-3.1.0.11/src/Makefile.am
--- squid-3.1.0.10/src/Makefile.am	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/Makefile.am	2009-07-19 17:11:09.000000000 +1200
@@ -15,6 +15,10 @@
 DNSSERVER =
 endif
 
+DNSSOURCE += \
+	DnsLookupDetails.h \
+	DnsLookupDetails.cc
+
 SNMP_ALL_SOURCE = \
 	snmp_core.cc \
 	snmp_agent.cc
@@ -233,6 +237,8 @@
 	dns.cc \
 	dnsserver.cc \
 	dns_internal.cc \
+	DnsLookupDetails.cc \
+	DnsLookupDetails.h \
 	htcp.cc \
 	htcp.h \
 	ipc.cc \
diff -u -r -N squid-3.1.0.10/src/Makefile.in squid-3.1.0.11/src/Makefile.in
--- squid-3.1.0.10/src/Makefile.in	2009-07-18 16:16:47.000000000 +1200
+++ squid-3.1.0.11/src/Makefile.in	2009-07-19 17:11:33.000000000 +1200
@@ -179,17 +179,17 @@
 	DiskIO/ReadRequest.cc DiskIO/ReadRequest.h \
 	DiskIO/WriteRequest.cc DiskIO/WriteRequest.h DiskIO/DiskFile.h \
 	DiskIO/DiskIOStrategy.h DiskIO/IORequestor.h \
-	DiskIO/DiskIOModule.h dlink.h dlink.cc dns_internal.cc dns.cc \
-	enums.h errorpage.cc errorpage.h ETag.cc event.cc event.h \
-	EventLoop.h EventLoop.cc external_acl.cc ExternalACL.h \
-	ExternalACLEntry.cc ExternalACLEntry.h fd.cc fde.cc fde.h \
-	filemap.cc forward.cc forward.h fqdncache.cc ftp.cc Generic.h \
-	globals.h gopher.cc helper.cc helper.h HierarchyLogEntry.h \
-	htcp.cc htcp.h http.cc http.h HttpStatusLine.cc \
-	HttpStatusLine.h HttpHdrCc.cc HttpHdrRange.cc HttpHdrSc.cc \
-	HttpHdrSc.h HttpHdrScTarget.cc HttpHdrScTarget.h \
-	HttpHdrContRange.cc HttpHdrContRange.h HttpHeader.cc \
-	HttpHeader.h HttpHeaderMask.h HttpHeaderRange.h \
+	DiskIO/DiskIOModule.h dlink.h dlink.cc dns_internal.cc \
+	DnsLookupDetails.h DnsLookupDetails.cc dns.cc enums.h \
+	errorpage.cc errorpage.h ETag.cc event.cc event.h EventLoop.h \
+	EventLoop.cc external_acl.cc ExternalACL.h ExternalACLEntry.cc \
+	ExternalACLEntry.h fd.cc fde.cc fde.h filemap.cc forward.cc \
+	forward.h fqdncache.cc ftp.cc Generic.h globals.h gopher.cc \
+	helper.cc helper.h HierarchyLogEntry.h htcp.cc htcp.h http.cc \
+	http.h HttpStatusLine.cc HttpStatusLine.h HttpHdrCc.cc \
+	HttpHdrRange.cc HttpHdrSc.cc HttpHdrSc.h HttpHdrScTarget.cc \
+	HttpHdrScTarget.h HttpHdrContRange.cc HttpHdrContRange.h \
+	HttpHeader.cc HttpHeader.h HttpHeaderMask.h HttpHeaderRange.h \
 	HttpHeaderTools.cc HttpBody.cc HttpMsg.cc HttpMsg.h \
 	HttpReply.cc HttpReply.h HttpRequest.cc HttpRequest.h \
 	HttpRequestMethod.cc HttpRequestMethod.h HttpVersion.h ICP.h \
@@ -230,8 +230,10 @@
 @USE_DELAY_POOLS_TRUE@am__objects_7 = $(am__objects_6)
 am__objects_8 = DiskIO/DiskIOModule.$(OBJEXT) \
 	DiskIO/ReadRequest.$(OBJEXT) DiskIO/WriteRequest.$(OBJEXT)
-@USE_DNSSERVER_FALSE@am__objects_9 = dns_internal.$(OBJEXT)
-@USE_DNSSERVER_TRUE@am__objects_9 = dns.$(OBJEXT)
+@USE_DNSSERVER_FALSE@am__objects_9 = dns_internal.$(OBJEXT) \
+@USE_DNSSERVER_FALSE@	DnsLookupDetails.$(OBJEXT)
+@USE_DNSSERVER_TRUE@am__objects_9 = dns.$(OBJEXT) \
+@USE_DNSSERVER_TRUE@	DnsLookupDetails.$(OBJEXT)
 @ENABLE_HTCP_TRUE@am__objects_10 = htcp.$(OBJEXT)
 @USE_IPC_WIN32_FALSE@am__objects_11 = ipc.$(OBJEXT)
 @USE_IPC_WIN32_TRUE@am__objects_11 = ipc_win32.$(OBJEXT)
@@ -305,10 +307,11 @@
 	DelaySpec.cc DelaySpec.h DelayTagged.cc DelayTagged.h \
 	DelayUser.cc DelayUser.h DelayVector.cc DelayVector.h \
 	NullDelayId.cc NullDelayId.h dns.cc dnsserver.cc \
-	dns_internal.cc htcp.cc htcp.h ipc.cc ipc_win32.cc \
-	ProfStats.cc LeakFinder.cc LeakFinder.h snmp_core.cc \
-	snmp_agent.cc unlinkd.cc ssl_support.cc ssl_support.h win32.cc \
-	WinSvc.cc LoadableModule.h LoadableModule.cc LoadableModules.h \
+	dns_internal.cc DnsLookupDetails.cc DnsLookupDetails.h htcp.cc \
+	htcp.h ipc.cc ipc_win32.cc ProfStats.cc LeakFinder.cc \
+	LeakFinder.h snmp_core.cc snmp_agent.cc unlinkd.cc \
+	ssl_support.cc ssl_support.h win32.cc WinSvc.cc \
+	LoadableModule.h LoadableModule.cc LoadableModules.h \
 	LoadableModules.cc DiskIO/DiskThreads/aiops.cc \
 	DiskIO/DiskThreads/aiops_win32.cc
 am__objects_23 = DiskIO/DiskIOModules_gen.$(OBJEXT)
@@ -392,18 +395,18 @@
 	DelaySpec.cc DelaySpec.h DelayTagged.cc DelayTagged.h \
 	DelayUser.cc DelayUser.h DelayVector.cc DelayVector.h \
 	NullDelayId.cc NullDelayId.h disk.cc dlink.h dlink.cc \
-	dns_internal.cc dns.cc errorpage.cc ETag.cc external_acl.cc \
-	ExternalACLEntry.cc fd.cc fde.cc forward.cc fqdncache.cc \
-	ftp.cc gopher.cc helper.cc htcp.cc htcp.h http.cc HttpBody.cc \
-	HttpHeader.cc HttpHeaderTools.cc HttpHdrCc.cc \
-	HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
-	HttpHdrScTarget.cc HttpMsg.cc HttpReply.cc HttpStatusLine.cc \
-	icp_v2.cc icp_v3.cc ipc.cc ipc_win32.cc ipcache.cc int.cc \
-	internal.cc list.cc logfile.cc multicast.cc mem_node.cc \
-	MemBuf.cc MemObject.cc mime.cc neighbors.cc Packer.cc \
-	Parsing.cc pconn.cc peer_digest.cc peer_select.cc \
-	peer_sourcehash.cc peer_userhash.cc redirect.cc referer.cc \
-	refresh.cc RemovalPolicy.cc Server.cc snmp_core.cc \
+	dns_internal.cc DnsLookupDetails.h DnsLookupDetails.cc dns.cc \
+	errorpage.cc ETag.cc external_acl.cc ExternalACLEntry.cc fd.cc \
+	fde.cc forward.cc fqdncache.cc ftp.cc gopher.cc helper.cc \
+	htcp.cc htcp.h http.cc HttpBody.cc HttpHeader.cc \
+	HttpHeaderTools.cc HttpHdrCc.cc HttpHdrContRange.cc \
+	HttpHdrRange.cc HttpHdrSc.cc HttpHdrScTarget.cc HttpMsg.cc \
+	HttpReply.cc HttpStatusLine.cc icp_v2.cc icp_v3.cc ipc.cc \
+	ipc_win32.cc ipcache.cc int.cc internal.cc list.cc logfile.cc \
+	multicast.cc mem_node.cc MemBuf.cc MemObject.cc mime.cc \
+	neighbors.cc Packer.cc Parsing.cc pconn.cc peer_digest.cc \
+	peer_select.cc peer_sourcehash.cc peer_userhash.cc redirect.cc \
+	referer.cc refresh.cc RemovalPolicy.cc Server.cc snmp_core.cc \
 	snmp_agent.cc ssl_support.cc ssl_support.h stat.cc StatHist.cc \
 	stmem.cc store.cc store_client.cc store_digest.cc store_dir.cc \
 	store_io.cc store_key_md5.cc store_log.cc store_rebuild.cc \
@@ -610,11 +613,12 @@
 	DelayPool.cc DelayPool.h DelayPools.h DelaySpec.cc DelaySpec.h \
 	DelayTagged.cc DelayTagged.h DelayUser.cc DelayUser.h \
 	DelayVector.cc DelayVector.h NullDelayId.cc NullDelayId.h \
-	disk.cc dlink.h dlink.cc dns_internal.cc dns.cc errorpage.cc \
-	ETag.cc external_acl.cc ExternalACLEntry.cc fd.cc fde.cc \
-	forward.cc fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc \
-	htcp.h http.cc HttpBody.cc HttpHeader.cc HttpHeaderTools.cc \
-	HttpHdrCc.cc HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
+	disk.cc dlink.h dlink.cc dns_internal.cc DnsLookupDetails.h \
+	DnsLookupDetails.cc dns.cc errorpage.cc ETag.cc \
+	external_acl.cc ExternalACLEntry.cc fd.cc fde.cc forward.cc \
+	fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc htcp.h http.cc \
+	HttpBody.cc HttpHeader.cc HttpHeaderTools.cc HttpHdrCc.cc \
+	HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
 	HttpHdrScTarget.cc HttpMsg.cc HttpReply.cc HttpStatusLine.cc \
 	icp_v2.cc icp_v3.cc ipc.cc ipc_win32.cc ipcache.cc int.cc \
 	internal.cc list.cc logfile.cc multicast.cc mem_node.cc \
@@ -701,11 +705,12 @@
 	DelayPool.cc DelayPool.h DelayPools.h DelaySpec.cc DelaySpec.h \
 	DelayTagged.cc DelayTagged.h DelayUser.cc DelayUser.h \
 	DelayVector.cc DelayVector.h NullDelayId.cc NullDelayId.h \
-	disk.cc dlink.h dlink.cc dns_internal.cc dns.cc errorpage.cc \
-	ETag.cc external_acl.cc ExternalACLEntry.cc fd.cc fde.cc \
-	forward.cc fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc \
-	htcp.h http.cc HttpBody.cc HttpHeader.cc HttpHeaderTools.cc \
-	HttpHdrCc.cc HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
+	disk.cc dlink.h dlink.cc dns_internal.cc DnsLookupDetails.h \
+	DnsLookupDetails.cc dns.cc errorpage.cc ETag.cc \
+	external_acl.cc ExternalACLEntry.cc fd.cc fde.cc forward.cc \
+	fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc htcp.h http.cc \
+	HttpBody.cc HttpHeader.cc HttpHeaderTools.cc HttpHdrCc.cc \
+	HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
 	HttpHdrScTarget.cc HttpMsg.cc HttpReply.cc HttpStatusLine.cc \
 	icp_v2.cc icp_v3.cc ipc.cc ipc_win32.cc ipcache.cc int.cc \
 	internal.cc list.cc logfile.cc multicast.cc mem_node.cc \
@@ -792,11 +797,12 @@
 	DelayPool.cc DelayPool.h DelayPools.h DelaySpec.cc DelaySpec.h \
 	DelayTagged.cc DelayTagged.h DelayUser.cc DelayUser.h \
 	DelayVector.cc DelayVector.h NullDelayId.cc NullDelayId.h \
-	disk.cc dlink.h dlink.cc dns_internal.cc dns.cc errorpage.cc \
-	ETag.cc external_acl.cc ExternalACLEntry.cc fd.cc fde.cc \
-	forward.cc fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc \
-	htcp.h http.cc HttpBody.cc HttpHeader.cc HttpHeaderTools.cc \
-	HttpHdrCc.cc HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
+	disk.cc dlink.h dlink.cc dns_internal.cc DnsLookupDetails.h \
+	DnsLookupDetails.cc dns.cc errorpage.cc ETag.cc \
+	external_acl.cc ExternalACLEntry.cc fd.cc fde.cc forward.cc \
+	fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc htcp.h http.cc \
+	HttpBody.cc HttpHeader.cc HttpHeaderTools.cc HttpHdrCc.cc \
+	HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
 	HttpHdrScTarget.cc HttpMsg.cc HttpReply.cc HttpStatusLine.cc \
 	icp_v2.cc icp_v3.cc ipc.cc ipc_win32.cc ipcache.cc int.cc \
 	internal.cc list.cc logfile.cc multicast.cc mem_node.cc \
@@ -974,11 +980,12 @@
 	DelayPool.cc DelayPool.h DelayPools.h DelaySpec.cc DelaySpec.h \
 	DelayTagged.cc DelayTagged.h DelayUser.cc DelayUser.h \
 	DelayVector.cc DelayVector.h NullDelayId.cc NullDelayId.h \
-	disk.cc dlink.h dlink.cc dns_internal.cc dns.cc errorpage.cc \
-	ETag.cc external_acl.cc ExternalACLEntry.cc fd.cc fde.cc \
-	forward.cc fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc \
-	htcp.h http.cc HttpBody.cc HttpHeader.cc HttpHeaderTools.cc \
-	HttpHdrCc.cc HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
+	disk.cc dlink.h dlink.cc dns_internal.cc DnsLookupDetails.h \
+	DnsLookupDetails.cc dns.cc errorpage.cc ETag.cc \
+	external_acl.cc ExternalACLEntry.cc fd.cc fde.cc forward.cc \
+	fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc htcp.h http.cc \
+	HttpBody.cc HttpHeader.cc HttpHeaderTools.cc HttpHdrCc.cc \
+	HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
 	HttpHdrScTarget.cc HttpMsg.cc HttpReply.cc HttpStatusLine.cc \
 	icp_v2.cc icp_v3.cc ipc.cc ipc_win32.cc ipcache.cc int.cc \
 	internal.cc list.cc logfile.cc multicast.cc mem_node.cc \
@@ -1106,11 +1113,12 @@
 	DelayPool.cc DelayPool.h DelayPools.h DelaySpec.cc DelaySpec.h \
 	DelayTagged.cc DelayTagged.h DelayUser.cc DelayUser.h \
 	DelayVector.cc DelayVector.h NullDelayId.cc NullDelayId.h \
-	disk.cc dlink.h dlink.cc dns_internal.cc dns.cc errorpage.cc \
-	ETag.cc external_acl.cc ExternalACLEntry.cc fd.cc fde.cc \
-	forward.cc fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc \
-	htcp.h http.cc HttpBody.cc HttpHdrCc.cc HttpHdrContRange.cc \
-	HttpHdrRange.cc HttpHdrSc.cc HttpHdrScTarget.cc HttpHeader.cc \
+	disk.cc dlink.h dlink.cc dns_internal.cc DnsLookupDetails.h \
+	DnsLookupDetails.cc dns.cc errorpage.cc ETag.cc \
+	external_acl.cc ExternalACLEntry.cc fd.cc fde.cc forward.cc \
+	fqdncache.cc ftp.cc gopher.cc helper.cc htcp.cc htcp.h http.cc \
+	HttpBody.cc HttpHdrCc.cc HttpHdrContRange.cc HttpHdrRange.cc \
+	HttpHdrSc.cc HttpHdrScTarget.cc HttpHeader.cc \
 	HttpHeaderTools.cc HttpMsg.cc HttpReply.cc HttpRequest.cc \
 	HttpRequestMethod.cc HttpStatusLine.cc icp_v2.cc icp_v3.cc \
 	int.cc internal.cc ipc.cc ipc_win32.cc ipcache.cc list.cc \
@@ -1484,8 +1492,10 @@
 	-I$(top_builddir)/include @SQUID_CPPUNIT_INC@ \
 	-I$(top_srcdir)/lib -I$(top_builddir)/src
 AUTOMAKE_OPTIONS = subdir-objects
-@USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.cc
-@USE_DNSSERVER_TRUE@DNSSOURCE = dns.cc
+@USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.cc DnsLookupDetails.h \
+@USE_DNSSERVER_FALSE@	DnsLookupDetails.cc
+@USE_DNSSERVER_TRUE@DNSSOURCE = dns.cc DnsLookupDetails.h \
+@USE_DNSSERVER_TRUE@	DnsLookupDetails.cc
 @USE_DNSSERVER_FALSE@DNSSERVER = 
 @USE_DNSSERVER_TRUE@DNSSERVER = dnsserver
 SNMP_ALL_SOURCE = \
@@ -1617,6 +1627,8 @@
 	dns.cc \
 	dnsserver.cc \
 	dns_internal.cc \
+	DnsLookupDetails.cc \
+	DnsLookupDetails.h \
 	htcp.cc \
 	htcp.h \
 	ipc.cc \
@@ -3647,6 +3659,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DelayUser.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DelayVector.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DescriptorSet.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DnsLookupDetails.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ETag.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EventLoop.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ExternalACLEntry.Po@am__quote@
diff -u -r -N squid-3.1.0.10/src/multicast.cc squid-3.1.0.11/src/multicast.cc
--- squid-3.1.0.10/src/multicast.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/multicast.cc	2009-07-19 17:11:14.000000000 +1200
@@ -50,7 +50,7 @@
 }
 
 void
-mcastJoinGroups(const ipcache_addrs * ia, void *datanotused)
+mcastJoinGroups(const ipcache_addrs *ia, const DnsLookupDetails &, void *datanotused)
 {
 #ifdef IP_MULTICAST_TTL
     int fd = theInIcpConnection;
diff -u -r -N squid-3.1.0.10/src/neighbors.cc squid-3.1.0.11/src/neighbors.cc
--- squid-3.1.0.10/src/neighbors.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/neighbors.cc	2009-07-19 17:11:14.000000000 +1200
@@ -1230,7 +1230,7 @@
 }
 
 static void
-peerDNSConfigure(const ipcache_addrs * ia, void *data)
+peerDNSConfigure(const ipcache_addrs *ia, const DnsLookupDetails &, void *data)
 {
     peer *p = (peer *)data;
 
@@ -1399,7 +1399,7 @@
 }
 
 static void
-peerProbeConnectDone(int fd, comm_err_t status, int xerrno, void *data)
+peerProbeConnectDone(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
 {
     peer *p = (peer*)data;
 
diff -u -r -N squid-3.1.0.10/src/protos.h squid-3.1.0.11/src/protos.h
--- squid-3.1.0.10/src/protos.h	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/protos.h	2009-07-19 17:11:14.000000000 +1200
@@ -127,8 +127,6 @@
 SQUIDCEXTERN void dnsShutdown(void);
 SQUIDCEXTERN void dnsInit(void);
 SQUIDCEXTERN void dnsSubmit(const char *lookup, HLPCB * callback, void *data);
-/// XXX: a temporary hack to work around the missing DNS error info
-const char *dns_error_message_safe();
 
 /* dns_internal.c */
 SQUIDCEXTERN void idnsInit(void);
diff -u -r -N squid-3.1.0.10/src/send-announce.cc squid-3.1.0.11/src/send-announce.cc
--- squid-3.1.0.10/src/send-announce.cc	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/send-announce.cc	2009-07-19 17:11:14.000000000 +1200
@@ -55,7 +55,7 @@
 }
 
 static void
-send_announce(const ipcache_addrs * ia, void *junk)
+send_announce(const ipcache_addrs *ia, const DnsLookupDetails &, void *junk)
 {
     LOCAL_ARRAY(char, tbuf, 256);
     LOCAL_ARRAY(char, sndbuf, BUFSIZ);
diff -u -r -N squid-3.1.0.10/src/Server.cc squid-3.1.0.11/src/Server.cc
--- squid-3.1.0.10/src/Server.cc	2009-07-18 16:16:18.000000000 +1200
+++ squid-3.1.0.11/src/Server.cc	2009-07-19 17:11:12.000000000 +1200
@@ -39,10 +39,11 @@
 #include "HttpReply.h"
 #include "TextException.h"
 #include "errorpage.h"
+#include "SquidTime.h"
 
 #if USE_ADAPTATION
 #include "adaptation/AccessCheck.h"
-#include "adaptation/Service.h"
+#include "adaptation/Iterator.h"
 #endif
 
 // implemented in client_side_reply.cc until sides have a common parent
@@ -173,6 +174,10 @@
 
     completed = true;
 
+    HttpRequest *r = originalRequest();
+    r->hier.total_response_time = r->hier.first_conn_start.tv_sec ?
+        tvSubMsec(r->hier.first_conn_start, current_time) : -1;
+    
     if (requestBodySource != NULL)
         stopConsumingFrom(requestBodySource);
 
@@ -503,24 +508,11 @@
 }
 
 #if USE_ADAPTATION
-/*
- * Initiate an ICAP transaction.  Return true on success.
- * Caller will handle error condition by generating a Squid error message
- * or take other action.
- */
-bool
-ServerStateData::startAdaptation(Adaptation::ServicePointer service, HttpRequest *cause)
+/// Initiate an asynchronous adaptation transaction which will call us back.
+void
+ServerStateData::startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause)
 {
     debugs(11, 5, "ServerStateData::startAdaptation() called");
-    if (!service) {
-        debugs(11, 3, "ServerStateData::startAdaptation fails: lack of service");
-        return false;
-    }
-    if (service->broken()) {
-        debugs(11, 3, "ServerStateData::startAdaptation fails: broken service");
-        return false;
-    }
-
     // check whether we should be sending a body as well
     // start body pipe to feed ICAP transaction if needed
     assert(!virginBodyDestination);
@@ -536,9 +528,10 @@
             virginBodyDestination->setBodySize(size);
     }
 
-    adaptedHeadSource = initiateAdaptation(service->makeXactLauncher(
-                                               this, vrep, cause));
-    return adaptedHeadSource != NULL;
+    adaptedHeadSource = initiateAdaptation(
+        new Adaptation::Iterator(this, vrep, cause, group));
+    startedAdaptation = adaptedHeadSource != NULL;
+    Must(startedAdaptation);
 }
 
 // properly cleans up ICAP-related state
@@ -753,7 +746,7 @@
 }
 
 void
-ServerStateData::adaptationAclCheckDone(Adaptation::ServicePointer service)
+ServerStateData::adaptationAclCheckDone(Adaptation::ServiceGroupPointer group)
 {
     adaptationAccessCheckPending = false;
 
@@ -768,34 +761,22 @@
     }
     // TODO: Should we check receivedBodyTooLarge on the server-side as well?
 
-    startedAdaptation = startAdaptation(service, originalRequest());
-
-    if (!startedAdaptation && (!service || service->cfg().bypass)) {
-        // handle ICAP start failure when no service was selected
-        // or where the selected service was optional
+    if (!group) {
+        debugs(11,3, HERE << "no adapation needed");
         setFinalReply(virginReply());
         processReplyBody();
         return;
     }
 
-    if (!startedAdaptation) {
-        // handle start failure for an essential ICAP service
-        ErrorState *err = errorCon(ERR_ICAP_FAILURE,
-                                   HTTP_INTERNAL_SERVER_ERROR, originalRequest());
-        err->xerrno = errno;
-        errorAppendEntry(entry, err);
-        abortTransaction("ICAP start failure");
-        return;
-    }
-
+    startAdaptation(group, originalRequest());
     processReplyBody();
 }
 
 void
-ServerStateData::adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data)
+ServerStateData::adaptationAclCheckDoneWrapper(Adaptation::ServiceGroupPointer group, void *data)
 {
     ServerStateData *state = (ServerStateData *)data;
-    state->adaptationAclCheckDone(service);
+    state->adaptationAclCheckDone(group);
 }
 #endif
 
diff -u -r -N squid-3.1.0.10/src/Server.h squid-3.1.0.11/src/Server.h
--- squid-3.1.0.10/src/Server.h	2009-07-18 16:16:22.000000000 +1200
+++ squid-3.1.0.11/src/Server.h	2009-07-19 17:11:12.000000000 +1200
@@ -86,8 +86,8 @@
     virtual  HttpRequest *originalRequest();
 
 #if USE_ADAPTATION
-    void adaptationAclCheckDone(Adaptation::ServicePointer service);
-    static void adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data);
+    void adaptationAclCheckDone(Adaptation::ServiceGroupPointer group);
+    static void adaptationAclCheckDoneWrapper(Adaptation::ServiceGroupPointer group, void *data);
 
     // ICAPInitiator: start an ICAP transaction and receive adapted headers.
     virtual void noteAdaptationAnswer(HttpMsg *message);
@@ -141,7 +141,7 @@
     bool abortOnBadEntry(const char *abortReason);
 
 #if USE_ADAPTATION
-    bool startAdaptation(Adaptation::ServicePointer service, HttpRequest *cause);
+    void startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause);
     void adaptVirginReplyBody(const char *buf, ssize_t len);
     void cleanAdaptation();
     virtual bool doneWithAdaptation() const;   /**< did we end ICAP communication? */
diff -u -r -N squid-3.1.0.10/src/structs.h squid-3.1.0.11/src/structs.h
--- squid-3.1.0.10/src/structs.h	2009-07-18 16:16:27.000000000 +1200
+++ squid-3.1.0.11/src/structs.h	2009-07-19 17:11:15.000000000 +1200
@@ -183,6 +183,7 @@
     } Timeout;
     size_t maxRequestHeaderSize;
     int64_t maxRequestBodySize;
+    int64_t maxChunkedRequestBodySize;
     size_t maxReplyHeaderSize;
     acl_size_t *ReplyBodySize;
 
@@ -264,6 +265,10 @@
 
         customlog *accesslogs;
 
+#if ICAP_CLIENT        
+        customlog *icaplogs;
+#endif
+
         int rotateNumber;
     } Log;
     char *adminEmail;
@@ -479,6 +484,9 @@
         acl_access *followXFF;
 #endif /* FOLLOW_X_FORWARDED_FOR */
 
+#if ICAP_CLIENT
+        acl_access* icap;
+#endif
     } accessList;
     acl_deny_info_list *denyInfoList;
     authConfig authConfiguration;
diff -u -r -N squid-3.1.0.10/src/tunnel.cc squid-3.1.0.11/src/tunnel.cc
--- squid-3.1.0.10/src/tunnel.cc	2009-07-18 16:16:28.000000000 +1200
+++ squid-3.1.0.11/src/tunnel.cc	2009-07-19 17:11:15.000000000 +1200
@@ -553,12 +553,14 @@
 
 
 static void
-tunnelConnectDone(int fdnotused, comm_err_t status, int xerrno, void *data)
+tunnelConnectDone(int fdnotused, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     HttpRequest *request = tunnelState->request;
     ErrorState *err = NULL;
 
+    request->recordLookup(dns);
+
     if (tunnelState->servers->_peer)
         hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
                       tunnelState->servers->_peer->host);
@@ -573,7 +575,7 @@
         debugs(26, 4, "tunnelConnect: Unknown host: " << tunnelState->host);
         err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request);
         *tunnelState->status_ptr = HTTP_NOT_FOUND;
-        err->dnsserver_msg = xstrdup(dns_error_message_safe());
+        err->dnsError = dns.error;
         err->callback = tunnelErrorComplete;
         err->callback_data = tunnelState;
         errorSend(tunnelState->client.fd(), err);
diff -u -r -N squid-3.1.0.10/src/typedefs.h squid-3.1.0.11/src/typedefs.h
--- squid-3.1.0.10/src/typedefs.h	2009-07-18 16:16:28.000000000 +1200
+++ squid-3.1.0.11/src/typedefs.h	2009-07-19 17:11:15.000000000 +1200
@@ -199,9 +199,10 @@
 typedef void DUCB(int errflag, void *data);	/* disk unlink CB */
 typedef void DTCB(int errflag, void *data);	/* disk trunc CB */
 
-typedef void FQDNH(const char *, void *);
+class DnsLookupDetails;
+typedef void FQDNH(const char *, const DnsLookupDetails &details, void *);
 typedef void IDCB(const char *ident, void *data);
-typedef void IPH(const ipcache_addrs *, void *);
+typedef void IPH(const ipcache_addrs *, const DnsLookupDetails &details, void *);
 typedef void IRCB(struct peer *, peer_t, protocol_t, void *, void *data);
 
 class FwdServer;
