Address more comments from Amos.

- Change debug levels in existing code.
- Rename htcpHandle to htcpHandleMsg.
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [EMAIL PROTECTED]
# target_branch: http://www.squid-cache.org/bzr/squid3/trunk/
# testament_sha1: b772ff430ab6af3664e574b0a92e21d6e3d7856a
# timestamp: 2008-09-23 09:45:25 +1000
# base_revision_id: [EMAIL PROTECTED]
#   s3t40xszpvb50lx2
# 
# Begin patch
=== modified file 'src/Server.cc'
--- src/Server.cc	2008-09-03 01:00:39 +0000
+++ src/Server.cc	2008-09-18 02:55:19 +0000
@@ -46,7 +46,7 @@
 #endif
 
 // implemented in client_side_reply.cc until sides have a common parent
-extern void purgeEntriesByUrl(const char *url);
+extern void purgeEntriesByUrl(HttpRequest * req, const char *url);
 
 
 ServerStateData::ServerStateData(FwdState *theFwdState): AsyncJob("ServerStateData"),requestSender(NULL)
@@ -402,7 +402,7 @@
 
 // purges entries that match the value of a given HTTP [response] header
 static void
-purgeEntriesByHeader(const HttpRequest *req, const char *reqUrl, HttpMsg *rep, http_hdr_type hdr)
+purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, HttpMsg *rep, http_hdr_type hdr)
 {
     const char *hdrUrl, *absUrl;
     
@@ -426,7 +426,7 @@
         return;
     }
     
