webpack-dev-service 0.7.2 → 0.8.1
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 +6 -10
- package/client/cjs/client.cjs +1 -1
- package/client/cjs/events.cjs +1 -1
- package/client/cjs/hot.cjs +1 -1
- package/client/cjs/index.cjs +1 -1
- package/client/cjs/main.cjs +1 -1
- package/client/cjs/ui/overlay.cjs +1 -1
- package/client/cjs/ui/progress.cjs +1 -1
- package/client/cjs/ui/utils.cjs +1 -1
- package/client/esm/client.js +1 -1
- package/client/esm/events.js +1 -1
- package/client/esm/hot.js +1 -1
- package/client/esm/index.js +1 -1
- package/client/esm/main.js +1 -1
- package/client/esm/ui/overlay.js +1 -1
- package/client/esm/ui/progress.js +1 -1
- package/client/esm/ui/utils.js +1 -1
- package/global.d.ts +2 -0
- package/package.json +13 -6
- package/server/cjs/dev/Files.cjs +394 -0
- package/server/cjs/dev/index.cjs +62 -0
- package/server/cjs/dev/middleware.cjs +69 -0
- package/server/cjs/dev/utils/boundary.cjs +42 -0
- package/server/cjs/dev/utils/common.cjs +98 -0
- package/server/cjs/dev/utils/compose.cjs +58 -0
- package/server/cjs/dev/utils/getPaths.cjs +71 -0
- package/server/cjs/dev/utils/http.cjs +69 -0
- package/server/cjs/dev/utils/ready.cjs +26 -0
- package/server/cjs/dev/utils/setupHooks.cjs +95 -0
- package/server/cjs/dev/utils/setupOutputFileSystem.cjs +64 -0
- package/server/cjs/dev/utils/setupWatching.cjs +43 -0
- package/server/cjs/dev/utils/setupWriteToDisk.cjs +62 -0
- package/server/cjs/{hot.cjs → hot/Socket.cjs} +6 -20
- package/server/cjs/hot/index.cjs +36 -0
- package/server/cjs/index.cjs +11 -15
- package/server/esm/dev/Files.js +384 -0
- package/server/esm/dev/index.js +60 -0
- package/server/esm/dev/middleware.js +67 -0
- package/server/esm/dev/utils/boundary.js +40 -0
- package/server/esm/dev/utils/common.js +98 -0
- package/server/esm/dev/utils/compose.js +56 -0
- package/server/esm/dev/utils/getPaths.js +69 -0
- package/server/esm/dev/utils/http.js +65 -0
- package/server/esm/dev/utils/ready.js +24 -0
- package/server/esm/dev/utils/setupHooks.js +87 -0
- package/server/esm/dev/utils/setupOutputFileSystem.js +62 -0
- package/server/esm/dev/utils/setupWatching.js +41 -0
- package/server/esm/dev/utils/setupWriteToDisk.js +60 -0
- package/server/esm/{hot.js → hot/Socket.js} +6 -20
- package/server/esm/hot/index.js +34 -0
- package/server/esm/index.js +11 -9
- package/types/server/dev/Files.d.ts +83 -0
- package/types/server/dev/index.d.ts +8 -0
- package/types/server/dev/interface.d.ts +46 -0
- package/types/server/dev/middleware.d.ts +6 -0
- package/types/server/dev/utils/boundary.d.ts +8 -0
- package/types/server/dev/utils/common.d.ts +44 -0
- package/types/server/dev/utils/compose.d.ts +18 -0
- package/types/server/dev/utils/getPaths.d.ts +10 -0
- package/types/server/dev/utils/http.d.ts +22 -0
- package/types/server/dev/utils/ready.d.ts +5 -0
- package/types/server/dev/utils/setupHooks.d.ts +5 -0
- package/types/server/dev/utils/setupOutputFileSystem.d.ts +5 -0
- package/types/server/dev/utils/setupWatching.d.ts +5 -0
- package/types/server/dev/utils/setupWriteToDisk.d.ts +5 -0
- package/types/server/hot/Socket.d.ts +27 -0
- package/types/server/{hot.d.ts → hot/index.d.ts} +7 -10
- package/types/server/index.d.ts +4 -4
- package/server/cjs/dev.cjs +0 -50
- package/server/esm/dev.js +0 -42
- package/types/server/dev.d.ts +0 -17
package/README.md
CHANGED
|
@@ -168,20 +168,16 @@ const compiler = webpack({
|
|
|
168
168
|
const port = 8000;
|
|
169
169
|
const app = new Koa();
|
|
170
170
|
const fs = createMemfs();
|
|
171
|
-
const server = dev(compiler, {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
app.use(async (ctx, next) => {
|
|
175
|
-
ctx.set({
|
|
171
|
+
const server = dev(compiler, {
|
|
172
|
+
outputFileSystem: fs,
|
|
173
|
+
headers: {
|
|
176
174
|
'Cache-Control': 'no-store',
|
|
177
175
|
'Access-Control-Allow-Origin': '*',
|
|
178
176
|
'Access-Control-Allow-Methods': '*',
|
|
179
177
|
'Access-Control-Allow-Headers': '*',
|
|
180
178
|
'X-Content-Type-Options': 'nosniff',
|
|
181
179
|
'Access-Control-Allow-Credentials': 'true'
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
await next();
|
|
180
|
+
}
|
|
185
181
|
});
|
|
186
182
|
|
|
187
183
|
app.use(server);
|
|
@@ -196,8 +192,8 @@ app.on('error', error => {
|
|
|
196
192
|
});
|
|
197
193
|
|
|
198
194
|
app.listen(port, () => {
|
|
199
|
-
server.
|
|
200
|
-
logger.info(`server run at: \u001B[36mhttp://127.0.0.1:${port}\u001B[0m`);
|
|
195
|
+
server.ready(() => {
|
|
196
|
+
server.logger.info(`server run at: \u001B[36mhttp://127.0.0.1:${port}\u001B[0m`);
|
|
201
197
|
});
|
|
202
198
|
});
|
|
203
199
|
```
|
package/client/cjs/client.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/cjs/events.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/cjs/hot.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/cjs/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/cjs/main.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/cjs/ui/utils.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/esm/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/esm/events.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/esm/hot.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/esm/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/esm/ui/overlay.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/client/esm/ui/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @package webpack-dev-service
|
|
3
3
|
* @license MIT
|
|
4
|
-
* @version 0.
|
|
4
|
+
* @version 0.8.1
|
|
5
5
|
* @author nuintun <nuintun@qq.com>
|
|
6
6
|
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
7
|
* @see https://github.com/nuintun/webpack-dev-service#readme
|
package/global.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webpack-dev-service",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "A koa 2 middleware for webpack development and hot reloading.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -89,22 +89,29 @@
|
|
|
89
89
|
"@nuintun/ansi": "^0.0.3",
|
|
90
90
|
"@types/koa": "^2.15.0",
|
|
91
91
|
"@types/ws": "^8.5.10",
|
|
92
|
-
"
|
|
92
|
+
"destroy": "^1.2.0",
|
|
93
|
+
"etag": "^1.8.1",
|
|
94
|
+
"memfs": "^4.8.0",
|
|
95
|
+
"range-parser": "^1.2.1",
|
|
96
|
+
"supports-color": "^9.4.0",
|
|
93
97
|
"tslib": "^2.6.2",
|
|
94
|
-
"webpack-dev-middleware": "^7.1.0",
|
|
95
98
|
"ws": "^8.16.0"
|
|
96
99
|
},
|
|
97
100
|
"devDependencies": {
|
|
98
101
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
99
102
|
"@swc/core": "^1.4.8",
|
|
100
103
|
"@swc/helpers": "^0.5.7",
|
|
104
|
+
"@types/destroy": "^1.0.3",
|
|
105
|
+
"@types/etag": "^1.8.3",
|
|
101
106
|
"@types/koa-compose": "^3.2.8",
|
|
102
107
|
"@types/node": "^20.11.30",
|
|
108
|
+
"@types/range-parser": "^1.2.7",
|
|
103
109
|
"@types/react": "^18.2.67",
|
|
104
110
|
"@types/react-dom": "^18.2.22",
|
|
105
111
|
"css-loader": "^6.10.0",
|
|
106
112
|
"html-webpack-plugin": "^5.6.0",
|
|
107
|
-
"koa": "^2.15.
|
|
113
|
+
"koa": "^2.15.2",
|
|
114
|
+
"koa-compress": "^5.1.1",
|
|
108
115
|
"magic-string": "^0.30.8",
|
|
109
116
|
"memfs": "^4.8.0",
|
|
110
117
|
"mini-css-extract-plugin": "^2.8.1",
|
|
@@ -115,8 +122,8 @@
|
|
|
115
122
|
"rollup": "^4.13.0",
|
|
116
123
|
"swc-loader": "^0.2.6",
|
|
117
124
|
"tsc-alias": "^1.8.8",
|
|
118
|
-
"typescript": "^5.4.
|
|
119
|
-
"webpack": "^5.
|
|
125
|
+
"typescript": "^5.4.3",
|
|
126
|
+
"webpack": "^5.91.0"
|
|
120
127
|
},
|
|
121
128
|
"peerDependencies": {
|
|
122
129
|
"webpack": "^5.0.0"
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @package webpack-dev-service
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @version 0.8.1
|
|
5
|
+
* @author nuintun <nuintun@qq.com>
|
|
6
|
+
* @description A koa 2 middleware for webpack development and hot reloading.
|
|
7
|
+
* @see https://github.com/nuintun/webpack-dev-service#readme
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const etag = require('etag');
|
|
13
|
+
const destroy = require('destroy');
|
|
14
|
+
const stream = require('stream');
|
|
15
|
+
const parseRange = require('range-parser');
|
|
16
|
+
const boundary = require('./utils/boundary.cjs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const http = require('./utils/http.cjs');
|
|
19
|
+
const common = require('./utils/common.cjs');
|
|
20
|
+
|
|
21
|
+
function _interopDefault(e) {
|
|
22
|
+
return e && e.__esModule ? e : { default: e };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const etag__default = /*#__PURE__*/ _interopDefault(etag);
|
|
26
|
+
const destroy__default = /*#__PURE__*/ _interopDefault(destroy);
|
|
27
|
+
const parseRange__default = /*#__PURE__*/ _interopDefault(parseRange);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @module Files
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* @class Files
|
|
34
|
+
*/
|
|
35
|
+
class Files {
|
|
36
|
+
/**
|
|
37
|
+
* @constructor
|
|
38
|
+
* @description Create files service.
|
|
39
|
+
* @param root Files service root.
|
|
40
|
+
* @param options Files service options.
|
|
41
|
+
*/
|
|
42
|
+
constructor(root, options) {
|
|
43
|
+
this.options = options;
|
|
44
|
+
this.root = common.unixify(path.resolve(root));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @private
|
|
48
|
+
* @method isConditionalGET
|
|
49
|
+
* @description Check if request is conditional GET.
|
|
50
|
+
* @param context Koa context.
|
|
51
|
+
*/
|
|
52
|
+
isConditionalGET(context) {
|
|
53
|
+
const { request } = context;
|
|
54
|
+
return !!(
|
|
55
|
+
request.get('If-Match') ||
|
|
56
|
+
request.get('If-None-Match') ||
|
|
57
|
+
request.get('If-Modified-Since') ||
|
|
58
|
+
request.get('if-Unmodified-Since')
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @private
|
|
63
|
+
* @method isPreconditionFailure
|
|
64
|
+
* @description Check if request precondition failure.
|
|
65
|
+
* @param context Koa context.
|
|
66
|
+
*/
|
|
67
|
+
isPreconditionFailure(context) {
|
|
68
|
+
const { request, response } = context;
|
|
69
|
+
// If-Match.
|
|
70
|
+
const match = request.get('If-Match');
|
|
71
|
+
if (match) {
|
|
72
|
+
const etag = response.get('ETag');
|
|
73
|
+
return !etag || (match !== '*' && !http.isETagFresh(match, etag));
|
|
74
|
+
}
|
|
75
|
+
// If-Unmodified-Since.
|
|
76
|
+
const unmodifiedSince = Date.parse(request.get('If-Unmodified-Since'));
|
|
77
|
+
if (!isNaN(unmodifiedSince)) {
|
|
78
|
+
const lastModified = Date.parse(response.get('Last-Modified'));
|
|
79
|
+
return isNaN(lastModified) || lastModified > unmodifiedSince;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* @private
|
|
85
|
+
* @method isRangeFresh
|
|
86
|
+
* @description Check if request range fresh.
|
|
87
|
+
* @param context Koa context.
|
|
88
|
+
*/
|
|
89
|
+
isRangeFresh(context) {
|
|
90
|
+
const { request, response } = context;
|
|
91
|
+
const ifRange = request.get('If-Range');
|
|
92
|
+
// No If-Range.
|
|
93
|
+
if (!ifRange) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
// If-Range as etag.
|
|
97
|
+
if (http.isETag(ifRange)) {
|
|
98
|
+
const etag = response.get('ETag');
|
|
99
|
+
return !!(etag && http.isETagFresh(ifRange, etag));
|
|
100
|
+
}
|
|
101
|
+
// If-Range as modified date.
|
|
102
|
+
const lastModified = response.get('Last-Modified');
|
|
103
|
+
return Date.parse(lastModified) <= Date.parse(ifRange);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* @private
|
|
107
|
+
* @method parseRange
|
|
108
|
+
* @description Parse range.
|
|
109
|
+
* @param context Koa context.
|
|
110
|
+
* @param stats File stats.
|
|
111
|
+
*/
|
|
112
|
+
parseRange(context, stats) {
|
|
113
|
+
const { size } = stats;
|
|
114
|
+
// Range support.
|
|
115
|
+
if (this.options.acceptRanges !== false) {
|
|
116
|
+
const range = context.request.get('Range');
|
|
117
|
+
// Range fresh.
|
|
118
|
+
if (range && this.isRangeFresh(context)) {
|
|
119
|
+
// Parse range -1 -2 or [].
|
|
120
|
+
const parsed = parseRange__default.default(size, range, { combine: true });
|
|
121
|
+
// -1 signals an unsatisfiable range.
|
|
122
|
+
// -2 signals a malformed header string.
|
|
123
|
+
if (parsed === -1 || parsed === -2) {
|
|
124
|
+
return parsed;
|
|
125
|
+
}
|
|
126
|
+
// Ranges ok, support multiple ranges.
|
|
127
|
+
if (parsed.type === 'bytes') {
|
|
128
|
+
// Set 206 status.
|
|
129
|
+
context.status = 206;
|
|
130
|
+
const { length } = parsed;
|
|
131
|
+
// Multiple ranges.
|
|
132
|
+
if (length > 1) {
|
|
133
|
+
// Content-Length.
|
|
134
|
+
let contentLength = 0;
|
|
135
|
+
// Ranges.
|
|
136
|
+
const ranges = [];
|
|
137
|
+
// Range boundary.
|
|
138
|
+
const boundary$1 = `<${boundary.generate()}>`;
|
|
139
|
+
// Range suffix.
|
|
140
|
+
const suffix = `\r\n--${boundary$1}--\r\n`;
|
|
141
|
+
// Multipart Content-Type.
|
|
142
|
+
const contentType = `Content-Type: ${context.type}`;
|
|
143
|
+
// Override Content-Type.
|
|
144
|
+
context.type = `multipart/byteranges; boundary=${boundary$1}`;
|
|
145
|
+
// Map ranges.
|
|
146
|
+
for (let index = 0; index < length; index++) {
|
|
147
|
+
const { start, end } = parsed[index];
|
|
148
|
+
// The first prefix boundary no \r\n.
|
|
149
|
+
const prefixHead = index > 0 ? '\r\n' : '';
|
|
150
|
+
const contentRange = `Content-Range: bytes ${start}-${end}/${size}`;
|
|
151
|
+
const prefix = `${prefixHead}--${boundary$1}\r\n${contentType}\r\n${contentRange}\r\n\r\n`;
|
|
152
|
+
// Compute Content-Length
|
|
153
|
+
contentLength += end - start + 1 + Buffer.byteLength(prefix);
|
|
154
|
+
// Cache range.
|
|
155
|
+
ranges.push({ start, end, prefix });
|
|
156
|
+
}
|
|
157
|
+
// The last add suffix boundary.
|
|
158
|
+
ranges[length - 1].suffix = suffix;
|
|
159
|
+
// Compute Content-Length.
|
|
160
|
+
contentLength += Buffer.byteLength(suffix);
|
|
161
|
+
// Set Content-Length.
|
|
162
|
+
context.length = contentLength;
|
|
163
|
+
// Return ranges.
|
|
164
|
+
return ranges;
|
|
165
|
+
} else {
|
|
166
|
+
const [{ start, end }] = parsed;
|
|
167
|
+
// Set Content-Length.
|
|
168
|
+
context.length = end - start + 1;
|
|
169
|
+
// Set Content-Range.
|
|
170
|
+
context.set('Content-Range', `bytes ${start}-${end}/${size}`);
|
|
171
|
+
// Return ranges.
|
|
172
|
+
return parsed;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Set Content-Length.
|
|
178
|
+
context.length = size;
|
|
179
|
+
// Return ranges.
|
|
180
|
+
return [{ start: 0, end: Math.max(size - 1) }];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* @private
|
|
184
|
+
* @method setupHeaders
|
|
185
|
+
* @description Setup headers
|
|
186
|
+
* @param context Koa context
|
|
187
|
+
* @param path File path
|
|
188
|
+
* @param stats File stats
|
|
189
|
+
*/
|
|
190
|
+
setupHeaders(context, path$1, stats) {
|
|
191
|
+
const { options } = this;
|
|
192
|
+
const { headers, cacheControl } = options;
|
|
193
|
+
// Set headers.
|
|
194
|
+
if (headers) {
|
|
195
|
+
if (common.isFunction(headers)) {
|
|
196
|
+
context.set(headers(path$1, stats));
|
|
197
|
+
} else {
|
|
198
|
+
context.set(headers);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Set status.
|
|
202
|
+
context.status = 200;
|
|
203
|
+
// Set Content-Type.
|
|
204
|
+
context.type = path.extname(path$1);
|
|
205
|
+
// ETag.
|
|
206
|
+
if (options.etag === false) {
|
|
207
|
+
context.remove('ETag');
|
|
208
|
+
} else {
|
|
209
|
+
// Set ETag.
|
|
210
|
+
context.set('ETag', etag__default.default(stats));
|
|
211
|
+
}
|
|
212
|
+
// Accept-Ranges.
|
|
213
|
+
if (options.acceptRanges === false) {
|
|
214
|
+
context.remove('Accept-Ranges');
|
|
215
|
+
} else {
|
|
216
|
+
// Set Accept-Ranges.
|
|
217
|
+
context.set('Accept-Ranges', 'bytes');
|
|
218
|
+
}
|
|
219
|
+
// Cache-Control.
|
|
220
|
+
if (cacheControl && common.isString(cacheControl)) {
|
|
221
|
+
// Set Cache-Control.
|
|
222
|
+
context.set('Cache-Control', cacheControl);
|
|
223
|
+
}
|
|
224
|
+
// Last-Modified.
|
|
225
|
+
if (options.lastModified === false) {
|
|
226
|
+
context.remove('Last-Modified');
|
|
227
|
+
} else {
|
|
228
|
+
// Set mtime utc string.
|
|
229
|
+
context.set('Last-Modified', stats.mtime.toUTCString());
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* @private
|
|
234
|
+
* @method readTo
|
|
235
|
+
* @description Read file.
|
|
236
|
+
* @param stream Destination stream.
|
|
237
|
+
* @param path File path.
|
|
238
|
+
* @param range Read range.
|
|
239
|
+
* @param end Is destory destination stream after read.
|
|
240
|
+
*/
|
|
241
|
+
readTo(stream, path, range, end) {
|
|
242
|
+
const { fs } = this.options;
|
|
243
|
+
return new Promise((resolve, reject) => {
|
|
244
|
+
// Create file stream.
|
|
245
|
+
const file = fs.createReadStream(path, range);
|
|
246
|
+
// File read stream open.
|
|
247
|
+
if (range.prefix) {
|
|
248
|
+
file.once('open', () => {
|
|
249
|
+
// Write prefix boundary.
|
|
250
|
+
stream.write(range.prefix);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// File read stream error.
|
|
254
|
+
file.once('error', error => {
|
|
255
|
+
// Unpipe.
|
|
256
|
+
file.unpipe(stream);
|
|
257
|
+
// Reject.
|
|
258
|
+
reject(error);
|
|
259
|
+
// Destroy file stream.
|
|
260
|
+
destroy__default.default(file);
|
|
261
|
+
});
|
|
262
|
+
// File read stream end.
|
|
263
|
+
if (range.suffix) {
|
|
264
|
+
file.once('end', () => {
|
|
265
|
+
// Push suffix boundary.
|
|
266
|
+
stream.write(range.suffix);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
// File read stream close.
|
|
270
|
+
file.once('close', () => {
|
|
271
|
+
// Unpipe.
|
|
272
|
+
file.unpipe(stream);
|
|
273
|
+
// Resolve.
|
|
274
|
+
resolve(true);
|
|
275
|
+
// Destroy file stream.
|
|
276
|
+
destroy__default.default(file);
|
|
277
|
+
});
|
|
278
|
+
// Write data to buffer.
|
|
279
|
+
file.pipe(stream, { end });
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* @private
|
|
284
|
+
* @method send
|
|
285
|
+
* @description Send file.
|
|
286
|
+
* @param context Koa context.
|
|
287
|
+
* @param path File path.
|
|
288
|
+
* @param ranges Read ranges.
|
|
289
|
+
*/
|
|
290
|
+
async send(context, path, ranges) {
|
|
291
|
+
// Set stream body, highWaterMark 64kb.
|
|
292
|
+
const stream$1 = new stream.PassThrough({
|
|
293
|
+
highWaterMark: 65536
|
|
294
|
+
});
|
|
295
|
+
// Set response body.
|
|
296
|
+
context.body = stream$1;
|
|
297
|
+
// Ranges length.
|
|
298
|
+
let { length } = ranges;
|
|
299
|
+
// Read file ranges.
|
|
300
|
+
try {
|
|
301
|
+
for (const range of ranges) {
|
|
302
|
+
await this.readTo(stream$1, path, range, --length === 0);
|
|
303
|
+
}
|
|
304
|
+
} catch (_a) {
|
|
305
|
+
// End stream when read exception.
|
|
306
|
+
stream$1.end();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* @public
|
|
311
|
+
* @method response
|
|
312
|
+
* @description Response to koa context.
|
|
313
|
+
* @param context Koa context.
|
|
314
|
+
*/
|
|
315
|
+
async response(context) {
|
|
316
|
+
const { root } = this;
|
|
317
|
+
// Only support GET and HEAD (405).
|
|
318
|
+
if (context.method !== 'GET' && context.method !== 'HEAD') {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
// Get path of file.
|
|
322
|
+
const path$1 = common.unixify(path.join(root, context.path));
|
|
323
|
+
// Malicious path (403).
|
|
324
|
+
if (common.isOutRoot(path$1, root)) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
// Get file system.
|
|
328
|
+
const { fs } = this.options;
|
|
329
|
+
// File stats.
|
|
330
|
+
let stats;
|
|
331
|
+
// Get file stats.
|
|
332
|
+
try {
|
|
333
|
+
stats = await common.fstat(fs, path$1);
|
|
334
|
+
} catch (_a) {
|
|
335
|
+
// 404 | 500.
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
// File not exist (404 | 500).
|
|
339
|
+
if (!stats) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
// Is directory (403).
|
|
343
|
+
if (stats.isDirectory()) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
// Not a directory but has trailing slash (404).
|
|
347
|
+
if (common.hasTrailingSlash(path$1)) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
// Setup headers.
|
|
351
|
+
this.setupHeaders(context, path$1, stats);
|
|
352
|
+
// Conditional get support.
|
|
353
|
+
if (this.isConditionalGET(context)) {
|
|
354
|
+
// Request precondition failure.
|
|
355
|
+
if (this.isPreconditionFailure(context)) {
|
|
356
|
+
return context.throw(412);
|
|
357
|
+
}
|
|
358
|
+
// Request fresh (304).
|
|
359
|
+
if (context.fresh) {
|
|
360
|
+
// Set status.
|
|
361
|
+
context.status = 304;
|
|
362
|
+
// Set body null.
|
|
363
|
+
context.body = null;
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Head request.
|
|
368
|
+
if (context.method === 'HEAD') {
|
|
369
|
+
// Set Content-Length.
|
|
370
|
+
context.length = stats.size;
|
|
371
|
+
// Set body null
|
|
372
|
+
context.body = null;
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
// Parsed ranges.
|
|
376
|
+
const ranges = this.parseRange(context, stats);
|
|
377
|
+
// 416
|
|
378
|
+
if (ranges === -1) {
|
|
379
|
+
// Set Content-Range.
|
|
380
|
+
context.set('Content-Range', `bytes */${stats.size}`);
|
|
381
|
+
// Unsatisfiable 416.
|
|
382
|
+
return context.throw(416);
|
|
383
|
+
}
|
|
384
|
+
// 400.
|
|
385
|
+
if (ranges === -2) {
|
|
386
|
+
return context.throw(400);
|
|
387
|
+
}
|
|
388
|
+
// Send file.
|
|
389
|
+
this.send(context, path$1, ranges);
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
module.exports = Files;
|