3 * $Id: htcp.cc,v 1.81 2008/02/26 21:49:34 amosjeffries Exp $
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"
48 typedef struct _Countstr Countstr
;
50 typedef struct _htcpHeader htcpHeader
;
52 typedef struct _htcpDataHeader htcpDataHeader
;
54 typedef struct _htcpDataHeaderSquid htcpDataHeaderSquid
;
56 typedef struct _htcpAuthHeader htcpAuthHeader
;
58 typedef struct _htcpStuff htcpStuff
;
60 typedef struct _htcpDetail htcpDetail
;
75 struct _htcpDataHeaderSquid
80 unsigned int opcode
:4;
81 unsigned int response
:4;
83 unsigned int response
:4;
84 unsigned int opcode
:4;
88 unsigned int reserved
:6;
94 unsigned int reserved
:6;
100 struct _htcpDataHeader
125 /* RR == 0 --> F1 = RESPONSE DESIRED FLAG */
126 /* RR == 1 --> F1 = MESSAGE OVERALL FLAG */
127 /* RR == 0 --> REQUEST */
128 /* RR == 1 --> RESPONSE */
130 struct _htcpAuthHeader
139 class htcpSpecifier
: public StoreClient
143 MEMPROXY_CLASS(htcpSpecifier
);
145 void created (StoreEntry
*newEntry
);
147 void checkedHit(StoreEntry
*e
);
149 void setFrom (IPAddress
&from
);
150 void setDataHeader (htcpDataHeader
*);
155 HttpRequest
*request
;
158 HttpRequest
*checkHitRequest
;
160 IPAddress from
; // was a ptr. return to such IFF needed. otherwise copy should do.
161 htcpDataHeader
*dhdr
;
164 MEMPROXY_CLASS_INLINE(htcpSpecifier
) /**DOCS_NOSEMI*/
194 static const char *const htcpOpcodeStr
[] =
205 * values for htcpDataHeader->response
210 OPCODE_UNIMPLEMENTED
,
211 MAJOR_VERSION_UNSUPPORTED
,
212 MINOR_VERSION_UNSUPPORTED
,
217 * values for htcpDataHeader->RR
224 static u_int32_t msg_id_counter
= 0;
225 static int htcpInSocket
= -1;
226 static int htcpOutSocket
= -1;
227 #define N_QUERIED_KEYS 8192
228 static u_int32_t queried_id
[N_QUERIED_KEYS
];
229 static cache_key queried_keys
[N_QUERIED_KEYS
][SQUID_MD5_DIGEST_LENGTH
];
231 static IPAddress queried_addr
[N_QUERIED_KEYS
];
232 static MemAllocator
*htcpDetailPool
= NULL
;
234 static int old_squid_format
= 0;
237 static ssize_t
htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
);
238 static htcpSpecifier
*htcpUnpackSpecifier(char *buf
, int sz
);
239 static htcpDetail
*htcpUnpackDetail(char *buf
, int sz
);
240 static ssize_t
htcpBuildAuth(char *buf
, size_t buflen
);
241 static ssize_t
htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
);
242 static ssize_t
htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
243 static ssize_t
htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
);
244 static ssize_t
htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
245 static ssize_t
htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
);
246 static ssize_t
htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
);
247 static void htcpFreeSpecifier(htcpSpecifier
* s
);
248 static void htcpFreeDetail(htcpDetail
* s
);
250 static void htcpHandleMsg(char *buf
, int sz
, IPAddress
&from
);
252 static void htcpHandleMon(htcpDataHeader
*, char *buf
, int sz
, IPAddress
&from
);
254 static void htcpHandleNop(htcpDataHeader
*, char *buf
, int sz
, IPAddress
&from
);
256 static void htcpHandleSet(htcpDataHeader
*, char *buf
, int sz
, IPAddress
&from
);
258 static void htcpHandleTst(htcpDataHeader
*, char *buf
, int sz
, IPAddress
&from
);
259 static void htcpRecv(int fd
, void *data
);
261 static void htcpSend(const char *buf
, int len
, IPAddress
&to
);
263 static void htcpTstReply(htcpDataHeader
*, StoreEntry
*, htcpSpecifier
*, IPAddress
&);
265 static void htcpHandleTstRequest(htcpDataHeader
*, char *buf
, int sz
, IPAddress
&from
);
267 static void htcpHandleTstResponse(htcpDataHeader
*, char *, int, IPAddress
&);
270 htcpHexdump(const char *tag
, const char *s
, int sz
)
276 debugs(31, 3, "htcpHexdump " << tag
);
277 memset(hex
, '\0', 80);
279 for (i
= 0; i
< sz
; i
++) {
281 snprintf(&hex
[k
* 3], 4, " %02x", (int) *(s
+ i
));
283 if (k
< 15 && i
< (sz
- 1))
286 debugs(31, 3, "\t" << hex
);
288 memset(hex
, '\0', 80);
295 * STUFF FOR SENDING HTCP MESSAGES
299 htcpBuildAuth(char *buf
, size_t buflen
)
303 assert(2 == sizeof(u_int16_t
));
304 auth
.length
= htons(2);
306 if (buflen
< copy_sz
)
308 xmemcpy(buf
, &auth
, copy_sz
);
313 htcpBuildCountstr(char *buf
, size_t buflen
, const char *s
)
319 if (buflen
- off
< 2)
327 debugs(31, 3, "htcpBuildCountstr: LENGTH = " << len
);
329 debugs(31, 3, "htcpBuildCountstr: TEXT = {" << (s
? s
: "<NULL>") << "}");
331 length
= htons((u_int16_t
) len
);
333 xmemcpy(buf
+ off
, &length
, 2);
337 if (buflen
- off
< len
)
341 xmemcpy(buf
+ off
, s
, len
);
349 htcpBuildSpecifier(char *buf
, size_t buflen
, htcpStuff
* stuff
)
353 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.method
);
360 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.uri
);
367 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.version
);
374 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->S
.req_hdrs
);
381 debugs(31, 3, "htcpBuildSpecifier: size " << off
);
387 htcpBuildDetail(char *buf
, size_t buflen
, htcpStuff
* stuff
)
391 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.resp_hdrs
);
398 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.entity_hdrs
);
405 s
= htcpBuildCountstr(buf
+ off
, buflen
- off
, stuff
->D
.cache_hdrs
);
416 htcpBuildTstOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
421 debugs(31, 3, "htcpBuildTstOpData: RR_REQUEST");
422 return htcpBuildSpecifier(buf
, buflen
, stuff
);
425 debugs(31, 3, "htcpBuildTstOpData: RR_RESPONSE");
426 debugs(31, 3, "htcpBuildTstOpData: F1 = " << stuff
->f1
);
428 if (stuff
->f1
) /* cache miss */
431 return htcpBuildDetail(buf
, buflen
, stuff
);
434 fatal_dump("htcpBuildTstOpData: bad RR value");
441 htcpBuildClrOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
447 debugs(31, 3, "htcpBuildClrOpData: RR_REQUEST");
448 reason
= htons((u_short
)stuff
->reason
);
449 xmemcpy(buf
, &reason
, 2);
450 return htcpBuildSpecifier(buf
+ 2, buflen
- 2, stuff
) + 2;
454 fatal_dump("htcpBuildClrOpData: bad RR value");
461 htcpBuildOpData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
464 debugs(31, 3, "htcpBuildOpData: opcode " << htcpOpcodeStr
[stuff
->op
]);
469 off
= htcpBuildTstOpData(buf
+ off
, buflen
, stuff
);
473 off
= htcpBuildClrOpData(buf
+ off
, buflen
, stuff
);
485 htcpBuildData(char *buf
, size_t buflen
, htcpStuff
* stuff
)
489 size_t hdr_sz
= sizeof(htcpDataHeader
);
495 off
+= hdr_sz
; /* skip! */
497 op_data_sz
= htcpBuildOpData(buf
+ off
, buflen
- off
, stuff
);
504 debugs(31, 3, "htcpBuildData: hdr.length = " << off
);
506 hdr
.length
= (u_int16_t
) off
;
508 hdr
.opcode
= stuff
->op
;
510 hdr
.response
= stuff
->response
;
516 hdr
.msg_id
= stuff
->msg_id
;
518 /* convert multi-byte fields */
519 hdr
.length
= htons(hdr
.length
);
521 hdr
.msg_id
= htonl(hdr
.msg_id
);
523 if (!old_squid_format
) {
524 xmemcpy(buf
, &hdr
, hdr_sz
);
526 htcpDataHeaderSquid hdrSquid
;
527 memset(&hdrSquid
, 0, sizeof(hdrSquid
));
528 hdrSquid
.length
= hdr
.length
;
529 hdrSquid
.opcode
= hdr
.opcode
;
530 hdrSquid
.response
= hdr
.response
;
531 hdrSquid
.F1
= hdr
.F1
;
532 hdrSquid
.RR
= hdr
.RR
;
533 xmemcpy(buf
, &hdrSquid
, hdr_sz
);
536 debugs(31, 3, "htcpBuildData: size " << off
);
542 * Build an HTCP packet into buf, maximum length buflen.
543 * Returns the packet length, or zero on failure.
546 htcpBuildPacket(char *buf
, size_t buflen
, htcpStuff
* stuff
)
550 size_t hdr_sz
= sizeof(htcpHeader
);
552 /* skip the header -- we don't know the overall length */
554 if (buflen
< hdr_sz
) {
559 s
= htcpBuildData(buf
+ off
, buflen
- off
, stuff
);
566 s
= htcpBuildAuth(buf
+ off
, buflen
- off
);
573 hdr
.length
= htons((u_int16_t
) off
);
576 if (old_squid_format
)
581 xmemcpy(buf
, &hdr
, hdr_sz
);
583 debugs(31, 3, "htcpBuildPacket: size " << off
);
590 htcpSend(const char *buf
, int len
, IPAddress
&to
)
594 debugs(31, 3, "htcpSend: " << to
);
595 htcpHexdump("htcpSend", buf
, len
);
597 x
= comm_udp_sendto(htcpOutSocket
,
603 debugs(31, 3, "htcpSend: FD " << htcpOutSocket
<< " sendto: " << xstrerror());
605 statCounter
.htcp
.pkts_sent
++;
609 * STUFF FOR RECEIVING HTCP MESSAGES
614 htcpSpecifier::setFrom (IPAddress
&aSocket
)
620 htcpSpecifier::setDataHeader (htcpDataHeader
*aDataHeader
)
626 htcpFreeSpecifier(htcpSpecifier
* s
)
628 HTTPMSGUNLOCK(s
->request
);
634 htcpFreeDetail(htcpDetail
* d
)
636 htcpDetailPool
->free(d
);
640 * Unpack an HTCP SPECIFIER in place
641 * This will overwrite any following AUTH block
643 static htcpSpecifier
*
644 htcpUnpackSpecifier(char *buf
, int sz
)
646 htcpSpecifier
*s
= new htcpSpecifier
;
647 HttpRequestMethod method
;
649 /* Find length of METHOD */
650 u_int16_t l
= ntohs(*(u_int16_t
*) buf
);
655 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack METHOD");
656 htcpFreeSpecifier(s
);
667 /* Find length of URI */
668 l
= ntohs(*(u_int16_t
*) buf
);
673 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack URI");
674 htcpFreeSpecifier(s
);
678 /* Add terminating null to METHOD */
690 /* Find length of VERSION */
691 l
= ntohs(*(u_int16_t
*) buf
);
696 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack VERSION");
697 htcpFreeSpecifier(s
);
701 /* Add terminating null to URI */
713 /* Find length of REQ-HDRS */
714 l
= ntohs(*(u_int16_t
*) buf
);
719 debugs(31, 3, "htcpUnpackSpecifier: failed to unpack REQ-HDRS");
720 htcpFreeSpecifier(s
);
724 /* Add terminating null to URI */
736 debugs(31, 3, "htcpUnpackSpecifier: " << sz
<< " bytes left");
739 * Add terminating null to REQ-HDRS. This is possible because we allocated
740 * an extra byte when we received the packet. This will overwrite any following
748 method
= HttpRequestMethod(s
->method
, NULL
);
750 s
->request
= HttpRequest::CreateFromUrlAndMethod(s
->uri
, method
== METHOD_NONE
? HttpRequestMethod(METHOD_GET
) : method
);
753 HTTPMSGLOCK(s
->request
);
759 * Unpack an HTCP DETAIL in place
760 * This will overwrite any following AUTH block
763 htcpUnpackDetail(char *buf
, int sz
)
765 htcpDetail
*d
= static_cast<htcpDetail
*>(htcpDetailPool
->alloc());
767 /* Find length of RESP-HDRS */
768 u_int16_t l
= ntohs(*(u_int16_t
*) buf
);
773 debugs(31, 3, "htcpUnpackDetail: failed to unpack RESP_HDRS");
785 /* Find length of ENTITY-HDRS */
786 l
= ntohs(*(u_int16_t
*) buf
);
791 debugs(31, 3, "htcpUnpackDetail: failed to unpack ENTITY_HDRS");
796 /* Add terminating null to RESP-HDRS */
799 /* Set ENTITY-HDRS */
802 d
->entity_hdrs
= buf
;
808 /* Find length of CACHE-HDRS */
809 l
= ntohs(*(u_int16_t
*) buf
);
814 debugs(31, 3, "htcpUnpackDetail: failed to unpack CACHE_HDRS");
819 /* Add terminating null to ENTITY-HDRS */
831 debugs(31, 3, "htcpUnpackDetail: " << sz
<< " bytes left");
834 * Add terminating null to CACHE-HDRS. This is possible because we allocated
835 * an extra byte when we received the packet. This will overwrite any following
845 htcpAccessCheck(acl_access
* acl
, htcpSpecifier
* s
, IPAddress
&from
)
847 ACLChecklist checklist
;
848 checklist
.src_addr
= from
;
849 checklist
.my_addr
.SetNoAddr();
850 checklist
.request
= HTTPMSGLOCK(s
->request
);
851 checklist
.accessList
= cbdataReference(acl
);
852 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
853 int result
= checklist
.fastCheck();
859 htcpTstReply(htcpDataHeader
* dhdr
, StoreEntry
* e
, htcpSpecifier
* spec
, IPAddress
&from
)
862 static char pkt
[8192];
863 HttpHeader
hdr(hoHtcpReply
);
872 memset(&stuff
, '\0', sizeof(stuff
));
874 stuff
.rr
= RR_RESPONSE
;
876 stuff
.response
= e
? 0 : 1;
877 debugs(31, 3, "htcpTstReply: response = " << stuff
.response
);
878 stuff
.msg_id
= dhdr
->msg_id
;
883 packerToMemInit(&p
, &mb
);
884 stuff
.S
.method
= spec
->method
;
885 stuff
.S
.uri
= spec
->uri
;
886 stuff
.S
.version
= spec
->version
;
887 stuff
.S
.req_hdrs
= spec
->req_hdrs
;
889 e
->timestamp
<= squid_curtime
?
890 squid_curtime
- e
->timestamp
: 0);
892 stuff
.D
.resp_hdrs
= xstrdup(mb
.buf
);
893 debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff
.D
.resp_hdrs
<< "}");
898 hdr
.putTime(HDR_EXPIRES
, e
->expires
);
901 hdr
.putTime(HDR_LAST_MODIFIED
, e
->lastmod
);
905 stuff
.D
.entity_hdrs
= xstrdup(mb
.buf
);
907 debugs(31, 3, "htcpTstReply: entity_hdrs = {" << stuff
.D
.entity_hdrs
<< "}");
913 if ((host
= urlHostname(spec
->uri
))) {
914 netdbHostData(host
, &samp
, &rtt
, &hops
);
917 snprintf(cto_buf
, 128, "%s %d %f %d",
918 host
, samp
, 0.001 * rtt
, hops
);
919 hdr
.putExt("Cache-to-Origin", cto_buf
);
924 stuff
.D
.cache_hdrs
= xstrdup(mb
.buf
);
925 debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff
.D
.cache_hdrs
<< "}");
931 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
933 safe_free(stuff
.D
.resp_hdrs
);
934 safe_free(stuff
.D
.entity_hdrs
);
935 safe_free(stuff
.D
.cache_hdrs
);
939 debugs(31, 3, "htcpTstReply: htcpBuildPacket() failed");
943 htcpSend(pkt
, (int) pktlen
, from
);
948 htcpClrReply(htcpDataHeader
* dhdr
, int purgeSucceeded
, IPAddress
&from
)
951 static char pkt
[8192];
954 /* If dhdr->F1 == 0, no response desired */
959 memset(&stuff
, '\0', sizeof(stuff
));
963 stuff
.rr
= RR_RESPONSE
;
967 stuff
.response
= purgeSucceeded
? 0 : 2;
969 debugs(31, 3, "htcpClrReply: response = " << stuff
.response
);
971 stuff
.msg_id
= dhdr
->msg_id
;
973 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");
1025 else if (!e
->validToSend()) {
1026 debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
1028 else if (refreshCheckHTCP(e
, checkHitRequest
)) {
1029 debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
1032 debugs(31, 3, "htcpCheckHit: YES!?");
1040 htcpClrStoreEntry(StoreEntry
* e
)
1042 debugs(31, 4, "htcpClrStoreEntry: Clearing store for entry: " << e
->url() );
1043 e
->releaseRequest();
1047 htcpClrStore(const htcpSpecifier
* s
)
1049 HttpRequest
*request
= s
->request
;
1051 StoreEntry
*e
= NULL
;
1054 if (request
== NULL
) {
1055 debugs(31, 3, "htcpClrStore: failed to parse URL");
1059 /* Parse request headers */
1060 blk_end
= s
->req_hdrs
+ strlen(s
->req_hdrs
);
1062 if (!request
->header
.parse(s
->req_hdrs
, blk_end
)) {
1063 debugs(31, 2, "htcpClrStore: failed to parse request headers");
1067 /* Lookup matching entries. This matches both GET and HEAD */
1068 while ((e
= storeGetPublicByRequest(request
)) != NULL
) {
1070 htcpClrStoreEntry(e
);
1076 debugs(31, 4, "htcpClrStore: Cleared " << released
<< " matching entries");
1079 debugs(31, 4, "htcpClrStore: No matching entry found");
1086 htcpHandleTst(htcpDataHeader
* hdr
, char *buf
, int sz
, IPAddress
&from
)
1088 debugs(31, 3, "htcpHandleTst: sz = " << sz
);
1090 if (hdr
->RR
== RR_REQUEST
)
1091 htcpHandleTstRequest(hdr
, buf
, sz
, from
);
1093 htcpHandleTstResponse(hdr
, buf
, sz
, from
);
1096 HtcpReplyData::HtcpReplyData() : hdr(hoHtcpReply
)
1101 htcpHandleTstResponse(htcpDataHeader
* hdr
, char *buf
, int sz
, IPAddress
&from
)
1103 htcpReplyData htcpReply
;
1104 cache_key
*key
= NULL
;
1107 htcpDetail
*d
= NULL
;
1110 if (queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] != hdr
->msg_id
)
1112 debugs(31, 2, "htcpHandleTstResponse: No matching query id '" <<
1113 hdr
->msg_id
<< "' (expected " <<
1114 queried_id
[hdr
->msg_id
% N_QUERIED_KEYS
] << ") from '" <<
1120 key
= queried_keys
[hdr
->msg_id
% N_QUERIED_KEYS
];
1124 debugs(31, 3, "htcpHandleTstResponse: No query key for response id '" << hdr
->msg_id
<< "' from '" << from
<< "'");
1128 peer
= &queried_addr
[hdr
->msg_id
% N_QUERIED_KEYS
];
1130 if ( *peer
!= from
|| peer
->GetPort() != from
.GetPort() )
1132 debugs(31, 3, "htcpHandleTstResponse: Unexpected response source " << from
);
1138 debugs(31, 2, "htcpHandleTstResponse: error condition, F1/MO == 1");
1142 htcpReply
.msg_id
= hdr
->msg_id
;
1143 debugs(31, 3, "htcpHandleTstResponse: msg_id = " << htcpReply
.msg_id
);
1144 htcpReply
.hit
= hdr
->response
? 0 : 1;
1148 debugs(31, 3, "htcpHandleTstResponse: MISS");
1151 debugs(31, 3, "htcpHandleTstResponse: HIT");
1152 d
= htcpUnpackDetail(buf
, sz
);
1155 debugs(31, 3, "htcpHandleTstResponse: bad DETAIL");
1159 if ((t
= d
->resp_hdrs
))
1160 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1162 if ((t
= d
->entity_hdrs
))
1163 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1165 if ((t
= d
->cache_hdrs
))
1166 htcpReply
.hdr
.parse(t
, t
+ strlen(t
));
1169 debugs(31, 3, "htcpHandleTstResponse: key (" << key
<< ") " << storeKeyText(key
));
1170 neighborsHtcpReply(key
, &htcpReply
, from
);
1171 htcpReply
.hdr
.clean();
1179 htcpHandleTstRequest(htcpDataHeader
* dhdr
, char *buf
, int sz
, IPAddress
&from
)
1181 /* buf should be a SPECIFIER */
1186 debugs(31, 3, "htcpHandleTst: nothing to do");
1193 /* s is a new object */
1194 s
= htcpUnpackSpecifier(buf
, sz
);
1198 s
->setDataHeader (dhdr
);
1202 debugs(31, 2, "htcpHandleTstRequest: htcpUnpackSpecifier failed");
1208 debugs(31, 2, "htcpHandleTstRequest: failed to parse request");
1209 htcpFreeSpecifier(s
);
1213 if (!htcpAccessCheck(Config
.accessList
.htcp
, s
, from
))
1215 debugs(31, 2, "htcpHandleTstRequest: Access denied");
1216 htcpFreeSpecifier(s
);
1220 debugs(31, 3, "htcpHandleTstRequest: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1221 debugs(31, 3, "htcpHandleTstRequest: " << s
->req_hdrs
);
1226 htcpSpecifier::checkedHit(StoreEntry
*e
)
1229 htcpTstReply(dhdr
, e
, this, from
); /* hit */
1231 htcpTstReply(dhdr
, NULL
, NULL
, from
); /* cache miss */
1233 htcpFreeSpecifier(this);
1238 htcpHandleMon(htcpDataHeader
* hdr
, char *buf
, int sz
, IPAddress
&from
)
1240 debugs(31, 3, "htcpHandleMon: Unimplemented");
1245 htcpHandleSet(htcpDataHeader
* hdr
, char *buf
, int sz
, IPAddress
&from
)
1247 debugs(31, 3, "htcpHandleSet: Unimplemented");
1252 htcpHandleClr(htcpDataHeader
* hdr
, char *buf
, int sz
, IPAddress
&from
)
1255 /* buf[0/1] is reserved and reason */
1256 int reason
= buf
[1] << 4;
1257 debugs(31, 3, "htcpHandleClr: reason=" << reason
);
1261 /* buf should be a SPECIFIER */
1265 debugs(31, 4, "htcpHandleClr: nothing to do");
1269 s
= htcpUnpackSpecifier(buf
, sz
);
1273 debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
1277 if (!htcpAccessCheck(Config
.accessList
.htcp_clr
, s
, from
))
1279 debugs(31, 2, "htcpHandleClr: Access denied");
1280 htcpFreeSpecifier(s
);
1284 debugs(31, 5, "htcpHandleClr: " << s
->method
<< " " << s
->uri
<< " " << s
->version
);
1285 debugs(31, 5, "htcpHandleClr: request headers: " << s
->req_hdrs
);
1287 /* Release objects from cache
1288 * analog to clientPurgeRequest in client_side.c
1291 switch (htcpClrStore(s
))
1295 htcpClrReply(hdr
, 1, from
); /* hit */
1299 htcpClrReply(hdr
, 0, from
); /* miss */
1306 htcpFreeSpecifier(s
);
1310 * Forward a CLR request to all peers who have requested that CLRs be
1311 * forwarded to them.
1314 htcpForwardClr(char *buf
, int sz
)
1318 for (p
= Config
.peers
; p
; p
= p
->next
) {
1319 if (!p
->options
.htcp
) {
1322 if (!p
->options
.htcp_forward_clr
) {
1326 htcpSend(buf
, sz
, p
->in_addr
);
1331 * Do the first pass of handling an HTCP message. This used to be two
1332 * separate functions, htcpHandle and htcpHandleData. They were merged to
1333 * allow for forwarding HTCP packets easily to other peers if desired.
1335 * This function now works out what type of message we have received and then
1336 * hands it off to other functions to break apart message-specific data.
1339 htcpHandleMsg(char *buf
, int sz
, IPAddress
&from
)
1347 if ((size_t)sz
< sizeof(htcpHeader
))
1349 debugs(31, 3, "htcpHandle: msg size less than htcpHeader size");
1353 htcpHexdump("htcpHandle", buf
, sz
);
1354 xmemcpy(&htcpHdr
, buf
, sizeof(htcpHeader
));
1355 htcpHdr
.length
= ntohs(htcpHdr
.length
);
1357 if (htcpHdr
.minor
== 0)
1358 old_squid_format
= 1;
1360 old_squid_format
= 0;
1362 debugs(31, 3, "htcpHandle: htcpHdr.length = " << htcpHdr
.length
);
1363 debugs(31, 3, "htcpHandle: htcpHdr.major = " << htcpHdr
.major
);
1364 debugs(31, 3, "htcpHandle: htcpHdr.minor = " << htcpHdr
.minor
);
1366 if (sz
!= htcpHdr
.length
)
1368 debugs(31, 3, "htcpHandle: sz/" << sz
<< " != htcpHdr.length/" <<
1369 htcpHdr
.length
<< " from " << from
);
1374 if (htcpHdr
.major
!= 0)
1376 debugs(31, 3, "htcpHandle: Unknown major version " << htcpHdr
.major
<< " from " << from
);
1381 hbuf
= buf
+ sizeof(htcpHeader
);
1382 hsz
= sz
- sizeof(htcpHeader
);
1384 if ((size_t)hsz
< sizeof(htcpDataHeader
))
1386 debugs(31, 3, "htcpHandleData: msg size less than htcpDataHeader size");
1390 if (!old_squid_format
)
1392 xmemcpy(&hdr
, hbuf
, sizeof(hdr
));
1394 htcpDataHeaderSquid hdrSquid
;
1395 xmemcpy(&hdrSquid
, hbuf
, sizeof(hdrSquid
));
1396 hdr
.length
= hdrSquid
.length
;
1397 hdr
.opcode
= hdrSquid
.opcode
;
1398 hdr
.response
= hdrSquid
.response
;
1399 hdr
.F1
= hdrSquid
.F1
;
1400 hdr
.RR
= hdrSquid
.RR
;
1402 hdr
.msg_id
= hdrSquid
.msg_id
;
1405 hdr
.length
= ntohs(hdr
.length
);
1406 hdr
.msg_id
= ntohl(hdr
.msg_id
);
1407 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1408 debugs(31, 3, "htcpHandleData: length = " << hdr
.length
);
1410 if (hdr
.opcode
>= HTCP_END
) {
1411 debugs(31, 3, "htcpHandleData: client " << from
<< ", opcode " << hdr
.opcode
<< " out of range");
1415 debugs(31, 3, "htcpHandleData: opcode = " << hdr
.opcode
<< " " << htcpOpcodeStr
[hdr
.opcode
]);
1416 debugs(31, 3, "htcpHandleData: response = " << hdr
.response
);
1417 debugs(31, 3, "htcpHandleData: F1 = " << hdr
.F1
);
1418 debugs(31, 3, "htcpHandleData: RR = " << hdr
.RR
);
1419 debugs(31, 3, "htcpHandleData: msg_id = " << hdr
.msg_id
);
1421 if (hsz
< hdr
.length
) {
1422 debugs(31, 3, "htcpHandleData: sz < hdr.length");
1427 * set sz = hdr.length so we ignore any AUTH fields following
1430 hsz
= (int) hdr
.length
;
1431 hbuf
+= sizeof(htcpDataHeader
);
1432 hsz
-= sizeof(htcpDataHeader
);
1433 debugs(31, 3, "htcpHandleData: hsz = " << hsz
);
1435 htcpHexdump("htcpHandleData", hbuf
, hsz
);
1437 switch (hdr
.opcode
) {
1439 htcpHandleNop(&hdr
, hbuf
, hsz
, from
);
1442 htcpHandleTst(&hdr
, hbuf
, hsz
, from
);
1445 htcpHandleMon(&hdr
, hbuf
, hsz
, from
);
1448 htcpHandleSet(&hdr
, hbuf
, hsz
, from
);
1451 htcpHandleClr(&hdr
, hbuf
, hsz
, from
);
1452 htcpForwardClr(buf
, sz
);
1460 htcpRecv(int fd
, void *data
)
1462 static char buf
[8192];
1464 static IPAddress from
;
1466 /* Receive up to 8191 bytes, leaving room for a null */
1468 len
= comm_udp_recvfrom(fd
, buf
, sizeof(buf
) - 1, 0, from
);
1470 debugs(31, 3, "htcpRecv: FD " << fd
<< ", " << len
<< " bytes from " << from
);
1473 statCounter
.htcp
.pkts_recv
++;
1475 htcpHandleMsg(buf
, len
, from
);
1477 commSetSelect(fd
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1481 * ======================================================================
1483 * ======================================================================
1489 if (Config
.Port
.htcp
<= 0) {
1490 debugs(31, 1, "HTCP Disabled.");
1494 IPAddress incomingAddr
= Config
.Addrs
.udp_incoming
;
1495 incomingAddr
.SetPort(Config
.Port
.htcp
);
1498 htcpInSocket
= comm_open(SOCK_DGRAM
,
1505 if (htcpInSocket
< 0)
1506 fatal("Cannot open HTCP Socket");
1508 commSetSelect(htcpInSocket
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1510 debugs(31, 1, "Accepting HTCP messages on port " << Config
.Port
.htcp
<< ", FD " << htcpInSocket
<< ".");
1512 if (!Config
.Addrs
.udp_outgoing
.IsNoAddr()) {
1513 IPAddress outgoingAddr
= Config
.Addrs
.udp_outgoing
;
1514 outgoingAddr
.SetPort(Config
.Port
.htcp
);
1517 htcpOutSocket
= comm_open(SOCK_DGRAM
,
1521 "Outgoing HTCP Socket");
1524 if (htcpOutSocket
< 0)
1525 fatal("Cannot open Outgoing HTCP Socket");
1527 commSetSelect(htcpOutSocket
, COMM_SELECT_READ
, htcpRecv
, NULL
, 0);
1529 debugs(31, 1, "Outgoing HTCP messages on port " << Config
.Port
.htcp
<< ", FD " << htcpOutSocket
<< ".");
1531 fd_note(htcpInSocket
, "Incoming HTCP socket");
1533 htcpOutSocket
= htcpInSocket
;
1536 if (!htcpDetailPool
) {
1537 htcpDetailPool
= memPoolCreate("htcpDetail", sizeof(htcpDetail
));
1542 htcpQuery(StoreEntry
* e
, HttpRequest
* req
, peer
* p
)
1544 cache_key
*save_key
;
1545 static char pkt
[8192];
1549 HttpHeader
hdr(hoRequest
);
1552 http_state_flags flags
;
1554 if (htcpInSocket
< 0)
1557 old_squid_format
= p
->options
.htcp_oldsquid
;
1558 memset(&flags
, '\0', sizeof(flags
));
1559 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1560 req
->http_ver
.major
, req
->http_ver
.minor
);
1561 stuff
.op
= HTCP_TST
;
1562 stuff
.rr
= RR_REQUEST
;
1565 stuff
.msg_id
= ++msg_id_counter
;
1566 stuff
.S
.method
= (char *) RequestMethodStr(req
->method
);
1567 stuff
.S
.uri
= (char *) e
->url();
1568 stuff
.S
.version
= vbuf
;
1569 HttpStateData::httpBuildRequestHeader(req
, req
, e
, &hdr
, flags
);
1571 packerToMemInit(&pa
, &mb
);
1575 stuff
.S
.req_hdrs
= mb
.buf
;
1576 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1579 debugs(31, 3, "htcpQuery: htcpBuildPacket() failed");
1583 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1585 queried_id
[stuff
.msg_id
% N_QUERIED_KEYS
] = stuff
.msg_id
;
1586 save_key
= queried_keys
[stuff
.msg_id
% N_QUERIED_KEYS
];
1587 storeKeyCopy(save_key
, (const cache_key
*)e
->key
);
1588 queried_addr
[stuff
.msg_id
% N_QUERIED_KEYS
] = p
->in_addr
;
1589 debugs(31, 3, "htcpQuery: key (" << save_key
<< ") " << storeKeyText(save_key
));
1593 * Send an HTCP CLR message for a specified item to a given peer.
1596 htcpClear(StoreEntry
* e
, const char *uri
, HttpRequest
* req
, const HttpRequestMethod
&method
, peer
* p
, htcp_clr_reason reason
)
1598 static char pkt
[8192];
1602 HttpHeader
hdr(hoRequest
);
1605 http_state_flags flags
;
1607 if (htcpInSocket
< 0)
1610 old_squid_format
= p
->options
.htcp_oldsquid
;
1611 memset(&flags
, '\0', sizeof(flags
));
1612 snprintf(vbuf
, sizeof(vbuf
), "%d/%d",
1613 req
->http_ver
.major
, req
->http_ver
.minor
);
1614 stuff
.op
= HTCP_CLR
;
1615 stuff
.rr
= RR_REQUEST
;
1618 stuff
.msg_id
= ++msg_id_counter
;
1620 case HTCP_CLR_INVALIDATION
:
1627 stuff
.S
.method
= (char *) RequestMethodStr(req
->method
);
1628 if (e
== NULL
|| e
->mem_obj
== NULL
) {
1632 stuff
.S
.uri
= xstrdup(uri
);
1634 stuff
.S
.uri
= (char *) e
->url();
1636 stuff
.S
.version
= vbuf
;
1637 if (reason
!= HTCP_CLR_INVALIDATION
) {
1638 HttpStateData::httpBuildRequestHeader(req
, req
, e
, &hdr
, flags
);
1640 packerToMemInit(&pa
, &mb
);
1644 stuff
.S
.req_hdrs
= mb
.buf
;
1646 stuff
.S
.req_hdrs
= NULL
;
1648 pktlen
= htcpBuildPacket(pkt
, sizeof(pkt
), &stuff
);
1649 if (reason
!= HTCP_CLR_INVALIDATION
) {
1656 debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
1660 htcpSend(pkt
, (int) pktlen
, p
->in_addr
);
1664 * htcpSocketShutdown only closes the 'in' socket if it is
1665 * different than the 'out' socket.
1668 htcpSocketShutdown(void)
1670 if (htcpInSocket
< 0)
1673 if (htcpInSocket
!= htcpOutSocket
) {
1674 debugs(12, 1, "FD " << htcpInSocket
<< " Closing HTCP socket");
1675 comm_close(htcpInSocket
);
1679 * Here we set 'htcpInSocket' to -1 even though the HTCP 'in'
1680 * and 'out' sockets might be just one FD. This prevents this
1681 * function from executing repeatedly. When we are really ready to
1682 * exit or restart, main will comm_close the 'out' descriptor.
1687 * Normally we only write to the outgoing HTCP socket, but
1688 * we also have a read handler there to catch messages sent
1689 * to that specific interface. During shutdown, we must
1690 * disable reading on the outgoing socket.
1692 /* XXX Don't we need this handler to read replies while shutting down?
1693 * I think there should be a separate hander for reading replies..
1695 assert(htcpOutSocket
> -1);
1697 commSetSelect(htcpOutSocket
, COMM_SELECT_READ
, NULL
, NULL
, 0);
1701 htcpSocketClose(void)
1703 htcpSocketShutdown();
1705 if (htcpOutSocket
> -1) {
1706 debugs(12, 1, "FD " << htcpOutSocket
<< " Closing HTCP socket");
1707 comm_close(htcpOutSocket
);