-    purgeEntriesByUrl(hdrUrl);
+    purgeEntriesByUrl(req, hdrUrl);
     
     if (absUrl != NULL) {
         safe_free(absUrl);
@@ -448,7 +448,7 @@
    // XXX: should we use originalRequest() here?
    const char *reqUrl = urlCanonical(request);
    debugs(88, 5, "maybe purging due to " << RequestMethodStr(request->method) << ' ' << reqUrl);
-   purgeEntriesByUrl(reqUrl);
+   purgeEntriesByUrl(request, reqUrl);
    purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_LOCATION);
    purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_CONTENT_LOCATION);
 }

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2008-08-10 05:05:45 +0000
+++ src/cache_cf.cc	2008-09-01 02:43:20 +0000
@@ -1731,6 +1731,22 @@
         } else if (!strcasecmp(token, "htcp-oldsquid")) {
             p->options.htcp = 1;
             p->options.htcp_oldsquid = 1;
+        } else if (!strcasecmp(token, "htcp-no-clr")) {
+            if (p->options.htcp_only_clr)
+        	fatalf("parse_peer: can't set htcp-no-clr and htcp-only-clr simultaneously");
+            p->options.htcp = 1;
+            p->options.htcp_no_clr = 1;
+        } else if (!strcasecmp(token, "htcp-no-purge-clr")) {
+            p->options.htcp = 1;
+            p->options.htcp_no_purge_clr = 1;
+        } else if (!strcasecmp(token, "htcp-only-clr")) {
+            if (p->options.htcp_no_clr)
+        	fatalf("parse_peer: can't set htcp-no-clr and htcp-only-clr simultaneously");
+            p->options.htcp = 1;
+            p->options.htcp_only_clr = 1;
+        } else if (!strcasecmp(token, "htcp-forward-clr")) {
+            p->options.htcp = 1;
+            p->options.htcp_forward_clr = 1;
 #endif
 
         } else if (!strcasecmp(token, "no-netdb-exchange")) {

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2008-09-20 09:43:40 +0000
+++ src/cf.data.pre	2008-09-22 00:04:12 +0000
@@ -1578,6 +1578,10 @@
 		     max-conn=n
 		     htcp
 		     htcp-oldsquid
+		     htcp-no-clr
+		     htcp-no-purge-clr
+		     htcp-only-clr
+		     htcp-forward-clr
 		     originserver
 		     name=xxx
 		     forceddomain=name
@@ -1720,6 +1724,20 @@
 		     You MUST also set htcp_access expicitly. The default of
 		     deny all will prevent peer traffic.		     
 
+		     use 'htcp-no-clr' to send HTCP to the neighbor but without
+		     sending any CLR requests.  This cannot be used with
+		     htcp-only-clr.
+		
+		     use 'htcp-no-purge-clr' to send HTCP to the neighbor
+		     including CLRs but only when they do not result from
+		     PURGE requests.
+		
+		     use 'htcp-only-clr' to send HTCP to the neighbor but ONLY
+		     CLR requests.  This cannot be used with htcp-no-clr.
+		
+		     use 'htcp-forward-clr' to forward any HTCP CLR requests
+		     this proxy receives to the peer.
+
 		     'originserver' causes this parent peer to be contacted as
 		     a origin server. Meant to be used in accelerator setups.
 

=== modified file 'src/client_side_reply.cc'
--- src/client_side_reply.cc	2008-09-20 05:00:47 +0000
+++ src/client_side_reply.cc	2008-09-22 00:04:12 +0000
@@ -719,23 +719,39 @@
  * keys depend on vary headers.
  */
 void
-purgeEntriesByUrl(const char *url)
+purgeEntriesByUrl(HttpRequest * req, const char *url)
 {
+#if USE_HTCP
+    bool get_or_head_sent = false;
+#endif
+    
     for (HttpRequestMethod m(METHOD_NONE); m != METHOD_ENUM_END; ++m) {
         if (m.isCacheble()) {
             if (StoreEntry *entry = storeGetPublic(url, m)) {
                 debugs(88, 5, "purging " << RequestMethodStr(m) << ' ' << url);
+#if USE_HTCP
+                neighborsHtcpClear(entry, url, req, m, HTCP_CLR_INVALIDATION);
+                if (m == METHOD_GET || m == METHOD_HEAD) {
+                    get_or_head_sent = true;
+                }
+#endif
                 entry->release();
             }
         }
     }
+
+#if USE_HTCP
+    if (!get_or_head_sent) {
+        neighborsHtcpClear(NULL, url, req, HttpRequestMethod(METHOD_GET), HTCP_CLR_INVALIDATION);
+    }
+#endif
 }
 
 void 
 clientReplyContext::purgeAllCached()
 {
 	const char *url = urlCanonical(http->request);
-    purgeEntriesByUrl(url);
+    purgeEntriesByUrl(http->request, url);
 }
 
 void
@@ -849,6 +865,9 @@
     if (!newEntry->isNull()) {
         /* Release the cached URI */
         debugs(88, 4, "clientPurgeRequest: GET '" << newEntry->url() << "'" );
+#if USE_HTCP
+        neighborsHtcpClear(newEntry, NULL, http->request, HttpRequestMethod(METHOD_GET), HTCP_CLR_PURGE);
+#endif
         newEntry->release();
         purgeStatus = HTTP_OK;
     }
@@ -862,6 +881,9 @@
 {
     if (newEntry && !newEntry->isNull()) {
         debugs(88, 4, "clientPurgeRequest: HEAD '" << newEntry->url() << "'" );
+#if USE_HTCP
+        neighborsHtcpClear(newEntry, NULL, http->request, HttpRequestMethod(METHOD_HEAD), HTCP_CLR_PURGE);
+#endif
         newEntry->release();
         purgeStatus = HTTP_OK;
     }
@@ -874,6 +896,9 @@
 
         if (entry) {
             debugs(88, 4, "clientPurgeRequest: Vary GET '" << entry->url() << "'" );
+#if USE_HTCP
+            neighborsHtcpClear(entry, NULL, http->request, HttpRequestMethod(METHOD_GET), HTCP_CLR_PURGE);
+#endif
             entry->release();
             purgeStatus = HTTP_OK;
         }
@@ -882,6 +907,9 @@
 
         if (entry) {
             debugs(88, 4, "clientPurgeRequest: Vary HEAD '" << entry->url() << "'" );
+#if USE_HTCP
+            neighborsHtcpClear(entry, NULL, http->request, HttpRequestMethod(METHOD_HEAD), HTCP_CLR_PURGE);
+#endif
             entry->release();
             purgeStatus = HTTP_OK;
         }

=== modified file 'src/enums.h'
--- src/enums.h	2008-07-11 20:43:43 +0000
+++ src/enums.h	2008-09-01 05:27:59 +0000
@@ -545,4 +545,15 @@
     DISABLE_PMTU_TRANSPARENT
 };
 
+#if USE_HTCP
+/*
+ * This should be in htcp.h but because neighborsHtcpClear is defined in
+ * protos.h it has to be here.
+ */
+typedef enum {
+    HTCP_CLR_PURGE,
+    HTCP_CLR_INVALIDATION,
+} htcp_clr_reason;
+#endif
+
 #endif /* SQUID_ENUMS_H */

=== modified file 'src/htcp.cc'
--- src/htcp.cc	2008-09-08 23:52:06 +0000
+++ src/htcp.cc	2008-09-22 23:43:29 +0000
@@ -176,6 +176,7 @@
     int rr;
     int f1;
     int response;
+    int reason;
     u_int32_t msg_id;
     htcpSpecifier S;
     htcpDetail D;
@@ -246,9 +247,7 @@
 static void htcpFreeSpecifier(htcpSpecifier * s);
 static void htcpFreeDetail(htcpDetail * s);
 
-static void htcpHandle(char *buf, int sz, IPAddress &from);
-
-static void htcpHandleData(char *buf, int sz, IPAddress &from);
+static void htcpHandleMsg(char *buf, int sz, IPAddress &from);
 
 static void htcpHandleMon(htcpDataHeader *, char *buf, int sz, IPAddress &from);
 
@@ -439,6 +438,26 @@
 }
 
 static ssize_t
