h2o_filecache_ref_t *h2o_filecache_open_file(h2o_filecache_t *cache, const char *path, int oflag) { khiter_t iter = kh_get(opencache_set, cache->hash, path); h2o_filecache_ref_t *ref; int dummy; /* lookup cache, and return the one if found */ if (iter != kh_end(cache->hash)) { ref = H2O_STRUCT_FROM_MEMBER(h2o_filecache_ref_t, _path, kh_key(cache->hash, iter)); ++ref->_refcnt; goto Exit; } /* create a new cache entry */ ref = h2o_mem_alloc(offsetof(h2o_filecache_ref_t, _path) + strlen(path) + 1); ref->_refcnt = 1; ref->_lru = (h2o_linklist_t){NULL}; strcpy(ref->_path, path); /* if cache is used, then... */ if (cache->capacity != 0) { /* purge one entry from LRU if cache is full */ if (kh_size(cache->hash) == cache->capacity) { h2o_filecache_ref_t *purge_ref = H2O_STRUCT_FROM_MEMBER(h2o_filecache_ref_t, _lru, cache->lru.prev); khiter_t purge_iter = kh_get(opencache_set, cache->hash, purge_ref->_path); assert(purge_iter != kh_end(cache->hash)); release_from_cache(cache, purge_iter); } /* assign the new entry */ ++ref->_refcnt; kh_put(opencache_set, cache->hash, ref->_path, &dummy); h2o_linklist_insert(cache->lru.next, &ref->_lru); } /* open the file, or memoize the error */ if ((ref->fd = open(path, oflag)) != -1 && fstat(ref->fd, &ref->st) == 0) { ref->_last_modified.str[0] = '\0'; ref->_etag.len = 0; } else { ref->open_err = errno; if (ref->fd != -1) { close(ref->fd); ref->fd = -1; } } Exit: /* if the cache entry retains an error, return it instead of the reference */ if (ref->fd == -1) { errno = ref->open_err; h2o_filecache_close_file(ref); ref = NULL; } return ref; }
static struct st_h2o_sendfile_generator_t *create_generator(h2o_req_t *req, const char *path, size_t path_len, int *is_dir, int flags) { struct st_h2o_sendfile_generator_t *self; h2o_filecache_ref_t *fileref; h2o_iovec_t content_encoding; *is_dir = 0; if ((flags & H2O_FILE_FLAG_SEND_COMPRESSED) != 0 && req->version >= 0x101) { int compressible_types = h2o_get_compressible_types(&req->headers); if (compressible_types != 0) { char *variant_path = h2o_mem_alloc_pool(&req->pool, path_len + sizeof(".gz")); memcpy(variant_path, path, path_len); #define TRY_VARIANT(mask, enc, ext) \ if ((compressible_types & mask) != 0) { \ strcpy(variant_path + path_len, ext); \ if ((fileref = h2o_filecache_open_file(req->conn->ctx->filecache, variant_path, O_RDONLY | O_CLOEXEC)) != NULL) { \ content_encoding = h2o_iovec_init(enc, sizeof(enc) - 1); \ goto Opened; \ } \ } TRY_VARIANT(H2O_COMPRESSIBLE_BROTLI, "br", ".br"); TRY_VARIANT(H2O_COMPRESSIBLE_GZIP, "gzip", ".gz"); #undef TRY_VARIANT } } if ((fileref = h2o_filecache_open_file(req->conn->ctx->filecache, path, O_RDONLY | O_CLOEXEC)) == NULL) return NULL; content_encoding = (h2o_iovec_t){}; Opened: if (S_ISDIR(fileref->st.st_mode)) { h2o_filecache_close_file(fileref); *is_dir = 1; return NULL; } self = h2o_mem_alloc_pool(&req->pool, sizeof(*self)); self->super.proceed = do_proceed; self->super.stop = do_close; self->file.ref = fileref; self->file.off = 0; self->req = NULL; self->bytesleft = self->file.ref->st.st_size; self->ranged.range_count = 0; self->ranged.range_infos = NULL; self->content_encoding = content_encoding; self->send_vary = (flags & H2O_FILE_FLAG_SEND_COMPRESSED) != 0; self->send_etag = (flags & H2O_FILE_FLAG_NO_ETAG) == 0; return self; }
static inline void release_from_cache(h2o_filecache_t *cache, khiter_t iter) { const char *path = kh_key(cache->hash, iter); h2o_filecache_ref_t *ref = H2O_STRUCT_FROM_MEMBER(h2o_filecache_ref_t, _path, path); /* detach from list */ kh_del(opencache_set, cache->hash, iter); h2o_linklist_unlink(&ref->_lru); /* and close */ h2o_filecache_close_file(ref); }
static void do_close(h2o_generator_t *_self, h2o_req_t *req) { struct st_h2o_sendfile_generator_t *self = (void *)_self; h2o_filecache_close_file(self->file.ref); }