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