+htcpBuildClrOpData(char *buf, size_t buflen, htcpStuff * stuff)
+{
+    u_short reason;
+    
+    switch (stuff->rr) {
+    case RR_REQUEST:
+        debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
+        reason = htons((u_short)stuff->reason);
+        xmemcpy(buf, &reason, 2);
+        return htcpBuildSpecifier(buf + 2, buflen - 2, stuff) + 2;
+    case RR_RESPONSE:
+        break;
+    default:
+        fatal_dump("htcpBuildClrOpData: bad RR value");
+    }
+    
+    return 0;
+}
+
+static ssize_t
 htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff)
 {
     ssize_t off = 0;
@@ -451,7 +470,7 @@
         break;
 
     case HTCP_CLR:
-        /* nothing to be done */
+        off = htcpBuildClrOpData(buf + off, buflen, stuff);
         break;
 
     default:
@@ -581,7 +600,7 @@
                         len);
 
     if (x < 0)
-        debugs(31, 1, "htcpSend: FD " << htcpOutSocket << " sendto: " << xstrerror());
+        debugs(31, 3, "htcpSend: FD " << htcpOutSocket << " sendto: " << xstrerror());
     else
         statCounter.htcp.pkts_sent++;
 }
@@ -633,7 +652,7 @@
     buf += 2;
 
     if (l > sz) {
-        debugs(31, 1, "htcpUnpackSpecifier: failed to unpack METHOD");
+        debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
         htcpFreeSpecifier(s);
         return NULL;
     }
@@ -651,7 +670,7 @@
     sz -= 2;
 
     if (l > sz) {
-        debugs(31, 1, "htcpUnpackSpecifier: failed to unpack URI");
+        debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
         htcpFreeSpecifier(s);
         return NULL;
     }
