]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemObject.cc
Made storeKeyText safe for entries that do not have a key set yet.
[thirdparty/squid.git] / src / MemObject.cc
CommitLineData
528b2c61 1
2/*
528b2c61 3 * DEBUG: section 19 Store Memory Primitives
4 * AUTHOR: Robert Collins
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
26ac0430 22 *
528b2c61 23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
26ac0430 27 *
528b2c61 28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
33
582c2af2 34#include "squid.h"
3e4bebf8 35#include "comm/Connection.h"
582c2af2 36#include "Generic.h"
af69c635 37#include "globals.h"
528b2c61 38#include "HttpReply.h"
582c2af2
FC
39#include "HttpRequest.h"
40#include "MemBuf.h"
41#include "MemObject.h"
42#include "profiler/Profiler.h"
4d5904f7 43#include "SquidConfig.h"
528b2c61 44#include "Store.h"
45#include "StoreClient.h"
582c2af2 46
9a0a18de 47#if USE_DELAY_POOLS
b67e2c8c 48#include "DelayPools.h"
49#endif
528b2c61 50
51/* TODO: make this global or private */
52#if URL_CHECKSUM_DEBUG
53static unsigned int url_checksum(const char *url);
54unsigned int
55url_checksum(const char *url)
56{
57 unsigned int ck;
c3031d67 58 SquidMD5_CTX M;
528b2c61 59 static unsigned char digest[16];
c3031d67 60 SquidMD5Init(&M);
61 SquidMD5Update(&M, (unsigned char *) url, strlen(url));
62 SquidMD5Final(digest, &M);
41d00cd3 63 memcpy(&ck, digest, sizeof(ck));
528b2c61 64 return ck;
65}
62e76326 66
528b2c61 67#endif
68
aa839030 69RemovalPolicy * mem_policy = NULL;
70
528b2c61 71size_t
72MemObject::inUseCount()
73{
9f9e06f3 74 return Pool().inUseCount();
528b2c61 75}
76
c877c0bc
AR
77const char *
78MemObject::storeId() const {
79 if (!storeId_.defined()) {
80 debugs(20, DBG_IMPORTANT, "Bug: Missing MemObject::storeId value");
81 dump();
82 storeId_ = "[unknown_URI]";
83 }
84 return storeId_.termedBuf();
9487bae9
AR
85}
86
c877c0bc
AR
87const char *
88MemObject::logUri() const {
89 return logUri_.defined() ? logUri_.termedBuf() : storeId();
90}
4a56ee8d 91
c877c0bc
AR
92bool
93MemObject::hasUris() const {
94 return storeId_.defined();
95}
4a56ee8d 96
c877c0bc
AR
97void
98MemObject::setUris(char const *aStoreId, char const *aLogUri, const HttpRequestMethod &aMethod)
99{
100 storeId_ = aStoreId;
101
102 // fast pointer comparison for a common storeCreateEntry(url,url,...) case
103 if (!aLogUri || aLogUri == aStoreId)
104 logUri_.clean(); // use storeId_ by default to minimize copying
105 else
106 logUri_ = aLogUri;
62e76326 107
c877c0bc 108 method = aMethod;
4a56ee8d 109
c877c0bc
AR
110#if URL_CHECKSUM_DEBUG
111 chksum = url_checksum(urlXXX());
528b2c61 112#endif
c877c0bc 113}
62e76326 114
c877c0bc
AR
115MemObject::MemObject(): smpCollapsed(false)
116{
117 debugs(20, 3, HERE << "new MemObject " << this);
118 _reply = new HttpReply;
119 HTTPMSGLOCK(_reply);
4a56ee8d 120
528b2c61 121 object_sz = -1;
4a56ee8d 122
528b2c61 123 /* XXX account log_url */
ddc9b32c
AR
124
125 swapout.decision = SwapOut::swNeedsCheck;
528b2c61 126}
127
128MemObject::~MemObject()
129{
e85137f1 130 debugs(20, 3, HERE << "del MemObject " << this);
c877c0bc 131 const Ctx ctx = ctx_enter(storeId_.termedBuf()); /* XXX: need URI? */
62e76326 132
c877c0bc
AR
133#if URL_CHECKSUM_DEBUG
134 checkUrlChecksum();
528b2c61 135#endif
62e76326 136
4475555f 137 if (!shutting_down) { // Store::Root() is FATALly missing during shutdown
c877c0bc 138 // TODO: Consider moving these to destroyMemoryObject
4475555f
AR
139 if (xitTable.index >= 0)
140 Store::Root().transientsDisconnect(*this);
141 if (memCache.index >= 0)
142 Store::Root().memoryDisconnect(*this);
143
144 assert(xitTable.index < 0);
145 assert(memCache.index < 0);
528b2c61 146 assert(swapout.sio == NULL);
4475555f 147 }
62e76326 148
528b2c61 149 data_hdr.freeContent();
62e76326 150
9cdee68d 151#if 0
528b2c61 152 /*
153 * There is no way to abort FD-less clients, so they might
9cdee68d 154 * still have mem->clients set.
528b2c61 155 */
9cdee68d 156 assert(clients.head == NULL);
157
158#endif
62e76326 159
6dd9f4bd 160 HTTPMSGUNLOCK(_reply);
62e76326 161
6dd9f4bd 162 HTTPMSGUNLOCK(request);
62e76326 163
528b2c61 164 ctx_exit(ctx); /* must exit before we free mem->url */
62e76326 165
528b2c61 166 safe_free(vary_headers);
167}
168
169void
170MemObject::unlinkRequest()
171{
6dd9f4bd 172 HTTPMSGUNLOCK(request);
528b2c61 173}
174
175void
55759ffb 176MemObject::write(const StoreIOBuffer &writeBuffer)
528b2c61 177{
1d5161bd 178 PROF_start(MemObject_write);
4a7a3d56 179 debugs(19, 6, "memWrite: offset " << writeBuffer.offset << " len " << writeBuffer.length);
528b2c61 180
528b2c61 181 /* We don't separate out mime headers yet, so ensure that the first
26ac0430 182 * write is at offset 0 - where they start
528b2c61 183 */
184 assert (data_hdr.endOffset() || writeBuffer.offset == 0);
185
186 assert (data_hdr.write (writeBuffer));
1d5161bd 187 PROF_stop(MemObject_write);
528b2c61 188}
189
190void
191MemObject::dump() const
192{
42a503bd 193 data_hdr.dump();
528b2c61 194#if 0
195 /* do we want this one? */
e0236918 196 debugs(20, DBG_IMPORTANT, "MemObject->data.origin_offset: " << (data_hdr.head ? data_hdr.head->nodeBuffer.offset : 0));
528b2c61 197#endif
62e76326 198
e0236918
FC
199 debugs(20, DBG_IMPORTANT, "MemObject->start_ping: " << start_ping.tv_sec << "."<< std::setfill('0') << std::setw(6) << start_ping.tv_usec);
200 debugs(20, DBG_IMPORTANT, "MemObject->inmem_hi: " << data_hdr.endOffset());
201 debugs(20, DBG_IMPORTANT, "MemObject->inmem_lo: " << inmem_lo);
202 debugs(20, DBG_IMPORTANT, "MemObject->nclients: " << nclients);
203 debugs(20, DBG_IMPORTANT, "MemObject->reply: " << _reply);
204 debugs(20, DBG_IMPORTANT, "MemObject->request: " << request);
c877c0bc
AR
205 debugs(20, DBG_IMPORTANT, "MemObject->logUri: " << logUri_);
206 debugs(20, DBG_IMPORTANT, "MemObject->storeId: " << storeId_);
528b2c61 207}
208
209HttpReply const *
210MemObject::getReply() const
211{
212 return _reply;
213}
214
4a56ee8d 215void
216MemObject::replaceHttpReply(HttpReply *newrep)
217{
6dd9f4bd 218 HTTPMSGUNLOCK(_reply);
b248c2a3
AJ
219 _reply = newrep;
220 HTTPMSGLOCK(_reply);
4a56ee8d 221}
222
26ac0430
AJ
223struct LowestMemReader : public unary_function<store_client, void> {
224 LowestMemReader(int64_t seed):current(seed) {}
62e76326 225
26ac0430 226 void operator() (store_client const &x) {
62e76326 227 if (x.memReaderHasLowerOffset(current))
228 current = x.copyInto.offset;
229 }
230
47f6e231 231 int64_t current;
528b2c61 232};
233
26ac0430
AJ
234struct StoreClientStats : public unary_function<store_client, void> {
235 StoreClientStats(MemBuf *anEntry):where(anEntry),index(0) {}
62e76326 236
26ac0430 237 void operator()(store_client const &x) {
aec55359
FC
238 x.dumpStats(where, index);
239 ++index;
528b2c61 240 }
62e76326 241
fcc35180 242 MemBuf *where;
528b2c61 243 size_t index;
244};
245
246void
83af6fa2 247MemObject::stat(MemBuf * mb) const
528b2c61 248{
2fe7eff9 249 mb->Printf("\t%s %s\n",
c877c0bc 250 RequestMethodStr(method), logUri());
83af6fa2
AJ
251 if (vary_headers)
252 mb->Printf("\tvary_headers: %s\n", vary_headers);
c91ca3ce
DK
253 mb->Printf("\tinmem_lo: %" PRId64 "\n", inmem_lo);
254 mb->Printf("\tinmem_hi: %" PRId64 "\n", data_hdr.endOffset());
255 mb->Printf("\tswapout: %" PRId64 " bytes queued\n",
47f6e231 256 swapout.queue_offset);
62e76326 257
528b2c61 258 if (swapout.sio.getRaw())
c91ca3ce 259 mb->Printf("\tswapout: %" PRId64 " bytes written\n",
47f6e231 260 (int64_t) swapout.sio->offset());
62e76326 261
fcc35180 262 StoreClientStats statsVisitor(mb);
62e76326 263
4cbb7fa8 264 for_each<StoreClientStats>(clients, statsVisitor);
528b2c61 265}
266
47f6e231 267int64_t
528b2c61 268MemObject::endOffset () const
269{
270 return data_hdr.endOffset();
271}
272
3756e5c0
AR
273void
274MemObject::markEndOfReplyHeaders()
275{
276 const int hdr_sz = endOffset();
277 assert(hdr_sz >= 0);
278 assert(_reply);
279 _reply->hdr_sz = hdr_sz;
280}
281
47f6e231 282int64_t
528b2c61 283MemObject::size() const
284{
62e76326 285 if (object_sz < 0)
286 return endOffset();
287
528b2c61 288 return object_sz;
289}
290
aa1a691e 291int64_t
9199139f
AR
292MemObject::expectedReplySize() const
293{
aa1a691e
AR
294 debugs(20, 7, HERE << "object_sz: " << object_sz);
295 if (object_sz >= 0) // complete() has been called; we know the exact answer
296 return object_sz;
297
298 if (_reply) {
299 const int64_t clen = _reply->bodySize(method);
300 debugs(20, 7, HERE << "clen: " << clen);
301 if (clen >= 0 && _reply->hdr_sz > 0) // yuck: HttpMsg sets hdr_sz to 0
302 return clen + _reply->hdr_sz;
303 }
304
305 return -1; // not enough information to predict
306}
307
528b2c61 308void
309MemObject::reset()
310{
311 assert(swapout.sio == NULL);
312 data_hdr.freeContent();
313 inmem_lo = 0;
314 /* Should we check for clients? */
315}
316
47f6e231 317int64_t
528b2c61 318MemObject::lowestMemReaderOffset() const
319{
320 LowestMemReader lowest (endOffset() + 1);
321
4cbb7fa8 322 for_each <LowestMemReader>(clients, lowest);
62e76326 323
528b2c61 324 return lowest.current;
325}
326
327/* XXX: This is wrong. It breaks *badly* on range combining */
328bool
329MemObject::readAheadPolicyCanRead() const
330{
47f6e231 331 return endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + Config.readAheadGap;
528b2c61 332}
333
334void
335MemObject::addClient(store_client *aClient)
336{
337 ++nclients;
338 dlinkAdd(aClient, &aClient->node, &clients);
339}
340
341#if URL_CHECKSUM_DEBUG
342void
343MemObject::checkUrlChecksum () const
344{
c877c0bc 345 assert(chksum == url_checksum(urlXXX()));
528b2c61 346}
62e76326 347
528b2c61 348#endif
349
350/*
351 * How much of the object data is on the disk?
352 */
47f6e231 353int64_t
528b2c61 354MemObject::objectBytesOnDisk() const
355{
356 /*
357 * NOTE: storeOffset() represents the disk file size,
358 * not the amount of object data on disk.
26ac0430 359 *
528b2c61 360 * If we don't have at least 'swap_hdr_sz' bytes
361 * then none of the object data is on disk.
362 *
363 * This should still be safe if swap_hdr_sz == 0,
364 * meaning we haven't even opened the swapout file
365 * yet.
366 */
62e76326 367
528b2c61 368 if (swapout.sio.getRaw() == NULL)
62e76326 369 return 0;
370
47f6e231 371 int64_t nwritten = swapout.sio->offset();
62e76326 372
ed013b6c 373 if (nwritten <= (int64_t)swap_hdr_sz)
62e76326 374 return 0;
375
47f6e231 376 return (nwritten - swap_hdr_sz);
528b2c61 377}
378
47f6e231 379int64_t
10aeba1d 380MemObject::policyLowestOffsetToKeep(bool swap) const
528b2c61 381{
382 /*
383 * Careful. lowest_offset can be greater than endOffset(), such
384 * as in the case of a range request.
385 */
47f6e231 386 int64_t lowest_offset = lowestMemReaderOffset();
62e76326 387
528b2c61 388 if (endOffset() < lowest_offset ||
ff4b33f4 389 endOffset() - inmem_lo > (int64_t)Config.Store.maxInMemObjSize ||
10aeba1d 390 (swap && !Config.onoff.memory_cache_first))
62e76326 391 return lowest_offset;
392
528b2c61 393 return inmem_lo;
394}
395
396void
397MemObject::trimSwappable()
398{
10aeba1d 399 int64_t new_mem_lo = policyLowestOffsetToKeep(1);
528b2c61 400 /*
401 * We should only free up to what we know has been written
402 * to disk, not what has been queued for writing. Otherwise
403 * there will be a chunk of the data which is not in memory
404 * and is not yet on disk.
405 * The -1 makes sure the page isn't freed until storeSwapOut has
aa1a691e 406 * walked to the next page.
528b2c61 407 */
47f6e231 408 int64_t on_disk;
62e76326 409
528b2c61 410 if ((on_disk = objectBytesOnDisk()) - 1 < new_mem_lo)
62e76326 411 new_mem_lo = on_disk - 1;
412
528b2c61 413 if (new_mem_lo == -1)
62e76326 414 new_mem_lo = 0; /* the above might become -1 */
415
528b2c61 416 data_hdr.freeDataUpto(new_mem_lo);
62e76326 417
528b2c61 418 inmem_lo = new_mem_lo;
419}
420
421void
422MemObject::trimUnSwappable()
423{
99921d9d
AR
424 if (const int64_t new_mem_lo = policyLowestOffsetToKeep(false)) {
425 assert (new_mem_lo > 0);
426 data_hdr.freeDataUpto(new_mem_lo);
427 inmem_lo = new_mem_lo;
428 } // else we should not trim anything at this time
528b2c61 429}
430
528b2c61 431bool
432MemObject::isContiguous() const
433{
47f6e231 434 bool result = data_hdr.hasContigousContentRange (Range<int64_t>(inmem_lo, endOffset()));
528b2c61 435 /* XXX : make this higher level */
bf8fe701 436 debugs (19, result ? 4 :3, "MemObject::isContiguous: Returning " << (result ? "true" : "false"));
528b2c61 437 return result;
438}
b67e2c8c 439
440int
384a7590 441MemObject::mostBytesWanted(int max, bool ignoreDelayPools) const
b67e2c8c 442{
9a0a18de 443#if USE_DELAY_POOLS
384a7590
JP
444 if (!ignoreDelayPools) {
445 /* identify delay id with largest allowance */
446 DelayId largestAllowance = mostBytesAllowed ();
447 return largestAllowance.bytesWanted(0, max);
448 }
449#endif
62e76326 450
b67e2c8c 451 return max;
b67e2c8c 452}
453
a46d2c0e 454void
455MemObject::setNoDelay(bool const newValue)
456{
9a0a18de 457#if USE_DELAY_POOLS
a46d2c0e 458
459 for (dlink_node *node = clients.head; node; node = node->next) {
460 store_client *sc = (store_client *) node->data;
461 sc->delayId.setNoDelay(newValue);
462 }
463
464#endif
465}
466
467void
468MemObject::delayRead(DeferredRead const &aRead)
469{
470 deferredReads.delayRead(aRead);
471}
472
473void
474MemObject::kickReads()
475{
476 deferredReads.kickReads(-1);
477}
478
9a0a18de 479#if USE_DELAY_POOLS
b67e2c8c 480DelayId
481MemObject::mostBytesAllowed() const
482{
483 int j;
484 int jmax = -1;
485 DelayId result;
62e76326 486
b67e2c8c 487 for (dlink_node *node = clients.head; node; node = node->next) {
62e76326 488 store_client *sc = (store_client *) node->data;
d576a6a6 489#if 0
62e76326 490 /* This test is invalid because the client may be writing data
491 * and thus will want data immediately.
492 * If we include the test, there is a race condition when too much
493 * data is read - if all sc's are writing when a read is scheduled.
494 * XXX: fixme.
495 */
496
497 if (!sc->callbackPending())
498 /* not waiting for more data */
499 continue;
500
d576a6a6 501#endif
62e76326 502
62e76326 503 j = sc->delayId.bytesWanted(0, sc->copyInto.length);
504
505 if (j > jmax) {
506 jmax = j;
507 result = sc->delayId;
508 }
b67e2c8c 509 }
62e76326 510
b67e2c8c 511 return result;
512}
62e76326 513
b67e2c8c 514#endif
5b55f1f1
CT
515
516int64_t
517MemObject::availableForSwapOut() const
518{
519 return endOffset() - swapout.queue_offset;
520}