diff options
author | Glenn Strauss <gstrauss@gluelogic.com> | 2021-05-03 22:18:20 -0400 |
---|---|---|
committer | Glenn Strauss <gstrauss@gluelogic.com> | 2021-05-06 17:35:00 -0400 |
commit | 9a5e1652bef3d392b0b698eda1dafbfa771a1f92 (patch) | |
tree | 772aa94b2ea9c6f51caa18640d7d8ee5ff252b83 | |
parent | 7ff6adc44c498f465cf247da03a44a6717fc1502 (diff) | |
download | lighttpd-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.c | 40 | ||||
-rw-r--r-- | src/mod_cgi.c | 10 | ||||
-rw-r--r-- | src/mod_cml.c | 3 | ||||
-rw-r--r-- | src/mod_dirlisting.c | 8 | ||||
-rw-r--r-- | src/mod_ssi.c | 3 | ||||
-rw-r--r-- | src/mod_staticfile.c | 14 | ||||
-rw-r--r-- | src/request.h | 3 | ||||
-rw-r--r-- | src/response.c | 30 | ||||
-rw-r--r-- | src/response.h | 4 |
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); |