Skip to content

Commit 45eef09

Browse files
authored
refactor: convert Request, Response, CodeResponseType, TokenResponseType to ES6 classes
Merge pull request #225 from menewman/fix-convert-request-response-classes-to-es6 thanks to @menewman
2 parents f460371 + 8ea6699 commit 45eef09

File tree

6 files changed

+124
-137
lines changed

6 files changed

+124
-137
lines changed

lib/request.js

Lines changed: 39 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,67 +7,53 @@
77
const InvalidArgumentError = require('./errors/invalid-argument-error');
88
const typeis = require('type-is');
99

10-
/**
11-
* Constructor.
12-
*/
13-
14-
function Request(options) {
15-
options = options || {};
10+
class Request {
11+
constructor({ headers, method, query, body, ...otherOptions } = {}) {
12+
if (!headers) {
13+
throw new InvalidArgumentError('Missing parameter: `headers`');
14+
}
1615

17-
if (!options.headers) {
18-
throw new InvalidArgumentError('Missing parameter: `headers`');
19-
}
16+
if (!method) {
17+
throw new InvalidArgumentError('Missing parameter: `method`');
18+
}
2019

21-
if (!options.method) {
22-
throw new InvalidArgumentError('Missing parameter: `method`');
23-
}
20+
if (!query) {
21+
throw new InvalidArgumentError('Missing parameter: `query`');
22+
}
2423

25-
if (!options.query) {
26-
throw new InvalidArgumentError('Missing parameter: `query`');
24+
this.body = body || {};
25+
this.headers = {};
26+
this.method = method;
27+
this.query = query;
28+
29+
// Store the headers in lower case.
30+
Object.entries(headers).forEach(([header, value]) => {
31+
this.headers[header.toLowerCase()] = value;
32+
});
33+
34+
// Store additional properties of the request object passed in
35+
Object.entries(otherOptions)
36+
.filter(([property]) => !this[property])
37+
.forEach(([property, value]) => {
38+
this[property] = value;
39+
});
2740
}
2841

29-
this.body = options.body || {};
30-
this.headers = {};
31-
this.method = options.method;
32-
this.query = options.query;
33-
34-
// Store the headers in lower case.
35-
for (const field in options.headers) {
36-
if (Object.prototype.hasOwnProperty.call(options.headers, field)) {
37-
this.headers[field.toLowerCase()] = options.headers[field];
38-
}
42+
/**
43+
* Get a request header.
44+
* @param {String} field
45+
*/
46+
get(field) {
47+
return this.headers[field.toLowerCase()];
3948
}
4049

41-
// Store additional properties of the request object passed in
42-
for (const property in options) {
43-
if (Object.prototype.hasOwnProperty.call(options, property) && !this[property]) {
44-
this[property] = options[property];
45-
}
50+
/**
51+
* Check if the content-type matches any of the given mime types.
52+
* @param {...String|Array} types
53+
*/
54+
is(...types) {
55+
return typeis(this, types.flat()) || false;
4656
}
4757
}
4858

49-
/**
50-
* Get a request header.
51-
*/
52-
53-
Request.prototype.get = function(field) {
54-
return this.headers[field.toLowerCase()];
55-
};
56-
57-
/**
58-
* Check if the content-type matches any of the given mime type.
59-
*/
60-
61-
Request.prototype.is = function(types) {
62-
if (!Array.isArray(types)) {
63-
types = [].slice.call(arguments);
64-
}
65-
66-
return typeis(this, types) || false;
67-
};
68-
69-
/**
70-
* Export constructor.
71-
*/
72-
7359
module.exports = Request;

lib/response-types/code-response-type.js

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,27 @@
77
const InvalidArgumentError = require('../errors/invalid-argument-error');
88
const url = require('url');
99

10-
/**
11-
* Constructor.
12-
*/
10+
class CodeResponseType {
11+
constructor(code) {
12+
if (!code) {
13+
throw new InvalidArgumentError('Missing parameter: `code`');
14+
}
1315

14-
function CodeResponseType(code) {
15-
if (!code) {
16-
throw new InvalidArgumentError('Missing parameter: `code`');
16+
this.code = code;
1717
}
1818

19-
this.code = code;
20-
}
19+
buildRedirectUri(redirectUri) {
20+
if (!redirectUri) {
21+
throw new InvalidArgumentError('Missing parameter: `redirectUri`');
22+
}
2123

22-
/**
23-
* Build redirect uri.
24-
*/
25-
26-
CodeResponseType.prototype.buildRedirectUri = function(redirectUri) {
27-
if (!redirectUri) {
28-
throw new InvalidArgumentError('Missing parameter: `redirectUri`');
29-
}
24+
const uri = url.parse(redirectUri, true);
3025

31-
const uri = url.parse(redirectUri, true);
26+
uri.query.code = this.code;
27+
uri.search = null;
3228

33-
uri.query.code = this.code;
34-
uri.search = null;
35-
36-
return uri;
37-
};
38-
39-
/**
40-
* Export constructor.
41-
*/
29+
return uri;
30+
}
31+
}
4232

4333
module.exports = CodeResponseType;

lib/response-types/token-response-type.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,10 @@
66

77
const ServerError = require('../errors/server-error');
88

9-
/**
10-
* Constructor.
11-
*/
12-
13-
function TokenResponseType() {
14-
throw new ServerError('Not implemented.');
9+
class TokenResponseType {
10+
constructor() {
11+
throw new ServerError('Not implemented.');
12+
}
1513
}
1614

17-
/**
18-
* Export constructor.
19-
*/
20-
2115
module.exports = TokenResponseType;

lib/response.js

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,45 @@
11
'use strict';
22

3-
/**
4-
* Constructor.
5-
*/
6-
7-
function Response(options) {
8-
options = options || {};
3+
class Response {
4+
constructor({ headers = {}, body = {}, ...otherOptions } = {}) {
5+
this.status = 200;
6+
this.body = body;
7+
this.headers = {};
8+
9+
// Store the headers in lower case.
10+
Object.entries(headers).forEach(([header, value]) => {
11+
this.headers[header.toLowerCase()] = value;
12+
});
13+
14+
// Store additional properties of the response object passed in
15+
Object.entries(otherOptions)
16+
.filter(([property]) => !this[property])
17+
.forEach(([property, value]) => {
18+
this[property] = value;
19+
});
20+
}
921

10-
this.body = options.body || {};
11-
this.headers = {};
12-
this.status = 200;
22+
/**
23+
* Get a response header.
24+
*/
25+
get(field) {
26+
return this.headers[field.toLowerCase()];
27+
}
1328

14-
// Store the headers in lower case.
15-
for (const field in options.headers) {
16-
if (Object.prototype.hasOwnProperty.call(options.headers, field)) {
17-
this.headers[field.toLowerCase()] = options.headers[field];
18-
}
29+
/**
30+
* Redirect response.
31+
*/
32+
redirect(url) {
33+
this.set('Location', url);
34+
this.status = 302;
1935
}
2036

21-
// Store additional properties of the response object passed in
22-
for (const property in options) {
23-
if (Object.prototype.hasOwnProperty.call(options, property) && !this[property]) {
24-
this[property] = options[property];
25-
}
37+
/**
38+
* Set a response header.
39+
*/
40+
set(field, value) {
41+
this.headers[field.toLowerCase()] = value;
2642
}
2743
}
2844

29-
/**
30-
* Get a response header.
31-
*/
32-
33-
Response.prototype.get = function(field) {
34-
return this.headers[field.toLowerCase()];
35-
};
36-
37-
/**
38-
* Redirect response.
39-
*/
40-
41-
Response.prototype.redirect = function(url) {
42-
this.set('Location', url);
43-
this.status = 302;
44-
};
45-
46-
/**
47-
* Set a response header.
48-
*/
49-
50-
Response.prototype.set = function(field, value) {
51-
this.headers[field.toLowerCase()] = value;
52-
};
53-
54-
/**
55-
* Export constructor.
56-
*/
57-
5845
module.exports = Response;

test/unit/request_test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@ describe('Request', function() {
127127
request.custom2.should.eql(originalRequest.custom2);
128128
});
129129

130+
it('should not allow overwriting methods on the Request prototype via custom properties', () => {
131+
const request = new Request({
132+
query: {},
133+
method: 'GET',
134+
headers: {
135+
'content-type': 'application/json'
136+
},
137+
get() {
138+
// malicious attempt to override the 'get' method
139+
return 'text/html';
140+
}
141+
});
142+
143+
request.get('content-type').should.equal('application/json');
144+
});
145+
130146
it('should allow getting of headers using `request.get`', function() {
131147
const originalRequest = generateBaseRequest();
132148

test/unit/response_test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,20 @@ describe('Request', function() {
8383
response.custom2.should.eql(originalResponse.custom2);
8484
});
8585

86+
it('should not allow overwriting methods on the Response prototype via custom properties', () => {
87+
const response = new Response({
88+
headers: {
89+
'content-type': 'application/json'
90+
},
91+
get() {
92+
// malicious attempt to override the 'get' method
93+
return 'text/html';
94+
}
95+
});
96+
97+
response.get('content-type').should.equal('application/json');
98+
});
99+
86100
it('should allow getting of headers using `response.get`', function() {
87101
const originalResponse = generateBaseResponse();
88102

0 commit comments

Comments
 (0)