@@ -674,7 +693,7 @@
     sz -= 2;
 
     if (l > sz) {
-        debugs(31, 1, "htcpUnpackSpecifier: failed to unpack VERSION");
+        debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
         htcpFreeSpecifier(s);
         return NULL;
     }
@@ -697,7 +716,7 @@
     sz -= 2;
 
     if (l > sz) {
-        debugs(31, 1, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
+        debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
         htcpFreeSpecifier(s);
         return NULL;
     }
@@ -751,7 +770,7 @@
     buf += 2;
 
     if (l > sz) {
-        debugs(31, 1, "htcpUnpackDetail: failed to unpack RESP_HDRS");
+        debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
         htcpFreeDetail(d);
         return NULL;
     }
@@ -769,7 +788,7 @@
     sz -= 2;
 
     if (l > sz) {
-        debugs(31, 1, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
+        debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
         htcpFreeDetail(d);
         return NULL;
     }
@@ -792,7 +811,7 @@
     sz -= 2;
 
     if (l > sz) {
-        debugs(31, 1, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
+        debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
         htcpFreeDetail(d);
         return NULL;
     }
@@ -917,7 +936,7 @@
 
     if (!pktlen)
     {
-        debugs(31, 1, "htcpTstReply: htcpBuildPacket() failed");
+        debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
         return;
     }
 
@@ -955,7 +974,7 @@
 
     if (pktlen == 0)
     {
-        debugs(31, 1, "htcpClrReply: htcpBuildPacket() failed");
+        debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
         return;
     }
 
@@ -1102,7 +1121,7 @@
 
     if (!key)
     {
-        debugs(31, 1, "htcpHandleTstResponse: No query key for response id '" << hdr->msg_id << "' from '" << from << "'");
+        debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr->msg_id << "' from '" << from << "'");
         return;
     }
 
@@ -1110,7 +1129,7 @@
 
     if ( *peer != from || peer->GetPort() != from.GetPort() )
     {
-        debugs(31, 1, "htcpHandleTstResponse: Unexpected response source " << from );
+        debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from );
         return;
     }
 
@@ -1133,7 +1152,7 @@
         d = htcpUnpackDetail(buf, sz);
 
         if (d == NULL) {
-            debugs(31, 1, "htcpHandleTstResponse: bad DETAIL");
+            debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
             return;
         }
 
@@ -1287,25 +1306,93 @@
     htcpFreeSpecifier(s);
 }
 
+/*
+ * Forward a CLR request to all peers who have requested that CLRs be
+ * forwarded to them.
+ */
 static void
+htcpForwardClr(char *buf, int sz)
+{
+    peer *p;
+    
+    for (p = Config.peers; p; p = p->next) {
+        if (!p->options.htcp) {
+            continue;
+        }
+        if (!p->options.htcp_forward_clr) {
+            continue;
+        }
+        
+        htcpSend(buf, sz, p->in_addr);
+    }
+}
 
