Skip to content

Commit 815b0aa

Browse files
committed
Adds a flag to prevent the automatic lower-casing of response headers.
Automatically lower-casing response headers is adhering to HTML specifications, but it can be desireable to prevent his behavior for testing. Closes #85 and #86
1 parent cf340ed commit 815b0aa

File tree

8 files changed

+84
-22
lines changed

8 files changed

+84
-22
lines changed

CONTRIBUTERS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
[JJ4TH](https://github.com/jj4th)
2626

27+
[Aparna Sathyanarayan](https://github.com/aparna-sath)
28+
2729
# Special Thanks
2830

2931
Alexander Zagniotov

README.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,21 @@ Some systems require you to `sudo` before running services on certain ports (lik
5353

5454
```
5555
stubby [-a <port>] [-c <file>] [-d <file>] [-h] [-k <file>] [-l <hostname>] [-p <file>] [-q]
56-
[-s <port>] [-t <port>] [-v] [-w]
57-
58-
-a, --admin <port> Port for admin portal. Defaults to 8889.
59-
-c, --cert <file> Certificate file. Use with --key.
60-
-d, --data <file> Data file to pre-load endoints. YAML or JSON format.
61-
-h, --help This help text.
62-
-k, --key <file> Private key file. Use with --cert.
63-
-l, --location <hostname> Hostname at which to bind stubby.
64-
-q, --quiet Prevent stubby from printing to the console.
65-
-p, --pfx <file> PFX file. Ignored if used with --key/--cert
66-
-s, --stubs <port> Port for stubs portal. Defaults to 8882.
67-
-t, --tls <port> Port for https stubs portal. Defaults to 7443.
68-
-v, --version Prints stubby's version number.
69-
-w, --watch Auto-reload data file when edits are made.
56+
[-s <port>] [-t <port>] [-v] [-w] [-H]
57+
58+
-a, --admin <port> Port for admin portal. Defaults to 8889.
59+
-c, --cert <file> Certificate file. Use with --key.
60+
-d, --data <file> Data file to pre-load endoints. YAML or JSON format.
61+
-h, --help This help text.
62+
-k, --key <file> Private key file. Use with --cert.
63+
-l, --location <hostname> Hostname at which to bind stubby.
64+
-q, --quiet Prevent stubby from printing to the console.
65+
-p, --pfx <file> PFX file. Ignored if used with --key/--cert
66+
-s, --stubs <port> Port for stubs portal. Defaults to 8882.
67+
-t, --tls <port> Port for https stubs portal. Defaults to 7443.
68+
-v, --version Prints stubby's version number.
69+
-w, --watch Auto-reload data file when edits are made.
70+
-H, --case-sensitive-headers Do not normalize response headers to lower-case.
7071
```
7172

7273
When used from the command-line, `stubby` responds to the `SIGHUP` signal to reload its configuration.
@@ -671,6 +672,7 @@ What can I do with it, you ask? Read on!
671672
* `quiet`: defaults to `true`. Pass in `false` to have console output (if available)
672673
* `_httpsOptions`: additional options to pass to the [underlying tls
673674
server](http://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener).
675+
* `caseSensitiveHeaders`: if false (the default), all response headers are lower-cased.
674676
* `callback`: takes one parameter: the error message (if there is one), undefined otherwise
675677

676678
#### start([callback])

src/console/cli.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ var options = [{
6969
name: 'watch',
7070
flag: 'w',
7171
description: 'Auto-reload data file when edits are made.'
72+
}, {
73+
name: 'case-sensitive-headers',
74+
flag: 'H',
75+
description: 'Do no automatically lower-case response headers.'
7276
}];
7377

7478
function help (go) {

src/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ Stubby.prototype.start = function (o, cb) {
9999

100100
if (errors) { return callback(errors); }
101101
if (options.datadir != null) { self.endpoints.datadir = options.datadir; }
102+
if (options['case-sensitive-headers'] != null) { self.endpoints.caseSensitiveHeaders = options['case-sensitive-headers']; }
102103

103104
self.endpoints.create(options.data, onEndpointLoaded);
104105

src/models/endpoint.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ var http = require('http');
66
var q = require('querystring');
77
var out = require('../console/out');
88

9-
function Endpoint (endpoint, datadir) {
9+
function Endpoint (endpoint, datadir, caseSensitiveHeaders) {
1010
if (endpoint == null) { endpoint = {}; }
1111
if (datadir == null) { datadir = process.cwd(); }
1212

1313
Object.defineProperty(this, 'datadir', { value: datadir });
1414

1515
this.request = purifyRequest(endpoint.request);
16-
this.response = purifyResponse(this, endpoint.response);
16+
this.response = purifyResponse(this, endpoint.response, caseSensitiveHeaders);
1717
this.hits = 0;
1818
}
1919

@@ -133,7 +133,7 @@ function purifyRequest (incoming) {
133133
return outgoing;
134134
}
135135

136-
function purifyResponse (me, incoming) {
136+
function purifyResponse (me, incoming, caseSensitiveHeaders) {
137137
var outgoing = [];
138138

139139
if (incoming == null) { incoming = []; }
@@ -145,7 +145,7 @@ function purifyResponse (me, incoming) {
145145
outgoing.push(record(me, response));
146146
} else {
147147
outgoing.push(pruneUndefined({
148-
headers: purifyHeaders(response.headers),
148+
headers: purifyHeaders(response.headers, caseSensitiveHeaders),
149149
status: parseInt(response.status, 10) || 200,
150150
latency: parseInt(response.latency, 10) || null,
151151
file: response.file,
@@ -157,13 +157,17 @@ function purifyResponse (me, incoming) {
157157
return outgoing;
158158
}
159159

160-
function purifyHeaders (incoming) {
160+
function purifyHeaders (incoming, caseSensitiveHeaders) {
161161
var prop;
162162
var outgoing = {};
163163

164164
for (prop in incoming) {
165165
if (Object.prototype.hasOwnProperty.call(incoming, prop)) {
166-
outgoing[prop.toLowerCase()] = incoming[prop];
166+
if (caseSensitiveHeaders) {
167+
outgoing[prop] = incoming[prop];
168+
} else {
169+
outgoing[prop.toLowerCase()] = incoming[prop];
170+
}
167171
}
168172
}
169173

src/models/endpoints.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function Endpoints (data, callback, datadir) {
1515
if (callback == null) { callback = noop; }
1616
if (datadir == null) { datadir = process.cwd(); }
1717

18+
this.caseSensitiveHeaders = false;
1819
this.datadir = datadir;
1920
this.db = {};
2021
this.lastId = 0;
@@ -27,7 +28,7 @@ Endpoints.prototype.create = function (data, callback) {
2728
if (callback == null) { callback = noop; }
2829

2930
function insert (item) {
30-
item = new Endpoint(item, self.datadir);
31+
item = new Endpoint(item, self.datadir, self.caseSensitiveHeaders);
3132
item.id = ++self.lastId;
3233
self.db[item.id] = item;
3334
callback(null, clone(item));

test/cli.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,26 @@ describe('CLI', function () {
231231
});
232232
});
233233

234+
describe('-H, --case-sensitive-headers', function () {
235+
it('should return default if no flag provided', function () {
236+
const expected = false;
237+
const actual = sut.getArgs([]);
238+
assert.strictEqual(actual['case-sensitive-headers'], expected);
239+
});
240+
241+
it('should return supplied value when provided', function () {
242+
const expected = true;
243+
const actual = sut.getArgs(['-H', expected]);
244+
assert.strictEqual(actual['case-sensitive-headers'], expected);
245+
});
246+
247+
it('should return supplied value when provided with full flag', function () {
248+
const expected = true;
249+
const actual = sut.getArgs(['--case-sensitive-headers', expected]);
250+
assert.strictEqual(actual['case-sensitive-headers'], expected);
251+
});
252+
});
253+
234254
describe('getArgs', function () {
235255
it('should gather all arguments', function () {
236256
var actual;
@@ -247,6 +267,7 @@ describe('CLI', function () {
247267
quiet: true,
248268
watch: filename,
249269
datadir: process.cwd(),
270+
'case-sensitive-headers': true,
250271
help: undefined, // eslint-disable-line no-undefined
251272
version: (require('../package.json')).version
252273
};
@@ -255,7 +276,7 @@ describe('CLI', function () {
255276
this.sandbox.stub(sut, 'cert').returns(expected.cert);
256277
this.sandbox.stub(sut, 'pfx').returns(expected.pfx);
257278

258-
actual = sut.getArgs(['-s', expected.stubs, '-a', expected.admin, '-d', filename, '-l', expected.location, '-k', 'mocked', '-c', 'mocked', '-p', 'mocked', '-t', expected.tls, '-q', '-w']);
279+
actual = sut.getArgs(['-s', expected.stubs, '-a', expected.admin, '-d', filename, '-l', expected.location, '-k', 'mocked', '-c', 'mocked', '-p', 'mocked', '-t', expected.tls, '-q', '-w', '-H']);
259280

260281
assert.deepStrictEqual(actual, expected);
261282
});

test/endpoint.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,33 @@ describe('Endpoint', function () {
269269
assert.deepStrictEqual(actual.request.headers, expected.request);
270270
});
271271

272+
it('should not lower case response headers properties if caseSensitiveHeaders is true', function () {
273+
var actual, expected;
274+
this.data.request = {
275+
headers: {
276+
'Content-Type': 'application/json'
277+
}
278+
};
279+
this.data.response = {
280+
headers: {
281+
'Content-Type': 'application/json'
282+
}
283+
};
284+
expected = {
285+
request: {
286+
'content-type': 'application/json'
287+
},
288+
response: {
289+
'Content-Type': 'application/json'
290+
}
291+
};
292+
293+
actual = new Endpoint(this.data, null, true);
294+
295+
assert.deepStrictEqual(actual.response[0].headers, expected.response);
296+
assert.deepStrictEqual(actual.request.headers, expected.request);
297+
});
298+
272299
it('should define multiple headers with same name', function () {
273300
var actual, expected;
274301
this.data.request = {

0 commit comments

Comments
 (0)