# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: benno@squid-cache.org-20080922234329-tgk8zhmf7c1kkbq8 # target_branch: http://www.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: b772ff430ab6af3664e574b0a92e21d6e3d7856a # timestamp: 2008-09-23 09:45:25 +1000 # base_revision_id: squid3@treenet.co.nz-20080922121310-\ # 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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