-htcpHandleData(char *buf, int sz, IPAddress &from)
+/*
+ * Do the first pass of handling an HTCP message.  This used to be two
+ * separate functions, htcpHandle and htcpHandleData.  They were merged to
+ * allow for forwarding HTCP packets easily to other peers if desired.
+ *
+ * This function now works out what type of message we have received and then
+ * hands it off to other functions to break apart message-specific data.
+ */
+static void
+htcpHandleMsg(char *buf, int sz, IPAddress &from)
 {
+    htcpHeader htcpHdr;
     htcpDataHeader hdr;
-
-    if ((size_t)sz < sizeof(htcpDataHeader))
-    {
-        debugs(31, 1, "htcpHandleData: msg size less than htcpDataHeader size");
+    char *hbuf;
+    int hsz;
+    assert (sz >= 0);
+
+    if ((size_t)sz < sizeof(htcpHeader))
+    {
+        debugs(31, 3, "htcpHandle: msg size less than htcpHeader size");
+        return;
+    }
+
+    htcpHexdump("htcpHandle", buf, sz);
+    xmemcpy(&htcpHdr, buf, sizeof(htcpHeader));
+    htcpHdr.length = ntohs(htcpHdr.length);
+
+    if (htcpHdr.minor == 0)
+        old_squid_format = 1;
+    else
+        old_squid_format = 0;
+
+    debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr.length);
+    debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr.major);
+    debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr.minor);
+
+    if (sz != htcpHdr.length)
+    {
+        debugs(31, 3, "htcpHandle: sz/" << sz << " != htcpHdr.length/" <<
+               htcpHdr.length << " from " << from );
+
+        return;
+    }
+
+    if (htcpHdr.major != 0)
+    {
+        debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr.major << " from " << from );
+
+        return;
+    }
+
+    hbuf = buf + sizeof(htcpHeader);
+    hsz = sz - sizeof(htcpHeader);
+
+    if ((size_t)hsz < sizeof(htcpDataHeader))
+    {
+        debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
         return;
     }
 
     if (!old_squid_format)
     {
-        xmemcpy(&hdr, buf, sizeof(hdr));
-    } else
-    {
+        xmemcpy(&hdr, hbuf, sizeof(hdr));
+    } else {
         htcpDataHeaderSquid hdrSquid;
-        xmemcpy(&hdrSquid, buf, sizeof(hdrSquid));
+        xmemcpy(&hdrSquid, hbuf, sizeof(hdrSquid));
         hdr.length = hdrSquid.length;
         hdr.opcode = hdrSquid.opcode;
         hdr.response = hdrSquid.response;
@@ -1317,12 +1404,11 @@
 
     hdr.length = ntohs(hdr.length);
     hdr.msg_id = ntohl(hdr.msg_id);
-    debugs(31, 3, "htcpHandleData: sz = " << sz);
+    debugs(31, 3, "htcpHandleData: hsz = " << hsz);
     debugs(31, 3, "htcpHandleData: length = " << hdr.length);
 
-    if (hdr.opcode >= HTCP_END)
-    {
-        debugs(31, 1, "htcpHandleData: client " << from << ", opcode " << hdr.opcode << " out of range");
+    if (hdr.opcode >= HTCP_END) {
+        debugs(31, 3, "htcpHandleData: client " << from << ", opcode " << hdr.opcode << " out of range");
         return;
     }
 
@@ -1332,9 +1418,8 @@
     debugs(31, 3, "htcpHandleData: RR = " << hdr.RR);
     debugs(31, 3, "htcpHandleData: msg_id = " << hdr.msg_id);
 
-    if (sz < hdr.length)
-    {
-        debugs(31, 1, "htcpHandleData: sz < hdr.length");
+    if (hsz < hdr.length) {
+        debugs(31, 3, "htcpHandleData: sz < hdr.length");
         return;
     }
 
@@ -1342,88 +1427,33 @@
      * set sz = hdr.length so we ignore any AUTH fields following
      * the DATA.
      */
-    sz = (int) hdr.length;
-
-    buf += sizeof(htcpDataHeader);
-
-    sz -= sizeof(htcpDataHeader);
-
-    debugs(31, 3, "htcpHandleData: sz = " << sz);
-
-    htcpHexdump("htcpHandleData", buf, sz);
-
-    switch (hdr.opcode)
-    {
-
+    hsz = (int) hdr.length;
+    hbuf += sizeof(htcpDataHeader);
+    hsz -= sizeof(htcpDataHeader);
+    debugs(31, 3, "htcpHandleData: hsz = " << hsz);
+
+    htcpHexdump("htcpHandleData", hbuf, hsz);
+
+    switch (hdr.opcode) {
     case HTCP_NOP:
-        htcpHandleNop(&hdr, buf, sz, from);
+        htcpHandleNop(&hdr, hbuf, hsz, from);
         break;
-
     case HTCP_TST:
-        htcpHandleTst(&hdr, buf, sz, from);
+        htcpHandleTst(&hdr, hbuf, hsz, from);
         break;
-
     case HTCP_MON:
-        htcpHandleMon(&hdr, buf, sz, from);
+        htcpHandleMon(&hdr, hbuf, hsz, from);
         break;
-
     case HTCP_SET:
-        htcpHandleSet(&hdr, buf, sz, from);
+        htcpHandleSet(&hdr, hbuf, hsz, from);
         break;
-
     case HTCP_CLR:
-        htcpHandleClr(&hdr, buf, sz, from);
+        htcpHandleClr(&hdr, hbuf, hsz, from);
+        htcpForwardClr(buf, sz);
         break;
-
     default:
-        return;
-    }
-}
-
-static void
-
-htcpHandle(char *buf, int sz, IPAddress &from)
-{
-    htcpHeader htcpHdr;
-    assert (sz >= 0);
-
-    if ((size_t)sz < sizeof(htcpHeader))
-    {
-        debugs(31, 1, "htcpHandle: msg size less than htcpHeader size");
-        return;
-    }
-
-    htcpHexdump("htcpHandle", buf, sz);
-    xmemcpy(&htcpHdr, buf, sizeof(htcpHeader));
-    htcpHdr.length = ntohs(htcpHdr.length);
-
-    if (htcpHdr.minor == 0)
-        old_squid_format = 1;
-    else
-        old_squid_format = 0;
-
-    debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr.length);
-    debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr.major);
-    debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr.minor);
-
-    if (sz != htcpHdr.length)
-    {
-        debugs(31, 1, "htcpHandle: sz/" << sz << " != htcpHdr.length/" <<
-               htcpHdr.length << " from " << from );
-
-        return;
-    }
-
-    if (htcpHdr.major != 0)
-    {
-        debugs(31, 1, "htcpHandle: Unknown major version " << htcpHdr.major << " from " << from );
-
-        return;
-    }
-
-    buf += sizeof(htcpHeader);
-    sz -= sizeof(htcpHeader);
-    htcpHandleData(buf, sz, from);
+        break;
+    }
 }
 
 static void
