From 2ef7846b5f99ebd4367d6a7ad8579fad6c62f54b Mon Sep 17 00:00:00 2001 From: Jonatan Nilsson Date: Sun, 29 Sep 2019 19:17:14 +0000 Subject: [PATCH] Remove accepts, cache-content-type and content-type. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace content-disposition with one that doesn’t use safe-buffer --- Readme.md | 14 +--- docs/api/context.md | 4 - docs/api/request.md | 135 ------------------------------- lib/context.js | 4 - lib/request.js | 135 ------------------------------- lib/response.js | 33 ++++++-- package.json | 6 +- test/request/accept.js | 27 ------- test/request/accepts.js | 94 --------------------- test/request/acceptsCharsets.js | 52 ------------ test/request/acceptsEncodings.js | 43 ---------- test/request/acceptsLanguages.js | 52 ------------ test/request/charset.js | 36 --------- 13 files changed, 31 insertions(+), 604 deletions(-) delete mode 100644 test/request/accept.js delete mode 100644 test/request/accepts.js delete mode 100644 test/request/acceptsCharsets.js delete mode 100644 test/request/acceptsEncodings.js delete mode 100644 test/request/acceptsLanguages.js delete mode 100644 test/request/charset.js diff --git a/Readme.md b/Readme.md index 4f5d62b..c126d4a 100644 --- a/Readme.md +++ b/Readme.md @@ -103,17 +103,6 @@ Koa's `Request` object provides helpful methods for working with http requests which delegate to an [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) from the node `http` module. -Here is an example of checking that a requesting client supports xml. - -```js -app.use(async (ctx, next) => { - ctx.assert(ctx.request.accepts('xml'), 406); - // equivalent to: - // if (!ctx.request.accepts('xml')) ctx.throw(406); - await next(); -}); -``` - Koa provides a `Response` object as the `response` property of the `Context`. Koa's `Response` object provides helpful methods for working with http responses which delegate to a [ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) @@ -136,8 +125,7 @@ app.use(async (ctx, next) => { ``` The `Context` object also provides shortcuts for methods on its `request` and `response`. In the prior -examples, `ctx.type` can be used instead of `ctx.response.type` and `ctx.accepts` can be used -instead of `ctx.request.accepts`. +examples, `ctx.type` can be used instead of `ctx.response.type`. For more information on `Request`, `Response` and `Context`, see the [Request API Reference](docs/api/request.md), [Response API Reference](docs/api/response.md) and [Context API Reference](docs/api/context.md). diff --git a/docs/api/context.md b/docs/api/context.md index 30b5226..55c9e19 100644 --- a/docs/api/context.md +++ b/docs/api/context.md @@ -172,10 +172,6 @@ Koa uses [http-assert](https://github.com/jshttp/http-assert) for assertions. - `ctx.ips` - `ctx.subdomains` - `ctx.is()` - - `ctx.accepts()` - - `ctx.acceptsEncodings()` - - `ctx.acceptsCharsets()` - - `ctx.acceptsLanguages()` - `ctx.get()` ## Response aliases diff --git a/docs/api/request.md b/docs/api/request.md index 0317c30..0d2eadb 100644 --- a/docs/api/request.md +++ b/docs/api/request.md @@ -241,141 +241,6 @@ if (ctx.is('image/*')) { } ``` -### Content Negotiation - - Koa's `request` object includes helpful content negotiation utilities powered by [accepts](http://github.com/expressjs/accepts) and [negotiator](https://github.com/federomero/negotiator). These utilities are: - -- `request.accepts(types)` -- `request.acceptsEncodings(types)` -- `request.acceptsCharsets(charsets)` -- `request.acceptsLanguages(langs)` - -If no types are supplied, __all__ acceptable types are returned. - -If multiple types are supplied, the best match will be returned. If no matches are found, a `false` is returned, and you should send a `406 "Not Acceptable"` response to the client. - -In the case of missing accept headers where any type is acceptable, the first type will be returned. Thus, the order of types you supply is important. - -### request.accepts(types) - - Check if the given `type(s)` is acceptable, returning the best match when true, otherwise `false`. The `type` value may be one or more mime type string - such as "application/json", the extension name - such as "json", or an array `["json", "html", "text/plain"]`. - -```js -// Accept: text/html -ctx.accepts('html'); -// => "html" - -// Accept: text/*, application/json -ctx.accepts('html'); -// => "html" -ctx.accepts('text/html'); -// => "text/html" -ctx.accepts('json', 'text'); -// => "json" -ctx.accepts('application/json'); -// => "application/json" - -// Accept: text/*, application/json -ctx.accepts('image/png'); -ctx.accepts('png'); -// => false - -// Accept: text/*;q=.5, application/json -ctx.accepts(['html', 'json']); -ctx.accepts('html', 'json'); -// => "json" - -// No Accept header -ctx.accepts('html', 'json'); -// => "html" -ctx.accepts('json', 'html'); -// => "json" -``` - - You may call `ctx.accepts()` as many times as you like, - or use a switch: - -```js -switch (ctx.accepts('json', 'html', 'text')) { - case 'json': break; - case 'html': break; - case 'text': break; - default: ctx.throw(406, 'json, html, or text only'); -} -``` - -### request.acceptsEncodings(encodings) - - Check if `encodings` are acceptable, returning the best match when true, otherwise `false`. Note that you should include `identity` as one of the encodings! - -```js -// Accept-Encoding: gzip -ctx.acceptsEncodings('gzip', 'deflate', 'identity'); -// => "gzip" - -ctx.acceptsEncodings(['gzip', 'deflate', 'identity']); -// => "gzip" -``` - - When no arguments are given all accepted encodings - are returned as an array: - -```js -// Accept-Encoding: gzip, deflate -ctx.acceptsEncodings(); -// => ["gzip", "deflate", "identity"] -``` - - Note that the `identity` encoding (which means no encoding) could be unacceptable if the client explicitly sends `identity;q=0`. Although this is an edge case, you should still handle the case where this method returns `false`. - -### request.acceptsCharsets(charsets) - - Check if `charsets` are acceptable, returning - the best match when true, otherwise `false`. - -```js -// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 -ctx.acceptsCharsets('utf-8', 'utf-7'); -// => "utf-8" - -ctx.acceptsCharsets(['utf-7', 'utf-8']); -// => "utf-8" -``` - - When no arguments are given all accepted charsets - are returned as an array: - -```js -// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 -ctx.acceptsCharsets(); -// => ["utf-8", "utf-7", "iso-8859-1"] -``` - -### request.acceptsLanguages(langs) - - Check if `langs` are acceptable, returning - the best match when true, otherwise `false`. - -```js -// Accept-Language: en;q=0.8, es, pt -ctx.acceptsLanguages('es', 'en'); -// => "es" - -ctx.acceptsLanguages(['en', 'es']); -// => "es" -``` - - When no arguments are given all accepted languages - are returned as an array: - -```js -// Accept-Language: en;q=0.8, es, pt -ctx.acceptsLanguages(); -// => ["es", "pt", "en"] -``` - ### request.idempotent Check if the request is idempotent. diff --git a/lib/context.js b/lib/context.js index 3e95755..dd54483 100644 --- a/lib/context.js +++ b/lib/context.js @@ -211,10 +211,6 @@ delegate(proto, 'response') */ delegate(proto, 'request') - .method('acceptsLanguages') - .method('acceptsEncodings') - .method('acceptsCharsets') - .method('accepts') .method('get') .method('is') .access('querystring') diff --git a/lib/request.js b/lib/request.js index 3a5e92d..0b763a9 100644 --- a/lib/request.js +++ b/lib/request.js @@ -7,8 +7,6 @@ const URL = require('url').URL; const net = require('net'); -const accepts = require('accepts'); -const contentType = require('content-type'); const stringify = require('url').format; const parse = require('parseurl'); const qs = require('querystring'); @@ -357,22 +355,6 @@ module.exports = { return this.req.socket; }, - /** - * Get the charset when present or undefined. - * - * @return {String} - * @api public - */ - - get charset() { - try { - const { parameters } = contentType.parse(this.req); - return parameters.charset || ''; - } catch (e) { - return ''; - } - }, - /** * Return parsed Content-Length when present. * @@ -484,123 +466,6 @@ module.exports = { .slice(offset); }, - /** - * Get accept object. - * Lazily memoized. - * - * @return {Object} - * @api private - */ - get accept() { - return this._accept || (this._accept = accepts(this.req)); - }, - - /** - * Set accept object. - * - * @param {Object} - * @api private - */ - set accept(obj) { - this._accept = obj; - }, - - /** - * Check if the given `type(s)` is acceptable, returning - * the best match when true, otherwise `false`, in which - * case you should respond with 406 "Not Acceptable". - * - * The `type` value may be a single mime type string - * such as "application/json", the extension name - * such as "json" or an array `["json", "html", "text/plain"]`. When a list - * or array is given the _best_ match, if any is returned. - * - * Examples: - * - * // Accept: text/html - * this.accepts('html'); - * // => "html" - * - * // Accept: text/*, application/json - * this.accepts('html'); - * // => "html" - * this.accepts('text/html'); - * // => "text/html" - * this.accepts('json', 'text'); - * // => "json" - * this.accepts('application/json'); - * // => "application/json" - * - * // Accept: text/*, application/json - * this.accepts('image/png'); - * this.accepts('png'); - * // => false - * - * // Accept: text/*;q=.5, application/json - * this.accepts(['html', 'json']); - * this.accepts('html', 'json'); - * // => "json" - * - * @param {String|Array} type(s)... - * @return {String|Array|false} - * @api public - */ - - accepts(...args) { - return this.accept.types(...args); - }, - - /** - * Return accepted encodings or best fit based on `encodings`. - * - * Given `Accept-Encoding: gzip, deflate` - * an array sorted by quality is returned: - * - * ['gzip', 'deflate'] - * - * @param {String|Array} encoding(s)... - * @return {String|Array} - * @api public - */ - - acceptsEncodings(...args) { - return this.accept.encodings(...args); - }, - - /** - * Return accepted charsets or best fit based on `charsets`. - * - * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5` - * an array sorted by quality is returned: - * - * ['utf-8', 'utf-7', 'iso-8859-1'] - * - * @param {String|Array} charset(s)... - * @return {String|Array} - * @api public - */ - - acceptsCharsets(...args) { - return this.accept.charsets(...args); - }, - - /** - * Return accepted languages or best fit based on `langs`. - * - * Given `Accept-Language: en;q=0.8, es, pt` - * an array sorted by quality is returned: - * - * ['es', 'pt', 'en'] - * - * @param {String|Array} lang(s)... - * @return {Array|String} - * @api public - */ - - acceptsLanguages(...args) { - return this.accept.languages(...args); - }, - /** * Check if the incoming request contains the "Content-Type" * header field, and it contains any of the give mime `type`s. diff --git a/lib/response.js b/lib/response.js index 6d1d11e..415b830 100644 --- a/lib/response.js +++ b/lib/response.js @@ -7,7 +7,6 @@ const contentDisposition = require('content-disposition'); const ensureErrorHandler = require('error-inject'); -const getType = require('cache-content-type'); const onFinish = require('on-finished'); const isJSON = require('koa-is-json'); const escape = require('escape-html'); @@ -267,7 +266,7 @@ module.exports = { if (!statuses.redirect[this.status]) this.status = 302; // html - if (this.ctx.accepts('html')) { + if (this.ctx.headers.accept && this.ctx.headers.accept.indexOf('html') >= 0) { url = escape(url); this.type = 'text/html; charset=utf-8'; this.body = `Redirecting to ${url}.`; @@ -307,10 +306,34 @@ module.exports = { * @api public */ - set type(type) { - type = getType(type); - if (type) { + set type(orgType) { + let type = orgType + // If full type is specified, pass it straight on. + // Otherwise we do some basic checking for most common + // supported mime types. + if (type.indexOf('/') > 0 || type.indexOf(';') > 0) { + if (type.indexOf(';') === -1 && type.indexOf('text') >= 0) { + type += '; charset=utf-8' + } this.set('Content-Type', type); + } else if (type.indexOf('json')) { + this.set('Content-Type', 'application/json; charset=utf-8'); + } else if (type.indexOf('html') => 0) { + this.set('Content-Type', 'text/html; charset=utf-8'); + } else if (type.indexOf('css') => 0) { + this.set('Content-Type', 'text/css; charset=utf-8'); + } else if (type.indexOf('js') => 0 || type.indexOf('javascript') => 0) { + this.set('Content-Type', 'application/javascript; charset=utf-8'); + } else if (type.indexOf('png') => 0) { + this.set('Content-Type', 'image/png'); + } else if (type.indexOf('jpg') => 0) { + this.set('Content-Type', 'image/jpeg'); + } else if (type.indexOf('jpeg') => 0) { + this.set('Content-Type', 'image/jpeg'); + } else if (type.indexOf('gif') => 0) { + this.set('Content-Type', 'image/gif'); + } else if (type.indexOf('text')) { + this.set('Content-Type', 'text/plain; charset=utf-8'); } else { this.remove('Content-Type'); } diff --git a/package.json b/package.json index 65001ae..56b2c4b 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,8 @@ ], "license": "MIT", "dependencies": { - "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", + "content-disposition": "jharrilim/content-disposition#572383f +", "cookies": "~0.7.1", "debug": "~3.1.0", "delegates": "^1.0.0", diff --git a/test/request/accept.js b/test/request/accept.js deleted file mode 100644 index 9178180..0000000 --- a/test/request/accept.js +++ /dev/null @@ -1,27 +0,0 @@ - -'use strict'; - -const Accept = require('accepts'); -const assert = require('assert'); -const context = require('../helpers/context'); - -describe('ctx.accept', () => { - it('should return an Accept instance', () => { - const ctx = context(); - ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; - assert(ctx.accept instanceof Accept); - }); -}); - -describe('ctx.accept=', () => { - it('should replace the accept object', () => { - const ctx = context(); - ctx.req.headers.accept = 'text/plain'; - assert.deepEqual(ctx.accepts(), ['text/plain']); - - const request = context.request(); - request.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; - ctx.accept = Accept(request.req); - assert.deepEqual(ctx.accepts(), ['text/html', 'text/plain', 'image/jpeg', 'application/*']); - }); -}); diff --git a/test/request/accepts.js b/test/request/accepts.js deleted file mode 100644 index 784b578..0000000 --- a/test/request/accepts.js +++ /dev/null @@ -1,94 +0,0 @@ - -'use strict'; - -const assert = require('assert'); -const context = require('../helpers/context'); - -describe('ctx.accepts(types)', () => { - describe('with no arguments', () => { - describe('when Accept is populated', () => { - it('should return all accepted types', () => { - const ctx = context(); - ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; - assert.deepEqual(ctx.accepts(), ['text/html', 'text/plain', 'image/jpeg', 'application/*']); - }); - }); - }); - - describe('with no valid types', () => { - describe('when Accept is populated', () => { - it('should return false', () => { - const ctx = context(); - ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; - assert.equal(ctx.accepts('image/png', 'image/tiff'), false); - }); - }); - - describe('when Accept is not populated', () => { - it('should return the first type', () => { - const ctx = context(); - assert.equal(ctx.accepts('text/html', 'text/plain', 'image/jpeg', 'application/*'), 'text/html'); - }); - }); - }); - - describe('when extensions are given', () => { - it('should convert to mime types', () => { - const ctx = context(); - ctx.req.headers.accept = 'text/plain, text/html'; - assert.equal(ctx.accepts('html'), 'html'); - assert.equal(ctx.accepts('.html'), '.html'); - assert.equal(ctx.accepts('txt'), 'txt'); - assert.equal(ctx.accepts('.txt'), '.txt'); - assert.equal(ctx.accepts('png'), false); - }); - }); - - describe('when an array is given', () => { - it('should return the first match', () => { - const ctx = context(); - ctx.req.headers.accept = 'text/plain, text/html'; - assert.equal(ctx.accepts(['png', 'text', 'html']), 'text'); - assert.equal(ctx.accepts(['png', 'html']), 'html'); - }); - }); - - describe('when multiple arguments are given', () => { - it('should return the first match', () => { - const ctx = context(); - ctx.req.headers.accept = 'text/plain, text/html'; - assert.equal(ctx.accepts('png', 'text', 'html'), 'text'); - assert.equal(ctx.accepts('png', 'html'), 'html'); - }); - }); - - describe('when present in Accept as an exact match', () => { - it('should return the type', () => { - const ctx = context(); - ctx.req.headers.accept = 'text/plain, text/html'; - assert.equal(ctx.accepts('text/html'), 'text/html'); - assert.equal(ctx.accepts('text/plain'), 'text/plain'); - }); - }); - - describe('when present in Accept as a type match', () => { - it('should return the type', () => { - const ctx = context(); - ctx.req.headers.accept = 'application/json, */*'; - assert.equal(ctx.accepts('text/html'), 'text/html'); - assert.equal(ctx.accepts('text/plain'), 'text/plain'); - assert.equal(ctx.accepts('image/png'), 'image/png'); - }); - }); - - describe('when present in Accept as a subtype match', () => { - it('should return the type', () => { - const ctx = context(); - ctx.req.headers.accept = 'application/json, text/*'; - assert.equal(ctx.accepts('text/html'), 'text/html'); - assert.equal(ctx.accepts('text/plain'), 'text/plain'); - assert.equal(ctx.accepts('image/png'), false); - assert.equal(ctx.accepts('png'), false); - }); - }); -}); diff --git a/test/request/acceptsCharsets.js b/test/request/acceptsCharsets.js deleted file mode 100644 index 0f09200..0000000 --- a/test/request/acceptsCharsets.js +++ /dev/null @@ -1,52 +0,0 @@ - -'use strict'; - -const assert = require('assert'); -const context = require('../helpers/context'); - -describe('ctx.acceptsCharsets()', () => { - describe('with no arguments', () => { - describe('when Accept-Charset is populated', () => { - it('should return accepted types', () => { - const ctx = context(); - ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; - assert.deepEqual(ctx.acceptsCharsets(), ['utf-8', 'utf-7', 'iso-8859-1']); - }); - }); - }); - - describe('with multiple arguments', () => { - describe('when Accept-Charset is populated', () => { - describe('if any types match', () => { - it('should return the best fit', () => { - const ctx = context(); - ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; - assert.equal(ctx.acceptsCharsets('utf-7', 'utf-8'), 'utf-8'); - }); - }); - - describe('if no types match', () => { - it('should return false', () => { - const ctx = context(); - ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; - assert.equal(ctx.acceptsCharsets('utf-16'), false); - }); - }); - }); - - describe('when Accept-Charset is not populated', () => { - it('should return the first type', () => { - const ctx = context(); - assert.equal(ctx.acceptsCharsets('utf-7', 'utf-8'), 'utf-7'); - }); - }); - }); - - describe('with an array', () => { - it('should return the best fit', () => { - const ctx = context(); - ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; - assert.equal(ctx.acceptsCharsets(['utf-7', 'utf-8']), 'utf-8'); - }); - }); -}); diff --git a/test/request/acceptsEncodings.js b/test/request/acceptsEncodings.js deleted file mode 100644 index 13a5da8..0000000 --- a/test/request/acceptsEncodings.js +++ /dev/null @@ -1,43 +0,0 @@ - -'use strict'; - -const assert = require('assert'); -const context = require('../helpers/context'); - -describe('ctx.acceptsEncodings()', () => { - describe('with no arguments', () => { - describe('when Accept-Encoding is populated', () => { - it('should return accepted types', () => { - const ctx = context(); - ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; - assert.deepEqual(ctx.acceptsEncodings(), ['gzip', 'compress', 'identity']); - assert.equal(ctx.acceptsEncodings('gzip', 'compress'), 'gzip'); - }); - }); - - describe('when Accept-Encoding is not populated', () => { - it('should return identity', () => { - const ctx = context(); - assert.deepEqual(ctx.acceptsEncodings(), ['identity']); - assert.equal(ctx.acceptsEncodings('gzip', 'deflate', 'identity'), 'identity'); - }); - }); - }); - - describe('with multiple arguments', () => { - it('should return the best fit', () => { - const ctx = context(); - ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; - assert.equal(ctx.acceptsEncodings('compress', 'gzip'), 'gzip'); - assert.equal(ctx.acceptsEncodings('gzip', 'compress'), 'gzip'); - }); - }); - - describe('with an array', () => { - it('should return the best fit', () => { - const ctx = context(); - ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; - assert.equal(ctx.acceptsEncodings(['compress', 'gzip']), 'gzip'); - }); - }); -}); diff --git a/test/request/acceptsLanguages.js b/test/request/acceptsLanguages.js deleted file mode 100644 index 7c84160..0000000 --- a/test/request/acceptsLanguages.js +++ /dev/null @@ -1,52 +0,0 @@ - -'use strict'; - -const assert = require('assert'); -const context = require('../helpers/context'); - -describe('ctx.acceptsLanguages(langs)', () => { - describe('with no arguments', () => { - describe('when Accept-Language is populated', () => { - it('should return accepted types', () => { - const ctx = context(); - ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; - assert.deepEqual(ctx.acceptsLanguages(), ['es', 'pt', 'en']); - }); - }); - }); - - describe('with multiple arguments', () => { - describe('when Accept-Language is populated', () => { - describe('if any types types match', () => { - it('should return the best fit', () => { - const ctx = context(); - ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; - assert.equal(ctx.acceptsLanguages('es', 'en'), 'es'); - }); - }); - - describe('if no types match', () => { - it('should return false', () => { - const ctx = context(); - ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; - assert.equal(ctx.acceptsLanguages('fr', 'au'), false); - }); - }); - }); - - describe('when Accept-Language is not populated', () => { - it('should return the first type', () => { - const ctx = context(); - assert.equal(ctx.acceptsLanguages('es', 'en'), 'es'); - }); - }); - }); - - describe('with an array', () => { - it('should return the best fit', () => { - const ctx = context(); - ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; - assert.equal(ctx.acceptsLanguages(['es', 'en']), 'es'); - }); - }); -}); diff --git a/test/request/charset.js b/test/request/charset.js deleted file mode 100644 index f2c01f8..0000000 --- a/test/request/charset.js +++ /dev/null @@ -1,36 +0,0 @@ - -'use strict'; - -const request = require('../helpers/context').request; -const assert = require('assert'); - -describe('req.charset', () => { - describe('with no content-type present', () => { - it('should return ""', () => { - const req = request(); - assert('' === req.charset); - }); - }); - - describe('with charset present', () => { - it('should return ""', () => { - const req = request(); - req.header['content-type'] = 'text/plain'; - assert('' === req.charset); - }); - }); - - describe('with a charset', () => { - it('should return the charset', () => { - const req = request(); - req.header['content-type'] = 'text/plain; charset=utf-8'; - assert.equal(req.charset, 'utf-8'); - }); - - it('should return "" if content-type is invalid', () => { - const req = request(); - req.header['content-type'] = 'application/json; application/text; charset=utf-8'; - assert.equal(req.charset, ''); - }); - }); -});