koa 2.8.1 → 2.11.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/History.md +41 -0
- package/Readme.md +1 -1
- package/lib/application.js +15 -4
- package/lib/context.js +1 -0
- package/lib/request.js +10 -7
- package/lib/response.js +43 -20
- package/package.json +9 -7
package/History.md
CHANGED
|
@@ -1,4 +1,45 @@
|
|
|
1
1
|
|
|
2
|
+
2.11.0 / 2019-10-28
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
**features**
|
|
6
|
+
* [[`422e539`](http://github.com/koajs/koa/commit/422e539e8989e65ba43ecc39ddbaa3c4f755d465)] - feat: support app.proxyIPHeader and app.maxIpsCount to make ctx.ips more security (Yiyu He <<dead_horse@qq.com>>)
|
|
7
|
+
* [[`d48d88e`](http://github.com/koajs/koa/commit/d48d88ee17b780c02123e6d657274cab456e943e)] - feat: implement response.has (#1397) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
8
|
+
|
|
9
|
+
**others**
|
|
10
|
+
* [[`4dc56f6`](http://github.com/koajs/koa/commit/4dc56f6d04e8f5fe12ba53a8a776653b3d7b60ed)] - chore: update ESLint and plugins/configs (#1407) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
11
|
+
* [[`be7d334`](http://github.com/koajs/koa/commit/be7d334778481639294cdf87f5c359a230aeb65b)] - chore: removes code duplication at handling HEAD method (#1400) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
12
|
+
* [[`f155785`](http://github.com/koajs/koa/commit/f155785e2bb42b5ddf0a8156401c6dafdf57ba8b)] - chore: support `writableEnded` (#1402) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
13
|
+
* [[`b968688`](http://github.com/koajs/koa/commit/b968688afe2c727ae141f50aa983d481dbc1dbbf)] - chore: add FUNDING.yml (#1403) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
14
|
+
* [[`4f96829`](http://github.com/koajs/koa/commit/4f968298f97394e488297ec32c8e927a3a322076)] - chore: remove isJSON in res.length (#1399) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
15
|
+
* [[`8be5626`](http://github.com/koajs/koa/commit/8be5626bbb54e6c899a1b71d22411709126d9fea)] - build: enable codecov partial coverage and use bash uploader (#1396) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
16
|
+
* [[`ef5c43b`](http://github.com/koajs/koa/commit/ef5c43bcbcf31819e032c3b7ae7654b7f8e9358b)] - chore: use rest params (#1393) (Konstantin Vyatkin <<tino@vtkn.io>>)
|
|
17
|
+
|
|
18
|
+
2.10.0 / 2019-10-12
|
|
19
|
+
==================
|
|
20
|
+
|
|
21
|
+
**features**
|
|
22
|
+
* [[`d7f7f77`](http://github.com/koajs/koa/commit/d7f7f77689e2eaef050686be2bdf3e72881a79ac)] - feat: support sameSite=none cookies (bump cookies dependency) (#1390) (Filip Skokan <<panva.ip@gmail.com>>)
|
|
23
|
+
|
|
24
|
+
2.9.0 / 2019-10-12
|
|
25
|
+
==================
|
|
26
|
+
|
|
27
|
+
**features**
|
|
28
|
+
* [[`2d1c598`](http://github.com/koajs/koa/commit/2d1c5981869e0fe6f5bc71b5c5582accfd125cc6)] - feat: export HttpError from http-errors library (Micheal Hill <<micheal.hill@trunkplatform.com>>)
|
|
29
|
+
|
|
30
|
+
**others**
|
|
31
|
+
* [[`cf70dbc`](http://github.com/koajs/koa/commit/cf70dbc6d2ba62bf1eb12b563dd5ecd27af6e2be)] - Chore: Use https in readme (#1389) (谭九鼎 <<109224573@qq.com>>)
|
|
32
|
+
|
|
33
|
+
2.8.2 / 2019-09-28
|
|
34
|
+
==================
|
|
35
|
+
|
|
36
|
+
**fixes**
|
|
37
|
+
* [[`54e8fab`](http://github.com/koajs/koa/commit/54e8fab3e3d907bbb264caf3e28a24773d0d6fdb)] - fix: encode redirect url if not already encoded (#1384) (fengmk2 <<fengmk2@gmail.com>>)
|
|
38
|
+
|
|
39
|
+
**others**
|
|
40
|
+
* [[`817b498`](http://github.com/koajs/koa/commit/817b49830571b45a8aec6b1fc1525434f5798c58)] - test: fix body test (#1375) (Robert Nagy <<ronagy@icloud.com>>)
|
|
41
|
+
* [[`f75d445`](http://github.com/koajs/koa/commit/f75d4455359ecdf30eeb676e2c7f31d4cf7b42ed)] - test: fix end after end (#1374) (Robert Nagy <<ronagy@icloud.com>>)
|
|
42
|
+
|
|
2
43
|
2.8.1 / 2019-08-19
|
|
3
44
|
==================
|
|
4
45
|
|
package/Readme.md
CHANGED
|
@@ -41,7 +41,7 @@ app.listen(3000);
|
|
|
41
41
|
|
|
42
42
|
- [Kick-Off-Koa](https://github.com/koajs/kick-off-koa) - An intro to Koa via a set of self-guided workshops.
|
|
43
43
|
- [Workshop](https://github.com/koajs/workshop) - A workshop to learn the basics of Koa, Express' spiritual successor.
|
|
44
|
-
- [Introduction Screencast](
|
|
44
|
+
- [Introduction Screencast](https://knowthen.com/episode-3-koajs-quickstart-guide/) - An introduction to installing and getting started with Koa
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
## Middleware
|
package/lib/application.js
CHANGED
|
@@ -10,7 +10,6 @@ const debug = require('debug')('koa:application');
|
|
|
10
10
|
const onFinished = require('on-finished');
|
|
11
11
|
const response = require('./response');
|
|
12
12
|
const compose = require('koa-compose');
|
|
13
|
-
const isJSON = require('koa-is-json');
|
|
14
13
|
const context = require('./context');
|
|
15
14
|
const request = require('./request');
|
|
16
15
|
const statuses = require('statuses');
|
|
@@ -21,6 +20,7 @@ const http = require('http');
|
|
|
21
20
|
const only = require('only');
|
|
22
21
|
const convert = require('koa-convert');
|
|
23
22
|
const deprecate = require('depd')('koa');
|
|
23
|
+
const { HttpError } = require('http-errors');
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Expose `Application` class.
|
|
@@ -41,6 +41,8 @@ module.exports = class Application extends Emitter {
|
|
|
41
41
|
* @param {string[]} [options.keys] Signed cookie keys
|
|
42
42
|
* @param {boolean} [options.proxy] Trust proxy headers
|
|
43
43
|
* @param {number} [options.subdomainOffset] Subdomain offset
|
|
44
|
+
* @param {boolean} [options.proxyIpHeader] proxy ip header, default to X-Forwarded-For
|
|
45
|
+
* @param {boolean} [options.maxIpsCount] max ips read from proxy ip header, default to 0 (means infinity)
|
|
44
46
|
*
|
|
45
47
|
*/
|
|
46
48
|
|
|
@@ -49,6 +51,8 @@ module.exports = class Application extends Emitter {
|
|
|
49
51
|
options = options || {};
|
|
50
52
|
this.proxy = options.proxy || false;
|
|
51
53
|
this.subdomainOffset = options.subdomainOffset || 2;
|
|
54
|
+
this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For';
|
|
55
|
+
this.maxIpsCount = options.maxIpsCount || 0;
|
|
52
56
|
this.env = options.env || process.env.NODE_ENV || 'development';
|
|
53
57
|
if (options.keys) this.keys = options.keys;
|
|
54
58
|
this.middleware = [];
|
|
@@ -224,9 +228,10 @@ function respond(ctx) {
|
|
|
224
228
|
return res.end();
|
|
225
229
|
}
|
|
226
230
|
|
|
227
|
-
if ('HEAD'
|
|
228
|
-
if (!res.headersSent &&
|
|
229
|
-
|
|
231
|
+
if ('HEAD' === ctx.method) {
|
|
232
|
+
if (!res.headersSent && !ctx.response.has('Content-Length')) {
|
|
233
|
+
const { length } = ctx.response;
|
|
234
|
+
if (Number.isInteger(length)) ctx.length = length;
|
|
230
235
|
}
|
|
231
236
|
return res.end();
|
|
232
237
|
}
|
|
@@ -257,3 +262,9 @@ function respond(ctx) {
|
|
|
257
262
|
}
|
|
258
263
|
res.end(body);
|
|
259
264
|
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Make HttpError available to consumers of the library so that consumers don't
|
|
268
|
+
* have a direct dependency upon `http-errors`
|
|
269
|
+
*/
|
|
270
|
+
module.exports.HttpError = HttpError;
|
package/lib/context.js
CHANGED
package/lib/request.js
CHANGED
|
@@ -432,10 +432,14 @@ module.exports = {
|
|
|
432
432
|
|
|
433
433
|
get ips() {
|
|
434
434
|
const proxy = this.app.proxy;
|
|
435
|
-
const val = this.get(
|
|
436
|
-
|
|
435
|
+
const val = this.get(this.app.proxyIpHeader);
|
|
436
|
+
let ips = proxy && val
|
|
437
437
|
? val.split(/\s*,\s*/)
|
|
438
438
|
: [];
|
|
439
|
+
if (this.app.maxIpsCount > 0) {
|
|
440
|
+
ips = ips.slice(-this.app.maxIpsCount);
|
|
441
|
+
}
|
|
442
|
+
return ips;
|
|
439
443
|
},
|
|
440
444
|
|
|
441
445
|
/**
|
|
@@ -622,15 +626,14 @@ module.exports = {
|
|
|
622
626
|
*
|
|
623
627
|
* this.is('html'); // => false
|
|
624
628
|
*
|
|
625
|
-
* @param {String|
|
|
629
|
+
* @param {String|String[]} [type]
|
|
630
|
+
* @param {String[]} [types]
|
|
626
631
|
* @return {String|false|null}
|
|
627
632
|
* @api public
|
|
628
633
|
*/
|
|
629
634
|
|
|
630
|
-
is(types) {
|
|
631
|
-
|
|
632
|
-
if (!Array.isArray(types)) types = [].slice.call(arguments);
|
|
633
|
-
return typeis(this.req, types);
|
|
635
|
+
is(type, ...types) {
|
|
636
|
+
return typeis(this.req, type, ...types);
|
|
634
637
|
},
|
|
635
638
|
|
|
636
639
|
/**
|
package/lib/response.js
CHANGED
|
@@ -9,7 +9,6 @@ const contentDisposition = require('content-disposition');
|
|
|
9
9
|
const ensureErrorHandler = require('error-inject');
|
|
10
10
|
const getType = require('cache-content-type');
|
|
11
11
|
const onFinish = require('on-finished');
|
|
12
|
-
const isJSON = require('koa-is-json');
|
|
13
12
|
const escape = require('escape-html');
|
|
14
13
|
const typeis = require('type-is').is;
|
|
15
14
|
const statuses = require('statuses');
|
|
@@ -19,6 +18,8 @@ const extname = require('path').extname;
|
|
|
19
18
|
const vary = require('vary');
|
|
20
19
|
const only = require('only');
|
|
21
20
|
const util = require('util');
|
|
21
|
+
const encodeUrl = require('encodeurl');
|
|
22
|
+
const Stream = require('stream');
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Prototype.
|
|
@@ -148,7 +149,7 @@ module.exports = {
|
|
|
148
149
|
if (!this._explicitStatus) this.status = 200;
|
|
149
150
|
|
|
150
151
|
// set the content-type only if not yet set
|
|
151
|
-
const setType = !this.
|
|
152
|
+
const setType = !this.has('Content-Type');
|
|
152
153
|
|
|
153
154
|
// string
|
|
154
155
|
if ('string' == typeof val) {
|
|
@@ -200,18 +201,15 @@ module.exports = {
|
|
|
200
201
|
*/
|
|
201
202
|
|
|
202
203
|
get length() {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (null == len) {
|
|
207
|
-
if (!body) return;
|
|
208
|
-
if ('string' == typeof body) return Buffer.byteLength(body);
|
|
209
|
-
if (Buffer.isBuffer(body)) return body.length;
|
|
210
|
-
if (isJSON(body)) return Buffer.byteLength(JSON.stringify(body));
|
|
211
|
-
return;
|
|
204
|
+
if (this.has('Content-Length')) {
|
|
205
|
+
return parseInt(this.get('Content-Length'), 10) || 0;
|
|
212
206
|
}
|
|
213
207
|
|
|
214
|
-
|
|
208
|
+
const { body } = this;
|
|
209
|
+
if (!body || body instanceof Stream) return undefined;
|
|
210
|
+
if ('string' === typeof body) return Buffer.byteLength(body);
|
|
211
|
+
if (Buffer.isBuffer(body)) return body.length;
|
|
212
|
+
return Buffer.byteLength(JSON.stringify(body));
|
|
215
213
|
},
|
|
216
214
|
|
|
217
215
|
/**
|
|
@@ -260,7 +258,7 @@ module.exports = {
|
|
|
260
258
|
redirect(url, alt) {
|
|
261
259
|
// location
|
|
262
260
|
if ('back' == url) url = this.ctx.get('Referrer') || alt || '/';
|
|
263
|
-
this.set('Location', url);
|
|
261
|
+
this.set('Location', encodeUrl(url));
|
|
264
262
|
|
|
265
263
|
// status
|
|
266
264
|
if (!statuses.redirect[this.status]) this.status = 302;
|
|
@@ -388,16 +386,14 @@ module.exports = {
|
|
|
388
386
|
* Check whether the response is one of the listed types.
|
|
389
387
|
* Pretty much the same as `this.request.is()`.
|
|
390
388
|
*
|
|
391
|
-
* @param {String|
|
|
389
|
+
* @param {String|String[]} [type]
|
|
390
|
+
* @param {String[]} [types]
|
|
392
391
|
* @return {String|false}
|
|
393
392
|
* @api public
|
|
394
393
|
*/
|
|
395
394
|
|
|
396
|
-
is(types) {
|
|
397
|
-
|
|
398
|
-
if (!types) return type || false;
|
|
399
|
-
if (!Array.isArray(types)) types = [].slice.call(arguments);
|
|
400
|
-
return typeis(type, types);
|
|
395
|
+
is(type, ...types) {
|
|
396
|
+
return typeis(this.type, type, ...types);
|
|
401
397
|
},
|
|
402
398
|
|
|
403
399
|
/**
|
|
@@ -420,6 +416,29 @@ module.exports = {
|
|
|
420
416
|
return this.header[field.toLowerCase()] || '';
|
|
421
417
|
},
|
|
422
418
|
|
|
419
|
+
/**
|
|
420
|
+
* Returns true if the header identified by name is currently set in the outgoing headers.
|
|
421
|
+
* The header name matching is case-insensitive.
|
|
422
|
+
*
|
|
423
|
+
* Examples:
|
|
424
|
+
*
|
|
425
|
+
* this.has('Content-Type');
|
|
426
|
+
* // => true
|
|
427
|
+
*
|
|
428
|
+
* this.get('content-type');
|
|
429
|
+
* // => true
|
|
430
|
+
*
|
|
431
|
+
* @param {String} field
|
|
432
|
+
* @return {boolean}
|
|
433
|
+
* @api public
|
|
434
|
+
*/
|
|
435
|
+
has(field) {
|
|
436
|
+
return typeof this.res.hasHeader === 'function'
|
|
437
|
+
? this.res.hasHeader(field)
|
|
438
|
+
// Node < 7.7
|
|
439
|
+
: field.toLowerCase() in this.headers;
|
|
440
|
+
},
|
|
441
|
+
|
|
423
442
|
/**
|
|
424
443
|
* Set header `field` to `val`, or pass
|
|
425
444
|
* an object of header fields.
|
|
@@ -501,7 +520,11 @@ module.exports = {
|
|
|
501
520
|
|
|
502
521
|
get writable() {
|
|
503
522
|
// can't write any more after response finished
|
|
504
|
-
|
|
523
|
+
// response.writableEnded is available since Node > 12.9
|
|
524
|
+
// https://nodejs.org/api/http.html#http_response_writableended
|
|
525
|
+
// response.finished is undocumented feature of previous Node versions
|
|
526
|
+
// https://stackoverflow.com/questions/16254385/undocumented-response-finished-in-node-js
|
|
527
|
+
if (this.res.writableEnded || this.res.finished) return false;
|
|
505
528
|
|
|
506
529
|
const socket = this.res.socket;
|
|
507
530
|
// There are already pending outgoing res, but still writable
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Koa web app framework",
|
|
5
5
|
"main": "lib/application.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,11 +26,12 @@
|
|
|
26
26
|
"cache-content-type": "^1.0.0",
|
|
27
27
|
"content-disposition": "~0.5.2",
|
|
28
28
|
"content-type": "^1.0.4",
|
|
29
|
-
"cookies": "~0.
|
|
29
|
+
"cookies": "~0.8.0",
|
|
30
30
|
"debug": "~3.1.0",
|
|
31
31
|
"delegates": "^1.0.0",
|
|
32
32
|
"depd": "^1.1.2",
|
|
33
33
|
"destroy": "^1.0.4",
|
|
34
|
+
"encodeurl": "^1.0.2",
|
|
34
35
|
"error-inject": "^1.0.0",
|
|
35
36
|
"escape-html": "^1.0.3",
|
|
36
37
|
"fresh": "~0.5.2",
|
|
@@ -39,7 +40,6 @@
|
|
|
39
40
|
"is-generator-function": "^1.0.7",
|
|
40
41
|
"koa-compose": "^4.1.0",
|
|
41
42
|
"koa-convert": "^1.2.0",
|
|
42
|
-
"koa-is-json": "^1.0.0",
|
|
43
43
|
"on-finished": "^2.3.0",
|
|
44
44
|
"only": "~0.0.2",
|
|
45
45
|
"parseurl": "^1.3.2",
|
|
@@ -49,11 +49,13 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"egg-bin": "^4.13.0",
|
|
52
|
-
"eslint": "^6.
|
|
52
|
+
"eslint": "^6.5.1",
|
|
53
53
|
"eslint-config-koa": "^2.0.0",
|
|
54
|
-
"eslint-config-standard": "^
|
|
55
|
-
"eslint-plugin-
|
|
56
|
-
"eslint-plugin-
|
|
54
|
+
"eslint-config-standard": "^14.1.0",
|
|
55
|
+
"eslint-plugin-import": "^2.18.2",
|
|
56
|
+
"eslint-plugin-node": "^10.0.0",
|
|
57
|
+
"eslint-plugin-promise": "^4.2.1",
|
|
58
|
+
"eslint-plugin-standard": "^4.0.1",
|
|
57
59
|
"mm": "^2.5.0",
|
|
58
60
|
"supertest": "^3.1.0"
|
|
59
61
|
},
|