@@ -36,34 +36,34 @@ namespace server {
36
36
37
37
namespace {
38
38
bool IsPathSafe (const std::string& path) {
39
- // 检查是否有".."路径组件,这可能导致目录遍历
39
+ // ! 检查是否有".."路径组件,这可能导致目录遍历
40
40
std::istringstream path_stream (path);
41
41
std::string component;
42
42
43
43
while (std::getline (path_stream, component, ' /' )) {
44
44
if (component == " .." )
45
- return false ; // 不允许上级目录访问
45
+ return false ; // ! 不允许上级目录访问
46
46
}
47
47
48
48
return true ;
49
49
}
50
50
}
51
51
52
- // 目录配置项
52
+ // ! 目录配置项
53
53
struct DirectoryConfig {
54
- std::string url_prefix; // URL前缀
55
- std::string local_path; // 本地路径
56
- std::string default_file; // 默认文件
54
+ std::string url_prefix; // ! URL前缀
55
+ std::string local_path; // ! 本地路径
56
+ std::string default_file; // ! 默认文件
57
57
};
58
58
59
- // 中间件私有数据结构
59
+ // ! 中间件私有数据结构
60
60
struct FileDownloaderMiddleware ::Data {
61
61
eventx::WorkThread worker;
62
- std::vector<DirectoryConfig> directories; // 目录配置列表
63
- std::map<std::string, std::string> path_mappings;// 特定路径映射
64
- std::map<std::string, std::string> mime_types; // MIME类型映射
65
- std::string default_mime_type; // 默认MIME类型
66
- bool directory_listing_enabled; // 是否允许目录列表
62
+ std::vector<DirectoryConfig> directories; // ! 目录配置列表
63
+ std::map<std::string, std::string> path_mappings;// ! 特定路径映射
64
+ std::map<std::string, std::string> mime_types; // ! MIME类型映射
65
+ std::string default_mime_type; // ! 默认MIME类型
66
+ bool directory_listing_enabled; // ! 是否允许目录列表
67
67
size_t switch_to_worker_filesize_threshold;
68
68
69
69
Data (event::Loop *wp_loop)
@@ -72,7 +72,7 @@ struct FileDownloaderMiddleware::Data {
72
72
, directory_listing_enabled(false )
73
73
, switch_to_worker_filesize_threshold(100 << 10 )
74
74
{
75
- // 初始化常见MIME类型
75
+ // ! 初始化常见MIME类型
76
76
mime_types[" html" ] = " text/html" ;
77
77
mime_types[" htm" ] = " text/html" ;
78
78
mime_types[" css" ] = " text/css" ;
@@ -109,25 +109,25 @@ FileDownloaderMiddleware::~FileDownloaderMiddleware() { delete d_; }
109
109
bool FileDownloaderMiddleware::addDirectory (const std::string& url_prefix,
110
110
const std::string& local_path,
111
111
const std::string& default_file) {
112
- // 验证URL前缀是否以'/'开头
112
+ // ! 验证URL前缀是否以'/'开头
113
113
if (url_prefix.empty () || url_prefix[0 ] != ' /' ) {
114
114
LogErr (" Invalid URL prefix: %s. Must start with '/'" , url_prefix.c_str ());
115
115
return false ;
116
116
}
117
117
118
- // 验证本地路径是否存在且是目录
118
+ // ! 验证本地路径是否存在且是目录
119
119
if (!util::fs::IsDirectoryExist (local_path)) {
120
120
LogErr (" Invalid local path: %s. Directory does not exist" , local_path.c_str ());
121
121
return false ;
122
122
}
123
123
124
- // 添加到目录列表
124
+ // ! 添加到目录列表
125
125
DirectoryConfig config;
126
126
config.url_prefix = url_prefix;
127
127
config.local_path = local_path;
128
128
config.default_file = default_file;
129
129
130
- // 确保本地路径以'/'结尾
130
+ // ! 确保本地路径以'/'结尾
131
131
if (!config.local_path .empty () && config.local_path .back () != ' /' )
132
132
config.local_path += ' /' ;
133
133
@@ -155,101 +155,101 @@ void FileDownloaderMiddleware::setMimeType(const std::string& ext, const std::st
155
155
void FileDownloaderMiddleware::handle (ContextSptr sp_ctx, const NextFunc& next) {
156
156
const auto & request = sp_ctx->req ();
157
157
158
- // 只处理GET和HEAD请求
158
+ // ! 只处理GET和HEAD请求
159
159
if (request.method != Method::kGet && request.method != Method::kHead ) {
160
160
next ();
161
161
return ;
162
162
}
163
163
164
164
const std::string& request_path = request.url .path ;
165
165
166
- // 检查特定路径映射
166
+ // ! 检查特定路径映射
167
167
auto mapping_it = d_->path_mappings .find (request_path);
168
168
if (mapping_it != d_->path_mappings .end ()) {
169
169
if (respondFile (sp_ctx, mapping_it->second ))
170
170
return ;
171
171
}
172
172
173
- // 查找匹配的目录配置
173
+ // ! 查找匹配的目录配置
174
174
for (const auto & dir : d_->directories ) {
175
- // 检查URL是否以该目录前缀开头
175
+ // ! 检查URL是否以该目录前缀开头
176
176
if (request_path.find (dir.url_prefix ) == 0 ) {
177
- // 获取相对路径部分
177
+ // ! 获取相对路径部分
178
178
std::string rel_path = request_path.substr (dir.url_prefix .length ());
179
179
180
- // 如果路径以'/'开头,去掉这个斜杠避免双斜杠
180
+ // ! 如果路径以'/'开头,去掉这个斜杠避免双斜杠
181
181
if (!rel_path.empty () && rel_path[0 ] == ' /' )
182
182
rel_path = rel_path.substr (1 );
183
183
184
- // 构造本地文件路径
184
+ // ! 构造本地文件路径
185
185
std::string file_path = dir.local_path + rel_path;
186
186
187
- // 检查路径安全性
187
+ // ! 检查路径安全性
188
188
if (!IsPathSafe (file_path)) {
189
189
LogWarn (" Unsafe path detected: %s" , file_path.c_str ());
190
190
sp_ctx->res ().status_code = StatusCode::k403_Forbidden;
191
191
return ;
192
192
}
193
193
194
194
auto file_type = util::fs::GetFileType (file_path);
195
- // 检查路径是否是目录
195
+ // ! 检查路径是否是目录
196
196
if (file_type == util::fs::FileType::kDirectory ) {
197
- // 如果是目录且路径不以'/'结尾,进行重定向
197
+ // ! 如果是目录且路径不以'/'结尾,进行重定向
198
198
if (!request_path.empty () && request_path.back () != ' /' ) {
199
199
sp_ctx->res ().status_code = StatusCode::k301_MovedPermanently;
200
200
sp_ctx->res ().headers [" Location" ] = request_path + " /" ;
201
201
return ;
202
202
}
203
203
204
- // 尝试访问默认文件
204
+ // ! 尝试访问默认文件
205
205
std::string default_file_path = file_path + dir.default_file ;
206
206
if (util::fs::GetFileType (default_file_path) == util::fs::FileType::kRegular ) {
207
207
if (respondFile (sp_ctx, default_file_path))
208
208
return ;
209
209
}
210
210
211
- // 如果允许目录列表,生成目录内容
211
+ // ! 如果允许目录列表,生成目录内容
212
212
if (d_->directory_listing_enabled ) {
213
213
if (respondDirectory (sp_ctx, file_path, request_path))
214
214
return ;
215
215
}
216
216
217
- // 否则返回403 Forbidden
217
+ // ! 否则返回403 Forbidden
218
218
LogNotice (" Directory listing disabled for: %s" , file_path.c_str ());
219
219
sp_ctx->res ().status_code = StatusCode::k403_Forbidden;
220
220
return ;
221
221
222
222
} else if (file_type == util::fs::FileType::kRegular ) {
223
- // 如果是普通文件,直接响应文件内容
223
+ // ! 如果是普通文件,直接响应文件内容
224
224
if (respondFile (sp_ctx, file_path))
225
225
return ;
226
226
}
227
227
}
228
228
}
229
229
230
- // 如果没有找到匹配的文件,传递给下一个中间件
230
+ // ! 如果没有找到匹配的文件,传递给下一个中间件
231
231
next ();
232
232
}
233
233
234
234
std::string FileDownloaderMiddleware::getMimeType (const std::string& filename) const {
235
- // 查找最后一个点的位置
235
+ // ! 查找最后一个点的位置
236
236
size_t dot_pos = filename.find_last_of (' .' );
237
237
if (dot_pos != std::string::npos) {
238
238
std::string ext = util::string::ToLower (filename.substr (dot_pos + 1 ));
239
- // 在MIME类型映射中查找
239
+ // ! 在MIME类型映射中查找
240
240
auto it = d_->mime_types .find (ext);
241
241
if (it != d_->mime_types .end ())
242
242
return it->second ;
243
243
}
244
244
245
- // 未找到匹配的MIME类型,返回默认值
245
+ // ! 未找到匹配的MIME类型,返回默认值
246
246
return d_->default_mime_type ;
247
247
}
248
248
249
249
bool FileDownloaderMiddleware::respondFile (ContextSptr sp_ctx, const std::string& file_path) {
250
250
auto & res = sp_ctx->res ();
251
251
252
- // 打开文件
252
+ // ! 打开文件
253
253
std::ifstream file (file_path, std::ios::binary | std::ios::ate);
254
254
if (!file.is_open ()) {
255
255
res.status_code = StatusCode::k404_NotFound;
@@ -258,11 +258,11 @@ bool FileDownloaderMiddleware::respondFile(ContextSptr sp_ctx, const std::string
258
258
259
259
res.headers [" Content-Type" ] = getMimeType (file_path);
260
260
261
- // 获取文件大小
261
+ // ! 获取文件大小
262
262
size_t file_size = static_cast <size_t >(file.tellg ());
263
263
file.seekg (0 , std::ios::beg);
264
264
265
- // 如果是HEAD请求,不返回内容
265
+ // ! 如果是HEAD请求,不返回内容
266
266
if (sp_ctx->req ().method == Method::kHead ) {
267
267
res.status_code = StatusCode::k200_OK;
268
268
res.headers [" Content-Length" ] = std::to_string (file_size);
@@ -302,7 +302,7 @@ bool FileDownloaderMiddleware::respondDirectory(ContextSptr sp_ctx,
302
302
const std::string& dir_path,
303
303
const std::string& url_path) {
304
304
try {
305
- // 生成HTML目录列表
305
+ // ! 生成HTML目录列表
306
306
std::stringstream html;
307
307
html << " <!DOCTYPE html>\n "
308
308
<< " <html>\n "
@@ -322,7 +322,7 @@ bool FileDownloaderMiddleware::respondDirectory(ContextSptr sp_ctx,
322
322
<< " <h1>Directory listing for " << url_path << " </h1>\n "
323
323
<< " <ul>\n " ;
324
324
325
- // 如果不是根目录,添加返回上级目录的链接
325
+ // ! 如果不是根目录,添加返回上级目录的链接
326
326
if (url_path != " /" ) {
327
327
size_t last_slash = url_path.find_last_of (' /' , url_path.size () - 2 );
328
328
if (last_slash != std::string::npos) {
@@ -331,7 +331,7 @@ bool FileDownloaderMiddleware::respondDirectory(ContextSptr sp_ctx,
331
331
}
332
332
}
333
333
334
- // 列出目录中的项目
334
+ // ! 列出目录中的项目
335
335
std::vector<std::string> entries;
336
336
if (!util::fs::ListDirectory (dir_path, entries)) {
337
337
LogErr (" Failed to list directory: %s" , dir_path.c_str ());
@@ -355,7 +355,7 @@ bool FileDownloaderMiddleware::respondDirectory(ContextSptr sp_ctx,
355
355
<< " </body>\n "
356
356
<< " </html>" ;
357
357
358
- // 设置响应
358
+ // ! 设置响应
359
359
auto & res = sp_ctx->res ();
360
360
res.status_code = StatusCode::k200_OK;
361
361
res.headers [" Content-Type" ] = " text/html; charset=utf-8" ;
0 commit comments