]>
Commit | Line | Data |
---|---|---|
9b7de833 | 1 | |
2 | /* | |
ddc0f170 | 3 | * $Id: peer_digest.cc,v 1.8 1998/04/09 21:15:02 rousskov Exp $ |
9b7de833 | 4 | * |
5 | * DEBUG: section 72 Peer Digest Routines | |
6 | * AUTHOR: Alex Rousskov | |
7 | * | |
8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
9 | * -------------------------------------------------------- | |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
31 | ||
32 | #include "squid.h" | |
33 | ||
7a2e5bb5 | 34 | #if SQUID_PEER_DIGEST |
35 | ||
9b7de833 | 36 | /* local types */ |
37 | ||
38 | /* local prototypes */ | |
395b813e | 39 | static void peerDigestClean(peer *p); |
40 | static time_t peerDigestNextDisDelay(const peer *p); | |
00485c29 | 41 | static time_t peerDigestExpiresDelay(const peer *p, const StoreEntry *e); |
395b813e | 42 | static void peerDigestDisable(peer *p); |
43 | static void peerDigestDelay(peer *p, int disable, time_t delay); | |
9b7de833 | 44 | static void peerDigestValidate(peer *p); |
45 | static void peerDigestRequest(peer *p); | |
46 | static void peerDigestFetchReply(void *data, char *buf, ssize_t size); | |
47 | static void peerDigestRequest(peer *p); | |
48 | static void peerDigestSwapInHeaders(void *data, char *buf, ssize_t size); | |
49 | static void peerDigestSwapInCBlock(void *data, char *buf, ssize_t size); | |
50 | static void peerDigestSwapInMask(void *data, char *buf, ssize_t size); | |
51 | static int peerDigestFetchedEnough(DigestFetchState *fetch, char *buf, ssize_t size, const char *step_name); | |
52 | static void peerDigestFetchFinish(DigestFetchState *fetch, char *buf, const char *err_msg); | |
53 | static int peerDigestSetCBlock(peer *p, const char *buf); | |
54 | static int peerDigestUpdateMask(peer *peer, int offset, const char *buf, int size); | |
395b813e | 55 | #define max_delay(t1,t2) ((t1) >= (t2) ? (t1) : (t2)) |
56 | ||
9b7de833 | 57 | |
58 | /* local constants */ | |
59 | #define StoreDigestCBlockSize sizeof(StoreDigestCBlock) | |
60 | ||
61 | /* min interval for requesting digests from the same peer */ | |
f868ab44 | 62 | static const time_t PeerDigestRequestMinGap = 10 * 60; /* seconds */ |
bd890734 | 63 | |
395b813e | 64 | /* min interval for requesting digests at start */ |
bd890734 | 65 | static const time_t GlobalDigestRequestMinGap = 1 * 60; /* seconds */ |
66 | ||
67 | /* local vars */ | |
68 | static time_t global_last_req_timestamp = 0; | |
9b7de833 | 69 | |
70 | void | |
71 | peerDigestInit(peer *p) | |
72 | { | |
73 | assert(p); | |
74 | assert(!p->digest.flags); | |
75 | assert(!p->digest.cd); | |
76 | assert(SM_PAGE_SIZE == 4096); /* we use MEM_4K_BUF */ | |
77 | if (EBIT_TEST(p->options, NEIGHBOR_NO_DIGEST)) { | |
395b813e | 78 | peerDigestDisable(p); |
9b7de833 | 79 | } else { |
80 | cbdataLock(p); | |
81 | peerDigestValidate(p); | |
82 | } | |
83 | EBIT_SET(p->digest.flags, PD_INITED); | |
84 | } | |
85 | ||
86 | /* no pending events or requests should exist when you call this */ | |
87 | static void | |
88 | peerDigestClean(peer *p) | |
89 | { | |
90 | if (!cbdataValid(p)) | |
00485c29 | 91 | debug(72, 2) ("peerDigest: note: peer '%s' was reset or deleted\n", p->host); |
9b7de833 | 92 | assert(!EBIT_TEST(p->digest.flags, PD_REQUESTED)); |
395b813e | 93 | peerDigestDisable(p); |
9b7de833 | 94 | cbdataUnlock(p); |
95 | } | |
96 | ||
395b813e | 97 | /* disables peer for good */ |
98 | static void | |
99 | peerDigestDisable(peer *p) | |
100 | { | |
101 | peerDigestDelay(p, 1, -1); | |
102 | } | |
103 | ||
00485c29 | 104 | /* next delay for a disabled entry */ |
395b813e | 105 | static time_t |
106 | peerDigestNextDisDelay(const peer *p) | |
107 | { | |
108 | assert(p); | |
109 | return p->digest.last_dis_delay ? | |
110 | 2 * p->digest.last_dis_delay : /* exponential backoff */ | |
111 | PeerDigestRequestMinGap; /* minimal delay */ | |
112 | } | |
113 | ||
00485c29 | 114 | /* artificially increases expires to avoid race conditions */ |
115 | static time_t | |
116 | peerDigestExpiresDelay(const peer *p, const StoreEntry *e) | |
117 | { | |
118 | assert(p); | |
119 | if (!e) | |
120 | return 0; | |
121 | if (e->expires > 0) | |
122 | return e->expires + PeerDigestRequestMinGap - squid_curtime; | |
123 | return PeerDigestRequestMinGap; | |
124 | } | |
125 | ||
126 | ||
395b813e | 127 | /* delays/disables digest for a psecified delay (disables forever if negative delay) */ |
128 | static void | |
129 | peerDigestDelay(peer *p, int disable, time_t delay) | |
130 | { | |
131 | assert(p); | |
132 | if (disable) { | |
133 | EBIT_SET(p->digest.flags, PD_DISABLED); | |
134 | p->digest.last_dis_delay = delay; | |
135 | } | |
136 | if (delay >= 0) { | |
137 | assert(delay || !disable); | |
138 | debug(72, 2) ("peerDigestDelay: %s: peer %s for %d secs till %s\n", | |
139 | disable ? "disabling" : "delaying", | |
140 | p->host, delay, mkrfc1123(squid_curtime + delay)); | |
141 | eventAdd("peerDigestValidate", (EVH*) peerDigestValidate, | |
142 | p, delay); | |
143 | } else { | |
144 | assert(disable); | |
145 | debug(72, 2) ("peerDigestDisable: disabling peer %s for good\n", | |
146 | p->host); | |
147 | /* just in case, will not need it anymore */ | |
148 | EBIT_CLR(p->digest.flags, PD_USABLE); | |
149 | } | |
150 | } | |
151 | ||
9b7de833 | 152 | /* request new digest if our copy is too old; schedule next validation */ |
153 | static void | |
154 | peerDigestValidate(peer *p) | |
155 | { | |
156 | StoreEntry *e = NULL; | |
157 | int do_request; | |
158 | time_t req_time = squid_curtime; | |
159 | assert(p); | |
160 | debug(72, 3) ("peerDigestValidate: digest %s\n", p->host); | |
161 | if (!cbdataValid(p)) { | |
162 | peerDigestClean(p); | |
163 | return; | |
164 | } | |
395b813e | 165 | debug(72, 3) ("current GMT time: %s\n", mkrfc1123(squid_curtime)); |
9b7de833 | 166 | assert(!EBIT_TEST(p->digest.flags, PD_REQUESTED)); |
395b813e | 167 | debug(72, 3) ("peerDigestValidate: %s was %s disabled\n", |
168 | p->host, p->digest.last_dis_delay ? "" : "not"); | |
169 | if (1 /* p->digest.cd */) { | |
9b7de833 | 170 | const cache_key *key; |
171 | key = storeKeyPublic(urlRInternal(p->host, p->http_port, NULL, StoreDigestUrlPath), METHOD_GET); | |
172 | e = storeGet(key); | |
173 | debug(72, 3) ("peerDigestValidate: %s store entry, key: %s, exp: %s\n", | |
174 | e ? "has" : "no", storeKeyText(key), mkrfc1123(e ? e->expires : 0)); | |
175 | } | |
176 | /* currently we rely on entry->expire information */ | |
00485c29 | 177 | { |
178 | const time_t exp_delay = peerDigestExpiresDelay(p, e); | |
179 | do_request = exp_delay <= 0; | |
180 | req_time = squid_curtime + exp_delay; | |
181 | if (req_time < squid_curtime) | |
182 | req_time = squid_curtime; | |
183 | } | |
bd890734 | 184 | /* do not request too often from one peer */ |
9b7de833 | 185 | if (req_time - p->digest.last_req_timestamp < PeerDigestRequestMinGap) { |
186 | if (do_request) { | |
bd890734 | 187 | debug(72, 2) ("peerDigestValidate: %s, avoiding too close peer requests (%d secs).\n", |
9b7de833 | 188 | p->host, req_time - p->digest.last_req_timestamp); |
189 | do_request = 0; | |
190 | } | |
191 | req_time = p->digest.last_req_timestamp + PeerDigestRequestMinGap; | |
192 | } | |
395b813e | 193 | /* at start, do not request too often from all peers */ |
194 | if (!EBIT_TEST(p->digest.flags, PD_INITED) && | |
195 | req_time - global_last_req_timestamp < GlobalDigestRequestMinGap) { | |
bd890734 | 196 | if (do_request) { |
197 | debug(72, 2) ("peerDigestValidate: %s, avoiding too close requests (%d secs).\n", | |
198 | p->host, req_time - global_last_req_timestamp); | |
199 | do_request = 0; | |
200 | } | |
201 | req_time = global_last_req_timestamp + GlobalDigestRequestMinGap; | |
395b813e | 202 | /* otherwise we have all but one peer returning at the same moment @?@ */ |
203 | debug(72, 5) ("peerDigestValidate: inc req_time (%+d) in anticipation of more reqs\n", | |
204 | (int)(req_time - global_last_req_timestamp)); | |
205 | global_last_req_timestamp = req_time; | |
bd890734 | 206 | } |
9b7de833 | 207 | /* start request if needed */ |
208 | if (do_request) { | |
209 | static nest_level = 0; | |
210 | nest_level++; | |
211 | assert(nest_level == 1); | |
395b813e | 212 | debug(72, 2) ("peerDigestValidate: %s requesting; old entry expires: %s\n", |
213 | p->host, e ? mkrfc1123(e->expires) : "no entry"); | |
214 | /* will eventually disable digests or call peerDigest Delay */ | |
9b7de833 | 215 | peerDigestRequest(p); |
216 | nest_level--; | |
395b813e | 217 | } else { |
218 | /* schedule next re-validation */ | |
219 | assert(req_time > squid_curtime); | |
220 | peerDigestDelay(p, !p->digest.cd, req_time - squid_curtime); | |
9b7de833 | 221 | } |
9b7de833 | 222 | } |
223 | ||
224 | /* ask peer cache for a fresh digest */ | |
225 | static void | |
226 | peerDigestRequest(peer *p) | |
227 | { | |
228 | StoreEntry *e, *old_e; | |
229 | char *url; | |
230 | const cache_key *key; | |
231 | request_t *req; | |
232 | DigestFetchState *fetch = NULL; | |
233 | assert(p); | |
234 | EBIT_SET(p->digest.flags, PD_REQUESTED); | |
235 | /* compute future request components */ | |
236 | url = urlRInternal(p->host, p->http_port, "", StoreDigestUrlPath); | |
237 | key = storeKeyPublic(url, METHOD_GET); | |
238 | debug(72,2) ("peerDigestRequest: %s key: %s\n", url, storeKeyText(key)); | |
395b813e | 239 | req = requestLink(urlParse(METHOD_GET, url)); |
9b7de833 | 240 | assert(req); |
241 | /* add custom headers */ | |
242 | /* rewrite this when requests get new header interface */ | |
243 | assert(!req->headers); | |
244 | { | |
245 | MemBuf mb; | |
246 | memBufDefInit(&mb); | |
247 | memBufPrintf(&mb, "Accept: %s,text/html\r\n", StoreDigestMimeStr); | |
248 | memBufPrintf(&mb, "Cache-control: only-if-cached\r\n"); | |
249 | memBufAppend(&mb, "\r\n", 2); | |
250 | /* kludge! */ | |
251 | assert(memBufFreeFunc(&mb) == &xfree); | |
252 | req->headers = mb.buf; | |
253 | req->headers_sz = mb.size; | |
254 | } | |
255 | /* create fetch state structure */ | |
256 | fetch = memAllocate(MEM_DIGEST_FETCH_STATE); | |
257 | cbdataAdd(fetch, MEM_DIGEST_FETCH_STATE); | |
258 | fetch->peer = p; | |
259 | fetch->start_time = squid_curtime; | |
260 | p->digest.last_req_timestamp = squid_curtime; | |
bd890734 | 261 | global_last_req_timestamp = squid_curtime; |
9b7de833 | 262 | EBIT_SET(req->flags, REQ_CACHABLE); |
9b7de833 | 263 | /* the rest is based on clientProcessExpired() */ |
264 | EBIT_SET(req->flags, REQ_REFRESH); | |
265 | old_e = fetch->old_entry = storeGet(key); | |
266 | if (old_e) { | |
267 | debug(72,5) ("peerDigestRequest: found old entry\n"); | |
268 | storeLockObject(old_e); | |
269 | storeCreateMemObject(old_e, url, url); | |
270 | storeClientListAdd(old_e, fetch); | |
271 | } | |
272 | e = fetch->entry = storeCreateEntry(url, url, req->flags, req->method); | |
395b813e | 273 | debug(72,5) ("peerDigestRequest: new entry is private: %d\n", |
9b7de833 | 274 | (int)EBIT_TEST(e->flag, KEY_PRIVATE)); |
275 | storeClientListAdd(e, fetch); | |
276 | /* set lastmod to trigger IMS request if possible */ | |
277 | if (old_e) | |
278 | e->lastmod = old_e->lastmod; | |
279 | fetch->offset = 0; | |
395b813e | 280 | debug(72,3) ("peerDigestRequest: forwarding to protoDispatch...\n"); |
9b7de833 | 281 | /* push towards peer cache */ |
282 | protoDispatch(0, e, req); | |
283 | storeClientCopy(e, 0, 0, SM_PAGE_SIZE, memAllocate(MEM_4K_BUF), | |
284 | peerDigestFetchReply, fetch); | |
285 | } | |
286 | ||
287 | /* waits for full http headers to be received and parses them */ | |
288 | static void | |
289 | peerDigestFetchReply(void *data, char *buf, ssize_t size) | |
290 | { | |
291 | DigestFetchState *fetch = data; | |
292 | peer *peer = fetch->peer; | |
293 | assert(peer && buf); | |
294 | assert(!fetch->offset); | |
295 | if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply")) | |
296 | return; | |
297 | if (headersEnd(buf, size)) { | |
298 | http_status status; | |
299 | HttpReply *reply = fetch->entry->mem_obj->reply; | |
300 | assert(reply); | |
301 | httpReplyParse(reply, buf); | |
302 | status = reply->sline.status; | |
303 | debug(72, 3) ("peerDigestFetchHeaders: status: %d, expires: %s\n", | |
304 | status, mkrfc1123(reply->expires)); | |
305 | /* this "if" is based on clientHandleIMSReply() */ | |
306 | if (status == HTTP_NOT_MODIFIED) { | |
307 | request_t *r = NULL; | |
308 | /* our old entry is fine */ | |
309 | assert(fetch->old_entry); | |
310 | if (!fetch->old_entry->mem_obj->request) | |
311 | fetch->old_entry->mem_obj->request = r = | |
312 | requestLink(fetch->old_entry->mem_obj->request); | |
313 | httpReplyUpdateOnNotModified(fetch->old_entry->mem_obj->reply, reply); | |
314 | storeTimestampsSet(fetch->old_entry); | |
315 | /* get rid of 304 reply */ | |
316 | storeUnregister(fetch->entry, fetch); | |
317 | /* paranoid assert: storeUnregister should not call us recursively */ | |
318 | assert(fetch->entry); | |
319 | storeUnlockObject(fetch->entry); | |
320 | fetch->entry = fetch->old_entry; | |
321 | fetch->old_entry = NULL; | |
322 | requestUnlink(r); | |
323 | fetch->entry->mem_obj->request = NULL; | |
324 | } else | |
325 | if (status == HTTP_OK) { | |
326 | /* get rid of old entry if any */ | |
327 | if (fetch->old_entry) { | |
328 | debug(72, 3) ("peerDigestFetchReply: got new digest, requesting release of old digest\n"); | |
329 | storeUnregister(fetch->old_entry, fetch); | |
330 | storeReleaseRequest(fetch->old_entry); | |
331 | storeUnlockObject(fetch->old_entry); | |
332 | fetch->old_entry = NULL; | |
333 | } | |
334 | } else { | |
335 | /* some kind of a bug */ | |
395b813e | 336 | peerDigestFetchFinish(fetch, buf, httpStatusLineReason(&reply->sline)); |
9b7de833 | 337 | return; |
338 | } | |
339 | /* must have a ready-to-use store entry if we got here */ | |
340 | /* can we stay with the old digest? */ | |
341 | if (status == HTTP_NOT_MODIFIED && fetch->peer->digest.cd) | |
342 | peerDigestFetchFinish(fetch, buf, NULL); | |
343 | else | |
344 | storeClientCopy(fetch->entry, /* have to swap in */ | |
345 | 0, 0, SM_PAGE_SIZE, buf, peerDigestSwapInHeaders, fetch); | |
346 | return; | |
347 | } else { | |
348 | /* need more data, do we have space? */ | |
349 | if (size >= SM_PAGE_SIZE) | |
350 | peerDigestFetchFinish(fetch, buf, "too big header"); | |
351 | else | |
352 | storeClientCopy(fetch->entry, size, 0, SM_PAGE_SIZE, buf, | |
353 | peerDigestFetchReply, fetch); | |
354 | } | |
355 | } | |
356 | ||
357 | /* fetch headers from disk, pass on to SwapInCBlock */ | |
358 | static void | |
359 | peerDigestSwapInHeaders(void *data, char *buf, ssize_t size) | |
360 | { | |
361 | DigestFetchState *fetch = data; | |
362 | peer *peer = fetch->peer; | |
363 | size_t hdr_size; | |
364 | assert(peer && buf); | |
365 | assert(!fetch->offset); | |
366 | if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders")) | |
367 | return; | |
368 | if ((hdr_size = headersEnd(buf, size))) { | |
369 | assert(fetch->entry->mem_obj->reply); | |
370 | if (!fetch->entry->mem_obj->reply->sline.status) | |
371 | httpReplyParse(fetch->entry->mem_obj->reply, buf); | |
372 | assert(fetch->entry->mem_obj->reply->sline.status == HTTP_OK); | |
373 | fetch->offset += hdr_size; | |
374 | storeClientCopy(fetch->entry, size, fetch->offset, | |
375 | SM_PAGE_SIZE, buf, | |
376 | peerDigestSwapInCBlock, fetch); | |
377 | } else { | |
378 | /* need more data, do we have space? */ | |
379 | if (size >= SM_PAGE_SIZE) | |
380 | peerDigestFetchFinish(fetch, buf, "too big stored header"); | |
381 | else | |
382 | storeClientCopy(fetch->entry, size, 0, SM_PAGE_SIZE, buf, | |
383 | peerDigestSwapInHeaders, fetch); | |
384 | } | |
385 | } | |
386 | ||
387 | static void | |
388 | peerDigestSwapInCBlock(void *data, char *buf, ssize_t size) | |
389 | { | |
390 | DigestFetchState *fetch = data; | |
391 | peer *peer = fetch->peer; | |
392 | HttpReply *rep = fetch->entry->mem_obj->reply; | |
393 | const int seen = fetch->offset + size; | |
394 | assert(peer && buf && rep); | |
395 | if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInCBlock")) | |
396 | return; | |
397 | if (size >= StoreDigestCBlockSize) { | |
398 | if (peerDigestSetCBlock(peer, buf)) { | |
399 | fetch->offset += StoreDigestCBlockSize; | |
400 | storeClientCopy(fetch->entry, seen, fetch->offset, | |
401 | SM_PAGE_SIZE, buf, | |
402 | peerDigestSwapInMask, fetch); | |
403 | } else { | |
404 | peerDigestFetchFinish(fetch, buf, "invalid digest cblock"); | |
405 | } | |
406 | } else { | |
407 | /* need more data, do we have space? */ | |
408 | if (size >= SM_PAGE_SIZE) | |
409 | peerDigestFetchFinish(fetch, buf, "too big cblock"); | |
410 | else | |
411 | storeClientCopy(fetch->entry, size, 0, SM_PAGE_SIZE, buf, | |
412 | peerDigestSwapInCBlock, fetch); | |
413 | } | |
414 | } | |
415 | ||
416 | static void | |
417 | peerDigestSwapInMask(void *data, char *buf, ssize_t size) | |
418 | { | |
419 | DigestFetchState *fetch = data; | |
420 | peer *peer = fetch->peer; | |
421 | HttpReply *rep = fetch->entry->mem_obj->reply; | |
422 | const int seen = fetch->offset + size; | |
423 | assert(peer && buf && rep); | |
395b813e | 424 | if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInMask")) |
9b7de833 | 425 | return; |
426 | if (peerDigestUpdateMask(peer, fetch->mask_offset, buf, size)) { | |
427 | fetch->offset += size; | |
428 | fetch->mask_offset += size; | |
429 | storeClientCopy(fetch->entry, seen, fetch->offset, | |
430 | SM_PAGE_SIZE, buf, | |
431 | peerDigestSwapInMask, fetch); | |
432 | } else { | |
433 | peerDigestFetchFinish(fetch, buf, "invalid mask"); | |
434 | } | |
435 | } | |
436 | ||
437 | static int | |
438 | peerDigestFetchedEnough(DigestFetchState *fetch, char *buf, ssize_t size, const char *step_name) | |
439 | { | |
440 | const char *reason = NULL; | |
441 | const char *no_bug = NULL; | |
442 | ||
395b813e | 443 | debug(72, 6) ("%s: %s offset: %d size: %d.\n", |
9b7de833 | 444 | step_name, fetch->peer->host, fetch->offset, size); |
445 | ||
446 | /* test exiting conditions */ | |
447 | if (size < 0) reason = "swap failure"; | |
448 | else if (!size) reason = no_bug = "eof"; | |
449 | else if (!fetch->entry) reason = "swap abort(?)"; | |
450 | else if (fetch->entry->store_status == STORE_ABORTED) reason = "swap abort"; | |
395b813e | 451 | else if (!cbdataValid(fetch->peer)) reason = "peer disappeared"; |
9b7de833 | 452 | |
453 | /* report exit reason */ | |
454 | if (reason) { | |
455 | debug(72, 3) ("%s: exiting on %s\n", step_name, reason); | |
456 | peerDigestFetchFinish(fetch, buf, no_bug ? NULL : reason); | |
457 | } | |
458 | return reason != NULL; | |
459 | } | |
460 | ||
461 | /* free state structures, disables digest on error */ | |
462 | /* this probably should mimic httpRequestFree() but it does not! @?@ @?@ */ | |
463 | static void | |
464 | peerDigestFetchFinish(DigestFetchState *fetch, char *buf, const char *err_msg) | |
465 | { | |
466 | peer *peer = fetch->peer; | |
467 | MemObject *mem = fetch->entry->mem_obj; | |
468 | request_t *req = mem->request; | |
469 | const time_t expires = fetch->entry->expires; | |
470 | const time_t fetch_resp_time = squid_curtime - fetch->start_time; | |
471 | const off_t b_read = (fetch->entry->swap_status == STORE_PENDING) ? mem->inmem_hi : mem->object_sz; | |
9b7de833 | 472 | if (!err_msg && !peer->digest.cd) |
473 | err_msg = "null digest (internal bug?)"; | |
474 | if (!err_msg && fetch->mask_offset != peer->digest.cd->mask_size) | |
475 | err_msg = "premature eof"; | |
476 | if (fetch->old_entry) { | |
477 | debug(72,2) ("peerDigestFetchFinish: deleting old entry\n"); | |
478 | storeUnregister(fetch->old_entry, fetch); | |
479 | storeReleaseRequest(fetch->old_entry); | |
480 | storeUnlockObject(fetch->old_entry); | |
481 | fetch->old_entry = NULL; | |
482 | } | |
395b813e | 483 | assert(fetch->entry); |
9b7de833 | 484 | if (err_msg) { |
485 | debug(72, 1) ("disabling corrupted (%s) digest from %s\n", | |
486 | err_msg, peer->host); | |
487 | if (peer->digest.cd) { | |
488 | cacheDigestDestroy(peer->digest.cd); | |
489 | peer->digest.cd = NULL; | |
490 | } | |
395b813e | 491 | /* disable for a while */ |
9b7de833 | 492 | EBIT_CLR(peer->digest.flags, PD_USABLE); |
395b813e | 493 | peerDigestDelay(peer, 1, |
00485c29 | 494 | max_delay( |
495 | peerDigestExpiresDelay(peer, fetch->entry), | |
395b813e | 496 | peerDigestNextDisDelay(peer))); |
9b7de833 | 497 | /* release buggy entry */ |
498 | storeReleaseRequest(fetch->entry); | |
499 | } else { | |
500 | storeComplete(fetch->entry); | |
501 | EBIT_SET(peer->digest.flags, PD_USABLE); | |
395b813e | 502 | EBIT_CLR(peer->digest.flags, PD_DISABLED); |
503 | peer->digest.last_dis_delay = 0; | |
00485c29 | 504 | peerDigestDelay(peer, 0, |
505 | max_delay(peerDigestExpiresDelay(peer, fetch->entry), 0)); | |
9b7de833 | 506 | } |
507 | storeUnregister(fetch->entry, fetch); | |
508 | storeUnlockObject(fetch->entry); | |
395b813e | 509 | requestUnlink(req); |
9b7de833 | 510 | fetch->entry = NULL; |
511 | cbdataFree(fetch); | |
512 | fetch = NULL; | |
513 | memFree(MEM_4K_BUF, buf); | |
395b813e | 514 | /* set it here and in peerDigestRequest to protect against long downloads */ |
515 | peer->digest.last_req_timestamp = squid_curtime; | |
516 | peer->digest.last_fetch_resp_time = fetch_resp_time; | |
9b7de833 | 517 | EBIT_CLR(peer->digest.flags, PD_REQUESTED); |
ddc0f170 | 518 | /* update global stats */ |
9b7de833 | 519 | kb_incr(&Counter.cd.kbytes_recv, (size_t)b_read); |
00485c29 | 520 | Counter.cd.msgs_recv++; |
ddc0f170 | 521 | /* update peer stats */ |
522 | kb_incr(&peer->digest.stats.kbytes_recv, (size_t)b_read); | |
523 | peer->digest.stats.msgs_recv++; | |
395b813e | 524 | debug(72, 2) ("peerDigestFetchFinish: %s done; took: %d secs; expires: %s\n", |
9b7de833 | 525 | peer->host, fetch_resp_time, mkrfc1123(expires)); |
9b7de833 | 526 | } |
527 | ||
528 | static int | |
529 | peerDigestSetCBlock(peer *peer, const char *buf) | |
530 | { | |
531 | StoreDigestCBlock cblock; | |
532 | int freed_size = 0; | |
533 | xmemcpy(&cblock, buf, sizeof(cblock)); | |
534 | /* network -> host conversions */ | |
535 | cblock.ver.current = ntohs(cblock.ver.current); | |
536 | cblock.ver.required = ntohs(cblock.ver.required); | |
537 | cblock.capacity = ntohl(cblock.capacity); | |
538 | cblock.count = ntohl(cblock.count); | |
539 | cblock.del_count = ntohl(cblock.del_count); | |
540 | cblock.mask_size = ntohl(cblock.mask_size); | |
541 | debug(72,2) ("got digest cblock from %s; ver: %d (req: %d)\n", | |
542 | peer->host, (int)cblock.ver.current, (int)cblock.ver.required); | |
543 | debug(72,2) ("\t size: %d bytes, e-cnt: %d, e-util: %d%%\n", | |
544 | cblock.mask_size, cblock.count, | |
545 | xpercentInt(cblock.count, cblock.capacity)); | |
546 | /* check version requirements */ | |
547 | if (cblock.ver.required > CacheDigestVer.current) { | |
548 | debug(72,1) ("%s digest requires version %d; have: %d\n", | |
549 | peer->host, cblock.ver.required, CacheDigestVer.current); | |
550 | return 0; | |
551 | } | |
552 | /* check consistency */ | |
553 | if (cblock.ver.required > cblock.ver.current || | |
554 | cblock.mask_size <= 0 || cblock.capacity <= 0) { | |
555 | debug(72,0) ("%s digest cblock is corrupted.\n", peer->host); | |
556 | return 0; | |
557 | } | |
558 | /* | |
559 | * no cblock bugs below this point | |
560 | */ | |
561 | /* check size changes */ | |
562 | if (peer->digest.cd && cblock.mask_size != peer->digest.cd->mask_size) { | |
563 | debug(72,2) ("%s digest changed size: %d -> %d\n", | |
564 | peer->host, cblock.mask_size, peer->digest.cd->mask_size); | |
565 | freed_size = peer->digest.cd->mask_size; | |
566 | cacheDigestDestroy(peer->digest.cd); | |
567 | peer->digest.cd = NULL; | |
568 | } | |
569 | if (!peer->digest.cd) { | |
570 | debug(72,2) ("cloning %s digest; size: %d (%+d) bytes\n", | |
571 | peer->host, cblock.mask_size, (int) (cblock.mask_size - freed_size)); | |
572 | peer->digest.cd = cacheDigestSizedCreate(cblock.mask_size, cblock.capacity); | |
573 | if (cblock.mask_size >= freed_size) | |
574 | kb_incr(&Counter.cd.memory, cblock.mask_size - freed_size); | |
575 | } | |
576 | /* these assignments leave us in an inconsistent state until we finish reading the digest */ | |
577 | peer->digest.cd->count = cblock.count; | |
578 | peer->digest.cd->del_count = cblock.del_count; | |
579 | return 1; | |
580 | } | |
581 | ||
582 | /* updates current mask. checks for overflows */ | |
583 | static int | |
584 | peerDigestUpdateMask(peer *peer, int offset, const char *buf, int size) | |
585 | { | |
586 | if (size) { | |
587 | assert(offset >= 0); | |
588 | assert(peer->digest.cd); | |
589 | if (offset + size > peer->digest.cd->mask_size) { | |
590 | debug(72,0) ("peerDigestUpdateMask: %s digest is larger than expected: %d > %d\n", | |
591 | peer->host, offset + size, peer->digest.cd->mask_size); | |
592 | return 0; | |
593 | } | |
594 | xmemcpy(peer->digest.cd->mask + offset, buf, size); | |
595 | } | |
596 | return 1; | |
597 | } | |
598 | ||
7a2e5bb5 | 599 | #endif |