@@ -1442,7 +1472,7 @@
     if (len)
         statCounter.htcp.pkts_recv++;
 
-    htcpHandle(buf, len, from);
+    htcpHandleMsg(buf, len, from);
 
     commSetSelect(fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
 }
@@ -1525,51 +1555,31 @@
         return;
 
     old_squid_format = p->options.htcp_oldsquid;
-
     memset(&flags, '\0', sizeof(flags));
-
     snprintf(vbuf, sizeof(vbuf), "%d/%d",
              req->http_ver.major, req->http_ver.minor);
-
     stuff.op = HTCP_TST;
-
     stuff.rr = RR_REQUEST;
-
     stuff.f1 = 1;
-
     stuff.response = 0;
-
     stuff.msg_id = ++msg_id_counter;
-
     stuff.S.method = (char *) RequestMethodStr(req->method);
-
     stuff.S.uri = (char *) e->url();
-
     stuff.S.version = vbuf;
-
     HttpStateData::httpBuildRequestHeader(req, req, e, &hdr, flags);
-
     mb.init();
-
     packerToMemInit(&pa, &mb);
-
     hdr.packInto(&pa);
-
     hdr.clean();
-
     packerClean(&pa);
-
     stuff.S.req_hdrs = mb.buf;
-
     pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
-
     mb.clean();
-
     if (!pktlen) {
-        debugs(31, 1, "htcpQuery: htcpBuildPacket() failed");
+        debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
         return;
     }
-
+    
     htcpSend(pkt, (int) pktlen, p->in_addr);
 
     queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id;
