summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2021-05-03 22:18:20 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2021-05-06 17:35:00 -0400
commit9a5e1652bef3d392b0b698eda1dafbfa771a1f92 (patch)
tree772aa94b2ea9c6f51caa18640d7d8ee5ff252b83
parent7ff6adc44c498f465cf247da03a44a6717fc1502 (diff)
downloadlighttpd-git-9a5e1652bef3d392b0b698eda1dafbfa771a1f92.tar.gz
[multiple] static file optimization; reuse cache
reuse cache lookup in common case of serving a static file rather than repeating the stat_cache_entry lookup (which is more work than memcmp() to re-check stat_cache_entry match)
-rw-r--r--src/http-header-glue.c40
-rw-r--r--src/mod_cgi.c10
-rw-r--r--src/mod_cml.c3
-rw-r--r--src/mod_dirlisting.c8
-rw-r--r--src/mod_ssi.c3
-rw-r--r--src/mod_staticfile.c14
-rw-r--r--src/request.h3
-rw-r--r--src/response.c30
-rw-r--r--src/response.h4
9 files changed, 77 insertions, 38 deletions
diff --git a/src/http-header-glue.c b/src/http-header-glue.c
index 5d197cdc..1eece1b5 100644
--- a/src/http-header-glue.c
+++ b/src/http-header-glue.c
@@ -305,16 +305,24 @@ handler_t http_response_reqbody_read_error (request_st * const r, int http_statu
}
-void http_response_send_file (request_st * const r, buffer * const path) {
- stat_cache_entry * const sce = stat_cache_get_entry_open(path, r->conf.follow_symlink);
- const buffer *mtime = NULL;
- int allow_caching = (0 == r->http_status || 200 == r->http_status);
-
- if (NULL == sce) {
- r->http_status = (errno == ENOENT) ? 404 : 403;
- log_error(r->conf.errh, __FILE__, __LINE__,
- "not a regular file: %s -> %s", r->uri.path.ptr, path->ptr);
- return;
+void http_response_send_file (request_st * const r, buffer * const path, stat_cache_entry *sce) {
+ if (NULL == sce
+ || (sce->fd < 0 && 0 != sce->st.st_size)) {
+ sce = stat_cache_get_entry_open(path, r->conf.follow_symlink);
+ if (NULL == sce) {
+ r->http_status = (errno == ENOENT) ? 404 : 403;
+ log_error(r->conf.errh, __FILE__, __LINE__,
+ "not a regular file: %s -> %s", r->uri.path.ptr, path->ptr);
+ return;
+ }
+ if (sce->fd < 0 && 0 != sce->st.st_size) {
+ r->http_status = (errno == ENOENT) ? 404 : 403;
+ if (r->conf.log_request_handling) {
+ log_perror(r->conf.errh, __FILE__, __LINE__,
+ "file open failed: %s", path->ptr);
+ }
+ return;
+ }
}
if (!r->conf.follow_symlink
@@ -340,14 +348,7 @@ void http_response_send_file (request_st * const r, buffer * const path) {
return;
}
- if (sce->fd < 0 && 0 != sce->st.st_size) {
- r->http_status = (errno == ENOENT) ? 404 : 403;
- if (r->conf.log_request_handling) {
- log_perror(r->conf.errh, __FILE__, __LINE__,
- "file open failed: %s", path->ptr);
- }
- return;
- }
+ int allow_caching = (0 == r->http_status || 200 == r->http_status);
/* set response content-type, if not set already */
@@ -386,6 +387,7 @@ void http_response_send_file (request_st * const r, buffer * const path) {
}
/* prepare header */
+ const buffer *mtime;
mtime = http_header_response_get(r, HTTP_HEADER_LAST_MODIFIED,
CONST_STR_LEN("Last-Modified"));
if (NULL == mtime) {
@@ -484,7 +486,7 @@ static void http_response_xsendfile (request_st * const r, buffer * const path,
}
}
- if (valid) http_response_send_file(r, path);
+ if (valid) http_response_send_file(r, path, NULL);
if (r->http_status >= 400 && status < 300) {
r->handler_module = NULL;
diff --git a/src/mod_cgi.c b/src/mod_cgi.c
index e2f863a5..18a136ef 100644
--- a/src/mod_cgi.c
+++ b/src/mod_cgi.c
@@ -747,7 +747,8 @@ URIHANDLER_FUNC(cgi_is_handled) {
data_string *ds;
if (NULL != r->handler_module) return HANDLER_GO_ON;
- if (buffer_is_empty(&r->physical.path)) return HANDLER_GO_ON;
+ /* r->physical.path is non-empty for handle_subrequest_start */
+ /*if (buffer_string_is_empty(&r->physical.path)) return HANDLER_GO_ON;*/
mod_cgi_patch_config(r, p);
if (NULL == p->conf.cgi) return HANDLER_GO_ON;
@@ -755,7 +756,12 @@ URIHANDLER_FUNC(cgi_is_handled) {
ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path);
if (NULL == ds) return HANDLER_GO_ON;
- st = stat_cache_path_stat(&r->physical.path);
+ /* r->tmp_sce is set in http_response_physical_path_check() and is valid
+ * in handle_subrequest_start callback -- handle_subrequest_start callbacks
+ * should not change r->physical.path (or should invalidate r->tmp_sce) */
+ st = r->tmp_sce && buffer_is_equal(&r->tmp_sce->name, &r->physical.path)
+ ? &r->tmp_sce->st
+ : stat_cache_path_stat(&r->physical.path);
if (NULL == st) return HANDLER_GO_ON;
/* (aside: CGI might be executable even if it is not readable) */
diff --git a/src/mod_cml.c b/src/mod_cml.c
index 566a8a6a..49a8ea1e 100644
--- a/src/mod_cml.c
+++ b/src/mod_cml.c
@@ -253,7 +253,8 @@ URIHANDLER_FUNC(mod_cml_power_magnet) {
URIHANDLER_FUNC(mod_cml_is_handled) {
plugin_data *p = p_d;
- if (buffer_string_is_empty(&r->physical.path)) return HANDLER_ERROR;
+ /* r->physical.path is non-empty for handle_subrequest_start */
+ /*if (buffer_string_is_empty(&r->physical.path)) return HANDLER_ERROR;*/
mod_cml_patch_config(r, p);
diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c
index de6b9c0a..96d019ee 100644
--- a/src/mod_dirlisting.c
+++ b/src/mod_dirlisting.c
@@ -1226,7 +1226,8 @@ URIHANDLER_FUNC(mod_dirlisting_subrequest_start) {
if (NULL != r->handler_module) return HANDLER_GO_ON;
if (!buffer_has_slash_suffix(&r->uri.path)) return HANDLER_GO_ON;
if (!http_method_get_or_head(r->http_method)) return HANDLER_GO_ON;
- if (buffer_string_is_empty(&r->physical.path)) return HANDLER_GO_ON;
+ /* r->physical.path is non-empty for handle_subrequest_start */
+ /*if (buffer_string_is_empty(&r->physical.path)) return HANDLER_GO_ON;*/
mod_dirlisting_patch_config(r, p);
@@ -1239,6 +1240,10 @@ URIHANDLER_FUNC(mod_dirlisting_subrequest_start) {
"URI : %s", r->uri.path.ptr);
}
+ #if 0 /* redundant check; not necessary */
+ /* r->physical.path is a dir since it ends in slash, or else
+ * http_response_physical_path_check() would have redirected
+ * before calling handle_subrequest_start */
if (!stat_cache_path_isdir(&r->physical.path)) {
if (errno == ENOTDIR)
return HANDLER_GO_ON;
@@ -1246,6 +1251,7 @@ URIHANDLER_FUNC(mod_dirlisting_subrequest_start) {
r->http_status = 500;
return HANDLER_FINISHED;
}
+ #endif
if (p->conf.cache) {
handler_t rc = mod_dirlisting_cache_check(r, p);
diff --git a/src/mod_ssi.c b/src/mod_ssi.c
index 126509e5..85dca59c 100644
--- a/src/mod_ssi.c
+++ b/src/mod_ssi.c
@@ -1238,7 +1238,8 @@ URIHANDLER_FUNC(mod_ssi_physical_path) {
plugin_data *p = p_d;
if (NULL != r->handler_module) return HANDLER_GO_ON;
- if (buffer_is_empty(&r->physical.path)) return HANDLER_GO_ON;
+ /* r->physical.path is non-empty for handle_subrequest_start */
+ /*if (buffer_string_is_empty(&r->physical.path)) return HANDLER_GO_ON;*/
mod_ssi_patch_config(r, p);
if (NULL == p->conf.ssi_extension) return HANDLER_GO_ON;
diff --git a/src/mod_staticfile.c b/src/mod_staticfile.c
index 036fac64..3dd5e5ab 100644
--- a/src/mod_staticfile.c
+++ b/src/mod_staticfile.c
@@ -7,6 +7,7 @@
#include "plugin.h"
#include "response.h"
+#include "stat_cache.h"
#include <stdlib.h>
#include <string.h>
@@ -99,10 +100,10 @@ SETDEFAULTS_FUNC(mod_staticfile_set_defaults) {
URIHANDLER_FUNC(mod_staticfile_subrequest) {
plugin_data * const p = p_d;
- if (r->http_status != 0) return HANDLER_GO_ON;
- if (buffer_is_empty(&r->physical.path)) return HANDLER_GO_ON;
if (NULL != r->handler_module) return HANDLER_GO_ON;
if (!http_method_get_head_post(r->http_method)) return HANDLER_GO_ON;
+ /* r->physical.path is non-empty for handle_subrequest_start */
+ /*if (buffer_string_is_empty(&r->physical.path)) return HANDLER_GO_ON;*/
mod_staticfile_patch_config(r, p);
@@ -127,7 +128,14 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) {
}
if (!p->conf.etags_used) r->conf.etag_flags = 0;
- http_response_send_file(r, &r->physical.path);
+
+ /* r->tmp_sce is set in http_response_physical_path_check() and is valid
+ * in handle_subrequest_start callback -- handle_subrequest_start callbacks
+ * should not change r->physical.path (or should invalidate r->tmp_sce) */
+ if (r->tmp_sce && !buffer_is_equal(&r->tmp_sce->name, &r->physical.path))
+ r->tmp_sce = NULL;
+
+ http_response_send_file(r, &r->physical.path, r->tmp_sce);
return HANDLER_FINISHED;
}
diff --git a/src/request.h b/src/request.h
index a3c891b0..0d20048e 100644
--- a/src/request.h
+++ b/src/request.h
@@ -14,6 +14,7 @@ struct log_error_st; /* declaration */
struct chunkqueue; /* declaration */
struct cond_cache_t; /* declaration */
struct cond_match_t; /* declaration */
+struct stat_cache_entry;/* declaration */
typedef struct {
unsigned int http_parseopts;
@@ -191,6 +192,8 @@ struct request_st {
struct chunkqueue write_queue; /* HTTP response queue [ file, mem ] */
struct chunkqueue read_queue; /* HTTP request queue [ mem ] */
struct chunkqueue reqbody_queue; /*(might use tempfiles)*/
+
+ struct stat_cache_entry *tmp_sce; /*(value valid only in sequential code)*/
};
diff --git a/src/response.c b/src/response.c
index c88ef9a8..2a557118 100644
--- a/src/response.c
+++ b/src/response.c
@@ -194,9 +194,9 @@ http_response_physical_path_error (request_st * const r, const int code, const c
static handler_t http_response_physical_path_check(request_st * const r) {
- const stat_cache_st *st = stat_cache_path_stat(&r->physical.path);
+ stat_cache_entry *sce = stat_cache_get_entry(&r->physical.path);
- if (st) {
+ if (__builtin_expect( (sce != NULL), 1)) {
/* file exists */
} else {
switch (errno) {
@@ -240,21 +240,23 @@ static handler_t http_response_physical_path_check(request_st * const r) {
/*(temporarily modify r->physical.path in-place)*/
r->physical.path.used = pathinfo - r->physical.path.ptr + 1;
*pathinfo = '\0';
- const stat_cache_st * const nst = stat_cache_path_stat(&r->physical.path);
+ stat_cache_entry * const nsce = stat_cache_get_entry(&r->physical.path);
*pathinfo = '/';
r->physical.path.used = pathused;
- if (NULL == nst) {
+ if (NULL == nsce) {
pathinfo = pathinfo != pprev ? pprev : NULL;
break;
}
- st = nst;
- if (!S_ISDIR(st->st_mode)) break;
+ sce = nsce;
+ if (!S_ISDIR(sce->st.st_mode)) break;
}
- if (NULL == pathinfo || !S_ISREG(st->st_mode)) {
+ if (NULL == pathinfo || !S_ISREG(sce->st.st_mode)) {
/* no it really doesn't exists */
return http_response_physical_path_error(r, 404, "-- file not found");
}
+ /* note: historical behavior checks S_ISREG() above, permitting
+ * path-info only on regular files, not dirs or special files */
/* we have a PATHINFO */
if (pathinfo) {
@@ -285,10 +287,16 @@ static handler_t http_response_physical_path_check(request_st * const r) {
return http_response_physical_path_error(r, 403, "-- access denied due to symlink restriction");
}
- if (S_ISREG(st->st_mode)) /*(common case)*/
+ /* r->tmp_sce is valid in handle_subrequest_start callback --
+ * handle_subrquest_start callbacks should not change r->physical.path
+ * (or should invalidate r->tmp_sce). r->tmp_sce is not reset between
+ * requests and is valid only for sequential code after this func succeeds*/
+ r->tmp_sce = sce;
+
+ if (S_ISREG(sce->st.st_mode)) /*(common case)*/
return HANDLER_GO_ON;
- if (S_ISDIR(st->st_mode)) {
+ if (S_ISDIR(sce->st.st_mode)) {
if (!buffer_has_slash_suffix(&r->uri.path)) {
http_response_redirect_to_directory(r, 301);
return HANDLER_FINISHED;
@@ -569,7 +577,7 @@ http_response_prepare (request_st * const r)
/*
* No module grabbed the request yet (like mod_access)
*
- * Go on and check of the file exists at all
+ * Go on and check if the file exists at all
*/
if (r->conf.log_request_handling) {
@@ -582,6 +590,8 @@ http_response_prepare (request_st * const r)
rc = http_response_physical_path_check(r);
if (HANDLER_GO_ON != rc) continue;
+ /* r->physical.path is non-empty and exists in the filesystem */
+
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__,
"-- handling subrequest");
diff --git a/src/response.h b/src/response.h
index 05a7a1fc..4b9943eb 100644
--- a/src/response.h
+++ b/src/response.h
@@ -8,6 +8,8 @@
#include "buffer.h"
#include "array.h"
+struct stat_cache_entry;/* declaration */
+
int http_response_parse(server *srv, request_st *r);
enum {
@@ -47,7 +49,7 @@ const buffer * http_response_set_last_modified(request_st *r, time_t lmtime);
int http_response_handle_cachable(request_st *r, const buffer *lmod, time_t lmtime);
void http_response_body_clear(request_st *r, int preserve_length);
void http_response_reset(request_st *r);
-void http_response_send_file (request_st *r, buffer *path);
+void http_response_send_file (request_st *r, buffer *path, struct stat_cache_entry *sce);
void http_response_backend_done (request_st *r);
void http_response_backend_error (request_st *r);
void http_response_upgrade_read_body_unknown(request_st *r);