Browse Source

[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)
master
Glenn Strauss 7 months ago
parent
commit
9a5e1652be
  1. 40
      src/http-header-glue.c
  2. 10
      src/mod_cgi.c
  3. 3
      src/mod_cml.c
  4. 8
      src/mod_dirlisting.c
  5. 3
      src/mod_ssi.c
  6. 14
      src/mod_staticfile.c
  7. 3
      src/request.h
  8. 30
      src/response.c
  9. 4
      src/response.h

40
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;

10
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) */

3
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);

8
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);

3
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;

14
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;
}

3
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)*/
};

30
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");

4
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);

Loading…
Cancel
Save