@maiyunnet/kebab 2.0.3 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -1
- package/package.json +1 -1
- package/tsconfig.json +1 -1
- package/index.ts +0 -33
- package/lib/buffer.ts +0 -152
- package/lib/captcha.ts +0 -63
- package/lib/consistent.ts +0 -219
- package/lib/core.ts +0 -880
- package/lib/crypto.ts +0 -384
- package/lib/db.ts +0 -719
- package/lib/dns.ts +0 -405
- package/lib/fs.ts +0 -527
- package/lib/jwt.ts +0 -276
- package/lib/kv.ts +0 -1489
- package/lib/lan.ts +0 -87
- package/lib/net/formdata.ts +0 -166
- package/lib/net/request.ts +0 -150
- package/lib/net/response.ts +0 -59
- package/lib/net.ts +0 -662
- package/lib/s3.ts +0 -235
- package/lib/scan.ts +0 -364
- package/lib/session.ts +0 -230
- package/lib/sql.ts +0 -1149
- package/lib/ssh/sftp.ts +0 -508
- package/lib/ssh/shell.ts +0 -123
- package/lib/ssh.ts +0 -191
- package/lib/text.ts +0 -626
- package/lib/time.ts +0 -254
- package/lib/ws.ts +0 -523
- package/lib/zip.ts +0 -447
- package/lib/zlib.ts +0 -350
- package/main.ts +0 -27
- package/sys/child.ts +0 -678
- package/sys/cmd.ts +0 -225
- package/sys/ctr.ts +0 -904
- package/sys/master.ts +0 -355
- package/sys/mod.ts +0 -1871
- package/sys/route.ts +0 -1113
- package/types/index.d.ts +0 -283
- package/www/example/ctr/main.ts +0 -9
- package/www/example/ctr/middle.ts +0 -26
- package/www/example/ctr/test.ts +0 -3218
- package/www/example/mod/test.ts +0 -47
- package/www/example/mod/testdata.ts +0 -30
- package/www/example/ws/mproxy.ts +0 -16
- package/www/example/ws/rproxy.ts +0 -14
- package/www/example/ws/test.ts +0 -36
package/lib/fs.ts
DELETED
|
@@ -1,527 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project: Kebab, User: JianSuoQiYue
|
|
3
|
-
* Date: 2019-3-29 23:03:07
|
|
4
|
-
* Last: 2020-3-11 22:21:51, 2022-12-29 01:18:25, 2023-12-13 20:50:09
|
|
5
|
-
*/
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as http from 'http';
|
|
8
|
-
import * as http2 from 'http2';
|
|
9
|
-
import * as mime from '@litert/mime';
|
|
10
|
-
import * as text from './text';
|
|
11
|
-
import * as core from './core';
|
|
12
|
-
import * as zlib from './zlib';
|
|
13
|
-
|
|
14
|
-
export function getContent(path: string, options?: {
|
|
15
|
-
'start'?: number;
|
|
16
|
-
'end'?: number;
|
|
17
|
-
}): Promise<Buffer | null>;
|
|
18
|
-
export function getContent(path: string, options: BufferEncoding | {
|
|
19
|
-
'encoding': BufferEncoding;
|
|
20
|
-
'start'?: number;
|
|
21
|
-
'end'?: number;
|
|
22
|
-
}): Promise<string | null>;
|
|
23
|
-
/**
|
|
24
|
-
* --- 读取完整文件或一段 ---
|
|
25
|
-
* @param path 文件路径
|
|
26
|
-
* @param options 编码或选项
|
|
27
|
-
*/
|
|
28
|
-
export async function getContent(path: string, options?: BufferEncoding | {
|
|
29
|
-
'encoding'?: BufferEncoding;
|
|
30
|
-
'start'?: number;
|
|
31
|
-
'end'?: number;
|
|
32
|
-
}): Promise<Buffer | string | null> {
|
|
33
|
-
if (typeof options === 'string') {
|
|
34
|
-
options = {
|
|
35
|
-
'encoding': options
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
else if (!options) {
|
|
39
|
-
options = {};
|
|
40
|
-
}
|
|
41
|
-
const encoding = options.encoding;
|
|
42
|
-
const start = options.start;
|
|
43
|
-
const end = options.end;
|
|
44
|
-
if (start ?? end) {
|
|
45
|
-
return new Promise(function(resolve) {
|
|
46
|
-
const rs = createReadStream(path, {
|
|
47
|
-
'encoding': encoding,
|
|
48
|
-
'start': start,
|
|
49
|
-
'end': end
|
|
50
|
-
});
|
|
51
|
-
const data: Buffer[] = [];
|
|
52
|
-
rs.on('data', (chunk) => {
|
|
53
|
-
if (!(chunk instanceof Buffer)) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
data.push(chunk);
|
|
57
|
-
}).on('end', function() {
|
|
58
|
-
const buf = Buffer.concat(data);
|
|
59
|
-
if (encoding) {
|
|
60
|
-
resolve(buf.toString());
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
resolve(buf);
|
|
64
|
-
}
|
|
65
|
-
}).on('error', function() {
|
|
66
|
-
resolve(null);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
try {
|
|
72
|
-
if (encoding) {
|
|
73
|
-
return await fs.promises.readFile(path, {
|
|
74
|
-
'encoding': encoding
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
return await fs.promises.readFile(path);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* --- 写入文件内容 ---
|
|
89
|
-
* @param path 文件路径
|
|
90
|
-
* @param data 要写入的内容
|
|
91
|
-
* @param options 选项
|
|
92
|
-
*/
|
|
93
|
-
export async function putContent(
|
|
94
|
-
path: string,
|
|
95
|
-
data: string | Buffer,
|
|
96
|
-
options: {
|
|
97
|
-
'encoding'?: BufferEncoding;
|
|
98
|
-
'mode'?: number;
|
|
99
|
-
'flag'?: string;
|
|
100
|
-
} = {}): Promise<boolean> {
|
|
101
|
-
try {
|
|
102
|
-
await fs.promises.writeFile(path, data, options);
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* --- 读取链接的 target ---
|
|
112
|
-
* @param path 要读取的路径
|
|
113
|
-
* @param encoding 编码
|
|
114
|
-
*/
|
|
115
|
-
export async function readLink(path: string, encoding?: BufferEncoding): Promise<string | null> {
|
|
116
|
-
try {
|
|
117
|
-
return await fs.promises.readlink(path, {
|
|
118
|
-
'encoding': encoding
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* --- 把源文件创建一个 link ---
|
|
128
|
-
* @param filePath 源文件
|
|
129
|
-
* @param linkPath 连接路径
|
|
130
|
-
* @param type 仅 Windows,类型,默认 file
|
|
131
|
-
*/
|
|
132
|
-
export async function symlink(filePath: string, linkPath: string, type?: 'dir' | 'file' | 'junction'): Promise<boolean> {
|
|
133
|
-
try {
|
|
134
|
-
await fs.promises.symlink(filePath, linkPath, type);
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* --- 删除一个文件 ---
|
|
144
|
-
* @param path 要删除的文件路径
|
|
145
|
-
*/
|
|
146
|
-
export async function unlink(path: string): Promise<boolean> {
|
|
147
|
-
for (let i = 0; i <= 2; ++i) {
|
|
148
|
-
try {
|
|
149
|
-
await fs.promises.unlink(path);
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
await core.sleep(250);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
try {
|
|
157
|
-
await fs.promises.unlink(path);
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* --- 获取对象是否存在,存在则返回 stats 对象,否则返回 null ---
|
|
167
|
-
* @param path 对象路径
|
|
168
|
-
*/
|
|
169
|
-
export async function stats(path: string): Promise<fs.Stats | null> {
|
|
170
|
-
try {
|
|
171
|
-
return await fs.promises.lstat(path);
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* --- 判断是否是目录或目录是否存在,是的话返回 stats ---
|
|
180
|
-
* @param path 判断路径
|
|
181
|
-
*/
|
|
182
|
-
export async function isDir(path: string): Promise<fs.Stats | false> {
|
|
183
|
-
const pstats = await stats(path);
|
|
184
|
-
if (!pstats?.isDirectory()) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
return pstats;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* --- 判断是否是文件或文件是否存在,是的话返回 stats ---
|
|
192
|
-
* @param path 判断路径
|
|
193
|
-
*/
|
|
194
|
-
export async function isFile(path: string): Promise<fs.Stats | false> {
|
|
195
|
-
const pstats = await stats(path);
|
|
196
|
-
if (!pstats?.isFile()) {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
return pstats;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* --- 深度创建目录,如果最末目录存在,则自动创建成功 ---
|
|
204
|
-
* @param path 要创建的路径,如 /a/b/c/
|
|
205
|
-
* @param mode 权限
|
|
206
|
-
*/
|
|
207
|
-
export async function mkdir(path: string, mode: number = 0o755): Promise<boolean> {
|
|
208
|
-
if (await isDir(path)) {
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
// --- 深度创建目录 ---
|
|
212
|
-
try {
|
|
213
|
-
await fs.promises.mkdir(path, {
|
|
214
|
-
'recursive': true,
|
|
215
|
-
'mode': mode
|
|
216
|
-
});
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
catch {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* --- 删除空目录 ---
|
|
226
|
-
* @param path 要删除的目录
|
|
227
|
-
*/
|
|
228
|
-
export async function rmdir(path: string): Promise<boolean> {
|
|
229
|
-
if (!(await isDir(path))) {
|
|
230
|
-
return true;
|
|
231
|
-
}
|
|
232
|
-
try {
|
|
233
|
-
await fs.promises.rmdir(path);
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* --- Danger 危险:危险函数,尽量不要使用 ---
|
|
243
|
-
* --- This f**king is a danger function, please don't use it ---
|
|
244
|
-
* --- 删除一个非空目录 ---
|
|
245
|
-
*/
|
|
246
|
-
export async function rmdirDeep(path: string): Promise<boolean> {
|
|
247
|
-
if (!path.endsWith('/')) {
|
|
248
|
-
path += '/';
|
|
249
|
-
}
|
|
250
|
-
const list = await readDir(path);
|
|
251
|
-
for (const item of list) {
|
|
252
|
-
const stat = await stats(item.name);
|
|
253
|
-
if (!stat) {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
if (stat.isDirectory()) {
|
|
257
|
-
// --- 目录 ---
|
|
258
|
-
const rtn = await rmdirDeep(path + item.name);
|
|
259
|
-
if (!rtn) {
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
const rtn = await unlink(path + item.name);
|
|
265
|
-
if (!rtn) {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return rmdir(path);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* --- 修改权限
|
|
275
|
-
* @param path 要修改的路径
|
|
276
|
-
* @param mod 权限
|
|
277
|
-
*/
|
|
278
|
-
export async function chmod(path: string, mod: string | number): Promise<boolean> {
|
|
279
|
-
try {
|
|
280
|
-
await fs.promises.chmod(path, mod);
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* --- 重命名/移动 文件文件夹 ---
|
|
290
|
-
* @param oldPath 老名
|
|
291
|
-
* @param newPath 新名
|
|
292
|
-
*/
|
|
293
|
-
export async function rename(oldPath: string, newPath: string): Promise<boolean> {
|
|
294
|
-
try {
|
|
295
|
-
await fs.promises.rename(oldPath, newPath);
|
|
296
|
-
return true;
|
|
297
|
-
}
|
|
298
|
-
catch {
|
|
299
|
-
return false;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* --- 获取文件夹下文件列表 ---
|
|
305
|
-
* @param path 文件夹路径
|
|
306
|
-
*/
|
|
307
|
-
export async function readDir(path: string, encoding?: BufferEncoding): Promise<fs.Dirent[]> {
|
|
308
|
-
try {
|
|
309
|
-
const list: fs.Dirent[] = [];
|
|
310
|
-
const dlist = await fs.promises.readdir(path, {
|
|
311
|
-
'encoding': encoding,
|
|
312
|
-
'withFileTypes': true
|
|
313
|
-
});
|
|
314
|
-
for (const item of dlist) {
|
|
315
|
-
if (item.name === '.' || item.name === '..') {
|
|
316
|
-
continue;
|
|
317
|
-
}
|
|
318
|
-
list.push(item);
|
|
319
|
-
}
|
|
320
|
-
return list;
|
|
321
|
-
}
|
|
322
|
-
catch {
|
|
323
|
-
return [];
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* --- 复制文件夹里的内容到另一个地方,失败不会回滚 ---
|
|
329
|
-
* @param from 源,末尾加 /
|
|
330
|
-
* @param to 目标,末尾加 /
|
|
331
|
-
* @param ignore 忽略的文件
|
|
332
|
-
*/
|
|
333
|
-
export async function copyFolder(from: string, to: string, ignore: RegExp[] = []): Promise<number> {
|
|
334
|
-
let num = 0;
|
|
335
|
-
// --- 如果源目录不存在或不是目录,则直接成功 :) ---
|
|
336
|
-
if (!await isDir(from)) {
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
// --- 遍历源目录文件和文件夹,准备复制 ---
|
|
340
|
-
const flist = await readDir(from);
|
|
341
|
-
/** --- to 目录是否检查是否存在,空目录不复制,所以确定有 item file 的时候才创建 --- */
|
|
342
|
-
let checkTo = false;
|
|
343
|
-
for (const item of flist) {
|
|
344
|
-
if (item.isDirectory()) {
|
|
345
|
-
const r = await copyFolder(from + item.name + '/', to + item.name + '/', ignore);
|
|
346
|
-
if (r === -1) {
|
|
347
|
-
return r;
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
num += r;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
else if (item.isFile()) {
|
|
354
|
-
// --- 先判断本文件是否被排除 ---
|
|
355
|
-
if (ignore.length > 0 && text.match(item.name, ignore)) {
|
|
356
|
-
continue;
|
|
357
|
-
}
|
|
358
|
-
if (!checkTo) {
|
|
359
|
-
if (!await mkdir(to)) {
|
|
360
|
-
return -1;
|
|
361
|
-
}
|
|
362
|
-
checkTo = true;
|
|
363
|
-
}
|
|
364
|
-
if (!(await copyFile(from + item.name, to + item.name))) {
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
++num;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return num;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* --- 复制文件 ---
|
|
375
|
-
* @param src 源文件
|
|
376
|
-
* @param dest 目标文件
|
|
377
|
-
*/
|
|
378
|
-
export async function copyFile(src: string, dest: string): Promise<boolean> {
|
|
379
|
-
try {
|
|
380
|
-
await fs.promises.copyFile(src, dest);
|
|
381
|
-
return true;
|
|
382
|
-
}
|
|
383
|
-
catch {
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* --- 创建读取文件的流 ---
|
|
390
|
-
* @param path 文件地址
|
|
391
|
-
* @param options 编码或配置
|
|
392
|
-
*/
|
|
393
|
-
export function createReadStream(path: string, options?: BufferEncoding | {
|
|
394
|
-
'flags'?: string;
|
|
395
|
-
'encoding'?: BufferEncoding;
|
|
396
|
-
'autoClose'?: boolean;
|
|
397
|
-
'start'?: number;
|
|
398
|
-
'end'?: number;
|
|
399
|
-
}): fs.ReadStream {
|
|
400
|
-
if (typeof options === 'string') {
|
|
401
|
-
options = {
|
|
402
|
-
'encoding': options
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
else if (!options) {
|
|
406
|
-
options = {};
|
|
407
|
-
}
|
|
408
|
-
return fs.createReadStream(path, {
|
|
409
|
-
'flags': options.flags,
|
|
410
|
-
'encoding': options.encoding,
|
|
411
|
-
'autoClose': options.autoClose,
|
|
412
|
-
'start': options.start,
|
|
413
|
-
'end': options.end
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* --- 读取文件写入到流,并等待写入完成 ---
|
|
419
|
-
* @param path 文件地址
|
|
420
|
-
* @param destination 要写入的流
|
|
421
|
-
* @param options 写入后是否终止写入流,默认终止
|
|
422
|
-
*/
|
|
423
|
-
export function pipe(path: string, destination: NodeJS.WritableStream, options?: {
|
|
424
|
-
'end'?: boolean;
|
|
425
|
-
}): Promise<boolean> {
|
|
426
|
-
return new Promise((resolve) => {
|
|
427
|
-
createReadStream(path).on('error', function() {
|
|
428
|
-
resolve(false);
|
|
429
|
-
}).on('end', function() {
|
|
430
|
-
resolve(true);
|
|
431
|
-
}).pipe(destination, options);
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* --- 创建写入文件的流 ---
|
|
437
|
-
* @param path 文件地址
|
|
438
|
-
* @param options 编码或配置
|
|
439
|
-
*/
|
|
440
|
-
export function createWriteStream(path: string, options?: BufferEncoding | {
|
|
441
|
-
'flags'?: string;
|
|
442
|
-
'encoding'?: BufferEncoding;
|
|
443
|
-
'mode'?: number;
|
|
444
|
-
'autoClose'?: boolean;
|
|
445
|
-
'start'?: number;
|
|
446
|
-
}): fs.WriteStream {
|
|
447
|
-
if (typeof options === 'string') {
|
|
448
|
-
options = {
|
|
449
|
-
'encoding': options
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
else if (!options) {
|
|
453
|
-
options = {};
|
|
454
|
-
}
|
|
455
|
-
return fs.createWriteStream(path, {
|
|
456
|
-
'flags': options.flags,
|
|
457
|
-
'encoding': options.encoding,
|
|
458
|
-
'mode': options.mode,
|
|
459
|
-
'autoClose': options.autoClose,
|
|
460
|
-
'start': options.start
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* --- 读取文件并输出到 http 的 response ---
|
|
466
|
-
* @param path 文件绝对路径
|
|
467
|
-
* @param req http 请求对象
|
|
468
|
-
* @param res http 响应对象
|
|
469
|
-
* @param stat 文件的 stat(如果有)
|
|
470
|
-
*/
|
|
471
|
-
export async function readToResponse(path: string,
|
|
472
|
-
req: http2.Http2ServerRequest | http.IncomingMessage,
|
|
473
|
-
res: http2.Http2ServerResponse | http.ServerResponse,
|
|
474
|
-
stat?: fs.Stats | null
|
|
475
|
-
): Promise<void> {
|
|
476
|
-
if (!stat) {
|
|
477
|
-
stat = await stats(path);
|
|
478
|
-
}
|
|
479
|
-
if (!stat) {
|
|
480
|
-
res.setHeader('content-length', 22);
|
|
481
|
-
res.writeHead(404);
|
|
482
|
-
res.end('<h1>404 Not found</h1><hr>Kebab');
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
// --- 判断缓存以及 MIME 和编码 ---
|
|
486
|
-
let charset = '';
|
|
487
|
-
const mimeData = mime.getData(path);
|
|
488
|
-
if (['htm', 'html', 'css', 'js', 'xml', 'jpg', 'jpeg', 'svg', 'gif', 'png'].includes(mimeData.extension)) {
|
|
489
|
-
charset = '; charset=utf-8';
|
|
490
|
-
// --- 这些文件可能需要缓存 ---
|
|
491
|
-
const hash = `W/"${stat.size.toString(16)}-${stat.mtime.getTime().toString(16)}"`;
|
|
492
|
-
const lastModified = stat.mtime.toUTCString();
|
|
493
|
-
res.setHeader('etag', hash);
|
|
494
|
-
res.setHeader('cache-control', 'public, max-age=600');
|
|
495
|
-
// --- 判断返回 304 吗 ---
|
|
496
|
-
const noneMatch = req.headers['if-none-match'];
|
|
497
|
-
const modifiedSince = req.headers['if-modified-since'];
|
|
498
|
-
if ((hash === noneMatch) && (lastModified === modifiedSince)) {
|
|
499
|
-
res.writeHead(304);
|
|
500
|
-
res.end();
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
res.setHeader('last-modified', lastModified);
|
|
504
|
-
}
|
|
505
|
-
else {
|
|
506
|
-
res.setHeader('cache-control', 'no-cache, must-revalidate');
|
|
507
|
-
}
|
|
508
|
-
// --- 设置 type ---
|
|
509
|
-
res.setHeader('content-type', mimeData.mime + charset);
|
|
510
|
-
// --- 判断客户端支持的压缩模式 ---
|
|
511
|
-
const encoding = req.headers['accept-encoding'] as string ?? '';
|
|
512
|
-
if (mimeData.compressible && (stat.size >= 1024)) {
|
|
513
|
-
// --- 压缩 ---
|
|
514
|
-
const compress = await zlib.compress(encoding, await getContent(path));
|
|
515
|
-
if (compress) {
|
|
516
|
-
res.setHeader('content-encoding', compress.type);
|
|
517
|
-
res.setHeader('content-length', Buffer.byteLength(compress.buffer));
|
|
518
|
-
res.writeHead(200);
|
|
519
|
-
res.end(compress.buffer);
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
// --- 不压缩 ---
|
|
524
|
-
res.setHeader('content-length', stat.size);
|
|
525
|
-
res.writeHead(200);
|
|
526
|
-
await pipe(path, res instanceof http2.Http2ServerResponse ? (res.stream ?? res) : res);
|
|
527
|
-
}
|