ngx_http_file_cache.c 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800
  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */
  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>
  8. #include <ngx_md5.h>
  9. static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,
  10. ngx_http_cache_t *c);
  11. static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);
  12. static ngx_int_t ngx_http_file_cache_lock_wait(ngx_http_request_t *r,
  13. ngx_http_cache_t *c);
  14. static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,
  15. ngx_http_cache_t *c);
  16. static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r,
  17. ngx_http_cache_t *c);
  18. #if (NGX_HAVE_FILE_AIO)
  19. static void ngx_http_cache_aio_event_handler(ngx_event_t *ev);
  20. #endif
  21. #if (NGX_THREADS)
  22. static ngx_int_t ngx_http_cache_thread_handler(ngx_thread_task_t *task,
  23. ngx_file_t *file);
  24. static void ngx_http_cache_thread_event_handler(ngx_event_t *ev);
  25. #endif
  26. static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
  27. ngx_http_cache_t *c);
  28. static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,
  29. ngx_path_t *path);
  30. static ngx_http_file_cache_node_t *
  31. ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
  32. static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  33. ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
  34. static void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary,
  35. size_t len, u_char *hash);
  36. static void ngx_http_file_cache_vary_header(ngx_http_request_t *r,
  37. ngx_md5_t *md5, ngx_str_t *name);
  38. static ngx_int_t ngx_http_file_cache_reopen(ngx_http_request_t *r,
  39. ngx_http_cache_t *c);
  40. static ngx_int_t ngx_http_file_cache_update_variant(ngx_http_request_t *r,
  41. ngx_http_cache_t *c);
  42. static void ngx_http_file_cache_cleanup(void *data);
  43. static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
  44. static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
  45. static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
  46. ngx_queue_t *q, u_char *name);
  47. static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);
  48. static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
  49. ngx_str_t *path);
  50. static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
  51. ngx_str_t *path);
  52. static ngx_int_t ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx,
  53. ngx_str_t *path);
  54. static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
  55. ngx_str_t *path);
  56. static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
  57. ngx_http_cache_t *c);
  58. static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
  59. ngx_str_t *path);
  60. static void ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache);
  61. ngx_str_t ngx_http_cache_status[] = {
  62. ngx_string("MISS"),
  63. ngx_string("BYPASS"),
  64. ngx_string("EXPIRED"),
  65. ngx_string("STALE"),
  66. ngx_string("UPDATING"),
  67. ngx_string("REVALIDATED"),
  68. ngx_string("HIT")
  69. };
  70. static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
  71. static ngx_int_t
  72. ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
  73. {
  74. ngx_http_file_cache_t *ocache = data;
  75. size_t len;
  76. ngx_uint_t n;
  77. ngx_http_file_cache_t *cache;
  78. cache = shm_zone->data;
  79. if (ocache) {
  80. if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
  81. ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  82. "cache \"%V\" uses the \"%V\" cache path "
  83. "while previously it used the \"%V\" cache path",
  84. &shm_zone->shm.name, &cache->path->name,
  85. &ocache->path->name);
  86. return NGX_ERROR;
  87. }
  88. for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {
  89. if (cache->path->level[n] != ocache->path->level[n]) {
  90. ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  91. "cache \"%V\" had previously different levels",
  92. &shm_zone->shm.name);
  93. return NGX_ERROR;
  94. }
  95. }
  96. cache->sh = ocache->sh;
  97. cache->shpool = ocache->shpool;
  98. cache->bsize = ocache->bsize;
  99. cache->max_size /= cache->bsize;
  100. if (!cache->sh->cold || cache->sh->loading) {
  101. cache->path->loader = NULL;
  102. }
  103. return NGX_OK;
  104. }
  105. cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
  106. if (shm_zone->shm.exists) {
  107. cache->sh = cache->shpool->data;
  108. cache->bsize = ngx_fs_bsize(cache->path->name.data);
  109. cache->max_size /= cache->bsize;
  110. return NGX_OK;
  111. }
  112. cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
  113. if (cache->sh == NULL) {
  114. return NGX_ERROR;
  115. }
  116. cache->shpool->data = cache->sh;
  117. ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
  118. ngx_http_file_cache_rbtree_insert_value);
  119. ngx_queue_init(&cache->sh->queue);
  120. cache->sh->cold = 1;
  121. cache->sh->loading = 0;
  122. cache->sh->size = 0;
  123. cache->sh->count = 0;
  124. cache->sh->watermark = (ngx_uint_t) -1;
  125. cache->bsize = ngx_fs_bsize(cache->path->name.data);
  126. cache->max_size /= cache->bsize;
  127. len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;
  128. cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
  129. if (cache->shpool->log_ctx == NULL) {
  130. return NGX_ERROR;
  131. }
  132. ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
  133. &shm_zone->shm.name);
  134. cache->shpool->log_nomem = 0;
  135. return NGX_OK;
  136. }
  137. ngx_int_t
  138. ngx_http_file_cache_new(ngx_http_request_t *r)
  139. {
  140. ngx_http_cache_t *c;
  141. c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
  142. if (c == NULL) {
  143. return NGX_ERROR;
  144. }
  145. if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {
  146. return NGX_ERROR;
  147. }
  148. r->cache = c;
  149. c->file.log = r->connection->log;
  150. c->file.fd = NGX_INVALID_FILE;
  151. return NGX_OK;
  152. }
  153. ngx_int_t
  154. ngx_http_file_cache_create(ngx_http_request_t *r)
  155. {
  156. ngx_http_cache_t *c;
  157. ngx_pool_cleanup_t *cln;
  158. ngx_http_file_cache_t *cache;
  159. c = r->cache;
  160. cache = c->file_cache;
  161. cln = ngx_pool_cleanup_add(r->pool, 0);
  162. if (cln == NULL) {
  163. return NGX_ERROR;
  164. }
  165. cln->handler = ngx_http_file_cache_cleanup;
  166. cln->data = c;
  167. if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
  168. return NGX_ERROR;
  169. }
  170. if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  171. return NGX_ERROR;
  172. }
  173. return NGX_OK;
  174. }
  175. void
  176. ngx_http_file_cache_create_key(ngx_http_request_t *r)
  177. {
  178. size_t len;
  179. ngx_str_t *key;
  180. ngx_uint_t i;
  181. ngx_md5_t md5;
  182. ngx_http_cache_t *c;
  183. c = r->cache;
  184. len = 0;
  185. ngx_crc32_init(c->crc32);
  186. ngx_md5_init(&md5);
  187. key = c->keys.elts;
  188. for (i = 0; i < c->keys.nelts; i++) {
  189. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  190. "http cache key: \"%V\"", &key[i]);
  191. len += key[i].len;
  192. ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
  193. ngx_md5_update(&md5, key[i].data, key[i].len);
  194. }
  195. c->header_start = sizeof(ngx_http_file_cache_header_t)
  196. + sizeof(ngx_http_file_cache_key) + len + 1;
  197. ngx_crc32_final(c->crc32);
  198. ngx_md5_final(c->key, &md5);
  199. ngx_memcpy(c->main, c->key, NGX_HTTP_CACHE_KEY_LEN);
  200. }
  201. ngx_int_t
  202. ngx_http_file_cache_open(ngx_http_request_t *r)
  203. {
  204. ngx_int_t rc, rv;
  205. ngx_uint_t test;
  206. ngx_http_cache_t *c;
  207. ngx_pool_cleanup_t *cln;
  208. ngx_open_file_info_t of;
  209. ngx_http_file_cache_t *cache;
  210. ngx_http_core_loc_conf_t *clcf;
  211. c = r->cache;
  212. if (c->waiting) {
  213. return NGX_AGAIN;
  214. }
  215. if (c->reading) {
  216. return ngx_http_file_cache_read(r, c);
  217. }
  218. cache = c->file_cache;
  219. if (c->node == NULL) {
  220. cln = ngx_pool_cleanup_add(r->pool, 0);
  221. if (cln == NULL) {
  222. return NGX_ERROR;
  223. }
  224. cln->handler = ngx_http_file_cache_cleanup;
  225. cln->data = c;
  226. }
  227. c->buffer_size = c->body_start;
  228. rc = ngx_http_file_cache_exists(cache, c);
  229. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  230. "http file cache exists: %i e:%d", rc, c->exists);
  231. if (rc == NGX_ERROR) {
  232. return rc;
  233. }
  234. if (rc == NGX_AGAIN) {
  235. return NGX_HTTP_CACHE_SCARCE;
  236. }
  237. if (rc == NGX_OK) {
  238. if (c->error) {
  239. return c->error;
  240. }
  241. c->temp_file = 1;
  242. test = c->exists ? 1 : 0;
  243. rv = NGX_DECLINED;
  244. } else { /* rc == NGX_DECLINED */
  245. test = cache->sh->cold ? 1 : 0;
  246. if (c->min_uses > 1) {
  247. if (!test) {
  248. return NGX_HTTP_CACHE_SCARCE;
  249. }
  250. rv = NGX_HTTP_CACHE_SCARCE;
  251. } else {
  252. c->temp_file = 1;
  253. rv = NGX_DECLINED;
  254. }
  255. }
  256. if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  257. return NGX_ERROR;
  258. }
  259. if (!test) {
  260. goto done;
  261. }
  262. clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  263. ngx_memzero(&of, sizeof(ngx_open_file_info_t));
  264. of.uniq = c->uniq;
  265. of.valid = clcf->open_file_cache_valid;
  266. of.min_uses = clcf->open_file_cache_min_uses;
  267. of.events = clcf->open_file_cache_events;
  268. of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
  269. of.read_ahead = clcf->read_ahead;
  270. if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
  271. != NGX_OK)
  272. {
  273. switch (of.err) {
  274. case 0:
  275. return NGX_ERROR;
  276. case NGX_ENOENT:
  277. case NGX_ENOTDIR:
  278. goto done;
  279. default:
  280. ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
  281. ngx_open_file_n " \"%s\" failed", c->file.name.data);
  282. return NGX_ERROR;
  283. }
  284. }
  285. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  286. "http file cache fd: %d", of.fd);
  287. c->file.fd = of.fd;
  288. c->file.log = r->connection->log;
  289. c->uniq = of.uniq;
  290. c->length = of.size;
  291. c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;
  292. c->buf = ngx_create_temp_buf(r->pool, c->body_start);
  293. if (c->buf == NULL) {
  294. return NGX_ERROR;
  295. }
  296. return ngx_http_file_cache_read(r, c);
  297. done:
  298. if (rv == NGX_DECLINED) {
  299. return ngx_http_file_cache_lock(r, c);
  300. }
  301. return rv;
  302. }
  303. static ngx_int_t
  304. ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
  305. {
  306. ngx_msec_t now, timer;
  307. ngx_http_file_cache_t *cache;
  308. if (!c->lock) {
  309. return NGX_DECLINED;
  310. }
  311. now = ngx_current_msec;
  312. cache = c->file_cache;
  313. ngx_shmtx_lock(&cache->shpool->mutex);
  314. timer = c->node->lock_time - now;
  315. if (!c->node->updating || (ngx_msec_int_t) timer <= 0) {
  316. c->node->updating = 1;
  317. c->node->lock_time = now + c->lock_age;
  318. c->updating = 1;
  319. c->lock_time = c->node->lock_time;
  320. }
  321. ngx_shmtx_unlock(&cache->shpool->mutex);
  322. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  323. "http file cache lock u:%d wt:%M",
  324. c->updating, c->wait_time);
  325. if (c->updating) {
  326. return NGX_DECLINED;
  327. }
  328. if (c->lock_timeout == 0) {
  329. return NGX_HTTP_CACHE_SCARCE;
  330. }
  331. c->waiting = 1;
  332. if (c->wait_time == 0) {
  333. c->wait_time = now + c->lock_timeout;
  334. c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;
  335. c->wait_event.data = r;
  336. c->wait_event.log = r->connection->log;
  337. }
  338. timer = c->wait_time - now;
  339. ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
  340. r->main->blocked++;
  341. return NGX_AGAIN;
  342. }
  343. static void
  344. ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
  345. {
  346. ngx_int_t rc;
  347. ngx_connection_t *c;
  348. ngx_http_request_t *r;
  349. r = ev->data;
  350. c = r->connection;
  351. ngx_http_set_log_request(c->log, r);
  352. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  353. "http file cache wait: \"%V?%V\"", &r->uri, &r->args);
  354. rc = ngx_http_file_cache_lock_wait(r, r->cache);
  355. if (rc == NGX_AGAIN) {
  356. return;
  357. }
  358. r->cache->waiting = 0;
  359. r->main->blocked--;
  360. if (r->main->terminated) {
  361. /*
  362. * trigger connection event handler if the request was
  363. * terminated
  364. */
  365. c->write->handler(c->write);
  366. } else {
  367. r->write_event_handler(r);
  368. ngx_http_run_posted_requests(c);
  369. }
  370. }
  371. static ngx_int_t
  372. ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
  373. {
  374. ngx_uint_t wait;
  375. ngx_msec_t now, timer;
  376. ngx_http_file_cache_t *cache;
  377. now = ngx_current_msec;
  378. timer = c->wait_time - now;
  379. if ((ngx_msec_int_t) timer <= 0) {
  380. ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  381. "cache lock timeout");
  382. c->lock_timeout = 0;
  383. return NGX_OK;
  384. }
  385. cache = c->file_cache;
  386. wait = 0;
  387. ngx_shmtx_lock(&cache->shpool->mutex);
  388. timer = c->node->lock_time - now;
  389. if (c->node->updating && (ngx_msec_int_t) timer > 0) {
  390. wait = 1;
  391. }
  392. ngx_shmtx_unlock(&cache->shpool->mutex);
  393. if (wait) {
  394. ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
  395. return NGX_AGAIN;
  396. }
  397. return NGX_OK;
  398. }
  399. static ngx_int_t
  400. ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)
  401. {
  402. u_char *p;
  403. time_t now;
  404. ssize_t n;
  405. ngx_str_t *key;
  406. ngx_int_t rc;
  407. ngx_uint_t i;
  408. ngx_http_file_cache_t *cache;
  409. ngx_http_file_cache_header_t *h;
  410. n = ngx_http_file_cache_aio_read(r, c);
  411. if (n < 0) {
  412. return n;
  413. }
  414. if ((size_t) n < c->header_start) {
  415. ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  416. "cache file \"%s\" is too small", c->file.name.data);
  417. return NGX_DECLINED;
  418. }
  419. h = (ngx_http_file_cache_header_t *) c->buf->pos;
  420. if (h->version != NGX_HTTP_CACHE_VERSION) {
  421. ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  422. "cache file \"%s\" version mismatch", c->file.name.data);
  423. return NGX_DECLINED;
  424. }
  425. if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) {
  426. ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  427. "cache file \"%s\" has md5 collision", c->file.name.data);
  428. return NGX_DECLINED;
  429. }
  430. p = c->buf->pos + sizeof(ngx_http_file_cache_header_t)
  431. + sizeof(ngx_http_file_cache_key);
  432. key = c->keys.elts;
  433. for (i = 0; i < c->keys.nelts; i++) {
  434. if (ngx_memcmp(p, key[i].data, key[i].len) != 0) {
  435. ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  436. "cache file \"%s\" has md5 collision",
  437. c->file.name.data);
  438. return NGX_DECLINED;
  439. }
  440. p += key[i].len;
  441. }
  442. if ((size_t) h->body_start > c->body_start) {
  443. ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  444. "cache file \"%s\" has too long header",
  445. c->file.name.data);
  446. return NGX_DECLINED;
  447. }
  448. if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) {
  449. ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  450. "cache file \"%s\" has incorrect vary length",
  451. c->file.name.data);
  452. return NGX_DECLINED;
  453. }
  454. if (h->vary_len) {
  455. ngx_http_file_cache_vary(r, h->vary, h->vary_len, c->variant);
  456. if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) {
  457. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  458. "http file cache vary mismatch");
  459. return ngx_http_file_cache_reopen(r, c);
  460. }
  461. }
  462. c->buf->last += n;
  463. c->valid_sec = h->valid_sec;
  464. c->updating_sec = h->updating_sec;
  465. c->error_sec = h->error_sec;
  466. c->last_modified = h->last_modified;
  467. c->date = h->date;
  468. c->valid_msec = h->valid_msec;
  469. c->body_start = h->body_start;
  470. c->etag.len = h->etag_len;
  471. c->etag.data = h->etag;
  472. r->cached = 1;
  473. cache = c->file_cache;
  474. if (cache->sh->cold) {
  475. ngx_shmtx_lock(&cache->shpool->mutex);
  476. if (!c->node->exists) {
  477. c->node->uses = 1;
  478. c->node->body_start = c->body_start;
  479. c->node->exists = 1;
  480. c->node->uniq = c->uniq;
  481. c->node->fs_size = c->fs_size;
  482. cache->sh->size += c->fs_size;
  483. }
  484. ngx_shmtx_unlock(&cache->shpool->mutex);
  485. }
  486. now = ngx_time();
  487. if (c->valid_sec < now) {
  488. c->stale_updating = c->valid_sec + c->updating_sec >= now;
  489. c->stale_error = c->valid_sec + c->error_sec >= now;
  490. ngx_shmtx_lock(&cache->shpool->mutex);
  491. if (c->node->updating) {
  492. rc = NGX_HTTP_CACHE_UPDATING;
  493. } else {
  494. c->node->updating = 1;
  495. c->updating = 1;
  496. c->lock_time = c->node->lock_time;
  497. rc = NGX_HTTP_CACHE_STALE;
  498. }
  499. ngx_shmtx_unlock(&cache->shpool->mutex);
  500. ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  501. "http file cache expired: %i %T %T",
  502. rc, c->valid_sec, now);
  503. return rc;
  504. }
  505. return NGX_OK;
  506. }
  507. static ssize_t
  508. ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
  509. {
  510. #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
  511. ssize_t n;
  512. ngx_http_core_loc_conf_t *clcf;
  513. clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  514. #endif
  515. #if (NGX_HAVE_FILE_AIO)
  516. if (clcf->aio == NGX_HTTP_AIO_ON && ngx_file_aio) {
  517. n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
  518. if (n != NGX_AGAIN) {
  519. c->reading = 0;
  520. return n;
  521. }
  522. c->reading = 1;
  523. c->file.aio->data = r;
  524. c->file.aio->handler = ngx_http_cache_aio_event_handler;
  525. ngx_add_timer(&c->file.aio->event, 60000);
  526. r->main->blocked++;
  527. r->aio = 1;
  528. return NGX_AGAIN;
  529. }
  530. #endif
  531. #if (NGX_THREADS)
  532. if (clcf->aio == NGX_HTTP_AIO_THREADS) {
  533. c->file.thread_task = c->thread_task;
  534. c->file.thread_handler = ngx_http_cache_thread_handler;
  535. c->file.thread_ctx = r;
  536. n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
  537. c->thread_task = c->file.thread_task;
  538. c->reading = (n == NGX_AGAIN);
  539. return n;
  540. }
  541. #endif
  542. return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
  543. }
  544. #if (NGX_HAVE_FILE_AIO)
  545. static void
  546. ngx_http_cache_aio_event_handler(ngx_event_t *ev)
  547. {
  548. ngx_event_aio_t *aio;
  549. ngx_connection_t *c;
  550. ngx_http_request_t *r;
  551. aio = ev->data;
  552. r = aio->data;
  553. c = r->connection;
  554. ngx_http_set_log_request(c->log, r);
  555. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  556. "http file cache aio: \"%V?%V\"", &r->uri, &r->args);
  557. if (ev->timedout) {
  558. ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  559. "aio operation took too long");
  560. ev->timedout = 0;
  561. return;
  562. }
  563. if (ev->timer_set) {
  564. ngx_del_timer(ev);
  565. }
  566. r->main->blocked--;
  567. r->aio = 0;
  568. if (r->main->terminated) {
  569. /*
  570. * trigger connection event handler if the request was
  571. * terminated
  572. */
  573. c->write->handler(c->write);
  574. } else {
  575. r->write_event_handler(r);
  576. ngx_http_run_posted_requests(c);
  577. }
  578. }
  579. #endif
  580. #if (NGX_THREADS)
  581. static ngx_int_t
  582. ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
  583. {
  584. ngx_str_t name;
  585. ngx_thread_pool_t *tp;
  586. ngx_http_request_t *r;
  587. ngx_http_core_loc_conf_t *clcf;
  588. r = file->thread_ctx;
  589. clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  590. tp = clcf->thread_pool;
  591. if (tp == NULL) {
  592. if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
  593. != NGX_OK)
  594. {
  595. return NGX_ERROR;
  596. }
  597. tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);
  598. if (tp == NULL) {
  599. ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  600. "thread pool \"%V\" not found", &name);
  601. return NGX_ERROR;
  602. }
  603. }
  604. task->event.data = r;
  605. task->event.handler = ngx_http_cache_thread_event_handler;
  606. if (ngx_thread_task_post(tp, task) != NGX_OK) {
  607. return NGX_ERROR;
  608. }
  609. ngx_add_timer(&task->event, 60000);
  610. r->main->blocked++;
  611. r->aio = 1;
  612. return NGX_OK;
  613. }
  614. static void
  615. ngx_http_cache_thread_event_handler(ngx_event_t *ev)
  616. {
  617. ngx_connection_t *c;
  618. ngx_http_request_t *r;
  619. r = ev->data;
  620. c = r->connection;
  621. ngx_http_set_log_request(c->log, r);
  622. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  623. "http file cache thread: \"%V?%V\"", &r->uri, &r->args);
  624. if (ev->timedout) {
  625. ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  626. "thread operation took too long");
  627. ev->timedout = 0;
  628. return;
  629. }
  630. if (ev->timer_set) {
  631. ngx_del_timer(ev);
  632. }
  633. r->main->blocked--;
  634. r->aio = 0;
  635. if (r->main->terminated) {
  636. /*
  637. * trigger connection event handler if the request was
  638. * terminated
  639. */
  640. c->write->handler(c->write);
  641. } else {
  642. r->write_event_handler(r);
  643. ngx_http_run_posted_requests(c);
  644. }
  645. }
  646. #endif
  647. static ngx_int_t
  648. ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  649. {
  650. ngx_int_t rc;
  651. ngx_http_file_cache_node_t *fcn;
  652. ngx_shmtx_lock(&cache->shpool->mutex);
  653. fcn = c->node;
  654. if (fcn == NULL) {
  655. fcn = ngx_http_file_cache_lookup(cache, c->key);
  656. }
  657. if (fcn) {
  658. ngx_queue_remove(&fcn->queue);
  659. if (c->node == NULL) {
  660. fcn->uses++;
  661. fcn->count++;
  662. }
  663. if (fcn->error) {
  664. if (fcn->valid_sec < ngx_time()) {
  665. goto renew;
  666. }
  667. rc = NGX_OK;
  668. goto done;
  669. }
  670. if (fcn->exists || fcn->uses >= c->min_uses) {
  671. c->exists = fcn->exists;
  672. if (fcn->body_start && !c->update_variant) {
  673. c->body_start = fcn->body_start;
  674. }
  675. rc = NGX_OK;
  676. goto done;
  677. }
  678. rc = NGX_AGAIN;
  679. goto done;
  680. }
  681. fcn = ngx_slab_calloc_locked(cache->shpool,
  682. sizeof(ngx_http_file_cache_node_t));
  683. if (fcn == NULL) {
  684. ngx_http_file_cache_set_watermark(cache);
  685. ngx_shmtx_unlock(&cache->shpool->mutex);
  686. (void) ngx_http_file_cache_forced_expire(cache);
  687. ngx_shmtx_lock(&cache->shpool->mutex);
  688. fcn = ngx_slab_calloc_locked(cache->shpool,
  689. sizeof(ngx_http_file_cache_node_t));
  690. if (fcn == NULL) {
  691. ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  692. "could not allocate node%s", cache->shpool->log_ctx);
  693. rc = NGX_ERROR;
  694. goto failed;
  695. }
  696. }
  697. cache->sh->count++;
  698. ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
  699. ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
  700. NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
  701. ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
  702. fcn->uses = 1;
  703. fcn->count = 1;
  704. renew:
  705. rc = NGX_DECLINED;
  706. fcn->valid_msec = 0;
  707. fcn->error = 0;
  708. fcn->exists = 0;
  709. fcn->valid_sec = 0;
  710. fcn->uniq = 0;
  711. fcn->body_start = 0;
  712. fcn->fs_size = 0;
  713. done:
  714. fcn->expire = ngx_time() + cache->inactive;
  715. ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
  716. c->uniq = fcn->uniq;
  717. c->error = fcn->error;
  718. c->node = fcn;
  719. failed:
  720. ngx_shmtx_unlock(&cache->shpool->mutex);
  721. return rc;
  722. }
  723. static ngx_int_t
  724. ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
  725. {
  726. u_char *p;
  727. ngx_http_cache_t *c;
  728. c = r->cache;
  729. if (c->file.name.len) {
  730. return NGX_OK;
  731. }
  732. c->file.name.len = path->name.len + 1 + path->len
  733. + 2 * NGX_HTTP_CACHE_KEY_LEN;
  734. c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
  735. if (c->file.name.data == NULL) {
  736. return NGX_ERROR;
  737. }
  738. ngx_memcpy(c->file.name.data, path->name.data, path->name.len);
  739. p = c->file.name.data + path->name.len + 1 + path->len;
  740. p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
  741. *p = '\0';
  742. ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);
  743. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  744. "cache file: \"%s\"", c->file.name.data);
  745. return NGX_OK;
  746. }
  747. static ngx_http_file_cache_node_t *
  748. ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
  749. {
  750. ngx_int_t rc;
  751. ngx_rbtree_key_t node_key;
  752. ngx_rbtree_node_t *node, *sentinel;
  753. ngx_http_file_cache_node_t *fcn;
  754. ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));
  755. node = cache->sh->rbtree.root;
  756. sentinel = cache->sh->rbtree.sentinel;
  757. while (node != sentinel) {
  758. if (node_key < node->key) {
  759. node = node->left;
  760. continue;
  761. }
  762. if (node_key > node->key) {
  763. node = node->right;
  764. continue;
  765. }
  766. /* node_key == node->key */
  767. fcn = (ngx_http_file_cache_node_t *) node;
  768. rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
  769. NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
  770. if (rc == 0) {
  771. return fcn;
  772. }
  773. node = (rc < 0) ? node->left : node->right;
  774. }
  775. /* not found */
  776. return NULL;
  777. }
  778. static void
  779. ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  780. ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  781. {
  782. ngx_rbtree_node_t **p;
  783. ngx_http_file_cache_node_t *cn, *cnt;
  784. for ( ;; ) {
  785. if (node->key < temp->key) {
  786. p = &temp->left;
  787. } else if (node->key > temp->key) {
  788. p = &temp->right;
  789. } else { /* node->key == temp->key */
  790. cn = (ngx_http_file_cache_node_t *) node;
  791. cnt = (ngx_http_file_cache_node_t *) temp;
  792. p = (ngx_memcmp(cn->key, cnt->key,
  793. NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
  794. < 0)
  795. ? &temp->left : &temp->right;
  796. }
  797. if (*p == sentinel) {
  798. break;
  799. }
  800. temp = *p;
  801. }
  802. *p = node;
  803. node->parent = temp;
  804. node->left = sentinel;
  805. node->right = sentinel;
  806. ngx_rbt_red(node);
  807. }
  808. static void
  809. ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len,
  810. u_char *hash)
  811. {
  812. u_char *p, *last;
  813. ngx_str_t name;
  814. ngx_md5_t md5;
  815. u_char buf[NGX_HTTP_CACHE_VARY_LEN];
  816. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  817. "http file cache vary: \"%*s\"", len, vary);
  818. ngx_md5_init(&md5);
  819. ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN);
  820. ngx_strlow(buf, vary, len);
  821. p = buf;
  822. last = buf + len;
  823. while (p < last) {
  824. while (p < last && (*p == ' ' || *p == ',')) { p++; }
  825. name.data = p;
  826. while (p < last && *p != ',' && *p != ' ') { p++; }
  827. name.len = p - name.data;
  828. if (name.len == 0) {
  829. break;
  830. }
  831. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  832. "http file cache vary: %V", &name);
  833. ngx_md5_update(&md5, name.data, name.len);
  834. ngx_md5_update(&md5, (u_char *) ":", sizeof(":") - 1);
  835. ngx_http_file_cache_vary_header(r, &md5, &name);
  836. ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1);
  837. }
  838. ngx_md5_final(hash, &md5);
  839. }
  840. static void
  841. ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5,
  842. ngx_str_t *name)
  843. {
  844. size_t len;
  845. u_char *p, *start, *last;
  846. ngx_uint_t i, multiple, normalize;
  847. ngx_list_part_t *part;
  848. ngx_table_elt_t *header;
  849. multiple = 0;
  850. normalize = 0;
  851. if (name->len == sizeof("Accept-Charset") - 1
  852. && ngx_strncasecmp(name->data, (u_char *) "Accept-Charset",
  853. sizeof("Accept-Charset") - 1) == 0)
  854. {
  855. normalize = 1;
  856. } else if (name->len == sizeof("Accept-Encoding") - 1
  857. && ngx_strncasecmp(name->data, (u_char *) "Accept-Encoding",
  858. sizeof("Accept-Encoding") - 1) == 0)
  859. {
  860. normalize = 1;
  861. } else if (name->len == sizeof("Accept-Language") - 1
  862. && ngx_strncasecmp(name->data, (u_char *) "Accept-Language",
  863. sizeof("Accept-Language") - 1) == 0)
  864. {
  865. normalize = 1;
  866. }
  867. part = &r->headers_in.headers.part;
  868. header = part->elts;
  869. for (i = 0; /* void */ ; i++) {
  870. if (i >= part->nelts) {
  871. if (part->next == NULL) {
  872. break;
  873. }
  874. part = part->next;
  875. header = part->elts;
  876. i = 0;
  877. }
  878. if (header[i].hash == 0) {
  879. continue;
  880. }
  881. if (header[i].key.len != name->len) {
  882. continue;
  883. }
  884. if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) {
  885. continue;
  886. }
  887. if (!normalize) {
  888. if (multiple) {
  889. ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  890. }
  891. ngx_md5_update(md5, header[i].value.data, header[i].value.len);
  892. multiple = 1;
  893. continue;
  894. }
  895. /* normalize spaces */
  896. p = header[i].value.data;
  897. last = p + header[i].value.len;
  898. while (p < last) {
  899. while (p < last && (*p == ' ' || *p == ',')) { p++; }
  900. start = p;
  901. while (p < last && *p != ',' && *p != ' ') { p++; }
  902. len = p - start;
  903. if (len == 0) {
  904. break;
  905. }
  906. if (multiple) {
  907. ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  908. }
  909. ngx_md5_update(md5, start, len);
  910. multiple = 1;
  911. }
  912. }
  913. }
  914. static ngx_int_t
  915. ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)
  916. {
  917. ngx_http_file_cache_t *cache;
  918. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  919. "http file cache reopen");
  920. if (c->secondary) {
  921. ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  922. "cache file \"%s\" has incorrect vary hash",
  923. c->file.name.data);
  924. return NGX_DECLINED;
  925. }
  926. cache = c->file_cache;
  927. ngx_shmtx_lock(&cache->shpool->mutex);
  928. c->node->count--;
  929. c->node = NULL;
  930. ngx_shmtx_unlock(&cache->shpool->mutex);
  931. c->secondary = 1;
  932. c->file.name.len = 0;
  933. c->body_start = c->buffer_size;
  934. ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  935. return ngx_http_file_cache_open(r);
  936. }
  937. ngx_int_t
  938. ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
  939. {
  940. ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf;
  941. u_char *p;
  942. ngx_str_t *key;
  943. ngx_uint_t i;
  944. ngx_http_cache_t *c;
  945. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  946. "http file cache set header");
  947. c = r->cache;
  948. ngx_memzero(h, sizeof(ngx_http_file_cache_header_t));
  949. h->version = NGX_HTTP_CACHE_VERSION;
  950. h->valid_sec = c->valid_sec;
  951. h->updating_sec = c->updating_sec;
  952. h->error_sec = c->error_sec;
  953. h->last_modified = c->last_modified;
  954. h->date = c->date;
  955. h->crc32 = c->crc32;
  956. h->valid_msec = (u_short) c->valid_msec;
  957. h->header_start = (u_short) c->header_start;
  958. h->body_start = (u_short) c->body_start;
  959. if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  960. h->etag_len = (u_char) c->etag.len;
  961. ngx_memcpy(h->etag, c->etag.data, c->etag.len);
  962. }
  963. if (c->vary.len) {
  964. if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  965. /* should not happen */
  966. c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  967. }
  968. h->vary_len = (u_char) c->vary.len;
  969. ngx_memcpy(h->vary, c->vary.data, c->vary.len);
  970. ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  971. ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  972. }
  973. if (ngx_http_file_cache_update_variant(r, c) != NGX_OK) {
  974. return NGX_ERROR;
  975. }
  976. p = buf + sizeof(ngx_http_file_cache_header_t);
  977. p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));
  978. key = c->keys.elts;
  979. for (i = 0; i < c->keys.nelts; i++) {
  980. p = ngx_copy(p, key[i].data, key[i].len);
  981. }
  982. *p = LF;
  983. return NGX_OK;
  984. }
  985. static ngx_int_t
  986. ngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c)
  987. {
  988. ngx_http_file_cache_t *cache;
  989. if (!c->secondary) {
  990. return NGX_OK;
  991. }
  992. if (c->vary.len
  993. && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) == 0)
  994. {
  995. return NGX_OK;
  996. }
  997. /*
  998. * if the variant hash doesn't match one we used as a secondary
  999. * cache key, switch back to the original key
  1000. */
  1001. cache = c->file_cache;
  1002. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1003. "http file cache main key");
  1004. ngx_shmtx_lock(&cache->shpool->mutex);
  1005. c->node->count--;
  1006. c->node->updating = 0;
  1007. c->node = NULL;
  1008. ngx_shmtx_unlock(&cache->shpool->mutex);
  1009. c->file.name.len = 0;
  1010. c->update_variant = 1;
  1011. ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN);
  1012. if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
  1013. return NGX_ERROR;
  1014. }
  1015. if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  1016. return NGX_ERROR;
  1017. }
  1018. return NGX_OK;
  1019. }
  1020. void
  1021. ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
  1022. {
  1023. off_t fs_size;
  1024. ngx_int_t rc;
  1025. ngx_file_uniq_t uniq;
  1026. ngx_file_info_t fi;
  1027. ngx_http_cache_t *c;
  1028. ngx_ext_rename_file_t ext;
  1029. ngx_http_file_cache_t *cache;
  1030. c = r->cache;
  1031. if (c->updated) {
  1032. return;
  1033. }
  1034. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1035. "http file cache update");
  1036. cache = c->file_cache;
  1037. c->updated = 1;
  1038. c->updating = 0;
  1039. uniq = 0;
  1040. fs_size = 0;
  1041. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1042. "http file cache rename: \"%s\" to \"%s\"",
  1043. tf->file.name.data, c->file.name.data);
  1044. ext.access = NGX_FILE_OWNER_ACCESS;
  1045. ext.path_access = NGX_FILE_OWNER_ACCESS;
  1046. ext.time = -1;
  1047. ext.create_path = 1;
  1048. ext.delete_file = 1;
  1049. ext.log = r->connection->log;
  1050. rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);
  1051. if (rc == NGX_OK) {
  1052. if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {
  1053. ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
  1054. ngx_fd_info_n " \"%s\" failed", tf->file.name.data);
  1055. rc = NGX_ERROR;
  1056. } else {
  1057. uniq = ngx_file_uniq(&fi);
  1058. fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
  1059. }
  1060. }
  1061. ngx_shmtx_lock(&cache->shpool->mutex);
  1062. c->node->count--;
  1063. c->node->error = 0;
  1064. c->node->uniq = uniq;
  1065. c->node->body_start = c->body_start;
  1066. cache->sh->size += fs_size - c->node->fs_size;
  1067. c->node->fs_size = fs_size;
  1068. if (rc == NGX_OK) {
  1069. c->node->exists = 1;
  1070. }
  1071. c->node->updating = 0;
  1072. ngx_shmtx_unlock(&cache->shpool->mutex);
  1073. }
  1074. void
  1075. ngx_http_file_cache_update_header(ngx_http_request_t *r)
  1076. {
  1077. ssize_t n;
  1078. ngx_err_t err;
  1079. ngx_file_t file;
  1080. ngx_file_info_t fi;
  1081. ngx_http_cache_t *c;
  1082. ngx_http_file_cache_header_t h;
  1083. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1084. "http file cache update header");
  1085. c = r->cache;
  1086. ngx_memzero(&file, sizeof(ngx_file_t));
  1087. file.name = c->file.name;
  1088. file.log = r->connection->log;
  1089. file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
  1090. if (file.fd == NGX_INVALID_FILE) {
  1091. err = ngx_errno;
  1092. /* cache file may have been deleted */
  1093. if (err == NGX_ENOENT) {
  1094. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1095. "http file cache \"%s\" not found",
  1096. file.name.data);
  1097. return;
  1098. }
  1099. ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
  1100. ngx_open_file_n " \"%s\" failed", file.name.data);
  1101. return;
  1102. }
  1103. /*
  1104. * make sure cache file wasn't replaced;
  1105. * if it was, do nothing
  1106. */
  1107. if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1108. ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
  1109. ngx_fd_info_n " \"%s\" failed", file.name.data);
  1110. goto done;
  1111. }
  1112. if (c->uniq != ngx_file_uniq(&fi)
  1113. || c->length != ngx_file_size(&fi))
  1114. {
  1115. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1116. "http file cache \"%s\" changed",
  1117. file.name.data);
  1118. goto done;
  1119. }
  1120. n = ngx_read_file(&file, (u_char *) &h,
  1121. sizeof(ngx_http_file_cache_header_t), 0);
  1122. if (n == NGX_ERROR) {
  1123. goto done;
  1124. }
  1125. if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {
  1126. ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  1127. ngx_read_file_n " read only %z of %z from \"%s\"",
  1128. n, sizeof(ngx_http_file_cache_header_t), file.name.data);
  1129. goto done;
  1130. }
  1131. if (h.version != NGX_HTTP_CACHE_VERSION
  1132. || h.last_modified != c->last_modified
  1133. || h.crc32 != c->crc32
  1134. || (size_t) h.header_start != c->header_start
  1135. || (size_t) h.body_start != c->body_start)
  1136. {
  1137. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1138. "http file cache \"%s\" content changed",
  1139. file.name.data);
  1140. goto done;
  1141. }
  1142. /*
  1143. * update cache file header with new data,
  1144. * notably h.valid_sec and h.date
  1145. */
  1146. ngx_memzero(&h, sizeof(ngx_http_file_cache_header_t));
  1147. h.version = NGX_HTTP_CACHE_VERSION;
  1148. h.valid_sec = c->valid_sec;
  1149. h.updating_sec = c->updating_sec;
  1150. h.error_sec = c->error_sec;
  1151. h.last_modified = c->last_modified;
  1152. h.date = c->date;
  1153. h.crc32 = c->crc32;
  1154. h.valid_msec = (u_short) c->valid_msec;
  1155. h.header_start = (u_short) c->header_start;
  1156. h.body_start = (u_short) c->body_start;
  1157. if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  1158. h.etag_len = (u_char) c->etag.len;
  1159. ngx_memcpy(h.etag, c->etag.data, c->etag.len);
  1160. }
  1161. if (c->vary.len) {
  1162. if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  1163. /* should not happen */
  1164. c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  1165. }
  1166. h.vary_len = (u_char) c->vary.len;
  1167. ngx_memcpy(h.vary, c->vary.data, c->vary.len);
  1168. ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  1169. ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  1170. }
  1171. (void) ngx_write_file(&file, (u_char *) &h,
  1172. sizeof(ngx_http_file_cache_header_t), 0);
  1173. done:
  1174. if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1175. ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
  1176. ngx_close_file_n " \"%s\" failed", file.name.data);
  1177. }
  1178. }
  1179. ngx_int_t
  1180. ngx_http_cache_send(ngx_http_request_t *r)
  1181. {
  1182. ngx_int_t rc;
  1183. ngx_buf_t *b;
  1184. ngx_chain_t out;
  1185. ngx_http_cache_t *c;
  1186. c = r->cache;
  1187. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1188. "http file cache send: %s", c->file.name.data);
  1189. /* we need to allocate all before the header would be sent */
  1190. b = ngx_calloc_buf(r->pool);
  1191. if (b == NULL) {
  1192. return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1193. }
  1194. b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
  1195. if (b->file == NULL) {
  1196. return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1197. }
  1198. rc = ngx_http_send_header(r);
  1199. if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
  1200. return rc;
  1201. }
  1202. b->file_pos = c->body_start;
  1203. b->file_last = c->length;
  1204. b->in_file = (c->length - c->body_start) ? 1 : 0;
  1205. b->last_buf = (r == r->main) ? 1 : 0;
  1206. b->last_in_chain = 1;
  1207. b->sync = (b->last_buf || b->in_file) ? 0 : 1;
  1208. b->file->fd = c->file.fd;
  1209. b->file->name = c->file.name;
  1210. b->file->log = r->connection->log;
  1211. out.buf = b;
  1212. out.next = NULL;
  1213. return ngx_http_output_filter(r, &out);
  1214. }
  1215. void
  1216. ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)
  1217. {
  1218. ngx_http_file_cache_t *cache;
  1219. ngx_http_file_cache_node_t *fcn;
  1220. if (c->updated || c->node == NULL) {
  1221. return;
  1222. }
  1223. cache = c->file_cache;
  1224. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1225. "http file cache free, fd: %d", c->file.fd);
  1226. ngx_shmtx_lock(&cache->shpool->mutex);
  1227. fcn = c->node;
  1228. fcn->count--;
  1229. if (c->updating && fcn->lock_time == c->lock_time) {
  1230. fcn->updating = 0;
  1231. }
  1232. if (c->error) {
  1233. fcn->error = c->error;
  1234. if (c->valid_sec) {
  1235. fcn->valid_sec = c->valid_sec;
  1236. fcn->valid_msec = c->valid_msec;
  1237. }
  1238. } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {
  1239. ngx_queue_remove(&fcn->queue);
  1240. ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1241. ngx_slab_free_locked(cache->shpool, fcn);
  1242. cache->sh->count--;
  1243. c->node = NULL;
  1244. }
  1245. ngx_shmtx_unlock(&cache->shpool->mutex);
  1246. c->updated = 1;
  1247. c->updating = 0;
  1248. if (c->temp_file) {
  1249. if (tf && tf->file.fd != NGX_INVALID_FILE) {
  1250. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1251. "http file cache incomplete: \"%s\"",
  1252. tf->file.name.data);
  1253. if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
  1254. ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,
  1255. ngx_delete_file_n " \"%s\" failed",
  1256. tf->file.name.data);
  1257. }
  1258. }
  1259. }
  1260. if (c->wait_event.timer_set) {
  1261. ngx_del_timer(&c->wait_event);
  1262. }
  1263. }
  1264. static void
  1265. ngx_http_file_cache_cleanup(void *data)
  1266. {
  1267. ngx_http_cache_t *c = data;
  1268. if (c->updated) {
  1269. return;
  1270. }
  1271. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1272. "http file cache cleanup");
  1273. if (c->updating && !c->background) {
  1274. ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,
  1275. "stalled cache updating, error:%ui", c->error);
  1276. }
  1277. ngx_http_file_cache_free(c, NULL);
  1278. }
  1279. static time_t
  1280. ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
  1281. {
  1282. u_char *name, *p;
  1283. size_t len;
  1284. time_t wait;
  1285. ngx_uint_t tries;
  1286. ngx_path_t *path;
  1287. ngx_queue_t *q, *sentinel;
  1288. ngx_http_file_cache_node_t *fcn;
  1289. u_char key[2 * NGX_HTTP_CACHE_KEY_LEN];
  1290. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1291. "http file cache forced expire");
  1292. path = cache->path;
  1293. len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
  1294. name = ngx_alloc(len + 1, ngx_cycle->log);
  1295. if (name == NULL) {
  1296. return 10;
  1297. }
  1298. ngx_memcpy(name, path->name.data, path->name.len);
  1299. wait = 10;
  1300. tries = 20;
  1301. sentinel = NULL;
  1302. ngx_shmtx_lock(&cache->shpool->mutex);
  1303. for ( ;; ) {
  1304. if (ngx_queue_empty(&cache->sh->queue)) {
  1305. break;
  1306. }
  1307. q = ngx_queue_last(&cache->sh->queue);
  1308. if (q == sentinel) {
  1309. break;
  1310. }
  1311. fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
  1312. ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1313. "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
  1314. fcn->count, fcn->exists,
  1315. fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
  1316. if (fcn->count == 0) {
  1317. ngx_http_file_cache_delete(cache, q, name);
  1318. wait = 0;
  1319. break;
  1320. }
  1321. if (fcn->deleting) {
  1322. wait = 1;
  1323. break;
  1324. }
  1325. p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
  1326. sizeof(ngx_rbtree_key_t));
  1327. len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1328. (void) ngx_hex_dump(p, fcn->key, len);
  1329. /*
  1330. * abnormally exited workers may leave locked cache entries,
  1331. * and although it may be safe to remove them completely,
  1332. * we prefer to just move them to the top of the inactive queue
  1333. */
  1334. ngx_queue_remove(q);
  1335. fcn->expire = ngx_time() + cache->inactive;
  1336. ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
  1337. ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1338. "ignore long locked inactive cache entry %*s, count:%d",
  1339. (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
  1340. if (sentinel == NULL) {
  1341. sentinel = q;
  1342. }
  1343. if (--tries) {
  1344. continue;
  1345. }
  1346. wait = 1;
  1347. break;
  1348. }
  1349. ngx_shmtx_unlock(&cache->shpool->mutex);
  1350. ngx_free(name);
  1351. return wait;
  1352. }
  1353. static time_t
  1354. ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
  1355. {
  1356. u_char *name, *p;
  1357. size_t len;
  1358. time_t now, wait;
  1359. ngx_path_t *path;
  1360. ngx_msec_t elapsed;
  1361. ngx_queue_t *q;
  1362. ngx_http_file_cache_node_t *fcn;
  1363. u_char key[2 * NGX_HTTP_CACHE_KEY_LEN];
  1364. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1365. "http file cache expire");
  1366. path = cache->path;
  1367. len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
  1368. name = ngx_alloc(len + 1, ngx_cycle->log);
  1369. if (name == NULL) {
  1370. return 10;
  1371. }
  1372. ngx_memcpy(name, path->name.data, path->name.len);
  1373. now = ngx_time();
  1374. ngx_shmtx_lock(&cache->shpool->mutex);
  1375. for ( ;; ) {
  1376. if (ngx_quit || ngx_terminate) {
  1377. wait = 1;
  1378. break;
  1379. }
  1380. if (ngx_queue_empty(&cache->sh->queue)) {
  1381. wait = 10;
  1382. break;
  1383. }
  1384. q = ngx_queue_last(&cache->sh->queue);
  1385. fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
  1386. wait = fcn->expire - now;
  1387. if (wait > 0) {
  1388. wait = wait > 10 ? 10 : wait;
  1389. break;
  1390. }
  1391. ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1392. "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
  1393. fcn->count, fcn->exists,
  1394. fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
  1395. if (fcn->count == 0) {
  1396. ngx_http_file_cache_delete(cache, q, name);
  1397. goto next;
  1398. }
  1399. if (fcn->deleting) {
  1400. wait = 1;
  1401. break;
  1402. }
  1403. p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
  1404. sizeof(ngx_rbtree_key_t));
  1405. len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1406. (void) ngx_hex_dump(p, fcn->key, len);
  1407. /*
  1408. * abnormally exited workers may leave locked cache entries,
  1409. * and although it may be safe to remove them completely,
  1410. * we prefer to just move them to the top of the inactive queue
  1411. */
  1412. ngx_queue_remove(q);
  1413. fcn->expire = ngx_time() + cache->inactive;
  1414. ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
  1415. ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1416. "ignore long locked inactive cache entry %*s, count:%d",
  1417. (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
  1418. next:
  1419. if (++cache->files >= cache->manager_files) {
  1420. wait = 0;
  1421. break;
  1422. }
  1423. ngx_time_update();
  1424. elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
  1425. if (elapsed >= cache->manager_threshold) {
  1426. wait = 0;
  1427. break;
  1428. }
  1429. }
  1430. ngx_shmtx_unlock(&cache->shpool->mutex);
  1431. ngx_free(name);
  1432. return wait;
  1433. }
  1434. static void
  1435. ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
  1436. u_char *name)
  1437. {
  1438. u_char *p;
  1439. size_t len;
  1440. ngx_path_t *path;
  1441. ngx_http_file_cache_node_t *fcn;
  1442. fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
  1443. if (fcn->exists) {
  1444. cache->sh->size -= fcn->fs_size;
  1445. path = cache->path;
  1446. p = name + path->name.len + 1 + path->len;
  1447. p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
  1448. sizeof(ngx_rbtree_key_t));
  1449. len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1450. p = ngx_hex_dump(p, fcn->key, len);
  1451. *p = '\0';
  1452. fcn->count++;
  1453. fcn->deleting = 1;
  1454. ngx_shmtx_unlock(&cache->shpool->mutex);
  1455. len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
  1456. ngx_create_hashed_filename(path, name, len);
  1457. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1458. "http file cache expire: \"%s\"", name);
  1459. if (ngx_delete_file(name) == NGX_FILE_ERROR) {
  1460. ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
  1461. ngx_delete_file_n " \"%s\" failed", name);
  1462. }
  1463. ngx_shmtx_lock(&cache->shpool->mutex);
  1464. fcn->count--;
  1465. fcn->deleting = 0;
  1466. }
  1467. if (fcn->count == 0) {
  1468. ngx_queue_remove(q);
  1469. ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1470. ngx_slab_free_locked(cache->shpool, fcn);
  1471. cache->sh->count--;
  1472. }
  1473. }
  1474. static ngx_msec_t
  1475. ngx_http_file_cache_manager(void *data)
  1476. {
  1477. ngx_http_file_cache_t *cache = data;
  1478. off_t size, free;
  1479. time_t wait;
  1480. ngx_msec_t elapsed, next;
  1481. ngx_uint_t count, watermark;
  1482. cache->last = ngx_current_msec;
  1483. cache->files = 0;
  1484. next = (ngx_msec_t) ngx_http_file_cache_expire(cache) * 1000;
  1485. if (next == 0) {
  1486. next = cache->manager_sleep;
  1487. goto done;
  1488. }
  1489. for ( ;; ) {
  1490. ngx_shmtx_lock(&cache->shpool->mutex);
  1491. size = cache->sh->size;
  1492. count = cache->sh->count;
  1493. watermark = cache->sh->watermark;
  1494. ngx_shmtx_unlock(&cache->shpool->mutex);
  1495. ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1496. "http file cache size: %O c:%ui w:%i",
  1497. size, count, (ngx_int_t) watermark);
  1498. if (size < cache->max_size && count < watermark) {
  1499. if (!cache->min_free) {
  1500. break;
  1501. }
  1502. free = ngx_fs_available(cache->path->name.data);
  1503. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1504. "http file cache free: %O", free);
  1505. if (free > cache->min_free) {
  1506. break;
  1507. }
  1508. }
  1509. wait = ngx_http_file_cache_forced_expire(cache);
  1510. if (wait > 0) {
  1511. next = (ngx_msec_t) wait * 1000;
  1512. break;
  1513. }
  1514. if (ngx_quit || ngx_terminate) {
  1515. break;
  1516. }
  1517. if (++cache->files >= cache->manager_files) {
  1518. next = cache->manager_sleep;
  1519. break;
  1520. }
  1521. ngx_time_update();
  1522. elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
  1523. if (elapsed >= cache->manager_threshold) {
  1524. next = cache->manager_sleep;
  1525. break;
  1526. }
  1527. }
  1528. done:
  1529. elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
  1530. ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1531. "http file cache manager: %ui e:%M n:%M",
  1532. cache->files, elapsed, next);
  1533. return next;
  1534. }
  1535. static void
  1536. ngx_http_file_cache_loader(void *data)
  1537. {
  1538. ngx_http_file_cache_t *cache = data;
  1539. ngx_tree_ctx_t tree;
  1540. if (!cache->sh->cold || cache->sh->loading) {
  1541. return;
  1542. }
  1543. if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
  1544. return;
  1545. }
  1546. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1547. "http file cache loader");
  1548. tree.init_handler = NULL;
  1549. tree.file_handler = ngx_http_file_cache_manage_file;
  1550. tree.pre_tree_handler = ngx_http_file_cache_manage_directory;
  1551. tree.post_tree_handler = ngx_http_file_cache_noop;
  1552. tree.spec_handler = ngx_http_file_cache_delete_file;
  1553. tree.data = cache;
  1554. tree.alloc = 0;
  1555. tree.log = ngx_cycle->log;
  1556. cache->last = ngx_current_msec;
  1557. cache->files = 0;
  1558. if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
  1559. cache->sh->loading = 0;
  1560. return;
  1561. }
  1562. cache->sh->cold = 0;
  1563. cache->sh->loading = 0;
  1564. ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
  1565. "http file cache: %V %.3fM, bsize: %uz",
  1566. &cache->path->name,
  1567. ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
  1568. cache->bsize);
  1569. }
  1570. static ngx_int_t
  1571. ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1572. {
  1573. return NGX_OK;
  1574. }
  1575. static ngx_int_t
  1576. ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1577. {
  1578. ngx_msec_t elapsed;
  1579. ngx_http_file_cache_t *cache;
  1580. cache = ctx->data;
  1581. if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
  1582. (void) ngx_http_file_cache_delete_file(ctx, path);
  1583. }
  1584. if (++cache->files >= cache->loader_files) {
  1585. ngx_http_file_cache_loader_sleep(cache);
  1586. } else {
  1587. ngx_time_update();
  1588. elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
  1589. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1590. "http file cache loader time elapsed: %M", elapsed);
  1591. if (elapsed >= cache->loader_threshold) {
  1592. ngx_http_file_cache_loader_sleep(cache);
  1593. }
  1594. }
  1595. return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
  1596. }
  1597. static ngx_int_t
  1598. ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1599. {
  1600. if (path->len >= 5
  1601. && ngx_strncmp(path->data + path->len - 5, "/temp", 5) == 0)
  1602. {
  1603. return NGX_DECLINED;
  1604. }
  1605. return NGX_OK;
  1606. }
  1607. static void
  1608. ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
  1609. {
  1610. ngx_msleep(cache->loader_sleep);
  1611. ngx_time_update();
  1612. cache->last = ngx_current_msec;
  1613. cache->files = 0;
  1614. }
  1615. static ngx_int_t
  1616. ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
  1617. {
  1618. u_char *p;
  1619. ngx_int_t n;
  1620. ngx_uint_t i;
  1621. ngx_http_cache_t c;
  1622. ngx_http_file_cache_t *cache;
  1623. if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
  1624. return NGX_ERROR;
  1625. }
  1626. /*
  1627. * Temporary files in cache have a suffix consisting of a dot
  1628. * followed by 10 digits.
  1629. */
  1630. if (name->len >= 2 * NGX_HTTP_CACHE_KEY_LEN + 1 + 10
  1631. && name->data[name->len - 10 - 1] == '.')
  1632. {
  1633. return NGX_OK;
  1634. }
  1635. if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
  1636. ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
  1637. "cache file \"%s\" is too small", name->data);
  1638. return NGX_ERROR;
  1639. }
  1640. ngx_memzero(&c, sizeof(ngx_http_cache_t));
  1641. cache = ctx->data;
  1642. c.length = ctx->size;
  1643. c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;
  1644. p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
  1645. for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
  1646. n = ngx_hextoi(p, 2);
  1647. if (n == NGX_ERROR) {
  1648. return NGX_ERROR;
  1649. }
  1650. p += 2;
  1651. c.key[i] = (u_char) n;
  1652. }
  1653. return ngx_http_file_cache_add(cache, &c);
  1654. }
  1655. static ngx_int_t
  1656. ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  1657. {
  1658. ngx_http_file_cache_node_t *fcn;
  1659. ngx_shmtx_lock(&cache->shpool->mutex);
  1660. fcn = ngx_http_file_cache_lookup(cache, c->key);
  1661. if (fcn == NULL) {
  1662. fcn = ngx_slab_calloc_locked(cache->shpool,
  1663. sizeof(ngx_http_file_cache_node_t));
  1664. if (fcn == NULL) {
  1665. ngx_http_file_cache_set_watermark(cache);
  1666. if (cache->fail_time != ngx_time()) {
  1667. cache->fail_time = ngx_time();
  1668. ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1669. "could not allocate node%s", cache->shpool->log_ctx);
  1670. }
  1671. ngx_shmtx_unlock(&cache->shpool->mutex);
  1672. return NGX_ERROR;
  1673. }
  1674. cache->sh->count++;
  1675. ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
  1676. ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
  1677. NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
  1678. ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
  1679. fcn->uses = 1;
  1680. fcn->exists = 1;
  1681. fcn->fs_size = c->fs_size;
  1682. cache->sh->size += c->fs_size;
  1683. } else {
  1684. ngx_queue_remove(&fcn->queue);
  1685. }
  1686. fcn->expire = ngx_time() + cache->inactive;
  1687. ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
  1688. ngx_shmtx_unlock(&cache->shpool->mutex);
  1689. return NGX_OK;
  1690. }
  1691. static ngx_int_t
  1692. ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1693. {
  1694. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  1695. "http file cache delete: \"%s\"", path->data);
  1696. if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
  1697. ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  1698. ngx_delete_file_n " \"%s\" failed", path->data);
  1699. }
  1700. return NGX_OK;
  1701. }
  1702. static void
  1703. ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache)
  1704. {
  1705. cache->sh->watermark = cache->sh->count - cache->sh->count / 8;
  1706. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1707. "http file cache watermark: %ui", cache->sh->watermark);
  1708. }
  1709. time_t
  1710. ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
  1711. {
  1712. ngx_uint_t i;
  1713. ngx_http_cache_valid_t *valid;
  1714. if (cache_valid == NULL) {
  1715. return 0;
  1716. }
  1717. valid = cache_valid->elts;
  1718. for (i = 0; i < cache_valid->nelts; i++) {
  1719. if (valid[i].status == 0) {
  1720. return valid[i].valid;
  1721. }
  1722. if (valid[i].status == status) {
  1723. return valid[i].valid;
  1724. }
  1725. }
  1726. return 0;
  1727. }
  1728. char *
  1729. ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1730. {
  1731. char *confp = conf;
  1732. off_t max_size, min_free;
  1733. u_char *last, *p;
  1734. time_t inactive;
  1735. ssize_t size;
  1736. ngx_str_t s, name, *value;
  1737. ngx_int_t loader_files, manager_files;
  1738. ngx_msec_t loader_sleep, manager_sleep, loader_threshold,
  1739. manager_threshold;
  1740. ngx_uint_t i, n, use_temp_path;
  1741. ngx_array_t *caches;
  1742. ngx_http_file_cache_t *cache, **ce;
  1743. cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
  1744. if (cache == NULL) {
  1745. return NGX_CONF_ERROR;
  1746. }
  1747. cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  1748. if (cache->path == NULL) {
  1749. return NGX_CONF_ERROR;
  1750. }
  1751. use_temp_path = 1;
  1752. inactive = 600;
  1753. loader_files = 100;
  1754. loader_sleep = 50;
  1755. loader_threshold = 200;
  1756. manager_files = 100;
  1757. manager_sleep = 50;
  1758. manager_threshold = 200;
  1759. name.len = 0;
  1760. size = 0;
  1761. max_size = NGX_MAX_OFF_T_VALUE;
  1762. min_free = 0;
  1763. value = cf->args->elts;
  1764. cache->path->name = value[1];
  1765. if (cache->path->name.data[cache->path->name.len - 1] == '/') {
  1766. cache->path->name.len--;
  1767. }
  1768. if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
  1769. return NGX_CONF_ERROR;
  1770. }
  1771. for (i = 2; i < cf->args->nelts; i++) {
  1772. if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {
  1773. p = value[i].data + 7;
  1774. last = value[i].data + value[i].len;
  1775. for (n = 0; n < NGX_MAX_PATH_LEVEL && p < last; n++) {
  1776. if (*p > '0' && *p < '3') {
  1777. cache->path->level[n] = *p++ - '0';
  1778. cache->path->len += cache->path->level[n] + 1;
  1779. if (p == last) {
  1780. break;
  1781. }
  1782. if (*p++ == ':' && n < NGX_MAX_PATH_LEVEL - 1 && p < last) {
  1783. continue;
  1784. }
  1785. goto invalid_levels;
  1786. }
  1787. goto invalid_levels;
  1788. }
  1789. if (cache->path->len < 10 + NGX_MAX_PATH_LEVEL) {
  1790. continue;
  1791. }
  1792. invalid_levels:
  1793. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1794. "invalid \"levels\" \"%V\"", &value[i]);
  1795. return NGX_CONF_ERROR;
  1796. }
  1797. if (ngx_strncmp(value[i].data, "use_temp_path=", 14) == 0) {
  1798. if (ngx_strcmp(&value[i].data[14], "on") == 0) {
  1799. use_temp_path = 1;
  1800. } else if (ngx_strcmp(&value[i].data[14], "off") == 0) {
  1801. use_temp_path = 0;
  1802. } else {
  1803. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1804. "invalid use_temp_path value \"%V\", "
  1805. "it must be \"on\" or \"off\"",
  1806. &value[i]);
  1807. return NGX_CONF_ERROR;
  1808. }
  1809. continue;
  1810. }
  1811. if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {
  1812. name.data = value[i].data + 10;
  1813. p = (u_char *) ngx_strchr(name.data, ':');
  1814. if (p == NULL) {
  1815. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1816. "invalid keys zone size \"%V\"", &value[i]);
  1817. return NGX_CONF_ERROR;
  1818. }
  1819. name.len = p - name.data;
  1820. s.data = p + 1;
  1821. s.len = value[i].data + value[i].len - s.data;
  1822. size = ngx_parse_size(&s);
  1823. if (size == NGX_ERROR) {
  1824. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1825. "invalid keys zone size \"%V\"", &value[i]);
  1826. return NGX_CONF_ERROR;
  1827. }
  1828. if (size < (ssize_t) (2 * ngx_pagesize)) {
  1829. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1830. "keys zone \"%V\" is too small", &value[i]);
  1831. return NGX_CONF_ERROR;
  1832. }
  1833. continue;
  1834. }
  1835. if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
  1836. s.len = value[i].len - 9;
  1837. s.data = value[i].data + 9;
  1838. inactive = ngx_parse_time(&s, 1);
  1839. if (inactive == (time_t) NGX_ERROR) {
  1840. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1841. "invalid inactive value \"%V\"", &value[i]);
  1842. return NGX_CONF_ERROR;
  1843. }
  1844. continue;
  1845. }
  1846. if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
  1847. s.len = value[i].len - 9;
  1848. s.data = value[i].data + 9;
  1849. max_size = ngx_parse_offset(&s);
  1850. if (max_size < 0) {
  1851. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1852. "invalid max_size value \"%V\"", &value[i]);
  1853. return NGX_CONF_ERROR;
  1854. }
  1855. continue;
  1856. }
  1857. if (ngx_strncmp(value[i].data, "min_free=", 9) == 0) {
  1858. #if (NGX_WIN32 || NGX_HAVE_STATFS || NGX_HAVE_STATVFS)
  1859. s.len = value[i].len - 9;
  1860. s.data = value[i].data + 9;
  1861. min_free = ngx_parse_offset(&s);
  1862. if (min_free < 0) {
  1863. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1864. "invalid min_free value \"%V\"", &value[i]);
  1865. return NGX_CONF_ERROR;
  1866. }
  1867. #else
  1868. ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1869. "min_free is not supported "
  1870. "on this platform, ignored");
  1871. #endif
  1872. continue;
  1873. }
  1874. if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {
  1875. loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
  1876. if (loader_files == NGX_ERROR) {
  1877. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1878. "invalid loader_files value \"%V\"", &value[i]);
  1879. return NGX_CONF_ERROR;
  1880. }
  1881. continue;
  1882. }
  1883. if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) {
  1884. s.len = value[i].len - 13;
  1885. s.data = value[i].data + 13;
  1886. loader_sleep = ngx_parse_time(&s, 0);
  1887. if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
  1888. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1889. "invalid loader_sleep value \"%V\"", &value[i]);
  1890. return NGX_CONF_ERROR;
  1891. }
  1892. continue;
  1893. }
  1894. if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) {
  1895. s.len = value[i].len - 17;
  1896. s.data = value[i].data + 17;
  1897. loader_threshold = ngx_parse_time(&s, 0);
  1898. if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
  1899. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1900. "invalid loader_threshold value \"%V\"", &value[i]);
  1901. return NGX_CONF_ERROR;
  1902. }
  1903. continue;
  1904. }
  1905. if (ngx_strncmp(value[i].data, "manager_files=", 14) == 0) {
  1906. manager_files = ngx_atoi(value[i].data + 14, value[i].len - 14);
  1907. if (manager_files == NGX_ERROR) {
  1908. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1909. "invalid manager_files value \"%V\"", &value[i]);
  1910. return NGX_CONF_ERROR;
  1911. }
  1912. continue;
  1913. }
  1914. if (ngx_strncmp(value[i].data, "manager_sleep=", 14) == 0) {
  1915. s.len = value[i].len - 14;
  1916. s.data = value[i].data + 14;
  1917. manager_sleep = ngx_parse_time(&s, 0);
  1918. if (manager_sleep == (ngx_msec_t) NGX_ERROR) {
  1919. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1920. "invalid manager_sleep value \"%V\"", &value[i]);
  1921. return NGX_CONF_ERROR;
  1922. }
  1923. continue;
  1924. }
  1925. if (ngx_strncmp(value[i].data, "manager_threshold=", 18) == 0) {
  1926. s.len = value[i].len - 18;
  1927. s.data = value[i].data + 18;
  1928. manager_threshold = ngx_parse_time(&s, 0);
  1929. if (manager_threshold == (ngx_msec_t) NGX_ERROR) {
  1930. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1931. "invalid manager_threshold value \"%V\"", &value[i]);
  1932. return NGX_CONF_ERROR;
  1933. }
  1934. continue;
  1935. }
  1936. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1937. "invalid parameter \"%V\"", &value[i]);
  1938. return NGX_CONF_ERROR;
  1939. }
  1940. if (name.len == 0 || size == 0) {
  1941. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1942. "\"%V\" must have \"keys_zone\" parameter",
  1943. &cmd->name);
  1944. return NGX_CONF_ERROR;
  1945. }
  1946. cache->path->manager = ngx_http_file_cache_manager;
  1947. cache->path->loader = ngx_http_file_cache_loader;
  1948. cache->path->data = cache;
  1949. cache->path->conf_file = cf->conf_file->file.name.data;
  1950. cache->path->line = cf->conf_file->line;
  1951. cache->loader_files = loader_files;
  1952. cache->loader_sleep = loader_sleep;
  1953. cache->loader_threshold = loader_threshold;
  1954. cache->manager_files = manager_files;
  1955. cache->manager_sleep = manager_sleep;
  1956. cache->manager_threshold = manager_threshold;
  1957. if (ngx_add_path(cf, &cache->path) != NGX_OK) {
  1958. return NGX_CONF_ERROR;
  1959. }
  1960. cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
  1961. if (cache->shm_zone == NULL) {
  1962. return NGX_CONF_ERROR;
  1963. }
  1964. if (cache->shm_zone->data) {
  1965. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1966. "duplicate zone \"%V\"", &name);
  1967. return NGX_CONF_ERROR;
  1968. }
  1969. cache->shm_zone->init = ngx_http_file_cache_init;
  1970. cache->shm_zone->data = cache;
  1971. cache->use_temp_path = use_temp_path;
  1972. cache->inactive = inactive;
  1973. cache->max_size = max_size;
  1974. cache->min_free = min_free;
  1975. caches = (ngx_array_t *) (confp + cmd->offset);
  1976. ce = ngx_array_push(caches);
  1977. if (ce == NULL) {
  1978. return NGX_CONF_ERROR;
  1979. }
  1980. *ce = cache;
  1981. return NGX_CONF_OK;
  1982. }
  1983. char *
  1984. ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
  1985. void *conf)
  1986. {
  1987. char *p = conf;
  1988. time_t valid;
  1989. ngx_str_t *value;
  1990. ngx_int_t status;
  1991. ngx_uint_t i, n;
  1992. ngx_array_t **a;
  1993. ngx_http_cache_valid_t *v;
  1994. static ngx_uint_t statuses[] = { 200, 301, 302 };
  1995. a = (ngx_array_t **) (p + cmd->offset);
  1996. if (*a == NGX_CONF_UNSET_PTR) {
  1997. *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
  1998. if (*a == NULL) {
  1999. return NGX_CONF_ERROR;
  2000. }
  2001. }
  2002. value = cf->args->elts;
  2003. n = cf->args->nelts - 1;
  2004. valid = ngx_parse_time(&value[n], 1);
  2005. if (valid == (time_t) NGX_ERROR) {
  2006. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  2007. "invalid time value \"%V\"", &value[n]);
  2008. return NGX_CONF_ERROR;
  2009. }
  2010. if (n == 1) {
  2011. for (i = 0; i < 3; i++) {
  2012. v = ngx_array_push(*a);
  2013. if (v == NULL) {
  2014. return NGX_CONF_ERROR;
  2015. }
  2016. v->status = statuses[i];
  2017. v->valid = valid;
  2018. }
  2019. return NGX_CONF_OK;
  2020. }
  2021. for (i = 1; i < n; i++) {
  2022. if (ngx_strcmp(value[i].data, "any") == 0) {
  2023. status = 0;
  2024. } else {
  2025. status = ngx_atoi(value[i].data, value[i].len);
  2026. if (status < 100 || status > 599) {
  2027. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  2028. "invalid status \"%V\"", &value[i]);
  2029. return NGX_CONF_ERROR;
  2030. }
  2031. }
  2032. v = ngx_array_push(*a);
  2033. if (v == NULL) {
  2034. return NGX_CONF_ERROR;
  2035. }
  2036. v->status = status;
  2037. v->valid = valid;
  2038. }
  2039. return NGX_CONF_OK;
  2040. }