diff --git a/src/body.js b/src/body.js index 1970791..070cf3c 100644 --- a/src/body.js +++ b/src/body.js @@ -10,6 +10,7 @@ import ParamReader from './param-reader' import BufferReader from './buffer-reader' import zlib from 'zlib' import { EventEmitter } from 'events' +import _ from 'lodash' export default class Body extends EventEmitter { @@ -84,6 +85,22 @@ export default class Body extends EventEmitter { // ------------------------------------------------- + get headers() { + return this._getRawDataItem('headers') + } + + set headers(headers) { + this._setRawDataItem('headers', _.extend({}, headers)) + } + + // ------------------------------------------------- + + get origHeaders() { + return this._getRawDataItem('origHeaders') + } + + // ------------------------------------------------- + slow(opts) { if (opts) { this._setRawDataItem('slow', { diff --git a/src/request.js b/src/request.js index a8e0d1a..3242da6 100644 --- a/src/request.js +++ b/src/request.js @@ -37,11 +37,11 @@ export default class Request extends Body { /** * Getter/setter for HTTP protocol, e.g. 'http:' */ - get protocol(){ + get protocol() { return this._getRawDataItem('protocol') } - set protocol(protocol){ + set protocol(protocol) { if (!validProtocols.hasOwnProperty(protocol)) { throw new Error('invalid protocol: ' + protocol) // TODO: test this } @@ -51,12 +51,12 @@ export default class Request extends Body { /** * Getter/setter for host name. Does not incude port. */ - get hostname(){ + get hostname() { return this._getRawDataItem('hostname') } - set hostname(hostname){ - if (!hostname){ + set hostname(hostname) { + if (!hostname) { throw new Error('invalid hostname: ' + hostname) // TODO: test this } this._setRawDataItem('hostname', hostname) @@ -65,16 +65,16 @@ export default class Request extends Body { /** * Getter/setter for port. */ - get port(){ + get port() { return this._getRawDataItem('port') } - set port(port){ + set port(port) { if (port === undefined) { this._setRawDataItem('port', undefined) } else { let parsedPort = parseInt(port) - if (!parsedPort){ + if (!parsedPort) { throw new Error('invalid port: ' + port) // TODO: test this } this._setRawDataItem('port', parsedPort) @@ -84,12 +84,12 @@ export default class Request extends Body { /** * Getter/setter for HTTP method. */ - get method(){ + get method() { return this._getRawDataItem('method') } - set method(method){ - if (!method){ + set method(method) { + if (!method) { throw new Error('invalid method') // TODO: test this } this._setRawDataItem('method', method.toUpperCase()) @@ -98,30 +98,19 @@ export default class Request extends Body { /** * Getter/setter for URL. Root-relative. */ - get url(){ + get url() { return this._getRawDataItem('url') } - set url(aUrl){ - if (!/^\//.test(aUrl)){ + set url(aUrl) { + if (!/^\//.test(aUrl)) { throw new Error('invalid url, must start with /') // TODO: test this } this._setRawDataItem('url', aUrl) } - /** - * Getter/setter for HTTP request header object. - */ - get headers(){ - return this._getRawDataItem('headers') - } - - set headers(headers){ - this._setRawDataItem('headers', _.extend({}, headers)) - } - - fullUrl(u){ - if (u){ + fullUrl(u) { + if (u) { let purl = url.parse(u) if (purl.protocol) { this.protocol = purl.protocol } if (purl.hostname) { this.hostname = purl.hostname } @@ -139,27 +128,29 @@ export default class Request extends Body { } } - _setHttpSource(inReq, reverse){ + _setHttpSource(inReq, reverse) { + const origHeaders = _.extend({}, inReq.headers) + Object.freeze(origHeaders) let u = inReq.url - if (reverse){ + if (reverse) { u = url.resolve(reverse, u) } let purl = url.parse(u) - if (reverse){ + if (reverse) { inReq.headers.host = purl.host - if (!purl.protocol){ + if (!purl.protocol) { throw new Error('missing protocol on reverse proxy') } } let host = (() => { let aHost = inReq.headers.host let result = {} - if (aHost){ + if (aHost) { let matches = aHost.match(/^([^:]+)(:([\d]+))?$/) - if (matches){ + if (matches) { result.name = matches[1] let port = parseInt(matches[3]) - if (port){ + if (port) { result.port = port } } @@ -168,10 +159,11 @@ export default class Request extends Body { })() this.httpVersion = inReq.httpVersion this.headers = inReq.headers + this._setRawDataItem('origHeaders', origHeaders) this.protocol = purl.protocol || 'http:' this.hostname = purl.hostname || host.name let port = purl.port || host.port - if (port){ + if (port) { this.port = port } //this.port = purl.port || host.port || this._getDefaultPort() @@ -184,25 +176,25 @@ export default class Request extends Body { /** * Returns the default port given the current protocol. */ - _getDefaultPort(){ + _getDefaultPort() { return this.protocol === 'https:' ? 443 : 80 } // TODO: emit debug log events for things that are changed. - _finalize(){ + _finalize() { if (nonEntityMethods.hasOwnProperty(this.method)) { this.string = '' // TODO: test } - if (!this._source){ + if (!this._source) { this.string = '' // TODO: test? } - if (!this._source._isOriginal){ + if (!this._source._isOriginal) { delete this.headers['content-length'] - if (typeof this._source.finalize === 'function'){ + if (typeof this._source.finalize === 'function') { this._source.finalize() } - if (typeof this._source.size === 'function'){ + if (typeof this._source.size === 'function') { this.headers['content-length'] = this._source.size() } } @@ -211,7 +203,7 @@ export default class Request extends Body { // TODO: test if (removeHeaders.hasOwnProperty(name)) { delete this.headers[name] - } else if (this.headers[name] === undefined){ + } else if (this.headers[name] === undefined) { delete this.headers[name] } }) diff --git a/src/response.js b/src/response.js index 3b604a3..618ee91 100644 --- a/src/response.js +++ b/src/response.js @@ -36,21 +36,13 @@ export default class Response extends Body { this._setRawDataItem('statusCode', code) } - /** - * Getter/setter for HTTP response header object. - */ - get headers() { - return this._getRawDataItem('headers') - } - - set headers(headers) { - this._setRawDataItem('headers', _.extend({}, headers)) - } - _setHttpSource(inResp) { + const origHeaders = _.extend({}, inResp.headers) + Object.freeze(origHeaders) this.httpVersion = inResp.httpVersion this.statusCode = inResp.statusCode this.headers = inResp.headers + this._setRawDataItem('origHeaders', origHeaders) inResp._isOriginal = true this._source = inResp } diff --git a/test/request.test.js b/test/request.test.js index e5b07e2..162958c 100644 --- a/test/request.test.js +++ b/test/request.test.js @@ -32,7 +32,7 @@ function getRequestData() { describe('Request', function() { it('should construct', function() { - new Request() + new Request() // eslint-disable-line no-new }) it('should accept raw data', function() { @@ -99,6 +99,40 @@ describe('Request', function() { assert.deepEqual(req.headers, data.headers) }) + it('should get original headers', function() { + let req = new Request() + let data = getRequestData() + req._setHttpSource(data) + assert.deepEqual(req.origHeaders, data.headers) + }) + + it('should not set original headers', function() { + let req = new Request() + let data = getRequestData() + req._setHttpSource(data) + assert.throws(() => { + req.origHeaders = {} + }) + }) + + it('original headers are not modified', function() { + let req = new Request() + let data = getRequestData() + req._setHttpSource(data) + req.headers.foo = '1234' + assert.strictEqual(req.headers.foo, '1234') + assert.strictEqual(req.origHeaders.foo, undefined) + }) + + it('original headers should be frozen', function() { + let req = new Request() + let data = getRequestData() + req._setHttpSource(data) + assert.throws(() => { + req.origHeaders.foo = '2' + }) + }) + it('should get and set source', function() { let req = new Request() let data = getRequestData() diff --git a/test/response.test.js b/test/response.test.js index 8da2f21..b00a929 100644 --- a/test/response.test.js +++ b/test/response.test.js @@ -24,7 +24,7 @@ function getResponseData(){ describe('Response', function(){ it('should construct', function(){ - let resp = new Response() + new Response() // eslint-disable-line no-new }) it('should accept raw data', function(){ @@ -39,6 +39,40 @@ describe('Response', function(){ assert.deepEqual(resp.headers, data.headers) }) + it('should get original headers', function() { + let resp = new Response() + let data = getResponseData() + resp._setHttpSource(data) + assert.deepEqual(resp.origHeaders, data.headers) + }) + + it('should not set original headers', function() { + let resp = new Response() + let data = getResponseData() + resp._setHttpSource(data) + assert.throws(() => { + resp.origHeaders = {} + }) + }) + + it('original headers are not modified', function() { + let resp = new Response() + let data = getResponseData() + resp._setHttpSource(data) + resp.headers.foo = '1234' + assert.strictEqual(resp.headers.foo, '1234') + assert.strictEqual(resp.origHeaders.foo, undefined) + }) + + it('original headers should be frozen', function() { + let resp = new Response() + let data = getResponseData() + resp._setHttpSource(data) + assert.throws(() => { + resp.origHeaders.foo = '2' + }) + }) + it('should get and set status code', function(){ let resp = new Response() resp._setHttpSource(getResponseData())