static liHandlerResult expire(liVRequest *vr, gpointer param, gpointer *context) { struct tm tm; time_t expire_date; guint len; gint max_age; GString *date_str = vr->wrk->tmp_str; expire_rule *rule = param; guint num = rule->num; time_t now = (time_t)li_cur_ts(vr->wrk); UNUSED(context); if (rule->base == EXPIRE_ACCESS) { expire_date = now + num; max_age = num; } else { /* modification */ struct stat st; gint err; if (!vr->physical.path->len) return LI_HANDLER_GO_ON; switch (li_stat_cache_get(vr, vr->physical.path, &st, &err, NULL)) { case LI_HANDLER_GO_ON: break; case LI_HANDLER_WAIT_FOR_EVENT: return LI_HANDLER_WAIT_FOR_EVENT; default: return LI_HANDLER_GO_ON; } expire_date = st.st_mtime + num; if (expire_date < now) expire_date = now; max_age = expire_date - now; } /* format date */ g_string_set_size(date_str, 255); if (!gmtime_r(&expire_date, &tm)) { VR_ERROR(vr, "gmtime_r(%"G_GUINT64_FORMAT") failed: %s", (guint64)expire_date, g_strerror(errno)); return LI_HANDLER_GO_ON; } len = strftime(date_str->str, date_str->allocated_len, "%a, %d %b %Y %H:%M:%S GMT", &tm); if (len == 0) return LI_HANDLER_GO_ON; g_string_set_size(date_str, len); /* finally set the headers */ li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Expires"), GSTR_LEN(date_str)); g_string_truncate(date_str, 0); g_string_append_len(date_str, CONST_STR_LEN("max-age=")); li_string_append_int(date_str, max_age); li_http_header_append(vr->response.headers, CONST_STR_LEN("Cache-Control"), GSTR_LEN(date_str)); return LI_HANDLER_GO_ON; }
/* st, res, errno, msg = vr:stat(filename) * st: stat data (nil if not available (yet)) * res: error code (HANDLE_GO_ON if successful) * errno: errno returned by stat() (only for HANDLER_ERROR) * msg: error message for errno */ static int lua_vrequest_stat(lua_State *L) { liVRequest *vr; GString path; const char *filename; size_t filename_len; liHandlerResult res; int err = 0; struct stat st; if (lua_gettop(L) != 2) { lua_pushstring(L, "vr:stat(filename): incorrect number of arguments"); lua_error(L); } vr = li_lua_get_vrequest(L, 1); if (!vr || !lua_isstring(L, 2)) { lua_pushstring(L, "vr:stat(filename): wrong argument types"); lua_error(L); } filename = lua_tolstring(L, 2, &filename_len); path = li_const_gstring(filename, filename_len); res = li_stat_cache_get(vr, &path, &st, &err, NULL); switch (res) { case LI_HANDLER_GO_ON: li_lua_push_stat(L, &st); lua_pushinteger(L, res); return 2; case LI_HANDLER_WAIT_FOR_EVENT: lua_pushnil(L); lua_pushinteger(L, res); return 2; case LI_HANDLER_ERROR: lua_pushnil(L); lua_pushinteger(L, res); lua_pushinteger(L, err); lua_pushstring(L, g_strerror(err)); return 4; case LI_HANDLER_COMEBACK: VR_ERROR(vr, "%s", "Unexpected return value from li_stat_cache_get: LI_HANDLER_COMEBACK"); lua_pushnil(L); lua_pushinteger(L, LI_HANDLER_ERROR); return 2; } return 0; }
static liHandlerResult lua_handle(liVRequest *vr, gpointer param, gpointer *context) { lua_config *conf = (lua_config*) param; lua_worker_config *wc; gboolean timeout = FALSE; liHandlerResult res; UNUSED(context); wc = &conf->worker_config[vr->wrk->ndx]; if (wc->act) timeout = (conf->ttl > 0 && wc->ts_loaded + conf->ttl < li_cur_ts(vr->wrk)); if (!wc->act || timeout) { int err; struct stat st; time_t last_load; res = li_stat_cache_get(vr, conf->filename, &st, &err, NULL); switch (res) { case LI_HANDLER_ERROR: VR_ERROR(vr, "lua.handler: couldn't stat file '%s': %s", conf->filename->str, g_strerror(err)); return LI_HANDLER_ERROR; case LI_HANDLER_WAIT_FOR_EVENT: return LI_HANDLER_WAIT_FOR_EVENT; default: break; } last_load = wc->ts_loaded; wc->ts_loaded = li_cur_ts(vr->wrk); if (timeout && st.st_mtime <= last_load) { goto loaded; } li_action_release(vr->wrk->srv, wc->act); wc->act = NULL; if (!li_config_lua_load(&vr->wrk->LL, vr->wrk->srv, vr->wrk, conf->filename->str, &wc->act, FALSE, conf->args) || !wc->act) { VR_ERROR(vr, "lua.handler: couldn't load '%s'", conf->filename->str); return LI_HANDLER_ERROR; } } loaded: li_action_enter(vr, wc->act); return LI_HANDLER_GO_ON; }
static liHandlerResult flv(liVRequest *vr, gpointer param, gpointer *context) { gchar *start; guint len; goffset pos; liHandlerResult res; gboolean cachable; struct stat st; int err; int fd = -1; UNUSED(context); UNUSED(param); if (li_vrequest_is_handled(vr)) return LI_HANDLER_GO_ON; res = li_stat_cache_get(vr, vr->physical.path, &st, &err, &fd); if (res == LI_HANDLER_WAIT_FOR_EVENT) return res; if (res == LI_HANDLER_ERROR) { /* open or fstat failed */ if (fd != -1) close(fd); if (!li_vrequest_handle_direct(vr)) return LI_HANDLER_ERROR; switch (err) { case ENOENT: case ENOTDIR: vr->response.http_status = 404; return LI_HANDLER_GO_ON; case EACCES: vr->response.http_status = 403; return LI_HANDLER_GO_ON; default: VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err)); return LI_HANDLER_ERROR; } } else if (S_ISDIR(st.st_mode)) { if (fd != -1) close(fd); return LI_HANDLER_GO_ON; } else if (!S_ISREG(st.st_mode)) { if (fd != -1) close(fd); if (!li_vrequest_handle_direct(vr)) return LI_HANDLER_ERROR; vr->response.http_status = 403; } else { liChunkFile *cf; #ifdef FD_CLOEXEC fcntl(fd, F_SETFD, FD_CLOEXEC); #endif if (!li_vrequest_handle_direct(vr)) { close(fd); return LI_HANDLER_ERROR; } if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("start"), &start, &len)) { guint i; pos = 0; for (i = 0; i < len; i++) { if (start[i] >= '0' && start[i] <= '9') { pos *= 10; pos += start[i] - '0'; } } } else { pos = 0; } li_etag_set_header(vr, &st, &cachable); if (cachable) { vr->response.http_status = 304; close(fd); return LI_HANDLER_GO_ON; } vr->response.http_status = 200; li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv")); if (pos < 0 || pos > st.st_size) pos = 0; if (pos != 0) li_chunkqueue_append_mem(vr->direct_out, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9")); cf = li_chunkfile_new(NULL, fd, FALSE); li_chunkqueue_append_chunkfile(vr->direct_out, cf, pos, st.st_size - pos); li_chunkfile_release(cf); } return LI_HANDLER_GO_ON; }
static liHandlerResult cache_etag_handle(liVRequest *vr, gpointer param, gpointer *context) { cache_etag_context *ctx = (cache_etag_context*) param; cache_etag_file *cfile = (cache_etag_file*) *context; GList *etag_entry; liHttpHeader *etag; struct stat st; GString *tmp_str = vr->wrk->tmp_str; liHandlerResult res; int err, fd; if (!cfile) { if (vr->request.http_method != LI_HTTP_METHOD_GET) return LI_HANDLER_GO_ON; LI_VREQUEST_WAIT_FOR_RESPONSE_HEADERS(vr); if (vr->response.http_status != 200) return LI_HANDLER_GO_ON; /* Don't cache static files if filter list is empty */ if (NULL == vr->filters_out_first && vr->backend_source->out->is_closed && 0 == vr->backend_source->out->mem_usage) return LI_HANDLER_GO_ON; etag_entry = li_http_header_find_first(vr->response.headers, CONST_STR_LEN("etag")); if (!etag_entry) return LI_HANDLER_GO_ON; /* no etag -> no caching */ if (li_http_header_find_next(etag_entry, CONST_STR_LEN("etag"))) { VR_ERROR(vr, "%s", "duplicate etag header in response, will not cache it"); return LI_HANDLER_GO_ON; } etag = (liHttpHeader*) etag_entry->data; cfile = cache_etag_file_create(createFileName(vr, ctx->path, etag)); *context = cfile; } res = li_stat_cache_get(vr, cfile->filename, &st, &err, &fd); if (res == LI_HANDLER_WAIT_FOR_EVENT) return res; if (res == LI_HANDLER_GO_ON) { liFilter *f; if (!S_ISREG(st.st_mode)) { VR_ERROR(vr, "Unexpected file type for cache file '%s' (mode %o)", cfile->filename->str, (unsigned int) st.st_mode); close(fd); return LI_HANDLER_GO_ON; /* no caching */ } cfile->hit_fd = fd; #ifdef FD_CLOEXEC fcntl(cfile->hit_fd, F_SETFD, FD_CLOEXEC); #endif if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "cache hit for '%s'", vr->request.uri.path->str); } cfile->hit_length = st.st_size; g_string_truncate(tmp_str, 0); li_string_append_int(tmp_str, st.st_size); li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(tmp_str)); f = li_vrequest_add_filter_out(vr, cache_etag_filter_hit, NULL, NULL, NULL); if (NULL != f) { li_chunkqueue_append_file_fd(f->out, NULL, 0, cfile->hit_length, cfile->hit_fd); f->out->is_closed = TRUE; cfile->hit_fd = -1; } cache_etag_file_free(cfile); *context = NULL; return LI_HANDLER_GO_ON; } if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "cache miss for '%s'", vr->request.uri.path->str); } if (!cache_etag_file_start(vr, cfile)) { cache_etag_file_free(cfile); *context = NULL; return LI_HANDLER_GO_ON; /* no caching */ } li_vrequest_add_filter_out(vr, cache_etag_filter_miss, cache_etag_filter_free, NULL, cfile); *context = NULL; return LI_HANDLER_GO_ON; }