5 * DEBUG: section 31 Hypertext Caching Protocol
6 * AUTHOR: Duane Wesssels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 #include "ACLChecklist.h"
40 #include "SquidTime.h"
42 #include "StoreClient.h"
43 #include "HttpRequest.h"
47 #include "icmp/net_db.h"
49 typedef struct _Countstr Countstr
;
51 typedef struct _htcpHeader htcpHeader
;
53 typedef struct _htcpDataHeader htcpDataHeader
;
55 typedef struct _htcpDataHeaderSquid htcpDataHeaderSquid
;
57 typedef struct _htcpAuthHeader htcpAuthHeader
;
59 typedef struct _htcpStuff htcpStuff
;
61 typedef struct _htcpDetail htcpDetail
;
74 struct _htcpDataHeaderSquid
{
78 unsigned int opcode
:4;
79 unsigned int response
:4;
81 unsigned int response
:4;
82 unsigned int opcode
:4;
86 unsigned int reserved
:6;
92 unsigned int reserved
:6;
98 struct _htcpDataHeader
{
132 /* RR == 0 --> F1 = RESPONSE DESIRED FLAG */
133 /* RR == 1 --> F1 = MESSAGE OVERALL FLAG */
134 /* RR == 0 --> REQUEST */
135 /* RR == 1 --> RESPONSE */
137 struct _htcpAuthHeader
{
145 class htcpSpecifier
: public StoreClient
149 MEMPROXY_CLASS(htcpSpecifier
);
151 void created (StoreEntry
*newEntry
);
153 void checkedHit(StoreEntry
*e
);
155 void setFrom (IpAddress
&from
);
156 void setDataHeader (htcpDataHeader
*);
161 HttpRequest
*request
;
164 HttpRequest
*checkHitRequest
;
166 IpAddress from
; // was a ptr. return to such IFF needed. otherwise copy should do.
167 htcpDataHeader
*dhdr
;
170 MEMPROXY_CLASS_INLINE(htcpSpecifier
) /**DOCS_NOSEMI*/
198 static const char *const htcpOpcodeStr
[] = {
208 * values for htcpDataHeader->response
213 OPCODE_UNIMPLEMENTED
,
214 MAJOR_VERSION_UNSUPPORTED
,
215 MINOR_VERSION_UNSUPPORTED
,
220 * values for htcpDataHeader->RR
227 static u_int32_t msg_id_counter
= 0;
228 static int htcpInSocket
= -1;
229 static int htcpOutSocket
= -1;
230 #define N_QUERIED_KEYS 8192
231 static u_int32_t queried_id
[N_QUERIED_KEYS
];
232 static cache_key queried_keys
[N_QUERIED_KEYS
][SQUID_MD5_DIGEST_LENGTH
];
234 static IpAddress queried_addr
[N_QUERIED_KEYS
];
235 static MemAllocator
*htcpDetailPool
= NULL
;
237 static int old_squid_format
= 0;
240 static ssize_t
htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
);
241 static htcpSpecifier
*htcpUnpackSpecifier(char *buf
, int sz
);
242 static htcpDetail
*htcpUnpackDetail(char *buf
, int sz
);
243 static ssize_t
htcpBuildAuth(char *buf
, size_t buflen
);
244 static ssize_t
htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
);
245 static ssize_t
htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
246 static ssize_t
htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
);
247 static ssize_t
htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
248 static ssize_t
htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
);
249 static ssize_t
htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
250 static void htcpFreeSpecifier(htcpSpecifier
* s
);
251 static void htcpFreeDetail(htcpDetail
* s
);
253 static void htcpHandleMsg(char *buf
, int sz
, IpAddress
&from
);
255 static void htcpHandleMon(htcpDataHeader
*, char *buf
, int sz
, IpAddress
&from
);
257 static void htcpHandleNop(htcpDataHeader
*, char *buf
, int sz
, IpAddress
&from
);
259 static void htcpHandleSet(htcpDataHeader
*, char *buf
, int sz
, IpAddress
&from
);
261 static void htcpHandleTst(htcpDataHeader
*, char *buf
, int sz
, IpAddress
&from
);
262 static void htcpRecv(int fd
, void *data
);
264 static void htcpSend(const char *buf
, int len
, IpAddress
&to
);
266 static void htcpTstReply(htcpDataHeader
*, StoreEntry
*, htcpSpecifier
*, IpAddress
&);
268 static void htcpHandleTstRequest(htcpDataHeader
*, char *buf
, int sz
, IpAddress
&from
);
270 static void htcpHandleTstResponse(htcpDataHeader
*, char *, int, IpAddress
&);
273 htcpHexdump(const char *tag
, const char *s
, int sz
)
279 debugs(31, 3, "htcpHexdump " << tag
);
280 memset(hex
, '\0', 80);
282 for (i
= 0; i
< sz
; i
++) {
284 snprintf(&hex
[k
* 3], 4, " %02x", (int) *(s
+ i
));
286 if (k
< 15 && i
< (sz
- 1))
289 debugs(31, 3, "\t" << hex
);
291 memset(hex
, '\0', 80);
298 * STUFF FOR SENDING HTCP MESSAGES
302 htcpBuildAuth(char *buf
, size_t buflen
)
306 assert(2 == sizeof(u_int16_t
));
307 auth
.length
= htons(2);
309 if (buflen
< copy_sz
)
311 xmemcpy(buf
, &auth
, copy_sz
);
316 htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
)
322 if (buflen
- off
< 2)
330 debugs(31, 3, "htcpBuildCountstr: LENGTH = " << len
);
332 debugs(31, 3, "htcpBuildCountstr: TEXT = {" << (s
? s
: "<NULL>") << "}");
334 length
= htons((u_int16_t
) len
);
336 xmemcpy(buf
+ off
, &length
, 2);
340 if (buflen
- off
< len
)
344 xmemcpy(buf
+ off
, s
, len
);
352 htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
)
356 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.method
);
363 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.uri
);
370 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.version
);
377 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.req_hdrs
);
384 debugs(31, 3, "htcpBuildSpecifier: size " << off
);
390 htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
)
394 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.resp_hdrs
);
401 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.entity_hdrs
);
408 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.cache_hdrs
);
419 htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
424 debugs(31, 3, "htcpBuildTstOpData: RR_REQUEST");
425 return htcpBuildSpecifier(buf
, buflen
, stuff
);
428 debugs(31, 3, "htcpBuildTstOpData: RR_RESPONSE");
429 debugs(31, 3, "htcpBuildTstOpData: F1 = " << stuff
->f1
);
431 if (stuff
->f1
) /* cache miss */
434 return htcpBuildDetail(buf
, buflen
, stuff
);
437 fatal_dump("htcpBuildTstOpData: bad RR value");
444 htcpBuildClrOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
450 debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
451 reason
= htons((u_short
)stuff
->reason
);
452 xmemcpy(buf
, &reason
, 2);
453 return htcpBuildSpecifier(buf
+ 2, buflen
- 2, stuff
) + 2;
457 fatal_dump("htcpBuildClrOpData: bad RR value");
464 htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
467 debugs(31, 3, "htcpBuildOpData: opcode " << htcpOpcodeStr
[stuff
->op
]);
472 off
= htcpBuildTstOpData(buf
+ off
, buflen
, stuff
);
476 off
= htcpBuildClrOpData(buf
+ off
, buflen
, stuff
);
488 htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
492 size_t hdr_sz
= sizeof(htcpDataHeader
);
498 off
+= hdr_sz
; /* skip! */
500 op_data_sz
= htcpBuildOpData(buf
+ off
, buflen
- off
, stuff
);
507 debugs(31, 3, "htcpBuildData: hdr.length = " << off
);
509 hdr
.length
= (u_int16_t
) off
;
511 hdr
.opcode
= stuff
->op
;
513 hdr
.response
= stuff
->response
;
519 hdr
.msg_id
= stuff
->msg_id
;
521 /* convert multi-byte fields */
522 hdr
.length
= htons(hdr
.length
);
524 hdr
.msg_id
= htonl(hdr
.msg_id
);
526 if (!old_squid_format
) {
527 xmemcpy(buf
, &hdr
, hdr_sz
);
529 htcpDataHeaderSquid hdrSquid
;
530 memset(&hdrSquid
, 0, sizeof(hdrSquid
));
531 hdrSquid
.length
= hdr
.length
;
532 hdrSquid
.opcode
= hdr
.opcode
;
533 hdrSquid
.response
= hdr
.response
;
534 hdrSquid
.F1
= hdr
.F1
;
535 hdrSquid
.RR
= hdr
.RR
;
536 xmemcpy(buf
, &hdrSquid
, hdr_sz
);
539 debugs(31, 3, "htcpBuildData: size " << off
);
545 * Build an HTCP packet into buf, maximum length buflen.
546 * Returns the packet length, or zero on failure.
549 htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
)
553 size_t hdr_sz
= sizeof(htcpHeader
);
555 /* skip the header -- we don't know the overall length */
557 if (buflen
< hdr_sz
) {
562 s
= htcpBuildData(buf
+ off
, buflen
- off
, stuff
);
569 s
= htcpBuildAuth(buf
+ off
, buflen
- off
);
576 hdr
.length
= htons((u_int16_t
) off
);
579 if (old_squid_format
)
584 xmemcpy(buf
, &hdr
, hdr_sz
);
586 debugs(31, 3, "htcpBuildPacket: size " << off
);
593 htcpSend(const char *buf
, int len
, IpAddress
&to
)
597 debugs(31, 3, "htcpSend: " << to
);
598 htcpHexdump("htcpSend", buf
, len
);
600 x
= comm_udp_sendto(htcpOutSocket
,
606 debugs(31, 3, "htcpSend: FD " << htcpOutSocket
<< " sendto: " << xstrerror());
608 statCounter
.htcp
.pkts_sent
++;
612 * STUFF FOR RECEIVING HTCP MESSAGES
617 htcpSpecifier::setFrom (IpAddress
&aSocket
)
623 htcpSpecifier::setDataHeader (htcpDataHeader
*aDataHeader
)
629 htcpFreeSpecifier(htcpSpecifier
* s
)
631 HTTPMSGUNLOCK(s
->request
);
637 htcpFreeDetail(htcpDetail
* d
)
639 htcpDetailPool
->free(d
);
643 * Unpack an HTCP SPECIFIER in place
644 * This will overwrite any following AUTH block
646 static htcpSpecifier
*
647 htcpUnpackSpecifier(char *buf
, int sz
)
649 htcpSpecifier
*s
= new htcpSpecifier
;
650 HttpRequestMethod method
;
652 /* Find length of METHOD */
653 u_int16_t l
= ntohs(*(u_int16_t
*) buf
);
658 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
659 htcpFreeSpecifier(s
);
670 /* Find length of URI */
671 l
= ntohs(*(u_int16_t
*) buf
);
676 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
677 htcpFreeSpecifier(s
);
681 /* Add terminating null to METHOD */
693 /* Find length of VERSION */
694 l
= ntohs(*(u_int16_t
*) buf
);
699 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
700 htcpFreeSpecifier(s
);
704 /* Add terminating null to URI */
716 /* Find length of REQ-HDRS */
717 l
= ntohs(*(u_int16_t
*) buf
);
722 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
723 htcpFreeSpecifier(s
);
727 /* Add terminating null to URI */
739 debugs(31, 3, "htcpUnpackSpecifier: " << sz
<< " bytes left");
742 * Add terminating null to REQ-HDRS. This is possible because we allocated
743 * an extra byte when we received the packet. This will overwrite any following
751 method
= HttpRequestMethod(s
->method
, NULL
);
753 s
->request
= HttpRequest::CreateFromUrlAndMethod(s
->uri
, method
== METHOD_NONE
? HttpRequestMethod(METHOD_GET
) : method
);
756 HTTPMSGLOCK(s
->request
);
762 * Unpack an HTCP DETAIL in place
763 * This will overwrite any following AUTH block
766 htcpUnpackDetail(char *buf
, int sz
)
768 htcpDetail
*d
= static_cast<htcpDetail
*>(htcpDetailPool
->alloc());
770 /* Find length of RESP-HDRS */
771 u_int16_t l
= ntohs(*(u_int16_t
*) buf
);
776 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
788 /* Find length of ENTITY-HDRS */
789 l
= ntohs(*(u_int16_t
*) buf
);
794 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
799 /* Add terminating null to RESP-HDRS */
802 /* Set ENTITY-HDRS */
805 d
->entity_hdrs
= buf
;
811 /* Find length of CACHE-HDRS */
812 l
= ntohs(*(u_int16_t
*) buf
);
817 debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
822 /* Add terminating null to ENTITY-HDRS */
834 debugs(31, 3, "htcpUnpackDetail: " << sz
<< " bytes left");
837 * Add terminating null to CACHE-HDRS. This is possible because we allocated
838 * an extra byte when we received the packet. This will overwrite any following
848 htcpAccessCheck(acl_access
* acl
, htcpSpecifier
* s
, IpAddress
&from
)
850 ACLChecklist checklist
;
851 checklist
.src_addr
= from
;
852 checklist
.my_addr
.SetNoAddr();
853 checklist
.request
= HTTPMSGLOCK(s
->request
);
854 checklist
.accessList
= cbdataReference(acl
);
855 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
856 int result
= checklist
.fastCheck();
861 htcpTstReply(htcpDataHeader
* dhdr
, StoreEntry
* e
, htcpSpecifier
* spec
, IpAddress
&from
)
864 static char pkt
[8192];
865 HttpHeader
hdr(hoHtcpReply
);
869 memset(&stuff
, '\0', sizeof(stuff
));
871 stuff
.rr
= RR_RESPONSE
;
873 stuff
.response
= e
? 0 : 1;
874 debugs(31, 3, "htcpTstReply: response = " << stuff
.response
);
875 stuff
.msg_id
= dhdr
->msg_id
;
879 packerToMemInit(&p
, &mb
);
880 stuff
.S
.method
= spec
->method
;
881 stuff
.S
.uri
= spec
->uri
;
882 stuff
.S
.version
= spec
->version
;
883 stuff
.S
.req_hdrs
= spec
->req_hdrs
;
885 e
->timestamp
<= squid_curtime
?
886 squid_curtime
- e
->timestamp
: 0);
888 stuff
.D
.resp_hdrs
= xstrdup(mb
.buf
);
889 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff
.D
.resp_hdrs
<< "}");
894 hdr
.putTime(HDR_EXPIRES
, e
->expires
);
897 hdr
.putTime(HDR_LAST_MODIFIED
, e
->lastmod
);
901 stuff
.D
.entity_hdrs
= xstrdup(mb
.buf
);
903 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff
.D
.entity_hdrs
<< "}");
910 if (char *host
= urlHostname(spec
->uri
)) {
914 netdbHostData(host
, &samp
, &rtt
, &hops
);
918 snprintf(cto_buf
, 128, "%s %d %f %d",
919 host
, samp
, 0.001 * rtt
, hops
);
920 hdr
.putExt("Cache-to-Origin", cto_buf
);
923 #endif /* USE_ICMP */
926 stuff
.D
.cache_hdrs
= xstrdup(mb
.buf
);
927 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff
.D
.cache_hdrs
<< "}");
933 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
935 safe_free(stuff
.D
.resp_hdrs
);
936 safe_free(stuff
.D
.entity_hdrs
);
937 safe_free(stuff
.D
.cache_hdrs
);
940 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
944 htcpSend(pkt
, (int) pktlen
, from
);
949 htcpClrReply(htcpDataHeader
* dhdr
, int purgeSucceeded
, IpAddress
&from
)
952 static char pkt
[8192];
955 /* If dhdr->F1 == 0, no response desired */
960 memset(&stuff
, '\0', sizeof(stuff
));
964 stuff
.rr
= RR_RESPONSE
;
968 stuff
.response
= purgeSucceeded
? 0 : 2;
970 debugs(31, 3, "htcpClrReply: response = " << stuff
.response
);
972 stuff
.msg_id
= dhdr
->msg_id
;
974 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
977 debugs(31, 3, "htcpClrReply: htcpBuildPacket() failed");
981 htcpSend(pkt
, (int) pktlen
, from
);
986 htcpHandleNop(htcpDataHeader
* hdr
, char *buf
, int sz
, IpAddress
&from
)
988 debugs(31, 3, "htcpHandleNop: Unimplemented");
992 htcpSpecifier::checkHit()
995 checkHitRequest
= request
;
997 if (NULL
== checkHitRequest
) {
998 debugs(31, 3, "htcpCheckHit: NO; failed to parse URL");
999 checkedHit(NullStoreEntry::getInstance());
1003 blk_end
= req_hdrs
+ strlen(req_hdrs
);
1005 if (!checkHitRequest
->header
.parse(req_hdrs
, blk_end
)) {
1006 debugs(31, 3, "htcpCheckHit: NO; failed to parse request headers");
1007 delete checkHitRequest
;
1008 checkHitRequest
= NULL
;
1009 checkedHit(NullStoreEntry::getInstance());
1013 StoreEntry::getPublicByRequest(this, checkHitRequest
);
1017 htcpSpecifier::created (StoreEntry
*e
)
1019 StoreEntry
*hit
=NULL
;
1023 debugs(31, 3, "htcpCheckHit: NO; public object not found");
1024 } else if (!e
->validToSend()) {
1025 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
1026 } else if (refreshCheckHTCP(e
, checkHitRequest
)) {
1027 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
1029 debugs(31, 3, "htcpCheckHit: YES!?");
1037 htcpClrStoreEntry(StoreEntry
* e
)
1039 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e
->url() );
1040 e
->releaseRequest();
1044 htcpClrStore(const htcpSpecifier
* s
)
1046 HttpRequest
*request
= s
->request
;
1048 StoreEntry
*e
= NULL
;
1051 if (request
== NULL
) {
1052 debugs(31, 3, "htcpClrStore: failed to parse URL");
1056 /* Parse request headers */
1057 blk_end
= s
->req_hdrs
+ strlen(s
->req_hdrs
);
1059 if (!request
->header
.parse(s
->req_hdrs
, blk_end
)) {
1060 debugs(31, 2, "htcpClrStore: failed to parse request headers");
1064 /* Lookup matching entries. This matches both GET and HEAD */
1065 while ((e
= storeGetPublicByRequest(request
)) != NULL
) {
1067 htcpClrStoreEntry(e
);
1073 debugs(31, 4, "htcpClrStore: Cleared " << released
<< " matching entries");
1076 debugs(31, 4, "htcpClrStore: No matching entry found");
1083 htcpHandleTst(htcpDataHeader
* hdr
, char *buf
, int sz
, IpAddress
&from
)
1085 debugs(31, 3, "htcpHandleTst: sz = " << sz
);
1087 if (hdr
->RR
== RR_REQUEST
)
1088 htcpHandleTstRequest(hdr
, buf
, sz
, from
);
1090 htcpHandleTstResponse(hdr
, buf
, sz
, from
);
1093 HtcpReplyData::HtcpReplyData() : hdr(hoHtcpReply
)
1098 htcpHandleTstResponse(htcpDataHeader
* hdr
, char *buf
, int sz
, IpAddress
&from
)
1100 htcpReplyData htcpReply
;
1101 cache_key
*key
= NULL
;
1104 htcpDetail
*d
= NULL
;
1107 if (queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] != hdr
->msg_id
) {
1108 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1109 hdr
->msg_id
<< "' (expected " <<
1110 queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] << ") from '" <<
1116 key
= queried_keys
[hdr
->msg_id
% N_QUERIED_KEYS
];
1119 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr
->msg_id
<< "' from '" << from
<< "'");
1123 peer
= &queried_addr
[hdr
->msg_id
% N_QUERIED_KEYS
];
1125 if ( *peer
!= from
|| peer
->GetPort() != from
.GetPort() ) {
1126 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from
);
1131 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
1135 htcpReply
.msg_id
= hdr
->msg_id
;
1136 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply
.msg_id
);
1137 htcpReply
.hit
= hdr
->response
? 0 : 1;
1140 debugs(31, 3, "htcpHandleTstResponse: MISS");
1142 debugs(31, 3, "htcpHandleTstResponse: HIT");
1143 d
= htcpUnpackDetail(buf
, sz
);
1146 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
1150 if ((t
= d
->resp_hdrs
))
1151 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1153 if ((t
= d
->entity_hdrs
))
1154 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1156 if ((t
= d
->cache_hdrs
))
1157 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1160 debugs(31, 3, "htcpHandleTstResponse: key (" << key
<< ") " << storeKeyText(key
));
1161 neighborsHtcpReply(key
, &htcpReply
, from
);
1162 htcpReply
.hdr
.clean();
1170 htcpHandleTstRequest(htcpDataHeader
* dhdr
, char *buf
, int sz
, IpAddress
&from
)
1172 /* buf should be a SPECIFIER */
1176 debugs(31, 3, "htcpHandleTst: nothing to do");
1183 /* s is a new object */
1184 s
= htcpUnpackSpecifier(buf
, sz
);
1188 s
->setDataHeader (dhdr
);
1191 debugs(31, 2, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
1196 debugs(31, 2, "htcpHandleTstRequest: failed to parse request");
1197 htcpFreeSpecifier(s
);
1201 if (!htcpAccessCheck(Config
.accessList
.htcp
, s
, from
)) {
1202 debugs(31, 2, "htcpHandleTstRequest: Access denied");
1203 htcpFreeSpecifier(s
);
1207 debugs(31, 3, "htcpHandleTstRequest: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1208 debugs(31, 3, "htcpHandleTstRequest: " << s
->req_hdrs
);
1213 htcpSpecifier::checkedHit(StoreEntry
*e
)
1216 htcpTstReply(dhdr
, e
, this, from
); /* hit */
1218 htcpTstReply(dhdr
, NULL
, NULL
, from
); /* cache miss */
1220 htcpFreeSpecifier(this);
1225 htcpHandleMon(htcpDataHeader
* hdr
, char *buf
, int sz
, IpAddress
&from
)
1227 debugs(31, 3, "htcpHandleMon: Unimplemented");
1232 htcpHandleSet(htcpDataHeader
* hdr
, char *buf
, int sz
, IpAddress
&from
)
1234 debugs(31, 3, "htcpHandleSet: Unimplemented");
1239 htcpHandleClr(htcpDataHeader
* hdr
, char *buf
, int sz
, IpAddress
&from
)
1242 /* buf[0/1] is reserved and reason */
1243 int reason
= buf
[1] << 4;
1244 debugs(31, 3, "htcpHandleClr: reason=" << reason
);
1248 /* buf should be a SPECIFIER */
1251 debugs(31, 4, "htcpHandleClr: nothing to do");
1255 s
= htcpUnpackSpecifier(buf
, sz
);
1258 debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
1262 if (!htcpAccessCheck(Config
.accessList
.htcp_clr
, s
, from
)) {
1263 debugs(31, 2, "htcpHandleClr: Access denied");
1264 htcpFreeSpecifier(s
);
1268 debugs(31, 5, "htcpHandleClr: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1269 debugs(31, 5, "htcpHandleClr: request headers: " << s
->req_hdrs
);
1271 /* Release objects from cache
1272 * analog to clientPurgeRequest in client_side.c
1275 switch (htcpClrStore(s
)) {
1278 htcpClrReply(hdr
, 1, from
); /* hit */
1282 htcpClrReply(hdr
, 0, from
); /* miss */
1289 htcpFreeSpecifier(s
);
1293 * Forward a CLR request to all peers who have requested that CLRs be
1294 * forwarded to them.
1297 htcpForwardClr(char *buf
, int sz
)
1301 for (p
= Config
.peers
; p
; p
= p
->next
) {
1302 if (!p
->options
.htcp
) {
1305 if (!p
->options
.htcp_forward_clr
) {
1309 htcpSend(buf
, sz
, p
->in_addr
);
1314 * Do the first pass of handling an HTCP message. This used to be two
1315 * separate functions, htcpHandle and htcpHandleData. They were merged to
1316 * allow for forwarding HTCP packets easily to other peers if desired.
1318 * This function now works out what type of message we have received and then
1319 * hands it off to other functions to break apart message-specific data.
1322 htcpHandleMsg(char *buf
, int sz
, IpAddress
&from
)
1330 if ((size_t)sz
< sizeof(htcpHeader
)) {
1331 debugs(31, 3, "htcpHandle: msg size less than htcpHeader size");
1335 htcpHexdump("htcpHandle", buf
, sz
);
1336 xmemcpy(&htcpHdr
, buf
, sizeof(htcpHeader
));
1337 htcpHdr
.length
= ntohs(htcpHdr
.length
);
1339 if (htcpHdr
.minor
== 0)
1340 old_squid_format
= 1;
1342 old_squid_format
= 0;
1344 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr
.length
);
1345 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr
.major
);
1346 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr
.minor
);
1348 if (sz
!= htcpHdr
.length
) {
1349 debugs(31, 3, "htcpHandle: sz/" << sz
<< " != htcpHdr.length/" <<
1350 htcpHdr
.length
<< " from " << from
);
1355 if (htcpHdr
.major
!= 0) {
1356 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr
.major
<< " from " << from
);
1361 hbuf
= buf
+ sizeof(htcpHeader
);
1362 hsz
= sz
- sizeof(htcpHeader
);
1364 if ((size_t)hsz
< sizeof(htcpDataHeader
)) {
1365 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
1369 if (!old_squid_format
) {
1370 xmemcpy(&hdr
, hbuf
, sizeof(hdr
));
1372 htcpDataHeaderSquid hdrSquid
;
1373 xmemcpy(&hdrSquid
, hbuf
, sizeof(hdrSquid
));
1374 hdr
.length
= hdrSquid
.length
;
1375 hdr
.opcode
= hdrSquid
.opcode
;
1376 hdr
.response
= hdrSquid
.response
;
1377 hdr
.F1
= hdrSquid
.F1
;
1378 hdr
.RR
= hdrSquid
.RR
;
1380 hdr
.msg_id
= hdrSquid
.msg_id
;
1383 hdr
.length
= ntohs(hdr
.length
);
1384 hdr
.msg_id
= ntohl(hdr
.msg_id
);
1385 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1386 debugs(31, 3, "htcpHandleData: length = " << hdr
.length
);
1388 if (hdr
.opcode
>= HTCP_END
) {
1389 debugs(31, 3, "htcpHandleData: client " << from
<< ", opcode " << hdr
.opcode
<< " out of range");
1393 debugs(31, 3, "htcpHandleData: opcode = " << hdr
.opcode
<< " " << htcpOpcodeStr
[hdr
.opcode
]);
1394 debugs(31, 3, "htcpHandleData: response = " << hdr
.response
);
1395 debugs(31, 3, "htcpHandleData: F1 = " << hdr
.F1
);
1396 debugs(31, 3, "htcpHandleData: RR = " << hdr
.RR
);
1397 debugs(31, 3, "htcpHandleData: msg_id = " << hdr
.msg_id
);
1399 if (hsz
< hdr
.length
) {
1400 debugs(31, 3, "htcpHandleData: sz < hdr.length");
1405 * set sz = hdr.length so we ignore any AUTH fields following
1408 hsz
= (int) hdr
.length
;
1409 hbuf
+= sizeof(htcpDataHeader
);
1410 hsz
-= sizeof(htcpDataHeader
);
1411 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1413 htcpHexdump("htcpHandleData", hbuf
, hsz
);
1415 switch (hdr
.opcode
) {
1417 htcpHandleNop(&hdr
, hbuf
, hsz
, from
);
1420 htcpHandleTst(&hdr
, hbuf
, hsz
, from
);
1423 htcpHandleMon(&hdr
, hbuf
, hsz
, from
);
1426 htcpHandleSet(&hdr
, hbuf
, hsz
, from
);
1429 htcpHandleClr(&hdr
, hbuf
, hsz
, from
);
1430 htcpForwardClr(buf
, sz
);
1438 htcpRecv(int fd
, void *data
)
1440 static char buf
[8192];
1442 static IpAddress from
;
1444 /* Receive up to 8191 bytes, leaving room for a null */
1446 len
= comm_udp_recvfrom(fd
, buf
, sizeof(buf
) - 1, 0, from
);
1448 debugs(31, 3, "htcpRecv: FD " << fd
<< ", " << len
<< " bytes from " << from
);
1451 statCounter
.htcp
.pkts_recv
++;
1453 htcpHandleMsg(buf
, len
, from
);
1455 commSetSelect(fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1459 * ======================================================================
1461 * ======================================================================
1467 if (Config
.Port
.htcp
<= 0) {
1468 debugs(31, 1, "HTCP Disabled.");
1472 IpAddress incomingAddr
= Config
.Addrs
.udp_incoming
;
1473 incomingAddr
.SetPort(Config
.Port
.htcp
);
1476 htcpInSocket
= comm_open(SOCK_DGRAM
,
1483 if (htcpInSocket
< 0)
1484 fatal("Cannot open HTCP Socket");
1486 commSetSelect(htcpInSocket
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1488 debugs(31, 1, "Accepting HTCP messages on port " << Config
.Port
.htcp
<< ", FD " << htcpInSocket
<< ".");
1490 if (!Config
.Addrs
.udp_outgoing
.IsNoAddr()) {
1491 IpAddress outgoingAddr
= Config
.Addrs
.udp_outgoing
;
1492 outgoingAddr
.SetPort(Config
.Port
.htcp
);
1495 htcpOutSocket
= comm_open(SOCK_DGRAM
,
1499 "Outgoing HTCP Socket");
1502 if (htcpOutSocket
< 0)
1503 fatal("Cannot open Outgoing HTCP Socket");
1505 commSetSelect(htcpOutSocket
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1507 debugs(31, 1, "Outgoing HTCP messages on port " << Config
.Port
.htcp
<< ", FD " << htcpOutSocket
<< ".");
1509 fd_note(htcpInSocket
, "Incoming HTCP socket");
1511 htcpOutSocket
= htcpInSocket
;
1514 if (!htcpDetailPool
) {
1515 htcpDetailPool
= memPoolCreate("htcpDetail", sizeof(htcpDetail
));
1520 htcpQuery(StoreEntry
* e
, HttpRequest
* req
, peer
* p
)
1522 cache_key
*save_key
;
1523 static char pkt
[8192];
1527 HttpHeader
hdr(hoRequest
);
1530 http_state_flags flags
;
1532 if (htcpInSocket
< 0)
1535 old_squid_format
= p
->options
.htcp_oldsquid
;
1536 memset(&flags
, '\0', sizeof(flags
));
1537 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1538 req
->http_ver
.major
, req
->http_ver
.minor
);
1539 stuff
.op
= HTCP_TST
;
1540 stuff
.rr
= RR_REQUEST
;
1543 stuff
.msg_id
= ++msg_id_counter
;
1544 stuff
.S
.method
= (char *) RequestMethodStr(req
->method
);
1545 stuff
.S
.uri
= (char *) e
->url();
1546 stuff
.S
.version
= vbuf
;
1547 HttpStateData::httpBuildRequestHeader(req
, req
, e
, &hdr
, flags
);
1549 packerToMemInit(&pa
, &mb
);
1553 stuff
.S
.req_hdrs
= mb
.buf
;
1554 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1557 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1561 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1563 queried_id
[stuff
.msg_id
% N_QUERIED_KEYS
] = stuff
.msg_id
;
1564 save_key
= queried_keys
[stuff
.msg_id
% N_QUERIED_KEYS
];
1565 storeKeyCopy(save_key
, (const cache_key
*)e
->key
);
1566 queried_addr
[stuff
.msg_id
% N_QUERIED_KEYS
] = p
->in_addr
;
1567 debugs(31, 3, "htcpQuery: key (" << save_key
<< ") " << storeKeyText(save_key
));
1573 * Send an HTCP CLR message for a specified item to a given peer.
1576 htcpClear(StoreEntry
* e
, const char *uri
, HttpRequest
* req
, const HttpRequestMethod
&method
, peer
* p
, htcp_clr_reason reason
)
1578 static char pkt
[8192];
1582 HttpHeader
hdr(hoRequest
);
1585 http_state_flags flags
;
1587 if (htcpInSocket
< 0)
1590 old_squid_format
= p
->options
.htcp_oldsquid
;
1591 memset(&flags
, '\0', sizeof(flags
));
1592 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1593 req
->http_ver
.major
, req
->http_ver
.minor
);
1594 stuff
.op
= HTCP_CLR
;
1595 stuff
.rr
= RR_REQUEST
;
1598 stuff
.msg_id
= ++msg_id_counter
;
1600 case HTCP_CLR_INVALIDATION
:
1607 stuff
.S
.method
= (char *) RequestMethodStr(req
->method
);
1608 if (e
== NULL
|| e
->mem_obj
== NULL
) {
1612 stuff
.S
.uri
= xstrdup(uri
);
1614 stuff
.S
.uri
= (char *) e
->url();
1616 stuff
.S
.version
= vbuf
;
1617 if (reason
!= HTCP_CLR_INVALIDATION
) {
1618 HttpStateData::httpBuildRequestHeader(req
, req
, e
, &hdr
, flags
);
1620 packerToMemInit(&pa
, &mb
);
1624 stuff
.S
.req_hdrs
= mb
.buf
;
1626 stuff
.S
.req_hdrs
= NULL
;
1628 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1629 if (reason
!= HTCP_CLR_INVALIDATION
) {
1636 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1640 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1644 * htcpSocketShutdown only closes the 'in' socket if it is
1645 * different than the 'out' socket.
1648 htcpSocketShutdown(void)
1650 if (htcpInSocket
< 0)
1653 if (htcpInSocket
!= htcpOutSocket
) {
1654 debugs(12, 1, "FD " << htcpInSocket
<< " Closing HTCP socket");
1655 comm_close(htcpInSocket
);
1659 * Here we set 'htcpInSocket' to -1 even though the HTCP 'in'
1660 * and 'out' sockets might be just one FD. This prevents this
1661 * function from executing repeatedly. When we are really ready to
1662 * exit or restart, main will comm_close the 'out' descriptor.
1667 * Normally we only write to the outgoing HTCP socket, but
1668 * we also have a read handler there to catch messages sent
1669 * to that specific interface. During shutdown, we must
1670 * disable reading on the outgoing socket.
1672 /* XXX Don't we need this handler to read replies while shutting down?
1673 * I think there should be a separate hander for reading replies..
1675 assert(htcpOutSocket
> -1);
1677 commSetSelect(htcpOutSocket
, COMM_SELECT_READ
, NULL
, NULL
, 0);
1681 htcpSocketClose(void)
1683 htcpSocketShutdown();
1685 if (htcpOutSocket
> -1) {
1686 debugs(12, 1, "FD " << htcpOutSocket
<< " Closing HTCP socket");
1687 comm_close(htcpOutSocket
);