object.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. /*
  2. Copyright (c) 2003-2006 by Juliusz Chroboczek
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. */
  19. #include "polipo.h"
  20. int mindlesslyCacheVary = 0;
  21. int objectHashTableSize = 0;
  22. int log2ObjectHashTableSize;
  23. static ObjectPtr object_list = NULL;
  24. static ObjectPtr object_list_end = NULL;
  25. int objectExpiryScheduled;
  26. int publicObjectCount;
  27. int privateObjectCount;
  28. int cacheIsShared = 1;
  29. int publicObjectLowMark = 0, objectHighMark = 2048;
  30. static ObjectPtr *objectHashTable;
  31. int maxExpiresAge = (30 * 24 + 1) * 3600;
  32. int maxAge = (14 * 24 + 1) * 3600;
  33. float maxAgeFraction = 0.1;
  34. int maxNoModifiedAge = 23 * 60;
  35. int maxWriteoutWhenIdle = 64 * 1024;
  36. int maxObjectsWhenIdle = 32;
  37. int idleTime = 20;
  38. int dontCacheCookies = 0;
  39. void
  40. preinitObject()
  41. {
  42. CONFIG_VARIABLE_SETTABLE(idleTime, CONFIG_TIME, configIntSetter,
  43. "Time to remain idle before writing out.");
  44. CONFIG_VARIABLE_SETTABLE(maxWriteoutWhenIdle, CONFIG_INT, configIntSetter,
  45. "Amount of data to write at a time when idle.");
  46. CONFIG_VARIABLE_SETTABLE(maxObjectsWhenIdle, CONFIG_INT, configIntSetter,
  47. "Number of objects to write at a time "
  48. "when idle.");
  49. CONFIG_VARIABLE_SETTABLE(cacheIsShared, CONFIG_BOOLEAN, configIntSetter,
  50. "If false, ignore s-maxage and private.");
  51. CONFIG_VARIABLE_SETTABLE(mindlesslyCacheVary, CONFIG_BOOLEAN,
  52. configIntSetter,
  53. "If true, mindlessly cache negotiated objects.");
  54. CONFIG_VARIABLE(objectHashTableSize, CONFIG_INT,
  55. "Size of the object hash table (0 = auto).");
  56. CONFIG_VARIABLE(objectHighMark, CONFIG_INT,
  57. "High object count mark.");
  58. CONFIG_VARIABLE(publicObjectLowMark, CONFIG_INT,
  59. "Low object count mark (0 = auto).");
  60. CONFIG_VARIABLE_SETTABLE(maxExpiresAge, CONFIG_TIME, configIntSetter,
  61. "Max age for objects with Expires header.");
  62. CONFIG_VARIABLE_SETTABLE(maxAge, CONFIG_TIME, configIntSetter,
  63. "Max age for objects without Expires header.");
  64. CONFIG_VARIABLE_SETTABLE(maxAgeFraction, CONFIG_FLOAT, configFloatSetter,
  65. "Fresh fraction of modification time.");
  66. CONFIG_VARIABLE_SETTABLE(maxNoModifiedAge, CONFIG_TIME, configIntSetter,
  67. "Max age for objects without Last-modified.");
  68. CONFIG_VARIABLE_SETTABLE(dontCacheCookies, CONFIG_BOOLEAN, configIntSetter,
  69. "Work around cachable cookies.");
  70. }
  71. void
  72. initObject()
  73. {
  74. int q;
  75. if(objectHighMark < 16) {
  76. objectHighMark = 16;
  77. do_log(L_WARN, "Impossibly low objectHighMark -- setting to %d.\n",
  78. objectHighMark);
  79. }
  80. q = 0;
  81. if(publicObjectLowMark == 0) q = 1;
  82. if(publicObjectLowMark < 8 || publicObjectLowMark >= objectHighMark - 4) {
  83. publicObjectLowMark = objectHighMark / 2;
  84. if(!q)
  85. do_log(L_WARN, "Impossible publicObjectLowMark value -- "
  86. "setting to %d.\n", publicObjectLowMark);
  87. }
  88. q = 1;
  89. if(objectHashTableSize <= objectHighMark / 2 ||
  90. objectHashTableSize > objectHighMark * 1024) {
  91. if(objectHashTableSize != 0) q = 0;
  92. objectHashTableSize = objectHighMark * 16;
  93. }
  94. log2ObjectHashTableSize = log2_ceil(objectHashTableSize);
  95. objectHashTableSize = 1 << log2ObjectHashTableSize;
  96. if(!q)
  97. do_log(L_WARN, "Suspicious objectHashTableSize value -- "
  98. "setting to %d.\n", objectHashTableSize);
  99. object_list = NULL;
  100. object_list_end = NULL;
  101. publicObjectCount = 0;
  102. privateObjectCount = 0;
  103. objectHashTable = calloc(1 << log2ObjectHashTableSize,
  104. sizeof(ObjectPtr));
  105. if(!objectHashTable) {
  106. do_log(L_ERROR, "Couldn't allocate object hash table.\n");
  107. exit(1);
  108. }
  109. }
  110. ObjectPtr
  111. findObject(int type, const void *key, int key_size)
  112. {
  113. int h;
  114. ObjectPtr object;
  115. if(key_size >= 50000)
  116. return NULL;
  117. h = hash(type, key, key_size, log2ObjectHashTableSize);
  118. object = objectHashTable[h];
  119. if(!object)
  120. return NULL;
  121. if(object->type != type || object->key_size != key_size ||
  122. memcmp(object->key, key, key_size) != 0) {
  123. return NULL;
  124. }
  125. if(object->next)
  126. object->next->previous = object->previous;
  127. if(object->previous)
  128. object->previous->next = object->next;
  129. if(object_list == object)
  130. object_list = object->next;
  131. if(object_list_end == object)
  132. object_list_end = object->previous;
  133. object->previous = NULL;
  134. object->next = object_list;
  135. if(object_list)
  136. object_list->previous = object;
  137. object_list = object;
  138. if(!object_list_end)
  139. object_list_end = object;
  140. return retainObject(object);
  141. }
  142. ObjectPtr
  143. makeObject(int type, const void *key, int key_size, int public, int fromdisk,
  144. RequestFunction request, void* request_closure)
  145. {
  146. ObjectPtr object;
  147. int h;
  148. object = findObject(type, key, key_size);
  149. if(object != NULL) {
  150. if(public)
  151. return object;
  152. else
  153. privatiseObject(object, 0);
  154. }
  155. if(publicObjectCount + privateObjectCount >= objectHighMark) {
  156. if(!objectExpiryScheduled)
  157. discardObjects(0, 0);
  158. if(publicObjectCount + privateObjectCount >= objectHighMark) {
  159. return NULL;
  160. }
  161. }
  162. if(publicObjectCount >= publicObjectLowMark &&
  163. !objectExpiryScheduled) {
  164. TimeEventHandlerPtr event;
  165. event = scheduleTimeEvent(-1, discardObjectsHandler, 0, NULL);
  166. if(event)
  167. objectExpiryScheduled = 1;
  168. else
  169. do_log(L_ERROR, "Couldn't schedule object expiry.\n");
  170. }
  171. object = malloc(sizeof(ObjectRec));
  172. if(object == NULL)
  173. return NULL;
  174. object->type = type;
  175. object->request = request;
  176. object->request_closure = request_closure;
  177. object->key = malloc(key_size + 1);
  178. if(object->key == NULL) {
  179. free(object);
  180. return NULL;
  181. }
  182. memcpy(object->key, key, key_size);
  183. /* In order to make it more convenient to use keys as strings,
  184. they are NUL-terminated. */
  185. object->key[key_size] = '\0';
  186. object->key_size = key_size;
  187. object->flags = (public?OBJECT_PUBLIC:0) | OBJECT_INITIAL;
  188. if(public) {
  189. h = hash(object->type, object->key, object->key_size,
  190. log2ObjectHashTableSize);
  191. if(objectHashTable[h]) {
  192. writeoutToDisk(objectHashTable[h], objectHashTable[h]->size, -1);
  193. privatiseObject(objectHashTable[h], 0);
  194. assert(!objectHashTable[h]);
  195. }
  196. objectHashTable[h] = object;
  197. object->next = object_list;
  198. object->previous = NULL;
  199. if(object_list)
  200. object_list->previous = object;
  201. object_list = object;
  202. if(!object_list_end)
  203. object_list_end = object;
  204. } else {
  205. object->next = NULL;
  206. object->previous = NULL;
  207. }
  208. object->abort_data = NULL;
  209. object->code = 0;
  210. object->message = NULL;
  211. initCondition(&object->condition);
  212. object->headers = NULL;
  213. object->via = NULL;
  214. object->numchunks = 0;
  215. object->chunks = NULL;
  216. object->length = -1;
  217. object->date = -1;
  218. object->age = -1;
  219. object->expires = -1;
  220. object->last_modified = -1;
  221. object->atime = -1;
  222. object->etag = NULL;
  223. object->cache_control = 0;
  224. object->max_age = -1;
  225. object->s_maxage = -1;
  226. object->size = 0;
  227. object->requestor = NULL;
  228. object->disk_entry = NULL;
  229. if(object->flags & OBJECT_PUBLIC)
  230. publicObjectCount++;
  231. else
  232. privateObjectCount++;
  233. object->refcount = 1;
  234. if(public && fromdisk)
  235. objectGetFromDisk(object);
  236. return object;
  237. }
  238. void
  239. objectMetadataChanged(ObjectPtr object, int revalidate)
  240. {
  241. if(revalidate) {
  242. revalidateDiskEntry(object);
  243. } else {
  244. object->flags &= ~OBJECT_DISK_ENTRY_COMPLETE;
  245. dirtyDiskEntry(object);
  246. }
  247. return;
  248. }
  249. ObjectPtr
  250. retainObject(ObjectPtr object)
  251. {
  252. do_log(D_REFCOUNT, "O 0x%lx %d++\n",
  253. (unsigned long)object, object->refcount);
  254. object->refcount++;
  255. return object;
  256. }
  257. void
  258. releaseObject(ObjectPtr object)
  259. {
  260. do_log(D_REFCOUNT, "O 0x%lx %d--\n",
  261. (unsigned long)object, object->refcount);
  262. object->refcount--;
  263. if(object->refcount == 0) {
  264. assert(!object->condition.handlers &&
  265. !(object->flags & OBJECT_INPROGRESS));
  266. if(!(object->flags & OBJECT_PUBLIC))
  267. destroyObject(object);
  268. }
  269. }
  270. void
  271. releaseNotifyObject(ObjectPtr object)
  272. {
  273. do_log(D_REFCOUNT, "O 0x%lx %d--\n",
  274. (unsigned long)object, object->refcount);
  275. object->refcount--;
  276. if(object->refcount > 0) {
  277. notifyObject(object);
  278. } else {
  279. assert(!object->condition.handlers &&
  280. !(object->flags & OBJECT_INPROGRESS));
  281. if(!(object->flags & OBJECT_PUBLIC))
  282. destroyObject(object);
  283. }
  284. }
  285. void
  286. lockChunk(ObjectPtr object, int i)
  287. {
  288. do_log(D_LOCK, "Lock 0x%lx[%d]: ", (unsigned long)object, i);
  289. assert(i >= 0);
  290. if(i >= object->numchunks)
  291. objectSetChunks(object, i + 1);
  292. object->chunks[i].locked++;
  293. do_log(D_LOCK, "%d\n", object->chunks[i].locked);
  294. }
  295. void
  296. unlockChunk(ObjectPtr object, int i)
  297. {
  298. do_log(D_LOCK, "Unlock 0x%lx[%d]: ", (unsigned long)object, i);
  299. assert(i >= 0 && i < object->numchunks);
  300. assert(object->chunks[i].locked > 0);
  301. object->chunks[i].locked--;
  302. do_log(D_LOCK, "%d\n", object->chunks[i].locked);
  303. }
  304. int
  305. objectSetChunks(ObjectPtr object, int numchunks)
  306. {
  307. int n;
  308. if(numchunks <= object->numchunks)
  309. return 0;
  310. if(object->length >= 0)
  311. n = MAX(numchunks, (object->length + (CHUNK_SIZE - 1)) / CHUNK_SIZE);
  312. else
  313. n = MAX(numchunks,
  314. MAX(object->numchunks + 2, object->numchunks * 5 / 4));
  315. if(n == 0) {
  316. assert(object->chunks == NULL);
  317. } else if(object->numchunks == 0) {
  318. object->chunks = calloc(n, sizeof(ChunkRec));
  319. if(object->chunks == NULL) {
  320. return -1;
  321. }
  322. object->numchunks = n;
  323. } else {
  324. ChunkPtr newchunks;
  325. newchunks = realloc(object->chunks, n * sizeof(ChunkRec));
  326. if(newchunks == NULL)
  327. return -1;
  328. memset(newchunks + object->numchunks, 0,
  329. (n - object->numchunks) * sizeof(ChunkRec));
  330. object->chunks = newchunks;
  331. object->numchunks = n;
  332. }
  333. return 0;
  334. }
  335. ObjectPtr
  336. objectPartial(ObjectPtr object, int length, struct _Atom *headers)
  337. {
  338. object->headers = headers;
  339. if(length >= 0) {
  340. if(object->size > length) {
  341. abortObject(object, 502,
  342. internAtom("Inconsistent Content-Length"));
  343. notifyObject(object);
  344. return object;
  345. }
  346. }
  347. if(length >= 0)
  348. object->length = length;
  349. object->flags &= ~OBJECT_INITIAL;
  350. revalidateDiskEntry(object);
  351. notifyObject(object);
  352. return object;
  353. }
  354. static int
  355. objectAddChunk(ObjectPtr object, const char *data, int offset, int plen)
  356. {
  357. int i = offset / CHUNK_SIZE;
  358. int rc;
  359. assert(offset % CHUNK_SIZE == 0);
  360. assert(plen <= CHUNK_SIZE);
  361. if(object->numchunks <= i) {
  362. rc = objectSetChunks(object, i + 1);
  363. if(rc < 0)
  364. return -1;
  365. }
  366. lockChunk(object, i);
  367. if(object->chunks[i].data == NULL) {
  368. object->chunks[i].data = get_chunk();
  369. if(object->chunks[i].data == NULL)
  370. goto fail;
  371. }
  372. if(object->chunks[i].size >= plen) {
  373. unlockChunk(object, i);
  374. return 0;
  375. }
  376. if(object->size < offset + plen)
  377. object->size = offset + plen;
  378. object->chunks[i].size = plen;
  379. memcpy(object->chunks[i].data, data, plen);
  380. unlockChunk(object, i);
  381. return 0;
  382. fail:
  383. unlockChunk(object, i);
  384. return -1;
  385. }
  386. static int
  387. objectAddChunkEnd(ObjectPtr object, const char *data, int offset, int plen)
  388. {
  389. int i = offset / CHUNK_SIZE;
  390. int rc;
  391. assert(offset % CHUNK_SIZE != 0 &&
  392. offset % CHUNK_SIZE + plen <= CHUNK_SIZE);
  393. if(object->numchunks <= i) {
  394. rc = objectSetChunks(object, i + 1);
  395. if(rc < 0)
  396. return -1;
  397. }
  398. lockChunk(object, i);
  399. if(object->chunks[i].data == NULL)
  400. object->chunks[i].data = get_chunk();
  401. if(object->chunks[i].data == NULL)
  402. goto fail;
  403. if(offset > object->size) {
  404. goto fail;
  405. }
  406. if(object->chunks[i].size < offset % CHUNK_SIZE) {
  407. goto fail;
  408. }
  409. if(object->size < offset + plen)
  410. object->size = offset + plen;
  411. object->chunks[i].size = offset % CHUNK_SIZE + plen;
  412. memcpy(object->chunks[i].data + (offset % CHUNK_SIZE),
  413. data, plen);
  414. unlockChunk(object, i);
  415. return 0;
  416. fail:
  417. unlockChunk(object, i);
  418. return -1;
  419. }
  420. int
  421. objectAddData(ObjectPtr object, const char *data, int offset, int len)
  422. {
  423. int rc;
  424. do_log(D_OBJECT_DATA, "Adding data to 0x%lx (%d) at %d: %d bytes\n",
  425. (unsigned long)object, object->length, offset, len);
  426. if(len == 0)
  427. return 1;
  428. if(object->length >= 0) {
  429. if(offset + len > object->length) {
  430. do_log(L_ERROR,
  431. "Inconsistent object length (%d, should be at least %d).\n",
  432. object->length, offset + len);
  433. object->length = offset + len;
  434. }
  435. }
  436. object->flags &= ~OBJECT_FAILED;
  437. if(offset + len >= object->numchunks * CHUNK_SIZE) {
  438. rc = objectSetChunks(object, (offset + len - 1) / CHUNK_SIZE + 1);
  439. if(rc < 0) {
  440. return -1;
  441. }
  442. }
  443. if(offset % CHUNK_SIZE != 0) {
  444. int plen = CHUNK_SIZE - offset % CHUNK_SIZE;
  445. if(plen >= len)
  446. plen = len;
  447. rc = objectAddChunkEnd(object, data, offset, plen);
  448. if(rc < 0) {
  449. return -1;
  450. }
  451. offset += plen;
  452. data += plen;
  453. len -= plen;
  454. }
  455. while(len > 0) {
  456. int plen = (len >= CHUNK_SIZE) ? CHUNK_SIZE : len;
  457. rc = objectAddChunk(object, data, offset, plen);
  458. if(rc < 0) {
  459. return -1;
  460. }
  461. offset += plen;
  462. data += plen;
  463. len -= plen;
  464. }
  465. return 1;
  466. }
  467. void
  468. objectPrintf(ObjectPtr object, int offset, const char *format, ...)
  469. {
  470. char *buf;
  471. int rc;
  472. va_list args;
  473. va_start(args, format);
  474. buf = vsprintf_a(format, args);
  475. va_end(args);
  476. if(buf == NULL) {
  477. abortObject(object, 500, internAtom("Couldn't allocate string"));
  478. return;
  479. }
  480. rc = objectAddData(object, buf, offset, strlen(buf));
  481. free(buf);
  482. if(rc < 0)
  483. abortObject(object, 500, internAtom("Couldn't add data to object"));
  484. }
  485. int
  486. objectHoleSize(ObjectPtr object, int offset)
  487. {
  488. int size = 0, i;
  489. if(offset < 0 || offset / CHUNK_SIZE >= object->numchunks)
  490. return -1;
  491. if(offset % CHUNK_SIZE != 0) {
  492. if(object->chunks[offset / CHUNK_SIZE].size > offset % CHUNK_SIZE)
  493. return 0;
  494. else {
  495. size += CHUNK_SIZE - offset % CHUNK_SIZE;
  496. offset += CHUNK_SIZE - offset % CHUNK_SIZE;
  497. if(offset < 0) {
  498. /* Overflow */
  499. return -1;
  500. }
  501. }
  502. }
  503. for(i = offset / CHUNK_SIZE; i < object->numchunks; i++) {
  504. if(object->chunks[i].size == 0)
  505. size += CHUNK_SIZE;
  506. else
  507. break;
  508. }
  509. if(i >= object->numchunks)
  510. return -1;
  511. return size;
  512. }
  513. /* Returns 2 if the data is wholly in memory, 1 if it's available on disk.
  514. If the client request was a Range request, from & to specify the requested
  515. range; otherwise 'from' is 0 and 'to' is -1. */
  516. int
  517. objectHasData(ObjectPtr object, int from, int to)
  518. {
  519. int first, last, i, upto;
  520. if(to < 0) {
  521. if(object->length >= 0)
  522. to = object->length;
  523. else
  524. return 0;
  525. }
  526. first = from / CHUNK_SIZE;
  527. last = to / CHUNK_SIZE;
  528. if(from >= to)
  529. return 2;
  530. if(to > object->size) {
  531. upto = to;
  532. goto disk;
  533. }
  534. if(last > object->numchunks ||
  535. object->chunks[last].size > to % CHUNK_SIZE) {
  536. upto = to;
  537. goto disk;
  538. }
  539. for(i = last - 1; i >= first; i--) {
  540. if(object->chunks[i].size < CHUNK_SIZE) {
  541. upto = (i + 1) * CHUNK_SIZE;
  542. goto disk;
  543. }
  544. }
  545. return 2;
  546. disk:
  547. if(object->flags & OBJECT_DISK_ENTRY_COMPLETE)
  548. return 1;
  549. if(diskEntrySize(object) >= upto)
  550. return 1;
  551. return 0;
  552. }
  553. void
  554. destroyObject(ObjectPtr object)
  555. {
  556. int i;
  557. assert(object->refcount == 0 && !object->requestor);
  558. assert(!object->condition.handlers &&
  559. (object->flags & OBJECT_INPROGRESS) == 0);
  560. if(object->disk_entry)
  561. destroyDiskEntry(object, 0);
  562. if(object->flags & OBJECT_PUBLIC) {
  563. privatiseObject(object, 0);
  564. } else {
  565. object->type = -1;
  566. if(object->message) releaseAtom(object->message);
  567. if(object->key) free(object->key);
  568. if(object->headers) releaseAtom(object->headers);
  569. if(object->etag) free(object->etag);
  570. if(object->via) releaseAtom(object->via);
  571. for(i = 0; i < object->numchunks; i++) {
  572. assert(!object->chunks[i].locked);
  573. if(object->chunks[i].data)
  574. dispose_chunk(object->chunks[i].data);
  575. object->chunks[i].data = NULL;
  576. object->chunks[i].size = 0;
  577. }
  578. if(object->chunks) free(object->chunks);
  579. privateObjectCount--;
  580. free(object);
  581. }
  582. }
  583. void
  584. privatiseObject(ObjectPtr object, int linear)
  585. {
  586. int i, h;
  587. if(!(object->flags & OBJECT_PUBLIC)) {
  588. if(linear)
  589. object->flags |= OBJECT_LINEAR;
  590. return;
  591. }
  592. if(object->disk_entry)
  593. destroyDiskEntry(object, 0);
  594. object->flags &= ~OBJECT_PUBLIC;
  595. for(i = 0; i < object->numchunks; i++) {
  596. if(object->chunks[i].locked)
  597. break;
  598. if(object->chunks[i].data) {
  599. object->chunks[i].size = 0;
  600. dispose_chunk(object->chunks[i].data);
  601. object->chunks[i].data = NULL;
  602. }
  603. }
  604. h = hash(object->type, object->key, object->key_size,
  605. log2ObjectHashTableSize);
  606. assert(objectHashTable[h] == object);
  607. objectHashTable[h] = NULL;
  608. if(object->previous)
  609. object->previous->next = object->next;
  610. if(object_list == object)
  611. object_list = object->next;
  612. if(object->next)
  613. object->next->previous = object->previous;
  614. if(object_list_end == object)
  615. object_list_end = object->previous;
  616. object->previous = NULL;
  617. object->next = NULL;
  618. publicObjectCount--;
  619. privateObjectCount++;
  620. if(object->refcount == 0)
  621. destroyObject(object);
  622. else {
  623. if(linear)
  624. object->flags |= OBJECT_LINEAR;
  625. }
  626. }
  627. void
  628. abortObject(ObjectPtr object, int code, AtomPtr message)
  629. {
  630. int i;
  631. assert(code != 0);
  632. object->flags &= ~(OBJECT_INITIAL | OBJECT_VALIDATING);
  633. object->flags |= OBJECT_ABORTED;
  634. object->code = code;
  635. if(object->message) releaseAtom(object->message);
  636. object->message = message;
  637. object->length = 0;
  638. object->date = object->age;
  639. object->expires = object->age;
  640. object->last_modified = -1;
  641. if(object->etag) free(object->etag);
  642. object->etag = NULL;
  643. if(object->headers) releaseAtom(object->headers);
  644. object->headers = NULL;
  645. object->size = 0;
  646. for(i = 0; i < object->numchunks; i++) {
  647. if(object->chunks[i].data) {
  648. if(!object->chunks[i].locked) {
  649. dispose_chunk(object->chunks[i].data);
  650. object->chunks[i].data = NULL;
  651. object->chunks[i].size = 0;
  652. }
  653. }
  654. }
  655. privatiseObject(object, 0);
  656. }
  657. void
  658. supersedeObject(ObjectPtr object)
  659. {
  660. object->flags |= OBJECT_SUPERSEDED;
  661. destroyDiskEntry(object, 1);
  662. privatiseObject(object, 0);
  663. notifyObject(object);
  664. }
  665. void
  666. notifyObject(ObjectPtr object)
  667. {
  668. retainObject(object);
  669. signalCondition(&object->condition);
  670. releaseObject(object);
  671. }
  672. int
  673. discardObjectsHandler(TimeEventHandlerPtr event)
  674. {
  675. return discardObjects(0, 0);
  676. }
  677. void
  678. writeoutObjects(int all)
  679. {
  680. ObjectPtr object = object_list;
  681. int bytes;
  682. int objects;
  683. int n;
  684. if(diskIsClean) return;
  685. objects = 0;
  686. bytes = 0;
  687. while(object) {
  688. do {
  689. if(!all) {
  690. if(objects >= maxObjectsWhenIdle ||
  691. bytes >= maxWriteoutWhenIdle) {
  692. if(workToDo()) return;
  693. objects = 0;
  694. bytes = 0;
  695. }
  696. }
  697. n = writeoutToDisk(object, -1, all ? -1 : maxWriteoutWhenIdle);
  698. bytes += n;
  699. } while(!all && n == maxWriteoutWhenIdle);
  700. objects++;
  701. object = object->next;
  702. }
  703. diskIsClean = 1;
  704. }
  705. int
  706. discardObjects(int all, int force)
  707. {
  708. ObjectPtr object;
  709. int i;
  710. static int in_discardObjects = 0;
  711. TimeEventHandlerPtr event;
  712. if(in_discardObjects)
  713. return 0;
  714. in_discardObjects = 1;
  715. if(all || force || used_chunks >= CHUNKS(chunkHighMark) ||
  716. publicObjectCount >= publicObjectLowMark ||
  717. publicObjectCount + privateObjectCount >= objectHighMark) {
  718. object = object_list_end;
  719. while(object &&
  720. (all || force || used_chunks >= CHUNKS(chunkLowMark))) {
  721. if(force || ((object->flags & OBJECT_PUBLIC) &&
  722. object->numchunks > CHUNKS(chunkLowMark) / 4)) {
  723. int j;
  724. for(j = 0; j < object->numchunks; j++) {
  725. if(object->chunks[j].locked) {
  726. break;
  727. }
  728. if(object->chunks[j].size < CHUNK_SIZE) {
  729. continue;
  730. }
  731. writeoutToDisk(object, (j + 1) * CHUNK_SIZE, -1);
  732. dispose_chunk(object->chunks[j].data);
  733. object->chunks[j].data = NULL;
  734. object->chunks[j].size = 0;
  735. }
  736. }
  737. object = object->previous;
  738. }
  739. i = 0;
  740. object = object_list_end;
  741. while(object &&
  742. (all || force ||
  743. used_chunks - i > CHUNKS(chunkLowMark) ||
  744. used_chunks > CHUNKS(chunkCriticalMark) ||
  745. publicObjectCount > publicObjectLowMark)) {
  746. ObjectPtr next_object = object->previous;
  747. if(object->refcount == 0) {
  748. i += object->numchunks;
  749. writeoutToDisk(object, object->size, -1);
  750. privatiseObject(object, 0);
  751. } else if(all || force) {
  752. writeoutToDisk(object, object->size, -1);
  753. destroyDiskEntry(object, 0);
  754. }
  755. object = next_object;
  756. }
  757. object = object_list_end;
  758. if(force || used_chunks > CHUNKS(chunkCriticalMark)) {
  759. if(used_chunks > CHUNKS(chunkCriticalMark)) {
  760. do_log(L_WARN,
  761. "Short on chunk memory -- "
  762. "attempting to punch holes "
  763. "in the middle of objects.\n");
  764. }
  765. while(object &&
  766. (force || used_chunks > CHUNKS(chunkCriticalMark))) {
  767. if(force || (object->flags & OBJECT_PUBLIC)) {
  768. int j;
  769. for(j = object->numchunks - 1; j >= 0; j--) {
  770. if(object->chunks[j].locked)
  771. continue;
  772. if(object->chunks[j].size < CHUNK_SIZE)
  773. continue;
  774. writeoutToDisk(object, (j + 1) * CHUNK_SIZE, -1);
  775. dispose_chunk(object->chunks[j].data);
  776. object->chunks[j].data = NULL;
  777. object->chunks[j].size = 0;
  778. }
  779. }
  780. object = object->previous;
  781. }
  782. }
  783. event = scheduleTimeEvent(2, discardObjectsHandler, 0, NULL);
  784. if(event) {
  785. objectExpiryScheduled = 1;
  786. } else {
  787. objectExpiryScheduled = 0;
  788. do_log(L_ERROR, "Couldn't schedule object expiry.\n");
  789. }
  790. } else {
  791. objectExpiryScheduled = 0;
  792. }
  793. if(all) {
  794. if(privateObjectCount + publicObjectCount != 0) {
  795. do_log(L_WARN,
  796. "Discarded all objects, "
  797. "%d + %d objects left (%d chunks and %d atoms used).\n",
  798. publicObjectCount, privateObjectCount,
  799. used_chunks, used_atoms);
  800. } else if(used_chunks != 0) {
  801. do_log(L_WARN,
  802. "Discarded all objects, "
  803. "%d chunks and %d atoms left.\n",
  804. used_chunks, used_atoms);
  805. }
  806. diskIsClean = 1;
  807. }
  808. in_discardObjects = 0;
  809. return 1;
  810. }
  811. CacheControlRec no_cache_control = {0, -1, -1, -1, -1};
  812. int
  813. objectIsStale(ObjectPtr object, CacheControlPtr cache_control)
  814. {
  815. int stale = 0x7FFFFFFF;
  816. int flags;
  817. int max_age, s_maxage;
  818. time_t date;
  819. if(object->flags & OBJECT_INITIAL)
  820. return 0;
  821. if(object->date >= 0)
  822. date = object->date;
  823. else if(object->age >= 0)
  824. date = object->age;
  825. else
  826. date = current_time.tv_sec;
  827. if(cache_control == NULL)
  828. cache_control = &no_cache_control;
  829. flags = object->cache_control | cache_control->flags;
  830. if(cache_control->max_age >= 0) {
  831. if(object->max_age >= 0)
  832. max_age = MIN(cache_control->max_age, object->max_age);
  833. else
  834. max_age = cache_control->max_age;
  835. } else
  836. max_age = object->max_age;
  837. if(cache_control->s_maxage >= 0) {
  838. if(object->s_maxage >= 0)
  839. s_maxage = MIN(cache_control->s_maxage, object->s_maxage);
  840. else
  841. s_maxage = cache_control->s_maxage;
  842. } else
  843. s_maxage = object->s_maxage;
  844. if(max_age >= 0)
  845. stale = MIN(stale, object->age + max_age);
  846. if(cacheIsShared && s_maxage >= 0)
  847. stale = MIN(stale, object->age + s_maxage);
  848. if(object->expires >= 0 || object->max_age >= 0)
  849. stale = MIN(stale, object->age + maxExpiresAge);
  850. else
  851. stale = MIN(stale, object->age + maxAge);
  852. /* RFC 2616 14.9.3: server-side max-age overrides expires */
  853. if(object->expires >= 0 && object->max_age < 0) {
  854. /* This protects against clock skew */
  855. stale = MIN(stale, object->age + object->expires - date);
  856. }
  857. if(object->expires < 0 && object->max_age < 0) {
  858. /* No server-side information -- heuristic expiration */
  859. if(object->last_modified >= 0)
  860. /* Again, take care of clock skew */
  861. stale = MIN(stale,
  862. object->age +
  863. (date - object->last_modified) * maxAgeFraction);
  864. else
  865. stale = MIN(stale, object->age + maxNoModifiedAge);
  866. }
  867. if(!(flags & CACHE_MUST_REVALIDATE) &&
  868. !(cacheIsShared && (flags & CACHE_PROXY_REVALIDATE))) {
  869. /* Client side can relax transparency */
  870. if(cache_control->min_fresh >= 0) {
  871. if(cache_control->max_stale >= 0)
  872. stale = MIN(stale - cache_control->min_fresh,
  873. stale + cache_control->max_stale);
  874. else
  875. stale = stale - cache_control->min_fresh;
  876. } else if(cache_control->max_stale >= 0) {
  877. stale = stale + cache_control->max_stale;
  878. }
  879. }
  880. return current_time.tv_sec > stale;
  881. }
  882. int
  883. objectMustRevalidate(ObjectPtr object, CacheControlPtr cache_control)
  884. {
  885. int flags;
  886. if(cache_control == NULL)
  887. cache_control = &no_cache_control;
  888. if(object)
  889. flags = object->cache_control | cache_control->flags;
  890. else
  891. flags = cache_control->flags;
  892. if(flags & (CACHE_NO | CACHE_NO_HIDDEN | CACHE_NO_STORE))
  893. return 1;
  894. if(cacheIsShared && (flags & CACHE_PRIVATE))
  895. return 1;
  896. if(!mindlesslyCacheVary && (flags & CACHE_VARY))
  897. return 1;
  898. if(dontCacheCookies && (flags & CACHE_COOKIE))
  899. return 1;
  900. if(object)
  901. return objectIsStale(object, cache_control);
  902. return 0;
  903. }