/** uses/modifies wrk->tmp_str */ static void try_append_file(liVRequest *vr, GString **curbuf, const gchar *filename, gboolean encode_html) { GString *f = vr->wrk->tmp_str; g_string_truncate(f, 0); g_string_append_len(f, GSTR_LEN(vr->physical.path)); li_path_append_slash(f); g_string_append(f, filename); if (!encode_html) { int fd; struct stat st; while (-1 == (fd = open(f->str, O_RDONLY))) { if (errno == EINTR) continue; return; /* failed to open, ignore */ } if (-1 == fstat(fd, &st)) { close(fd); return; /* failed to open, ignore */ } if (st.st_size > MAX_INCLUDE_FILE_SIZE) { close(fd); return; /* file too big, ignore */ } /* flush current buffer and append file */ li_chunkqueue_append_string(vr->direct_out, *curbuf); *curbuf = g_string_sized_new(4*1024-1); li_chunkqueue_append_file_fd(vr->direct_out, NULL, 0, st.st_size, fd); } else { GError *error = NULL; gchar *contents; gsize length; if (!g_file_get_contents(f->str, &contents, &length, &error)) { g_error_free(error); return; /* ignore errors */ } if (length > MAX_INCLUDE_FILE_SIZE) { g_free(contents); return; /* file too big, ignore */ } g_string_append_len(*curbuf, CONST_STR_LEN("<pre>")); li_string_encode_append(contents, *curbuf, LI_ENCODING_HTML); g_string_append_len(*curbuf, CONST_STR_LEN("</pre>")); g_free(contents); } }
static liHandlerResult cache_etag_filter_hit(liVRequest *vr, liFilter *f) { cache_etag_file *cfile = (cache_etag_file*) f->param; UNUSED(vr); if (!cfile) return LI_HANDLER_GO_ON; if (!f->out->is_closed) li_chunkqueue_append_file_fd(f->out, NULL, 0, cfile->hit_length, cfile->hit_fd); cfile->hit_fd = -1; cache_etag_file_free(cfile); f->param = NULL; f->out->is_closed = TRUE; return LI_HANDLER_GO_ON; }
static int _lua_chunkqueue_add_file(lua_State *L, gboolean tempfile) { liChunkQueue *cq; const char *filename; GString g_filename; size_t len; struct stat st; int fd, err; goffset start, length; luaL_checkany(L, 2); cq = li_lua_get_chunkqueue(L, 1); if (cq == NULL) return 0; if (!lua_isstring(L, 2)) { lua_pushliteral(L, "chunkqueue:add expects filename as first parameter"); lua_error(L); return -1; } filename = lua_tolstring(L, 2, &len); g_filename = li_const_gstring(filename, len); if (LI_HANDLER_GO_ON != li_stat_cache_get_sync(NULL, &g_filename, &st, &err, &fd)) { lua_pushliteral(L, "chunkqueue:add couldn't open file: "); lua_pushvalue(L, 2); lua_concat(L, 2); lua_error(L); return -1; } start = 0; length = st.st_size; if (lua_gettop(L) >= 3) { if (!lua_isnumber(L, 3)) { lua_pushliteral(L, "chunkqueue:add expects number (or nothing) as second parameter"); lua_error(L); close(fd); return -1; } start = lua_tonumber(L, 3); } if (lua_gettop(L) >= 4) { if (!lua_isnumber(L, 4)) { lua_pushliteral(L, "chunkqueue:add expects number (or nothing) as third parameter"); lua_error(L); close(fd); return -1; } length = lua_tonumber(L, 3); } if (start < 0 || start >= st.st_size || length < 0 || start + length > st.st_size) { lua_pushliteral(L, "chunkqueue:add: Invalid start/length values"); lua_error(L); close(fd); return -1; } if (tempfile) { li_chunkqueue_append_tempfile_fd(cq, g_string_new_len(filename, len), start, length, fd); } else { li_chunkqueue_append_file_fd(cq, NULL, start, length, fd); } return 0; }
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; }