@@ -1580,6 +1590,77 @@
 }
 
 /*
+ * Send an HTCP CLR message for a specified item to a given peer.
+ */
+void
+htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, peer * p, htcp_clr_reason reason)
+{
+    static char pkt[8192];
+    ssize_t pktlen;
+    char vbuf[32];
+    htcpStuff stuff;
+    HttpHeader hdr(hoRequest);
+    Packer pa;
+    MemBuf mb;
+    http_state_flags flags;
+
+    if (htcpInSocket < 0)
+    	return;
+
+    old_squid_format = p->options.htcp_oldsquid;
+    memset(&flags, '\0', sizeof(flags));
+    snprintf(vbuf, sizeof(vbuf), "%d/%d",
+	req->http_ver.major, req->http_ver.minor);
+    stuff.op = HTCP_CLR;
+    stuff.rr = RR_REQUEST;
+    stuff.f1 = 0;
+    stuff.response = 0;
+    stuff.msg_id = ++msg_id_counter;
+    switch (reason) {
+    case HTCP_CLR_INVALIDATION:
+    	stuff.reason = 1;
+    	break;
+    default:
+    	stuff.reason = 0;
+    	break;
+    }
+    stuff.S.method = (char *) RequestMethodStr(req->method);
+    if (e == NULL || e->mem_obj == NULL) {
+    	if (uri == NULL) {
+            return;
+    	}
+    	stuff.S.uri = xstrdup(uri);
+    } else {
+    	stuff.S.uri = (char *) e->url();
+    }
+    stuff.S.version = vbuf;
+    if (reason != HTCP_CLR_INVALIDATION) {
+        HttpStateData::httpBuildRequestHeader(req, req, e, &hdr, flags);
+        mb.init();
+        packerToMemInit(&pa, &mb);
+        hdr.packInto(&pa);
+        hdr.clean();
+        packerClean(&pa);
+    	stuff.S.req_hdrs = mb.buf;
+    } else {
+        stuff.S.req_hdrs = NULL;
+    }
+    pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff);
+    if (reason != HTCP_CLR_INVALIDATION) {
+        mb.clean();
+    }
+    if (e == NULL) {
+    	xfree(stuff.S.uri);
+    }
+    if (!pktlen) {
+    	debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
+    	return;
+    }
+    
+    htcpSend(pkt, (int) pktlen, p->in_addr);
+}
+
+/*
  * htcpSocketShutdown only closes the 'in' socket if it is
  * different than the 'out' socket.
  */

=== modified file 'src/htcp.h'
--- src/htcp.h	2008-03-16 22:10:18 +0000
+++ src/htcp.h	2008-09-18 02:55:19 +0000
@@ -70,6 +70,9 @@
 SQUIDCEXTERN void htcpQuery(StoreEntry * e, HttpRequest * req, peer * p);
 
 /// \ingroup ServerProtocolHTCP
+SQUIDCEXTERN void htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, peer * p, htcp_clr_reason reason);
+
+/// \ingroup ServerProtocolHTCP
 SQUIDCEXTERN void htcpSocketShutdown(void);
 
 /// \ingroup ServerProtocolHTCP

=== modified file 'src/http.cc'
--- src/http.cc	2008-09-13 13:43:00 +0000
+++ src/http.cc	2008-09-18 02:55:19 +0000
@@ -274,6 +274,9 @@
 
     if (pe != NULL) {
         assert(e != pe);
+#if USE_HTCP
+        neighborsHtcpClear(e, NULL, e->mem_obj->request, e->mem_obj->method, HTCP_CLR_INVALIDATION);
+#endif
         pe->release();
     }
 
@@ -288,6 +291,9 @@
 
     if (pe != NULL) {
         assert(e != pe);
+#if USE_HTCP
+        neighborsHtcpClear(e, NULL, e->mem_obj->request, HttpRequestMethod(METHOD_HEAD), HTCP_CLR_INVALIDATION);
+#endif
         pe->release();
     }
 }

=== modified file 'src/neighbors.cc'
--- src/neighbors.cc	2008-09-21 05:08:44 +0000
+++ src/neighbors.cc	2008-09-22 00:53:13 +0000
@@ -672,8 +672,7 @@
         debugs(15, 3, "neighborsUdpPing: reqnum = " << reqnum);
 
 #if USE_HTCP
