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
IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWQmzLZYAM3f/gGR8QAB7////
f+f/7r////5gON6721EmQu6Y9XhSjxj0N5HrXRnvc8k88Hd3Xn22A5PR9333u+Gvazdbee9y8oq+
+93rYNW9zvVhru9u3ju7i9ur0NdaEtaKq9zOgXel8A17dtKbHNzyye2OvF4nbeS9YIHXnO9mnslr
rehj2w99e4vYqddzRsaugN3OB2M02fe8CvderpsPQUMJFBBMEDTSaYIBNRmhT0TGpkNAxMnqAaGT
1NBKCACaBJT2o1U3qjyZqelDeppMgAAAAANNANMmhCiTU9T0k2p6anp6U0aADJ+pANAAAAANAQpJ
NQxJlTxTD0Ihmmk0wmmjQDT1AepoDIAGgIpCaRoAJk000ZCYTJkCankMJqTNQxPUaeSeoHoCpJAA
gIE0m1PUMgUeqe0o9T1G1NHqHqGmgAABpfFNqwAi2oFrAQLQakwlEIa35fl+fOz7n9/32TZuDxPB
8vyucfdCAxptKKfwd2PwQ8JHwPxk+tTMlhLMnKci8XzzPrCrZpVpBfTAjpH7T3f9+lKrLWsn4JMj
v3jt/J0a7pNRa20cRW9KpuHCIbmRazM/VPNTJ0WRCINX46dNwH340bciqO8l7iQ9sHprTDn/aLqf
5Dn+Bj/uNj/g6/zF6zvFdZBPxAoIhzGXz59WyplJF0ddrITOhT6Czix8IkJ/MQjPPCYflihUgeEh
dxHtgxRgnwMtDqaN0Lketxi3KExIJRVhsXQbCgUW12RSgAILj7Oj3w1FKIAOgLkD51DJIVHSYQP9
uMDqTa8pHMWyI1MoRJmYcW0RIU98pjlPw+UXbf+BCSKVRbAVhpFdO+kwbGLXTSYTmgO9IFUBUMoZ
FMbQADEARUAAiBHm1YrnK1ap38c4pdyT8hSFJde1wRZGK80+NysSnWDPjbgDb8h7BAiLMBExVFLy
zUmQOh4lt14ak5ycrbhlBw6MOvbHhbSnXLyjh04D1b48JDjLQJnDjtrHV6sclk6vDO6AEAZYoHCF
IhcEslxCQ23FehTi0dcC5ujvkmZ2G8T1WZZmdTJQDptnEkH03Js6ndDWBDMG8TG7G+QAJocdWs3k
0uBiaidTrMbPC9DN1U7k6yszWpMGMyssuU9PVxmcyssuTt0+308+xPpd+unxnvCCAyRU7whAIFhE
XiCffxn7fYe/nOw5o8ZfFVDP2dXdUtAMbTQghEfnv4z9SZYgGRTLoQmHchxjDiIfVASs5p3MKWEZ
ouDWB3jBhbQlUCKzCZYWWJQDyskhXDBhg063Wqv6oB7I9s0NK04oUUpQNJSJQRVTSFFFVQKURFNR
ERRQ1VFFNRFK0+n0h0QX+QAT4/Fj63r9jMz/XKPxxP7PwVrWta1apZIVtR6uzWa0w77EOLzClrHA
phiKoiiCJ8QmMUjca7Qs8Vqq4HaUGzlnul3S9K3gFVoqCqZxcJoKBWlEkkDCCSCulzF2rS2eW+cb
bbxqbHFunEbMaMaOGms0t0qpVSqlVVTo0QGRRosiJB2aPNVxiikiSVx6ecm2BjUkaYUZaDI5iTW5
YIEiZ5LkGHKxApRmpoQuiXitFx/B/D7pz2MNh1aw2yjgq6Z3YwFR2jSE0tSEjcpwqbESKQFil2ml
MxsMbEjQMHS0aV1nKZZFAi1ypQIo5qxVUBJqrTUZVM2BH2BEhilXHKVcS8JOt2msFLCYkyKK6Y3o
2stsgrikAkg6OyUjK5U5knNafMNjYvWDWqtocN0hRShcy54SYshmGZIhmOZOVWzsAyCea42RvFra
y5viTyXYrjCmMKNGIYjNvB8Z6uJSCKSmknplCnbXnpjKC5gc4b5OZKygnmc7KuEby0/B0VQ6Qng/
x9B7GpdvYsHV1Kfqr75xzzK3CquEIqtdaV8IA8oIKQYSaC2Ze0OAuZRKPOK5OvigGj0+1GFtsiFk
oOp88Ztw+fVYJiHP5JEoQW9MHvtFbovPRdYK3vm35bWIHrREPlohE07rNAKD3RE2pI2WiyO/NOhP
/mQ8y+5E4oa3RqunCKF+/L92LV0IEkDNrza9dX6+I4XCuIZFs/Pwr3do5qBR7Jyrw93clIwVUKJr
dSYvjxREVoCzq5iUrKKMqreg1ED6DATdCyHNxmVV2tMiEgygENqR7lVPJA8w0Z680/zfUH8UUz2n
CaQBRAPeXT03ZRta6a0tHzXaheX5TQRQ9cQUVMHNEEGakLbt1Le2ifu6Sm/Dlq656uiXxSFnMLym
/bfRem0NO44T+yOrnx8KId4iIooooiJiiiiiiIooIOjmPiduW53+o8jPWHd3FCHa98TCu809/HZy
z1rXbqwLZjTiahCDRqWd60tEb6BAQxWikscnT1uGG2cWTU+SlIDRKq5LMlnSlSy3bVnWJ/QbNNOe
55ryPdmxp5gN5xpQaRz/w+YvrAJgEe973wH63jJ9/xaPi5RdTa+CxqcGYvY7/VWeuPfHn6KJe92S
trjXh+vn6bs9nw/L8ZkHi2xEB/mQDSyQFT2x2pLp09KdqbGb/E+v7TkRBQRNOQtaEFZU72otubbT
eTi9bg6XS6HXm17c2o6LoPDMBfCxdV9eIEbRlBWWdIbm3Pr37Re043nLr2neXiPjtLqXpphUXYXS
OEtJjsx3y2X235YPHZsxIZQpnsqpcpUQ2rZ4BtZw5yW+c++B7f6+P+QkTq73iShlfQHiIQEdxv5Y
7OXLK/k+PKzn1oD5UBnSQI0gwQ/MMbbQUhSGAGZFGAr+IU9v4eAfeIN57UCxahSY29cMM44nqEh6
cRB2nv6tq3Oz9VLTNJYw2+NvFFbGsMKtt1S9uWFOXD1N+ll4KfScYYA7QgUlHRBCDwgMECJ11mKo
gmg0kszLgjCEigqeG3ORzaSfbIjxp9tRm4CeXxmdLS2d92q6+zIcO6obh82JDUFNlsi8MdZKWT02
1dw26Kp4nx0z+p1Jl3g30A0JQSC4UJD9lD1DSxkEHYTzixFUl1Rnzav7m5WU4dY+z7t4d2L/NcKZ
D3Bt6+E+kMm0CD5qqSo6g12B6upjaG20g52t+zLdVjOpetMJrNBIUd9JtgOAcd4uwuTdiOKwDQHR
ju4rVFFmIqV+kKWDlbOJvOwnU/qiKmPsuDzQoWnerjgsuCS8Z2t81Rl4vZ04sS2Ofb8AIv6oBPS8
5FG9WOjF0fEqPtGwQOCHwvvBvBaA2GHmaL7GLmAC5KnjMDAEzKcQGGEfYiHX3h0k/YJBxrZMmAeA
ycdQatRbUdBt1m5NCy+8pRB5JkiIIiVn6XBvVVcbx+mtJE2JTlE5ylMjFnUdQdc1wtAHQaJCWT7A
oqNUZYQORAvKSi8sLYI4sIbsAlw6kKhhYaA5LhngG1YrlFlAk3GaTvbPLDf4zhdzx8a5PuphEmqS
Lbm4Pk64lGrUanrhojpZR0fw9z3gaPh5fLe+LuM7Cq6u7yFgRFEYQ0GC7AcB3u+iaDoXh0FFBQsM
KVqge2AszuraiTo6FVWMLpGheYJkg86e/6x7By9pNr2VV8XgrvLmVi5mZmZmGZWZmQkohJQkkkkg
kSu4dncO1IePi/N6MtrvufrwAHU71UQBFV5syoADc9fk57XuR8rofT6p9RCHdW6btSsUzxmC2EeK
QaaWklsbEzpIzysSY0J1W1vAmsynIHCaAMjpkOjfq22Q6FUgCSEYARgMiIsiUhOKAN56vT01uPJx
XAP/esbF8yRhqRoiXGEwtjhfjhUGFbQcZpPNSTHYLIzoySVI0zS0vpD8lSIHS9rdydFzh5MG9yys
CIuuw5M0xScsdMjMwSzdN1bOS5yym1amA+T+NkwkicJqbKgU0VmJcw/T+nBiuuwYKzrZpV6mcqNE
lRLKv5MgwYz3d3Zgimi2KjFg7tVxs9m2+BqVJFTRJaoWTMvtfKwtJKTYUJv94TNmJErF8VRn8wDU
YrMd0oxELIgKWmXELNRsBkjhvNrbIoay2wnBKy0ZjnaNXRfBYykV3X56qWsuePj5vJmSmW1xKcNk
bTZyWc3Jp1Uxf2JiZs5fdokQ1KIgiMcg8KE4o0BEccNiUhiIcnHGi5uXXPzSdpdzpfUx1gHlTmKI
kpdouc3fJqyaLL0kYUMPWKYOqzdq8WrqucLnivXv5zNwyZMGjm5t2Cnhw3YlL3yzMlttx3nssLC8
2bMi0oKy4zlREYxb7Uu7QtPchbUtwDjZPaLVGNnHTNuE3EByoDshAdui2JXjZBy8E8ACNzWA6AkL
OOGhTbr37dttt9M9dbrV7LMRF2oFGI0RGqVXbkuy/X1msNDjhJXFZNbaozkhguuOFyjMwmN92Ulo
gYSrbsJWWKNN1Jgk2hfP6s52cZeyDkKuAtHorDbmbmoxNqRFSQJF7AWIg9hRhIqHqfU8E3stERGf
f2oSqmMRQCZMKFQFEnMsyySAiLI0f+OluF1wpzDXLxbO3GPSlzipDcjel/aey4i1s4xWHFYXvmE8
qyAWIm5GrSmE4glhI3ETZNI5kbS2daU5tlssZIloulR47wWclLyUMWEYEmKJDrZAcTgwXHkzPaK1
GoJErATOZchR4API0cgLVymCTICbDswNlXRyYGjNuyy0tK0/gbov0jKJw5Dd3YuXeXhi+m2pdiUD
qgTrC6rYNYdDxM3k2FOxckGCApbRIJEz0+9OyhgwlUs6wEUVxbGTMSCeCjTnjx3gBC9ZSohts3uB
RC5nMiC5U92rC7d2YWYOpTQTKuuaPReuu6VEzTVlTaqMoBDDIuMJaazQY65oIYWoo4jByWU7rNGj
zeT4PSS5o7smzVmzfdsbrljwSPTdw0TJEiBcgWPUcgOaHKEz1CvsiKPG7wwVUeZfHSDtfJ25s0ZZ
2Jh5F2X6xTTbWdSWjNJL0RZF3Qd1x67d6vfHnEDMjRcaSkqML0WLN39C+sCtyesbiORQDERIgFVS
MvoRAjqF6RZU5m5z0DsgIGaU0khuE4UZWlntSXXLtKxYRYpGIpw1ZurhixwxFssFixdiLQJMCzS7
Ds2TaKjxlQb0LSkRqOQo0gchjwoDRLYhg579GiC0AxqjOJmviU2aLHxWnfqat9uy/NWsN0pO7K9E
QShKQkCvR108sFcMs9DxMlbiCYFRGLNU24puWYjlWSaqMIKygJZAa0V6F1c0Fy5saOmOOrEYpSfJ
0cI6ua9ewu5bLy91WSJG5HY6oLq+1gnQyDwolJmwppEEuJUUpQZyB6zvokZWXoggVkgHJtiHMU8F
Dol4ikDBU5ODBU7O+Sgr0REE0JfhxCCb+I0CTKWLmanKDAQw/KTbno82LJew0apwvbKUZGc8mih7
CJwOVFLlj1NBA0QDwUHInk3JvgJ5O7Jq1bMHg3X+nr7I9EXH0Eqcj8IvMrWHKio9i0JbssM9yMeK
Q8IVUHErqhTshRmohF1VLe/O5lnbbfCTpEIN0zVNCq2hQapXXbXVNeud2HDhxAuIsBnRUZDCll+e
HE3ylRKplipjkRs97pJOa/hU09HiuMGWGWyn0QWBpIosCttoEASIAycjoI457Fybk9cIm5F9ImDt
d7m9WhJlTV2Zj7/xs2k1b1dzu3RNCxZrwMFIakPEQYgtMJlNk8FUDO3aqYhePgcS4pTs2JBQ6KFT
REdHlg4si3ARIpuPQEiLEoSdNyh4NOhNZToptUTwgFBtxUn0NUWwVLjowvAcG5j6eN6cqjVuaJP9
Sp0CKimS94HJUuZGgb8rYdAwrnfZ0a0SKyKCqquoSOzk6EgRVJ2jHfeRKL7Q5SRVoC2KuHRlmxcm
LNZaEMYjJh3s7OGDZzcmLm8Fi1pnBEU7OS4pgyWGMlTAZInZUqWKES6+JXr2cNF6/51w5nXrivau
5u6r7zBw/L6IYxLQCu5dQD3Pcd606V60DpE60Koc2Tm1cczDcOhDlea4twGVT41nCVDnrAplVCt0
VNZTeZKNSxBmHoTrKiRvJDDF+8O82YAlRz4USoES+YJ8KhLTsiTfGhh0RwgagyjoiGxGAiJ3k9pr
leJhkzq2l5Z73pLhWXaktD29tXUdw7ZGzhrwE+7DsX5DPk1gdoGe2MFDwGRbDrRUzLM3jW9zJdkt
S7m1JqiWGTC6GnnX2BqGiUeuhYHswHIcHXWRUDqhqAMVBTAQgmQIXGaJUXLnR8DMKyXRh+We7DpZ
jySjYjgGNjklERUJKKXF0bJo5N99C7WOWehDYXwaKmoCbaHDyRfJAgSREEjGUVGy4T32FHdTQ5o2
FEC2pBa05K1zDCGk3NmFoVMmNzC5XIskSlzYOF6mjZozXqYPxRWKtzYiXGKnByULEiB2QNyHdDRQ
MlRTsqfIS5pIWpRA0grAtuMlXoywnjncoUOEpScjXGuhuavXsCtWxWGnfZIfNrSFuTNLZb1pj2YA
HVuk2wkQhZdeCwgS4GuvElBwlcOFaI35V1ms2EgOzQSNpWcVsU82gwv0Vl5nLAL+mNGpyEqvAwlS
9RQEvo4IDCptNjE2S6llpUOeqwc5bTCXsijtJYCRxIdG01aC4zkjUQadOnQqMlcI8OdzpCW3KaLO
q7Kzd0YKicqRzdFk91RGkqTMTpLteNLnOeHd2XvCnbW1bNG4X6rdmzxX8Y1varEsw5CWVJvPK9fM
iVOHMoMVEoKC8J15psxhXWq2xuI0xkHsIQKtg3RuJR75WbotjUyRFSgwZKmQexzakEwTJFSF2NDQ
4zkYMJBaV2WbaBgjGo4E1GYkWHgtsxd1mTNkyYqcLO7o2aO5qbt3iZsFzNm0cPZGG6sHg5uD57xP
Js0brNX4ubd2fdLnC5o1Yujd2cL3dZ3clMlz6JN/yn2CMg1UVHnBuDQJlrgwmPmjjnkpqJ22Ws2k
ra8Eg5Qdgb2a2e/LTehgQ2OC82FI2KFa2Tk0YysNLXL/THP5kyTQa0mmNGOmMGCd2JJ3REE/Q9DR
FMlBSbpQqepYpuUNiS4qQsj3KpHJRku+Xo0aGbVzXQvUkv7Ojqu43yqlZXUWdDJM+3RcmpcyQLmT
WrlTWp7wIqDD0GgRnKTQebZnJlRabIdUa3cSskHXiYE1kcCoqO++TFVkEoHRkieDgyQPBlE8iE+b
1edKkmkfWbJBRdjYwSHI2tdWjfyLAUc2vxxUqchG49onbB2ROZHBoVARNDkDREmehAJm5yckDcRw
Yc0VMDGxIsHkwQc2GcwUGKBQk4cjdTNc1XtGrh5H2k/AnKT905on1Uj64CHwEFL64eWy5zFoxgj7
egxVo1ao8ZoRrWrq29h09RY1QrREqSaTgvJTI75FRpBOEVWBQhMZhJFRJqCdAnR2KTHLAxW0LRGE
m7e83X4PFyYvX1xebVoyb1txfcnRTNHcvEwe7qRttcuKTPvhOcduB2vyzirIWREbsXs7FK8JBRVF
ootBDsuStBZj2nLp3DQrAyIJowZJlRSNsjYVMQeOMDlTY44maJCmRiiYaxIuZImDRoKktNabL2uK
DuLiZuUI5SqmXC1mTbdTqtJBs0auHJTk/H75EuXPqcLCzYp2OqmzVzZrMXJzauzjNuwYurEU3HHG
HHPZNxNpoiCeiB3WEGq97MM/vOCdIPeVgvoBybkKozAs3c8dKldoTua1IwWFcVg71gOSQQOFhC/V
oPa7PNFgwiXQPEWxRRs2zDJbIuEpJixYWSPAqIkm6zk7KkRVXNqQQZF21C0zJ9H0V9Cf23OwR2DX
JPXmLDwFHBSaIgjCGjwRO5OQOixIiFt6jkzBxAOSJgcZFXHZAcmfew5XdWlRvUWwtSI2emryX2cl
saqzJqwYLM8dHl5WPZE/BGE5lX9C9iJm94mxWK8WrVxhhZGQoTVVVCXo29hrXWdjZk4wYqZurmxb
PVJrBzl+1m7Y8lzRAikCJIc24ydm5c3KlDcwaMGiAwwo6tnks1cLMGq9sxaOjNcuWmKyTtL2jQsM
RiNLvE1PMHYo6xs5h5XO5BNOI0gGd1GwAPdEe5eyMweXSBoT5fKK9CIFtt4qtNgabpok+eqbljXn
qZocqFWhUIIGaoXZ1fNJanGu3O2d8QypDaRNRM0f6ZL0ux1ttWXGK2nwZER8TKX1MqvQu3XOd4la
Is4mM0XXHxSF8kaL7y/HDYnmbGzAXVGUBCZEsTJDns1e3te5OmzhUQcNdWV7Ze3W752L8c7HJ0Xy
ypu7LGDFewZvkj3HGFV3twWTVu6MF6nNk+odMcZizWaOryaOs65Sa5VW8kTh4uH2mmYx10shS9q1
wcsUNG5kuxYsV7uZN3KS2+2rku2ZNl0kkXMHgxXODV5yJw5uGhZ4uzh1YOHJep1aOhk6NGTdgmUN
H36FShEiYHFLkDBk+WwTFqefL9CmDk2LQwFhjCRcZw8+8R7xKqPfS8LYvct/f8Pb9EfDr0dOvUwe
CfmIDagcIDG5KLIqEDoSiDB5YFpAvOghAGhA4Q7eeYo86oEgsgUdyClfc5v2rMM8UhccIH2w5KSV
JQRWRH1vt0Vc7qKKV8Bmh6mF/GFrfRCW/CL+nCNqthBquMEBzb1Uxwgs4YuXT3nwPkQNpA+4D7ks
QkT8ag7h4WgkxA2ACQR9CBKJiHzfmNtCGjWpdDph+L8tYHanZ1IGGEyURCkSwS8xAnIISnwIGOI+
mPfWGWaaCICmZlmZoKYko0gaLIiCDDMwMXFmZmSSSQGIroE98H8IrJAnlQPCgYGkDdA0gaqSKhoJ
6UDECpRAxVQIVHU9neEIFkIovYifp5x9b/gPnB93rfQ+IwOQZCeMAKKQhlalFIiqhQ06Qfr6pO8M
9j/tYf76n9gkGUVIxct9Fx7Rl+BVr+v/32Yh/kAV/9CxDBcNjEw4Kf4gfZSLbpn5BqDfA96rDL3i
OQWWpHOD+lv5U6txzPnQ/q26o+2+RnJH+dSUrRUffHn8JzCCA6ASpUDnIIgD9IWMn2g7B9fBPLoT
OVv9PbSe+0sk1JtB4IPs8nrw6yqO35+sPMPmrK52scz7on40rZJsrsSsfrJNGfd2B+/BoQavJ3cq
0RJ6dAN/IFAvmwHPXLUr0JkWj6FsJDEveWukjz3sGGEqDJkeg+G8i7Z+JUyaT85b0/Tfj2OjP4i+
zXwpMfSJI/IICOQLT04Cs1nLRa17kqkByAq6Exar1gXfiULQYcYber7Tj/Kus7SI+nwMyJ0BH/WK
aQp0Km9NYXadU4iRKJyiUp6hKeicmYanW9xfAWogQGlEJM4FQqh3rC0ZlwdAnQI6B8Hn5PX0hMUN
FFUVVBSVTTfKYgOnj0J1YYekt8cvo9TDpo3YCCXr+wf7Sw+ogQPoJn2DIkz6z6yu+++0pNQkLRqy
avdgkTk0cnDNwuZv1f1tTmwfNq4bsG4YLP6mKzdLnVg7MF7ZkWccauF7hGOAgQKhUoOWLDGhxxSh
kyOVKxNhSRImjFzg6A7UKkDsuHJccwf1ddZCJ54HO2OOa1qrJ74PXNAOofSlCvE/wtnr6Hi3ECAa
kQJnyIiCMSWyY4SMjXrbiomwDUdBsOk0lhMuOk38RIOICB9KOKrEuvP1gNGJ7qjGW1ntVZ/y9s5P
uspIls4MnsZr5bnBfHL9wL2SsKyXZZH34EXfO1qok2+2JUkg+uOTZctVWf3SDQFjAi97uiBgHQ5d
OYlCQsCQ7KyrigyS2AfbcptLnhBfyExGHZ2N9ZnSyWfg6vg2w3xe76PtYsX4Mn3vo2LmZszZNWLg
xbtWLynynfB82a5ZZsfeYOjBspswdWLF2cMWTJyXLF5m5sWqzqZsGrNmyOfTpiZkkVHD6iBUsiZH
OyREcwbhU7GDdTm0cPjovefnycm7Bwpe5vBe6pycOx4uhw6/5IHZh6DHCnDBJcWMcMMElwCMCxjA
sTIwxIwMijDHDDAsYwZxIwSXEjBnGMCxycMCxS0UtKWlllpXJo83N4ueyPf9BzRfIlodr5ugBPiO
RAz/2/yI+/MLUPSvzLb70zCQ/1CUSKUpKJT+NJw9IKQHgJ5Lf83ATsPwRVeQDrQeWPOiECD3HA4H
aVJHroby2xkTIDsMn5vpel6z8ma9g2Xs3QxYtF6lz5KavzPopTZw0XMH3flZ1vbtV2a5zUwbsFKX
lMG5ocYqWENyg5AUznY/nDBIkKaOBSKqre88co9jqDgHPqMZIuMJI0Gg0momcEG/SEq9mYvQ7LLM
2LF6PFTF6PJu6+sH7DkFx46S43kjAkazAxK9YTVlF/xLcdIyOotMgZWZFYx5ditQGO/uGNHCCBt0
iAAga8yntLxlB+yWkfoqGE+tnO54FGh4xUkoUpJ9/P20NIj9prvcWSH/iNO8Ik/YX99asDOHBMuW
3qmCxDAGwY3AoYnRGNUeudHA3K3XxljtmraV+qcMT6N14GxgUsqqVYTlPkcSs5m7wmZzzXneeB3n
gFp4HgTKTQVHcOjyOrHJXO9gpzZvGIs5qekTmcPtwZt2zBk1ZslF7Fo+USlly9gsnNZ5yRNGTQs6
sVlnN1eEkHgyd2K51NoM17Y5ObdyWXsu8kD0epsoXlPXu9cZkZTGaN3o6PFZ8vsT+/k7tTR6uzyb
qPB5L15+ycwqKiRSVooRiiZaZLJ+eoxBZjaXmtePrXrIqEGDZFNMYeDEUHTkQJlOoEMyOzs5ERmJ
UaCo1pHmEhNd18ILdu8hEkAYeQcT5IJQbHBNtg2AwbRBDUvAQqF2qxS+Lq8/pJ+fllc0BEIP1TcY
3tStZe+yBKSEE5AeXllbVUiiuXioVqdMK/WNyd9rrzMc1WbvT6OUee3OV8YXHPG3hkDE8wTCZ+kJ
ck6y0xxKqtyIcDKaDuIMZ3kF52HUsUKkoKcpZZjhw3Pz5rI8XVOPXmfO0iXmg8xzPQFhz3EuRRSh
4qODhE2CxM7IHodYfa76vz+j00MtPajxW1O7dwiW9GMLf2e3WKfk9c5pHN/f31rkc6eL0eb1WYOb
h8HMT1eryYlzNiyXLL17duvbNly5ixarmDAs7MV69yKeD4qqT0GXBXoQIFNIo8wgeO4LlM8ABvYI
ioSpK5AZuhwQ0p3Og20iGAIbJs6E2jsjvUK7vEubqXvVccR+CFMWNNdif8+SD7rkk7nsfB9Ts6qN
lEj6vJyGgMDlNy53OFM9OvbjgNtdptvzyutZkJPOZjSbMnZ8GDNis5uzXuzNXr32ec5NFndrEbB9
jouRLQ92y9dOTBovwgzEcT5+b+f5SazbuPtOKReGb54z3FkIaQYa9sKazp+poYUB70iabkS1TN9y
BwVVcQ8LXKGFEJg6Myg2B4NQqgjcer4R2C3CbSt+AdJCjCkNpDEDS0Cv4LB5Rgm62ImTzpEbJEwm
IKaH6Ku06UGkvqusVzVintBAdCgJ+K8Jy4R0JCL5c6GE2uNULhEhAhZ6C5wyA4kHA/fP2o7jWoVK
qSvzGWo3UkiNFW/mfVealFPhsvRqftxIGoTYQIHJqGM8waEqQSEdjsz4J7vLw0cDdHsCtjpiClCE
6EJat49BM6SZ6DIvwnirf8T0EjgXGAsKl4kaFGQ7r2tj4sVKfobtl7J+qyyxws0Yt34fv/a2RFMm
BiMakjYMhGNi5I8ifcMeBjJg/Hcd1dHguaLOXLp5xw7PBm2eJw95J/e/oaNnxmzq9Hip3fAp5Obq
6tUyZNFmS5iyeK58HDhupe5fK9Zbks7skicN3DN6snl8qrixaoLl3Rk5NWzqz2d3skTh6afG+T7Z
P+UQ+6IOVEM8aXoCse/1We+MzjXzXtb7moX2FAqxJg9QDb75olxCRHsetAkHT2g+8c4noXsPXnE0
EmUwogUWjIVFjYdQwwWTRtZNlKPdJLh0a7J1IHCAQbSAaEwEDcMPf/a+8BG3D8k/uZ3p9+50JbMz
m/GyCjzgukeeL+b56HFpYfLHKSkJAJNAkUF9O4OMO0XBW2+CjpmGOR/SxUlQ5JGsBOhkB8+8Mkzr
1LL1QEyUpTl5gwDhVVdMNLLHqnksLXJY/szax+PrDbl9L4C7IatsGrtA5xx6aivIxffl07Sv6ZyZ
CHBfm+78ubGrVrWatRq1rS/kWXacZ/ega+3oyAESfVMBdIlyGjAEmTAXGxMOPD4nZwwTBRkYOFnA
32zWYBgnMoKaGHhp4rXGVFhv61pC8AQegFj8QC8vaegR3ToG3PkQD6IFO9A5Qzenzk1kgoElQCHe
D34PUj9gkkwiBgQyqa9zGIIE/afx/jIkyoy/gpW0UeJcCeACZQkRiDENj7nOLVVGQtSgQDKxHTtF
xR37AEFfDAMzycKsvw+/vzeQfw06Pb1sVR+K49Ityj+MHaDGD5vQjCVEiEANp7fiJh/vX6i7O4wQ
HZ8RUT6UJn2OKcnUga1HqN0bcwmQVefI8y33wE+r4idDn6wD7r3Zh3fIL2zSuUXKGITlA3CNty/B
HzrZAhqPaSJykumSNfgmRymA5J/I30SfQwSbuzPv9QCHs+ij8keMTGJkbSHGG1HE1Jr1cD0h3XVz
60tLpD+SXdzy0IYqkk9wo89yjnNdsuNI+w+fXNJknraIVKiFEkiHn8DUC4L0/XPFmV76mk+X3Owj
mPFHltySfnZMIOxPrTV6fM+P0RuJ3lI5KLQxHJbKmgulxAL186AZQgJT20n0ror4DS6RMJyA29xm
N5MT06pOGFyltuPPfJrSJESqhezyC+Xlk1tgZLvVGZMy7bUOYLiDeHIAwg/xk4w/oXItR9EZnETw
ROc6delu1nz72UihZMUfAPcsw9uEOJjKPxQPsQJcaBx33VQiCKIHkPiDpAtDAgeZy+TtF0awTkDH
3mjmepLkC3yFH12PivZeudef9bF3r6xNhnUejwVks4QhjEi1pkxkEAyUV6SROcEnCfyCoJuUX0KK
owq0H4vq+o/X9CfgZ9JE0snMauKqr7FpIn/TCLJEX+aRPrFnNDD1LyXXiilXiFo/WpJcgrJC/kZy
fxTsSEl8vH7SZIPkUkWBgOcMKJ6j+3Q5F6soN6VkTygt40zewZxP4TIIM3vBpIqKq/uQyXB7WhpJ
FeRcS+63twjjVahAUhS0yMkSAxqTOvZO3lsoPzobwGBERCqPWvjRJpekoPQwSONEw+bVDoQIQ9MH
wg/OAP5wjYp6UTlPQA3O5RrVVV/HBDJDmXV/QrUVwJ574lQ4hemR4D+bB0N3ep8UkG/DfPt82Y6s
orUIaLNmiQcxDEQCQP+0IyQISAUDi4IHzSpovLcluOFTINQToxcOe6TNEDuFOMSvz5mHMSmQTK0l
EY3GAhNjQeyQJgVVeq+KGjAiHMZz62JSozkiaKC/aV1evSDTwt6eTwXGGS/zRN4XcLlU5FiPQrsx
8N9HawsVDROopDBoK/SH3DrAo1mGvAm6DUTDAPEl8wnxP5x7x8gO/7jm9InYgQPGVf7hALhUpTKM
MQqRABD+DZpWId8B2k+DpumiHvQPqgfRUD3nts8eULwo+k5uKPnI6wGZ60/fUuD1kHiQ6nk84YwQ
Oe90tt/pk8YuVkZB7cHZT6h28ZmGmXn3IG4QIQLrOVJpCSv8IXLluFMgfG2I0HcS2BHtexebwE5R
7A2O7KiUfIfQE0yy7CQmB8W8fOg2CVE5NXZe1iWoUX6ZtONAeifgoSD9yJ/DlNqqqmJ80UqFi48r
LNY7JkVGaNBrRoG1oS0YBwjXr7O07xs6t6gsEo0acJCQKaDufsmjY+0h3PqiPl4HOCXcOo0oYFYZ
cXQmYAhtQM1YtyohkDoi+1RvM4lDowbCcBLbNQwpavcdh5ZOgYCkSIfd6pTgiHZmMnVgRKiPTLE6
OqSD+3iT6Yi6pypU/rtarust0F9WDDSRxpHbov9smXggedAqiHggQCsxGOC8iqrz6UOIIhGOwJAy
18WrQhvH8ws2Hfh8/KWYigwUPILhzh4+QVnnxRA9bngfv/eWz+k9vr5cf4vdZdVrBYojutOtWuaX
ILYkqyD9859S5F8SwqC1oWiLFQBvkZSJ/YXLQg8jhUaMD5bTPnIZKUxry9YTtlKKkAepyZGrAqJv
XmmSSU4P1sGMRApEjgv4+gWTzLAOhF8RL63Y4caBgECBGQJ/VnpVDVZlkGTid5KgUlJ4iTK4w2hV
6kP5RwfQEuGPtfc8lhg+UMB7EZP3A77hRwgLbAOcOfN32GhvComuY3kQzSjOSF995dVgZF2Kgaz1
/ZfcgrHg8JZBjgf6KquodsBEMQRBPQzRD4tqXQIkXsOIOpCxTYAcdJ/WBWAaAlZT7AluwAQekmFu
B1DPoCPLkk9anQ7BVfh+XDB7/fWClAkQT4AF7LsDghV2rziwXX/IiOvDA1mMkk3ffF6vd4+anX2E
bVWg3vMZ0OiHEvfOgoef172ktbTjf/AjAFvMu61VEVYmUX8ceXKw1LW4cAy52kTzC8gHzL7vyBs9
4GI69n6wVBmg3SMeN4hvOkQsSogbqmmd0DoL5IdhO+/TEU+cIoholFSkJMm1+kFztnf5bz+eDL0R
DlBfDO4B+4Tzrs8Bw8TEAXK4jLL1aRWtmIql8Q9zNBbthHfGp6JHoYk5om499Yhp+PFvc+HJJD4i
pL4ORSDEPWQnhjjD7yJbiQ8sQMuucTqcEDslQPCgc1eUAIC4tFLwGS7iW5EN8/A2bR4Cb1t+DlUf
Nx5tAG4Mt54XMfCXValkv7EIDpH/mEXcAwqk0VAmLxgF9Hm5x9aBTIcRiMrDYGxfAL5ZerYtOBQO
pA9RriPoNvxlIiE0gUe3McfENNKkL6XtG2/lBft49gvIXl0dKKhDMq5id5gx/zDYhcD81RuaiOBY
DMcTsJFxTsO8jcoPmQ7yLTnszOBfyxdoYBUTA8RS48D+Eh4eh9aHs4x/BfJ3rx0OMhNJHX4LoPac
iPWuzt0XyLwDfk8GHHiKAGmkk6RMdFyocF8hHsIu8VG+XuEkKu4TzA7zSAZ28Wp75XbV8j1xT5As
StRSSR3ZbTcbwM5QQEHQgOeMnTSxwIQ0BahvCxQkiWZtq7BdglBetUbhN96+RjQPWhNJoHhgA9CG
RvX8ERvx3nC1UZiSDizvXjByoXCusXea0SZeTeHSsCT0YNQgGLgOBHchy5mlqBfd71I+YG/ab1e1
I9hgD6GdGhny4vlH9X8NGgngf/F3JFOFCQCbMtlg