-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathFileCache.h
More file actions
148 lines (125 loc) · 5.23 KB
/
FileCache.h
File metadata and controls
148 lines (125 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#ifndef HV_FILE_CACHE_H_
#define HV_FILE_CACHE_H_
/*
* FileCache — Enhanced File Cache for libhv HTTP server
*
*/
#include <memory>
#include <string>
#include <mutex>
#include "hexport.h"
#include "hbuf.h"
#include "hstring.h"
#include "LRUCache.h"
// Default values — may be overridden at runtime via FileCache setters
#define FILE_CACHE_DEFAULT_HEADER_LENGTH 4096 // 4K
#define FILE_CACHE_DEFAULT_MAX_NUM 100
#define FILE_CACHE_DEFAULT_MAX_FILE_SIZE (1 << 22) // 4M
typedef struct file_cache_s {
mutable std::mutex mutex; // protects all mutable state below
std::string filepath;
struct stat st;
time_t open_time;
time_t stat_time;
uint32_t stat_cnt;
HBuf buf; // header_reserve + file_content
hbuf_t filebuf; // points into buf: file content region
hbuf_t httpbuf; // points into buf: header + file content after prepend
char last_modified[64];
char etag[64];
std::string content_type;
int header_reserve; // reserved bytes before file content
int header_used; // actual bytes used by prepend_header
file_cache_s() {
stat_cnt = 0;
header_reserve = FILE_CACHE_DEFAULT_HEADER_LENGTH;
header_used = 0;
memset(last_modified, 0, sizeof(last_modified));
memset(etag, 0, sizeof(etag));
}
// NOTE: caller must hold mutex.
// On Windows, Open() uses _wstat() directly instead of calling this.
bool is_modified() {
time_t mtime = st.st_mtime;
::stat(filepath.c_str(), &st);
return mtime != st.st_mtime;
}
// NOTE: caller must hold mutex
bool is_complete() {
if (S_ISDIR(st.st_mode)) return filebuf.len > 0;
return filebuf.len == (size_t)st.st_size;
}
// NOTE: caller must hold mutex — invalidates filebuf/httpbuf pointers
void resize_buf(size_t filesize, int reserved) {
if (reserved < 0) reserved = 0;
header_reserve = reserved;
buf.resize((size_t)reserved + filesize);
filebuf.base = buf.base + reserved;
filebuf.len = filesize;
// Invalidate httpbuf since buffer may have been reallocated
httpbuf.base = NULL;
httpbuf.len = 0;
header_used = 0;
}
void resize_buf(size_t filesize) {
resize_buf(filesize, header_reserve);
}
// Thread-safe: prepend header into reserved space.
// Returns true on success, false if header exceeds reserved space.
// On failure, httpbuf falls back to filebuf (body only, no header).
bool prepend_header(const char* header, int len) {
std::lock_guard<std::mutex> lock(mutex);
if (len <= 0 || len > header_reserve) {
// Safe fallback: point httpbuf at filebuf so callers always get valid data
httpbuf = filebuf;
header_used = 0;
return false;
}
httpbuf.base = filebuf.base - len;
httpbuf.len = (size_t)len + filebuf.len;
memcpy(httpbuf.base, header, len);
header_used = len;
return true;
}
// --- thread-safe accessors ---
int get_header_reserve() const { std::lock_guard<std::mutex> lock(mutex); return header_reserve; }
int get_header_used() const { std::lock_guard<std::mutex> lock(mutex); return header_used; }
int get_header_remaining() const { std::lock_guard<std::mutex> lock(mutex); return header_reserve - header_used; }
bool header_fits(int len) const { std::lock_guard<std::mutex> lock(mutex); return len > 0 && len <= header_reserve; }
} file_cache_t;
typedef std::shared_ptr<file_cache_t> file_cache_ptr;
class HV_EXPORT FileCache : public hv::LRUCache<std::string, file_cache_ptr> {
public:
int stat_interval; // seconds between stat() checks
int expired_time; // seconds before cache entry expires
int max_header_length; // reserved header bytes per entry
int max_file_size; // max cached file size (larger = large-file path)
explicit FileCache(size_t capacity = FILE_CACHE_DEFAULT_MAX_NUM);
struct OpenParam {
bool need_read;
int max_read; // per-request override for max file size
const char* path; // URL path (for directory listing)
size_t filesize; // [out] actual file size
int error; // [out] error code if Open returns NULL
OpenParam() {
need_read = true;
max_read = FILE_CACHE_DEFAULT_MAX_FILE_SIZE;
path = "/";
filesize = 0;
error = 0;
}
};
file_cache_ptr Open(const char* filepath, OpenParam* param);
bool Exists(const char* filepath) const;
bool Close(const char* filepath);
void RemoveExpiredFileCache();
int GetMaxHeaderLength() const { return max_header_length; }
int GetMaxFileSize() const { return max_file_size; }
int GetStatInterval() const { return stat_interval; }
int GetExpiredTime() const { return expired_time; }
void SetMaxHeaderLength(int len) { max_header_length = len < 0 ? 0 : len; }
void SetMaxFileSize(int size) { max_file_size = size < 1 ? 1 : size; }
protected:
file_cache_ptr Get(const char* filepath);
};
#endif // HV_FILE_CACHE_H_