-
-        if (p->options.htcp) {
+        if (p->options.htcp && !p->options.htcp_only_clr) {
             debugs(15, 3, "neighborsUdpPing: sending HTCP query");
             htcpQuery(entry, request, p);
         } else
@@ -1604,10 +1603,16 @@
         storeAppendPrintf(sentry, " closest-only");
 
 #if USE_HTCP
-
     if (p->options.htcp)
         storeAppendPrintf(sentry, " htcp");
-
+	if (p->options.htcp_oldsquid)
+	    storeAppendPrintf(sentry, " htcp-oldsquid");
+	if (p->options.htcp_no_clr)
+	    storeAppendPrintf(sentry, " htcp-no-clr");
+	if (p->options.htcp_no_purge_clr)
+	    storeAppendPrintf(sentry, " htcp-no-purge-clr");
+	if (p->options.htcp_only_clr)
+	    storeAppendPrintf(sentry, " htcp-only-clr");
 #endif
 
     if (p->options.no_netdb_exchange)
@@ -1762,7 +1767,6 @@
 
 #if USE_HTCP
 void
-
 neighborsHtcpReply(const cache_key * key, htcpReplyData * htcp, const IPAddress &from)
 {
     StoreEntry *e = Store::Root().get(key);
@@ -1832,4 +1836,28 @@
     mem->ping_reply_callback(p, ntype, PROTO_HTCP, htcp, mem->ircb_data);
 }
 
+/*
+ * Send HTCP CLR messages to all peers configured to receive them.
+ */
+void
+neighborsHtcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
+{
+    peer *p;
+    char buf[128];
+
+    for (p = Config.peers; p; p = p->next) {
+        if (!p->options.htcp) {
+            continue;
+        }
+        if (p->options.htcp_no_clr) {
+            continue;
+        }
+        if (p->options.htcp_no_purge_clr && reason == HTCP_CLR_PURGE) {
+            continue;
+        }
+        debugs(15, 3, "neighborsHtcpClear: sending CLR to " << p->in_addr.ToURL(buf, 128));
+        htcpClear(e, uri, req, method, p, reason);
+    }
+}
+
 #endif

=== modified file 'src/protos.h'
--- src/protos.h	2008-09-11 06:32:57 +0000
+++ src/protos.h	2008-09-18 02:55:19 +0000
@@ -385,6 +385,9 @@
 SQUIDCEXTERN void neighborsUdpAck(const cache_key *, icp_common_t *, const IPAddress &);
 SQUIDCEXTERN void neighborAdd(const char *, const char *, int, int, int, int, int);
 SQUIDCEXTERN void neighbors_init(void);
+#if USE_HTCP
+SQUIDCEXTERN void neighborsHtcpClear(StoreEntry *, const char *, HttpRequest *, const HttpRequestMethod &, htcp_clr_reason);
+#endif
 SQUIDCEXTERN peer *peerFindByName(const char *);
 SQUIDCEXTERN peer *peerFindByNameAndPort(const char *, unsigned short);
 SQUIDCEXTERN peer *getDefaultParent(HttpRequest * request);

=== modified file 'src/structs.h'
--- src/structs.h	2008-08-09 06:24:33 +0000
+++ src/structs.h	2008-09-01 02:43:20 +0000
@@ -925,6 +925,10 @@
 #if USE_HTCP
         unsigned int htcp:1;
         unsigned int htcp_oldsquid:1;
+        unsigned int htcp_no_clr:1;
+        unsigned int htcp_no_purge_clr:1;
+        unsigned int htcp_only_clr:1;
+        unsigned int htcp_forward_clr:1;
 #endif
         unsigned int no_netdb_exchange:1;
 #if DELAY_POOLS

# Begin bundle
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

Reply via email to