koa-files 4.1.7 → 5.0.0
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/README.md +1 -1
- package/cjs/ReadStream.cjs +66 -62
- package/cjs/ReadStream.d.cts +3 -45
- package/cjs/Service.cjs +25 -22
- package/cjs/Service.d.cts +2 -12
- package/cjs/index.cjs +3 -2
- package/cjs/utils/fs.cjs +2 -1
- package/cjs/utils/fs.d.cts +18 -5
- package/cjs/utils/hash.cjs +4 -3
- package/cjs/utils/http.cjs +8 -5
- package/cjs/utils/http.d.cts +2 -2
- package/cjs/utils/path.cjs +6 -5
- package/cjs/utils/type.cjs +2 -1
- package/esm/ReadStream.d.ts +3 -45
- package/esm/ReadStream.js +64 -60
- package/esm/Service.d.ts +2 -12
- package/esm/Service.js +17 -14
- package/esm/index.js +3 -2
- package/esm/utils/fs.d.ts +18 -5
- package/esm/utils/fs.js +2 -1
- package/esm/utils/hash.js +3 -2
- package/esm/utils/http.d.ts +2 -2
- package/esm/utils/http.js +7 -4
- package/esm/utils/path.js +3 -2
- package/esm/utils/type.js +2 -1
- package/package.json +13 -14
package/cjs/utils/path.cjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module koa-files
|
|
2
3
|
* @package koa-files
|
|
3
4
|
* @license MIT
|
|
4
|
-
* @version
|
|
5
|
+
* @version 5.0.0
|
|
5
6
|
* @author nuintun <nuintun@qq.com>
|
|
6
7
|
* @description A static files serving middleware for koa.
|
|
7
8
|
* @see https://github.com/nuintun/koa-files#readme
|
|
@@ -9,7 +10,7 @@
|
|
|
9
10
|
|
|
10
11
|
'use strict';
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
+
const node_path = require('node:path');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @module path
|
|
@@ -36,9 +37,9 @@ function hasTrailingSlash(path) {
|
|
|
36
37
|
* @param path The path to check.
|
|
37
38
|
* @param root The root path.
|
|
38
39
|
*/
|
|
39
|
-
function isOutRoot(path
|
|
40
|
-
path
|
|
41
|
-
return /\.\.(?:[\\/]|$)/.test(path
|
|
40
|
+
function isOutRoot(path, root) {
|
|
41
|
+
path = node_path.relative(root, path);
|
|
42
|
+
return /\.\.(?:[\\/]|$)/.test(path) || node_path.isAbsolute(path);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
exports.hasTrailingSlash = hasTrailingSlash;
|
package/cjs/utils/type.cjs
CHANGED
package/esm/ReadStream.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module ReadStream
|
|
3
3
|
*/
|
|
4
|
-
import { PathLike } from 'fs';
|
|
5
|
-
import { Readable } from 'stream';
|
|
6
4
|
import { Range } from './utils/http.js';
|
|
5
|
+
import { Readable } from 'node:stream';
|
|
7
6
|
import { FileSystem } from './utils/fs.js';
|
|
8
7
|
export interface Options {
|
|
9
8
|
fs: FileSystem;
|
|
@@ -13,67 +12,26 @@ interface Callback {
|
|
|
13
12
|
(error?: Error | null): void;
|
|
14
13
|
}
|
|
15
14
|
export declare class ReadStream extends Readable {
|
|
16
|
-
private
|
|
17
|
-
private readonly path;
|
|
18
|
-
private readonly ranges;
|
|
19
|
-
private bytesRead;
|
|
20
|
-
private fd;
|
|
21
|
-
private reading;
|
|
22
|
-
private currentRangeIndex;
|
|
23
|
-
private readState;
|
|
15
|
+
#private;
|
|
24
16
|
/**
|
|
25
17
|
* @constructor
|
|
26
18
|
* @param path The file path.
|
|
27
19
|
* @param ranges The ranges to read.
|
|
28
20
|
* @param options The stream options.
|
|
29
21
|
*/
|
|
30
|
-
constructor(path:
|
|
22
|
+
constructor(path: string, ranges: Range[], options: Options);
|
|
31
23
|
/**
|
|
32
24
|
* @override
|
|
33
25
|
* @method _construct
|
|
34
26
|
* @param callback The callback.
|
|
35
27
|
*/
|
|
36
28
|
_construct(callback: Callback): void;
|
|
37
|
-
/**
|
|
38
|
-
* @private
|
|
39
|
-
* @method getRange
|
|
40
|
-
*/
|
|
41
|
-
private getRange;
|
|
42
|
-
/**
|
|
43
|
-
* @private
|
|
44
|
-
* @method getPadding
|
|
45
|
-
* @param range The current range.
|
|
46
|
-
*/
|
|
47
|
-
private getPadding;
|
|
48
|
-
/**
|
|
49
|
-
* @private
|
|
50
|
-
* @method readPadding
|
|
51
|
-
* @param fd The file descriptor.
|
|
52
|
-
* @param range The current range.
|
|
53
|
-
* @param size The number of bytes to read.
|
|
54
|
-
*/
|
|
55
|
-
private readPadding;
|
|
56
|
-
/**
|
|
57
|
-
* @private
|
|
58
|
-
* @method readFileRange
|
|
59
|
-
* @param fd The file descriptor.
|
|
60
|
-
* @param range The current range.
|
|
61
|
-
* @param size The number of bytes to read.
|
|
62
|
-
*/
|
|
63
|
-
private readFileRange;
|
|
64
29
|
/**
|
|
65
30
|
* @override
|
|
66
31
|
* @method _read
|
|
67
32
|
* @param size The number of bytes to read.
|
|
68
33
|
*/
|
|
69
34
|
_read(size: number): void;
|
|
70
|
-
/**
|
|
71
|
-
* @private
|
|
72
|
-
* @method dispose
|
|
73
|
-
* @param error The error.
|
|
74
|
-
* @param callback The callback.
|
|
75
|
-
*/
|
|
76
|
-
private dispose;
|
|
77
35
|
/**
|
|
78
36
|
* @override
|
|
79
37
|
* @method _destroy
|
package/esm/ReadStream.js
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module koa-files
|
|
2
3
|
* @package koa-files
|
|
3
4
|
* @license MIT
|
|
4
|
-
* @version
|
|
5
|
+
* @version 5.0.0
|
|
5
6
|
* @author nuintun <nuintun@qq.com>
|
|
6
7
|
* @description A static files serving middleware for koa.
|
|
7
8
|
* @see https://github.com/nuintun/koa-files#readme
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import { Buffer } from 'buffer';
|
|
11
|
-
import { Readable } from 'stream';
|
|
11
|
+
import { Buffer } from 'node:buffer';
|
|
12
|
+
import { Readable } from 'node:stream';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @module ReadStream
|
|
15
16
|
*/
|
|
16
17
|
const DISPOSE_EVENT = Symbol('dispose');
|
|
17
18
|
class ReadStream extends Readable {
|
|
19
|
+
#path;
|
|
20
|
+
#fs;
|
|
21
|
+
#ranges;
|
|
22
|
+
#bytesRead = 0;
|
|
23
|
+
#fd = null;
|
|
24
|
+
#reading = false;
|
|
25
|
+
#currentRangeIndex = 0;
|
|
26
|
+
#readState = 0 /* ReadState.PREFIX */;
|
|
18
27
|
/**
|
|
19
28
|
* @constructor
|
|
20
29
|
* @param path The file path.
|
|
@@ -24,14 +33,9 @@ class ReadStream extends Readable {
|
|
|
24
33
|
constructor(path, ranges, options) {
|
|
25
34
|
const { fs, highWaterMark } = options;
|
|
26
35
|
super({ highWaterMark });
|
|
27
|
-
this
|
|
28
|
-
this
|
|
29
|
-
this
|
|
30
|
-
this.currentRangeIndex = 0;
|
|
31
|
-
this.readState = 0 /* ReadState.PREFIX */;
|
|
32
|
-
this.fs = fs;
|
|
33
|
-
this.path = path;
|
|
34
|
-
this.ranges = ranges;
|
|
36
|
+
this.#fs = fs;
|
|
37
|
+
this.#path = path;
|
|
38
|
+
this.#ranges = ranges;
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
41
|
* @override
|
|
@@ -39,27 +43,27 @@ class ReadStream extends Readable {
|
|
|
39
43
|
* @param callback The callback.
|
|
40
44
|
*/
|
|
41
45
|
_construct(callback) {
|
|
42
|
-
this
|
|
46
|
+
this.#fs.open(this.#path, 'r', (openError, fd) => {
|
|
43
47
|
if (openError == null) {
|
|
44
|
-
this
|
|
48
|
+
this.#fd = fd;
|
|
45
49
|
}
|
|
46
50
|
callback(openError);
|
|
47
51
|
});
|
|
48
52
|
}
|
|
49
53
|
/**
|
|
50
54
|
* @private
|
|
51
|
-
* @method getRange
|
|
55
|
+
* @method #getRange
|
|
52
56
|
*/
|
|
53
|
-
getRange() {
|
|
54
|
-
return this
|
|
57
|
+
#getRange() {
|
|
58
|
+
return this.#ranges[this.#currentRangeIndex];
|
|
55
59
|
}
|
|
56
60
|
/**
|
|
57
61
|
* @private
|
|
58
|
-
* @method getPadding
|
|
62
|
+
* @method #getPadding
|
|
59
63
|
* @param range The current range.
|
|
60
64
|
*/
|
|
61
|
-
getPadding(range) {
|
|
62
|
-
switch (this
|
|
65
|
+
#getPadding(range) {
|
|
66
|
+
switch (this.#readState) {
|
|
63
67
|
case 0 /* ReadState.PREFIX */:
|
|
64
68
|
return range.prefix;
|
|
65
69
|
case 2 /* ReadState.SUFFIX */:
|
|
@@ -68,42 +72,42 @@ class ReadStream extends Readable {
|
|
|
68
72
|
}
|
|
69
73
|
/**
|
|
70
74
|
* @private
|
|
71
|
-
* @method readPadding
|
|
75
|
+
* @method #readPadding
|
|
72
76
|
* @param fd The file descriptor.
|
|
73
77
|
* @param range The current range.
|
|
74
78
|
* @param size The number of bytes to read.
|
|
75
79
|
*/
|
|
76
|
-
readPadding(fd, range, size) {
|
|
80
|
+
#readPadding(fd, range, size) {
|
|
77
81
|
let bytesRead = 0;
|
|
78
|
-
const padding = this
|
|
82
|
+
const padding = this.#getPadding(range);
|
|
79
83
|
// If padding exists.
|
|
80
84
|
if (padding != null) {
|
|
81
85
|
const { length } = padding;
|
|
82
|
-
const begin = this
|
|
86
|
+
const begin = this.#bytesRead;
|
|
83
87
|
if (begin < length) {
|
|
84
88
|
bytesRead = Math.min(size, length - begin);
|
|
85
89
|
const end = begin + bytesRead;
|
|
86
90
|
this.push(padding.subarray(begin, end));
|
|
87
|
-
this
|
|
91
|
+
this.#bytesRead = end;
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
// If no padding or read completed.
|
|
91
95
|
if (bytesRead < size) {
|
|
92
|
-
this
|
|
96
|
+
this.#bytesRead = 0;
|
|
93
97
|
// Change read state.
|
|
94
|
-
switch (this
|
|
98
|
+
switch (this.#readState) {
|
|
95
99
|
case 0 /* ReadState.PREFIX */:
|
|
96
|
-
this
|
|
97
|
-
this
|
|
100
|
+
this.#readState = 1 /* ReadState.RANGE */;
|
|
101
|
+
this.#readFileRange(fd, range, size - bytesRead);
|
|
98
102
|
break;
|
|
99
103
|
case 2 /* ReadState.SUFFIX */:
|
|
100
|
-
this
|
|
101
|
-
this
|
|
102
|
-
const nextRange = this
|
|
104
|
+
this.#currentRangeIndex++;
|
|
105
|
+
this.#readState = 0 /* ReadState.PREFIX */;
|
|
106
|
+
const nextRange = this.#getRange();
|
|
103
107
|
if (nextRange == null) {
|
|
104
108
|
this.push(null);
|
|
105
109
|
} else {
|
|
106
|
-
this
|
|
110
|
+
this.#readPadding(fd, nextRange, size - bytesRead);
|
|
107
111
|
}
|
|
108
112
|
break;
|
|
109
113
|
}
|
|
@@ -111,23 +115,23 @@ class ReadStream extends Readable {
|
|
|
111
115
|
}
|
|
112
116
|
/**
|
|
113
117
|
* @private
|
|
114
|
-
* @method readFileRange
|
|
118
|
+
* @method #readFileRange
|
|
115
119
|
* @param fd The file descriptor.
|
|
116
120
|
* @param range The current range.
|
|
117
121
|
* @param size The number of bytes to read.
|
|
118
122
|
*/
|
|
119
|
-
readFileRange(fd, range, size) {
|
|
123
|
+
#readFileRange(fd, range, size) {
|
|
120
124
|
const { length } = range;
|
|
121
|
-
const
|
|
125
|
+
const bytesRead = this.#bytesRead;
|
|
122
126
|
// File range not finished.
|
|
123
127
|
if (bytesRead < length) {
|
|
124
|
-
this
|
|
128
|
+
this.#reading = true;
|
|
125
129
|
const bytesToRead = Math.min(size, length - bytesRead);
|
|
126
130
|
const buffer = Buffer.allocUnsafeSlow(bytesToRead);
|
|
127
131
|
const position = range.offset + bytesRead;
|
|
128
132
|
// Read file range.
|
|
129
|
-
this
|
|
130
|
-
this
|
|
133
|
+
this.#fs.read(fd, buffer, 0, bytesToRead, position, (readError, bytesRead, buffer) => {
|
|
134
|
+
this.#reading = false;
|
|
131
135
|
// Tell ._destroy() that it's safe to close the fd now.
|
|
132
136
|
if (this.destroyed) {
|
|
133
137
|
this.emit(DISPOSE_EVENT, readError);
|
|
@@ -137,19 +141,19 @@ class ReadStream extends Readable {
|
|
|
137
141
|
this.destroy(new RangeError('invalid read operation'));
|
|
138
142
|
} else {
|
|
139
143
|
this.push(buffer);
|
|
140
|
-
this
|
|
144
|
+
this.#bytesRead += bytesRead;
|
|
141
145
|
// file range read completed.
|
|
142
146
|
if (bytesRead < size) {
|
|
143
|
-
this
|
|
144
|
-
this
|
|
145
|
-
this
|
|
147
|
+
this.#bytesRead = 0;
|
|
148
|
+
this.#readState = 2 /* ReadState.SUFFIX */;
|
|
149
|
+
this.#readPadding(fd, range, size - bytesRead);
|
|
146
150
|
}
|
|
147
151
|
}
|
|
148
152
|
});
|
|
149
153
|
} else {
|
|
150
|
-
this
|
|
151
|
-
this
|
|
152
|
-
this
|
|
154
|
+
this.#bytesRead = 0;
|
|
155
|
+
this.#readState = 2 /* ReadState.SUFFIX */;
|
|
156
|
+
this.#readPadding(fd, range, size);
|
|
153
157
|
}
|
|
154
158
|
}
|
|
155
159
|
/**
|
|
@@ -158,21 +162,21 @@ class ReadStream extends Readable {
|
|
|
158
162
|
* @param size The number of bytes to read.
|
|
159
163
|
*/
|
|
160
164
|
_read(size) {
|
|
161
|
-
if (!this
|
|
162
|
-
const
|
|
163
|
-
const range = this
|
|
165
|
+
if (!this.#reading) {
|
|
166
|
+
const fd = this.#fd;
|
|
167
|
+
const range = this.#getRange();
|
|
164
168
|
// If fd or range is null, finish stream.
|
|
165
169
|
if (fd == null || range == null) {
|
|
166
170
|
this.push(null);
|
|
167
171
|
} else {
|
|
168
172
|
// Read bytes from range.
|
|
169
|
-
switch (this
|
|
173
|
+
switch (this.#readState) {
|
|
170
174
|
case 0 /* ReadState.PREFIX */:
|
|
171
175
|
case 2 /* ReadState.SUFFIX */:
|
|
172
|
-
this
|
|
176
|
+
this.#readPadding(fd, range, size);
|
|
173
177
|
break;
|
|
174
178
|
case 1 /* ReadState.RANGE */:
|
|
175
|
-
this
|
|
179
|
+
this.#readFileRange(fd, range, size);
|
|
176
180
|
break;
|
|
177
181
|
}
|
|
178
182
|
}
|
|
@@ -180,17 +184,17 @@ class ReadStream extends Readable {
|
|
|
180
184
|
}
|
|
181
185
|
/**
|
|
182
186
|
* @private
|
|
183
|
-
* @method dispose
|
|
187
|
+
* @method #dispose
|
|
184
188
|
* @param error The error.
|
|
185
189
|
* @param callback The callback.
|
|
186
190
|
*/
|
|
187
|
-
dispose(error, callback) {
|
|
188
|
-
const
|
|
191
|
+
#dispose(error, callback) {
|
|
192
|
+
const fd = this.#fd;
|
|
189
193
|
if (fd != null) {
|
|
190
|
-
this
|
|
194
|
+
this.#fd = null;
|
|
191
195
|
// Close the fd.
|
|
192
|
-
this
|
|
193
|
-
callback(error
|
|
196
|
+
this.#fs.close(fd, closeError => {
|
|
197
|
+
callback(error ?? closeError);
|
|
194
198
|
});
|
|
195
199
|
} else {
|
|
196
200
|
callback(error);
|
|
@@ -204,12 +208,12 @@ class ReadStream extends Readable {
|
|
|
204
208
|
*/
|
|
205
209
|
_destroy(error, callback) {
|
|
206
210
|
// Wait I/O completion.
|
|
207
|
-
if (this
|
|
211
|
+
if (this.#reading) {
|
|
208
212
|
this.once(DISPOSE_EVENT, disposeError => {
|
|
209
|
-
this
|
|
213
|
+
this.#dispose(error ?? disposeError, callback);
|
|
210
214
|
});
|
|
211
215
|
} else {
|
|
212
|
-
this
|
|
216
|
+
this.#dispose(error, callback);
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
}
|
package/esm/Service.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module Service
|
|
3
3
|
*/
|
|
4
4
|
import { Context } from 'koa';
|
|
5
|
-
import { Stats } from 'fs';
|
|
5
|
+
import { Stats } from 'node:fs';
|
|
6
6
|
import { FileSystem } from './utils/fs.js';
|
|
7
7
|
interface Headers {
|
|
8
8
|
[key: string]: string | string[];
|
|
@@ -29,8 +29,7 @@ export interface Options {
|
|
|
29
29
|
* @class Service
|
|
30
30
|
*/
|
|
31
31
|
export declare class Service {
|
|
32
|
-
private
|
|
33
|
-
private readonly options;
|
|
32
|
+
#private;
|
|
34
33
|
/**
|
|
35
34
|
* @constructor
|
|
36
35
|
* @description Create file service.
|
|
@@ -38,15 +37,6 @@ export declare class Service {
|
|
|
38
37
|
* @param options The file service options.
|
|
39
38
|
*/
|
|
40
39
|
constructor(root: string, options?: Options);
|
|
41
|
-
/**
|
|
42
|
-
* @private
|
|
43
|
-
* @method setupHeaders
|
|
44
|
-
* @description Setup headers.
|
|
45
|
-
* @param context The koa context.
|
|
46
|
-
* @param path The file path.
|
|
47
|
-
* @param stats The file stats.
|
|
48
|
-
*/
|
|
49
|
-
private setupHeaders;
|
|
50
40
|
/**
|
|
51
41
|
* @public
|
|
52
42
|
* @method respond
|
package/esm/Service.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module koa-files
|
|
2
3
|
* @package koa-files
|
|
3
4
|
* @license MIT
|
|
4
|
-
* @version
|
|
5
|
+
* @version 5.0.0
|
|
5
6
|
* @author nuintun <nuintun@qq.com>
|
|
6
7
|
* @description A static files serving middleware for koa.
|
|
7
8
|
* @see https://github.com/nuintun/koa-files#readme
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import createETag from 'etag';
|
|
11
|
-
import fs from 'fs';
|
|
12
|
+
import fs from 'node:fs';
|
|
12
13
|
import { ReadStream } from './ReadStream.js';
|
|
13
14
|
import { isFunction } from './utils/type.js';
|
|
14
15
|
import { stat } from './utils/fs.js';
|
|
15
|
-
import { resolve, extname, join } from 'path';
|
|
16
|
+
import { resolve, extname, join } from 'node:path';
|
|
16
17
|
import { unixify, isOutRoot, hasTrailingSlash } from './utils/path.js';
|
|
17
18
|
import { decodeURI, isConditionalGET, isPreconditionFailed, parseRanges } from './utils/http.js';
|
|
18
19
|
|
|
@@ -23,6 +24,8 @@ import { decodeURI, isConditionalGET, isPreconditionFailed, parseRanges } from '
|
|
|
23
24
|
* @class Service
|
|
24
25
|
*/
|
|
25
26
|
class Service {
|
|
27
|
+
#root;
|
|
28
|
+
#options;
|
|
26
29
|
/**
|
|
27
30
|
* @constructor
|
|
28
31
|
* @description Create file service.
|
|
@@ -30,26 +33,26 @@ class Service {
|
|
|
30
33
|
* @param options The file service options.
|
|
31
34
|
*/
|
|
32
35
|
constructor(root, options = {}) {
|
|
33
|
-
|
|
34
|
-
this.root = unixify(resolve(root));
|
|
36
|
+
this.#root = unixify(resolve(root));
|
|
35
37
|
const { ignore, headers, highWaterMark = 65536 } = options;
|
|
36
|
-
this
|
|
37
|
-
|
|
38
|
+
this.#options = {
|
|
39
|
+
...options,
|
|
40
|
+
fs: options.fs ?? fs,
|
|
38
41
|
ignore: isFunction(ignore) ? ignore : () => false,
|
|
39
42
|
headers: isFunction(headers) ? headers : () => headers,
|
|
40
43
|
highWaterMark: isFunction(highWaterMark) ? highWaterMark : () => highWaterMark
|
|
41
|
-
}
|
|
44
|
+
};
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
47
|
* @private
|
|
45
|
-
* @method setupHeaders
|
|
48
|
+
* @method #setupHeaders
|
|
46
49
|
* @description Setup headers.
|
|
47
50
|
* @param context The koa context.
|
|
48
51
|
* @param path The file path.
|
|
49
52
|
* @param stats The file stats.
|
|
50
53
|
*/
|
|
51
|
-
async setupHeaders({ response }, path, stats) {
|
|
52
|
-
const
|
|
54
|
+
async #setupHeaders({ response }, path, stats) {
|
|
55
|
+
const options = this.#options;
|
|
53
56
|
// Set status.
|
|
54
57
|
response.status = 200;
|
|
55
58
|
// Set Content-Type.
|
|
@@ -105,7 +108,7 @@ class Service {
|
|
|
105
108
|
return context.throw(400);
|
|
106
109
|
}
|
|
107
110
|
// Get service root.
|
|
108
|
-
const
|
|
111
|
+
const root = this.#root;
|
|
109
112
|
// Get file path.
|
|
110
113
|
const path = unixify(join(root, pathname));
|
|
111
114
|
// Malicious path (403).
|
|
@@ -113,7 +116,7 @@ class Service {
|
|
|
113
116
|
return false;
|
|
114
117
|
}
|
|
115
118
|
// Get options.
|
|
116
|
-
const
|
|
119
|
+
const options = this.#options;
|
|
117
120
|
// Is ignore path or file (403).
|
|
118
121
|
if (await options.ignore(path)) {
|
|
119
122
|
return false;
|
|
@@ -134,7 +137,7 @@ class Service {
|
|
|
134
137
|
// Koa response.
|
|
135
138
|
const { response } = context;
|
|
136
139
|
// Setup headers.
|
|
137
|
-
await this
|
|
140
|
+
await this.#setupHeaders(context, path, stats);
|
|
138
141
|
// Conditional get support.
|
|
139
142
|
if (isConditionalGET(context)) {
|
|
140
143
|
// Request precondition failed.
|
package/esm/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module koa-files
|
|
2
3
|
* @package koa-files
|
|
3
4
|
* @license MIT
|
|
4
|
-
* @version
|
|
5
|
+
* @version 5.0.0
|
|
5
6
|
* @author nuintun <nuintun@qq.com>
|
|
6
7
|
* @description A static files serving middleware for koa.
|
|
7
8
|
* @see https://github.com/nuintun/koa-files#readme
|
|
@@ -20,7 +21,7 @@ import { Service } from './Service.js';
|
|
|
20
21
|
*/
|
|
21
22
|
function server(root, options) {
|
|
22
23
|
const service = new Service(root, options);
|
|
23
|
-
if (options
|
|
24
|
+
if (options?.defer) {
|
|
24
25
|
return async (context, next) => {
|
|
25
26
|
await next();
|
|
26
27
|
await service.respond(context);
|
package/esm/utils/fs.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module fs
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import { PathLike, Stats } from 'node:fs';
|
|
5
|
+
type Position = number | bigint | null;
|
|
6
|
+
type OpenMode = string | number | undefined;
|
|
7
|
+
interface Callback<T extends unknown[] = []> {
|
|
8
|
+
(error?: Error | null, ...rest: T): void;
|
|
9
|
+
}
|
|
5
10
|
export interface FileSystem {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
close(fd: number, callback?: Callback): void;
|
|
12
|
+
read<T extends ArrayBufferView>(
|
|
13
|
+
fd: number,
|
|
14
|
+
buffer: T,
|
|
15
|
+
offset: number,
|
|
16
|
+
length: number,
|
|
17
|
+
position: Position,
|
|
18
|
+
callback: Callback<[bytesRead: number, buffer: T]>
|
|
19
|
+
): void;
|
|
20
|
+
stat(path: PathLike, callback: Callback<[stats: Stats]>): void;
|
|
21
|
+
open(path: PathLike, flags: OpenMode, callback: Callback<[fd: number]>): void;
|
|
10
22
|
}
|
|
11
23
|
/**
|
|
12
24
|
* @function stat
|
|
@@ -15,3 +27,4 @@ export interface FileSystem {
|
|
|
15
27
|
* @param path The file path.
|
|
16
28
|
*/
|
|
17
29
|
export declare function stat(fs: FileSystem, path: string): Promise<Stats | null>;
|
|
30
|
+
export {};
|
package/esm/utils/fs.js
CHANGED
package/esm/utils/hash.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module koa-files
|
|
2
3
|
* @package koa-files
|
|
3
4
|
* @license MIT
|
|
4
|
-
* @version
|
|
5
|
+
* @version 5.0.0
|
|
5
6
|
* @author nuintun <nuintun@qq.com>
|
|
6
7
|
* @description A static files serving middleware for koa.
|
|
7
8
|
* @see https://github.com/nuintun/koa-files#readme
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import { getRandomValues } from 'crypto';
|
|
11
|
+
import { getRandomValues } from 'node:crypto';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @module hash
|
package/esm/utils/http.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module http
|
|
3
3
|
*/
|
|
4
|
-
import { Stats } from 'fs';
|
|
5
4
|
import { Context } from 'koa';
|
|
6
|
-
import {
|
|
5
|
+
import { Stats } from 'node:fs';
|
|
6
|
+
import { Buffer } from 'node:buffer';
|
|
7
7
|
export interface Range {
|
|
8
8
|
offset: number;
|
|
9
9
|
length: number;
|
package/esm/utils/http.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module koa-files
|
|
2
3
|
* @package koa-files
|
|
3
4
|
* @license MIT
|
|
4
|
-
* @version
|
|
5
|
+
* @version 5.0.0
|
|
5
6
|
* @author nuintun <nuintun@qq.com>
|
|
6
7
|
* @description A static files serving middleware for koa.
|
|
7
8
|
* @see https://github.com/nuintun/koa-files#readme
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import { Buffer } from 'buffer';
|
|
11
11
|
import { generate } from './hash.js';
|
|
12
|
+
import { Buffer } from 'node:buffer';
|
|
12
13
|
import parseRange from 'range-parser';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -24,7 +25,7 @@ const SINGLE_ETAG_RE = /^(?:W\/)?"[ !#-\x7E\x80-\xFF]+"$/;
|
|
|
24
25
|
function decodeURI(URI) {
|
|
25
26
|
try {
|
|
26
27
|
return decodeURIComponent(URI);
|
|
27
|
-
} catch
|
|
28
|
+
} catch {
|
|
28
29
|
return -1;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -156,7 +157,9 @@ function parseRanges(context, stats) {
|
|
|
156
157
|
const length = end - start + 1;
|
|
157
158
|
const head = index > 0 ? '\r\n' : '';
|
|
158
159
|
const contentRange = `Content-Range: bytes ${start}-${end}/${size}`;
|
|
159
|
-
const prefix = Buffer.from(
|
|
160
|
+
const prefix = Buffer.from(
|
|
161
|
+
`${head}--${boundary}\r\n${contentType}\r\n${contentRange}\r\n\r\n`
|
|
162
|
+
);
|
|
160
163
|
// Compute Content-Length
|
|
161
164
|
contentLength += prefix.length + length;
|
|
162
165
|
// Cache range.
|
package/esm/utils/path.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module koa-files
|
|
2
3
|
* @package koa-files
|
|
3
4
|
* @license MIT
|
|
4
|
-
* @version
|
|
5
|
+
* @version 5.0.0
|
|
5
6
|
* @author nuintun <nuintun@qq.com>
|
|
6
7
|
* @description A static files serving middleware for koa.
|
|
7
8
|
* @see https://github.com/nuintun/koa-files#readme
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import { relative, isAbsolute } from 'path';
|
|
11
|
+
import { relative, isAbsolute } from 'node:path';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @module path
|
package/esm/utils/type.js
CHANGED