koa 2.5.1 → 2.6.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/History.md CHANGED
@@ -1,4 +1,48 @@
1
1
 
2
+ 2.6.1 / 2018-10-23
3
+ ==================
4
+
5
+ **fixes**
6
+ * [[`4964242`](http://github.com/koajs/koa/commit/49642428342e5f291eb9d690802e83ed830623b5)] - fix: use X-Forwarded-Host first on app.proxy present (#1263) (fengmk2 <<fengmk2@gmail.com>>)
7
+
8
+ 2.6.0 / 2018-10-23
9
+ ==================
10
+
11
+ **features**
12
+ * [[`9c5c58b`](http://github.com/koajs/koa/commit/9c5c58b18363494976185e7ddc790ac63de840ed)] - feat: use :authority header of http2 requests as host (#1262) (Martin Michaelis <<code@mgjm.de>>)
13
+ * [[`9146024`](http://github.com/koajs/koa/commit/9146024e1094e8bb871ab15d1b7fc556a710732f)] - feat: response.attachment append a parameter: options from contentDisposition (#1240) (小雷 <<863837949@qq.com>>)
14
+
15
+ **others**
16
+ * [[`d32623b`](http://github.com/koajs/koa/commit/d32623baa7a6273d47be67d587ad4ea0ecffc5de)] - docs: Update error-handling.md (#1239) (urugator <<j.placek@centrum.cz>>)
17
+
18
+ 2.5.3 / 2018-09-11
19
+ ==================
20
+
21
+ **fixes**
22
+ * [[`2ee32f5`](http://github.com/koajs/koa/commit/2ee32f50b88b383317e33cc0a4bfaa5f2eadead7)] - fix: pin debug@~3.1.0 avoid deprecated warnning (#1245) (fengmk2 <<fengmk2@gmail.com>>)
23
+
24
+ **others**
25
+ * [[`2180839`](http://github.com/koajs/koa/commit/2180839eda2cb16edcfda46ccfe24711680af850)] - docs: Update koa-vs-express.md (#1230) (Clayton Ray <<iamclaytonray@gmail.com>>)
26
+
27
+ 2.5.2 / 2018-07-12
28
+ ==================
29
+
30
+ * deps: upgrade all dependencies
31
+ * perf: avoid stringify when set header (#1220)
32
+ * perf: cache content type's result (#1218)
33
+ * perf: lazy init cookies and ip when first time use it (#1216)
34
+ * chore: fix comment & approve cov (#1214)
35
+ * docs: fix grammar
36
+ * test&cov: add test case (#1211)
37
+ * Lazily initialize `request.accept` and delegate `context.accept` (#1209)
38
+ * fix: use non deprecated custom inspect (#1198)
39
+ * Simplify processes in the getter `request.protocol` (#1203)
40
+ * docs: better demonstrate middleware flow (#1195)
41
+ * fix: Throw a TypeError instead of a AssertionError (#1199)
42
+ * chore: mistake in a comment (#1201)
43
+ * chore: use this.res.socket insteadof this.ctx.req.socket (#1177)
44
+ * chore: Using "listenerCount" instead of "listeners" (#1184)
45
+
2
46
  2.5.1 / 2018-04-27
3
47
  ==================
4
48
 
@@ -14,10 +14,8 @@ const isJSON = require('koa-is-json');
14
14
  const context = require('./context');
15
15
  const request = require('./request');
16
16
  const statuses = require('statuses');
17
- const Cookies = require('cookies');
18
- const accepts = require('accepts');
19
17
  const Emitter = require('events');
20
- const assert = require('assert');
18
+ const util = require('util');
21
19
  const Stream = require('stream');
22
20
  const http = require('http');
23
21
  const only = require('only');
@@ -46,6 +44,9 @@ module.exports = class Application extends Emitter {
46
44
  this.context = Object.create(context);
47
45
  this.request = Object.create(request);
48
46
  this.response = Object.create(response);
47
+ if (util.inspect.custom) {
48
+ this[util.inspect.custom] = this.inspect;
49
+ }
49
50
  }
50
51
 
51
52
  /**
@@ -125,7 +126,7 @@ module.exports = class Application extends Emitter {
125
126
  callback() {
126
127
  const fn = compose(this.middleware);
127
128
 
128
- if (!this.listeners('error').length) this.on('error', this.onerror);
129
+ if (!this.listenerCount('error')) this.on('error', this.onerror);
129
130
 
130
131
  const handleRequest = (req, res) => {
131
132
  const ctx = this.createContext(req, res);
@@ -167,12 +168,6 @@ module.exports = class Application extends Emitter {
167
168
  request.response = response;
168
169
  response.request = request;
169
170
  context.originalUrl = request.originalUrl = req.url;
170
- context.cookies = new Cookies(req, res, {
171
- keys: this.keys,
172
- secure: request.secure
173
- });
174
- request.ip = request.ips[0] || req.socket.remoteAddress || '';
175
- context.accept = request.accept = accepts(req);
176
171
  context.state = {};
177
172
  return context;
178
173
  }
@@ -185,7 +180,7 @@ module.exports = class Application extends Emitter {
185
180
  */
186
181
 
187
182
  onerror(err) {
188
- assert(err instanceof Error, `non-error thrown: ${err}`);
183
+ if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err));
189
184
 
190
185
  if (404 == err.status || err.expose) return;
191
186
  if (this.silent) return;
package/lib/context.js CHANGED
@@ -10,6 +10,9 @@ const createError = require('http-errors');
10
10
  const httpAssert = require('http-assert');
11
11
  const delegate = require('delegates');
12
12
  const statuses = require('statuses');
13
+ const Cookies = require('cookies');
14
+
15
+ const COOKIES = Symbol('context#cookies');
13
16
 
14
17
  /**
15
18
  * Context prototype.
@@ -126,6 +129,7 @@ const proto = module.exports = {
126
129
  const { res } = this;
127
130
 
128
131
  // first unset all headers
132
+ /* istanbul ignore else */
129
133
  if (typeof res.getHeaderNames === 'function') {
130
134
  res.getHeaderNames().forEach(name => res.removeHeader(name));
131
135
  } else {
@@ -150,9 +154,35 @@ const proto = module.exports = {
150
154
  this.status = err.status;
151
155
  this.length = Buffer.byteLength(msg);
152
156
  this.res.end(msg);
157
+ },
158
+
159
+ get cookies() {
160
+ if (!this[COOKIES]) {
161
+ this[COOKIES] = new Cookies(this.req, this.res, {
162
+ keys: this.app.keys,
163
+ secure: this.request.secure
164
+ });
165
+ }
166
+ return this[COOKIES];
167
+ },
168
+
169
+ set cookies(_cookies) {
170
+ this[COOKIES] = _cookies;
153
171
  }
154
172
  };
155
173
 
174
+ /**
175
+ * Custom inspection implementation for newer Node.js versions.
176
+ *
177
+ * @return {Object}
178
+ * @api public
179
+ */
180
+
181
+ /* istanbul ignore else */
182
+ if (util.inspect.custom) {
183
+ module.exports[util.inspect.custom] = module.exports.inspect;
184
+ }
185
+
156
186
  /**
157
187
  * Response delegation.
158
188
  */
@@ -194,6 +224,7 @@ delegate(proto, 'request')
194
224
  .access('query')
195
225
  .access('path')
196
226
  .access('url')
227
+ .access('accept')
197
228
  .getter('origin')
198
229
  .getter('href')
199
230
  .getter('subdomains')
package/lib/request.js CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  const URL = require('url').URL;
9
9
  const net = require('net');
10
+ const accepts = require('accepts');
10
11
  const contentType = require('content-type');
11
12
  const stringify = require('url').format;
12
13
  const parse = require('parseurl');
@@ -14,6 +15,9 @@ const qs = require('querystring');
14
15
  const typeis = require('type-is');
15
16
  const fresh = require('fresh');
16
17
  const only = require('only');
18
+ const util = require('util');
19
+
20
+ const IP = Symbol('context#ip');
17
21
 
18
22
  /**
19
23
  * Prototype.
@@ -226,7 +230,7 @@ module.exports = {
226
230
 
227
231
  /**
228
232
  * Set the search string. Same as
229
- * response.querystring= but included for ubiquity.
233
+ * request.querystring= but included for ubiquity.
230
234
  *
231
235
  * @param {String} str
232
236
  * @api public
@@ -248,7 +252,10 @@ module.exports = {
248
252
  get host() {
249
253
  const proxy = this.app.proxy;
250
254
  let host = proxy && this.get('X-Forwarded-Host');
251
- host = host || this.get('Host');
255
+ if (!host) {
256
+ if (this.req.httpVersionMajor >= 2) host = this.get(':authority');
257
+ if (!host) host = this.get('Host');
258
+ }
252
259
  if (!host) return '';
253
260
  return host.split(/\s*,\s*/)[0];
254
261
  },
@@ -278,6 +285,7 @@ module.exports = {
278
285
  */
279
286
 
280
287
  get URL() {
288
+ /* istanbul ignore else */
281
289
  if (!this.memoizedURL) {
282
290
  const protocol = this.protocol;
283
291
  const host = this.host;
@@ -397,11 +405,10 @@ module.exports = {
397
405
  */
398
406
 
399
407
  get protocol() {
400
- const proxy = this.app.proxy;
401
408
  if (this.socket.encrypted) return 'https';
402
- if (!proxy) return 'http';
403
- const proto = this.get('X-Forwarded-Proto') || 'http';
404
- return proto.split(/\s*,\s*/)[0];
409
+ if (!this.app.proxy) return 'http';
410
+ const proto = this.get('X-Forwarded-Proto');
411
+ return proto ? proto.split(/\s*,\s*/)[0] : 'http';
405
412
  },
406
413
 
407
414
  /**
@@ -437,6 +444,26 @@ module.exports = {
437
444
  : [];
438
445
  },
439
446
 
447
+ /**
448
+ * Return request's remote address
449
+ * When `app.proxy` is `true`, parse
450
+ * the "X-Forwarded-For" ip address list and return the first one
451
+ *
452
+ * @return {String}
453
+ * @api public
454
+ */
455
+
456
+ get ip() {
457
+ if (!this[IP]) {
458
+ this[IP] = this.ips[0] || this.socket.remoteAddress || '';
459
+ }
460
+ return this[IP];
461
+ },
462
+
463
+ set ip(_ip) {
464
+ this[IP] = _ip;
465
+ },
466
+
440
467
  /**
441
468
  * Return subdomains as an array.
442
469
  *
@@ -463,6 +490,27 @@ module.exports = {
463
490
  .slice(offset);
464
491
  },
465
492
 
493
+ /**
494
+ * Get accept object.
495
+ * Lazily memoized.
496
+ *
497
+ * @return {Object}
498
+ * @api private
499
+ */
500
+ get accept() {
501
+ return this._accept || (this._accept = accepts(this.req));
502
+ },
503
+
504
+ /**
505
+ * Set accept object.
506
+ *
507
+ * @param {Object}
508
+ * @api private
509
+ */
510
+ set accept(obj) {
511
+ return this._accept = obj;
512
+ },
513
+
466
514
  /**
467
515
  * Check if the given `type(s)` is acceptable, returning
468
516
  * the best match when true, otherwise `false`, in which
@@ -620,7 +668,7 @@ module.exports = {
620
668
  * // => "text/plain"
621
669
  *
622
670
  * this.get('Something');
623
- * // => undefined
671
+ * // => ''
624
672
  *
625
673
  * @param {String} field
626
674
  * @return {String}
@@ -665,3 +713,15 @@ module.exports = {
665
713
  ]);
666
714
  }
667
715
  };
716
+
717
+ /**
718
+ * Custom inspection implementation for newer Node.js versions.
719
+ *
720
+ * @return {Object}
721
+ * @api public
722
+ */
723
+
724
+ /* istanbul ignore else */
725
+ if (util.inspect.custom) {
726
+ module.exports[util.inspect.custom] = module.exports.inspect;
727
+ }
package/lib/response.js CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  const contentDisposition = require('content-disposition');
9
9
  const ensureErrorHandler = require('error-inject');
10
- const getType = require('mime-types').contentType;
10
+ const getType = require('cache-content-type');
11
11
  const onFinish = require('on-finished');
12
12
  const isJSON = require('koa-is-json');
13
13
  const escape = require('escape-html');
@@ -18,6 +18,7 @@ const assert = require('assert');
18
18
  const extname = require('path').extname;
19
19
  const vary = require('vary');
20
20
  const only = require('only');
21
+ const util = require('util');
21
22
 
22
23
  /**
23
24
  * Prototype.
@@ -33,7 +34,7 @@ module.exports = {
33
34
  */
34
35
 
35
36
  get socket() {
36
- return this.ctx.req.socket;
37
+ return this.res.socket;
37
38
  },
38
39
 
39
40
  /**
@@ -284,9 +285,9 @@ module.exports = {
284
285
  * @api public
285
286
  */
286
287
 
287
- attachment(filename) {
288
+ attachment(filename, options) {
288
289
  if (filename) this.type = extname(filename);
289
- this.set('Content-Disposition', contentDisposition(filename));
290
+ this.set('Content-Disposition', contentDisposition(filename, options));
290
291
  },
291
292
 
292
293
  /**
@@ -438,8 +439,8 @@ module.exports = {
438
439
  if (this.headerSent) return;
439
440
 
440
441
  if (2 == arguments.length) {
441
- if (Array.isArray(val)) val = val.map(String);
442
- else val = String(val);
442
+ if (Array.isArray(val)) val = val.map(v => typeof v === 'string' ? v : String(v));
443
+ else if (typeof val !== 'string') val = String(val);
443
444
  this.res.setHeader(field, val);
444
445
  } else {
445
446
  for (const key in field) {
@@ -545,3 +546,13 @@ module.exports = {
545
546
  this.res.flushHeaders();
546
547
  }
547
548
  };
549
+
550
+ /**
551
+ * Custom inspection implementation for newer Node.js versions.
552
+ *
553
+ * @return {Object}
554
+ * @api public
555
+ */
556
+ if (util.inspect.custom) {
557
+ module.exports[util.inspect.custom] = module.exports.inspect;
558
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koa",
3
- "version": "2.5.1",
3
+ "version": "2.6.1",
4
4
  "description": "Koa web app framework",
5
5
  "main": "lib/application.js",
6
6
  "scripts": {
@@ -22,30 +22,30 @@
22
22
  ],
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "accepts": "^1.2.2",
26
- "content-disposition": "~0.5.0",
27
- "content-type": "^1.0.0",
28
- "cookies": "~0.7.0",
29
- "debug": "*",
25
+ "accepts": "^1.3.5",
26
+ "cache-content-type": "^1.0.0",
27
+ "content-disposition": "~0.5.2",
28
+ "content-type": "^1.0.4",
29
+ "cookies": "~0.7.1",
30
+ "debug": "~3.1.0",
30
31
  "delegates": "^1.0.0",
31
- "depd": "^1.1.0",
32
- "destroy": "^1.0.3",
33
- "error-inject": "~1.0.0",
34
- "escape-html": "~1.0.1",
35
- "fresh": "^0.5.2",
36
- "http-assert": "^1.1.0",
37
- "http-errors": "^1.2.8",
38
- "is-generator-function": "^1.0.3",
39
- "koa-compose": "^4.0.0",
32
+ "depd": "^1.1.2",
33
+ "destroy": "^1.0.4",
34
+ "error-inject": "^1.0.0",
35
+ "escape-html": "^1.0.3",
36
+ "fresh": "~0.5.2",
37
+ "http-assert": "^1.3.0",
38
+ "http-errors": "^1.6.3",
39
+ "is-generator-function": "^1.0.7",
40
+ "koa-compose": "^4.1.0",
40
41
  "koa-convert": "^1.2.0",
41
42
  "koa-is-json": "^1.0.0",
42
- "mime-types": "^2.0.7",
43
- "on-finished": "^2.1.0",
44
- "only": "0.0.2",
45
- "parseurl": "^1.3.0",
46
- "statuses": "^1.2.0",
47
- "type-is": "^1.5.5",
48
- "vary": "^1.0.0"
43
+ "on-finished": "^2.3.0",
44
+ "only": "~0.0.2",
45
+ "parseurl": "^1.3.2",
46
+ "statuses": "^1.5.0",
47
+ "type-is": "^1.6.16",
48
+ "vary": "^1.1.2"
49
49
  },
50
50
  "devDependencies": {
51
51
  "eslint": "^3.17.1",
@@ -54,7 +54,7 @@
54
54
  "eslint-plugin-promise": "^3.5.0",
55
55
  "eslint-plugin-standard": "^2.1.1",
56
56
  "jest": "^20.0.0",
57
- "supertest": "^3.0.0"
57
+ "supertest": "^3.1.0"
58
58
  },
59
59
  "engines": {
60
60
  "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4"