Skip to content

Commit

Permalink
Adds a flag to prevent the automatic lower-casing of response headers.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mrak committed Jun 10, 2020
1 parent cf340ed commit 815b0aa
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

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

[Aparna Sathyanarayan](https://github.com/aparna-sath)

# Special Thanks

Alexander Zagniotov
Expand Down
30 changes: 16 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,21 @@ Some systems require you to `sudo` before running services on certain ports (lik

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

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

#### start([callback])
Expand Down
4 changes: 4 additions & 0 deletions src/console/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ var options = [{
name: 'watch',
flag: 'w',
description: 'Auto-reload data file when edits are made.'
}, {
name: 'case-sensitive-headers',
flag: 'H',
description: 'Do no automatically lower-case response headers.'
}];

function help (go) {
Expand Down
1 change: 1 addition & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Stubby.prototype.start = function (o, cb) {

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

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

Expand Down
16 changes: 10 additions & 6 deletions src/models/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ var http = require('http');
var q = require('querystring');
var out = require('../console/out');

function Endpoint (endpoint, datadir) {
function Endpoint (endpoint, datadir, caseSensitiveHeaders) {
if (endpoint == null) { endpoint = {}; }
if (datadir == null) { datadir = process.cwd(); }

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

this.request = purifyRequest(endpoint.request);
this.response = purifyResponse(this, endpoint.response);
this.response = purifyResponse(this, endpoint.response, caseSensitiveHeaders);
this.hits = 0;
}

Expand Down Expand Up @@ -133,7 +133,7 @@ function purifyRequest (incoming) {
return outgoing;
}

function purifyResponse (me, incoming) {
function purifyResponse (me, incoming, caseSensitiveHeaders) {
var outgoing = [];

if (incoming == null) { incoming = []; }
Expand All @@ -145,7 +145,7 @@ function purifyResponse (me, incoming) {
outgoing.push(record(me, response));
} else {
outgoing.push(pruneUndefined({
headers: purifyHeaders(response.headers),
headers: purifyHeaders(response.headers, caseSensitiveHeaders),
status: parseInt(response.status, 10) || 200,
latency: parseInt(response.latency, 10) || null,
file: response.file,
Expand All @@ -157,13 +157,17 @@ function purifyResponse (me, incoming) {
return outgoing;
}

function purifyHeaders (incoming) {
function purifyHeaders (incoming, caseSensitiveHeaders) {
var prop;
var outgoing = {};

for (prop in incoming) {
if (Object.prototype.hasOwnProperty.call(incoming, prop)) {
outgoing[prop.toLowerCase()] = incoming[prop];
if (caseSensitiveHeaders) {
outgoing[prop] = incoming[prop];
} else {
outgoing[prop.toLowerCase()] = incoming[prop];
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/models/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function Endpoints (data, callback, datadir) {
if (callback == null) { callback = noop; }
if (datadir == null) { datadir = process.cwd(); }

this.caseSensitiveHeaders = false;
this.datadir = datadir;
this.db = {};
this.lastId = 0;
Expand All @@ -27,7 +28,7 @@ Endpoints.prototype.create = function (data, callback) {
if (callback == null) { callback = noop; }

function insert (item) {
item = new Endpoint(item, self.datadir);
item = new Endpoint(item, self.datadir, self.caseSensitiveHeaders);
item.id = ++self.lastId;
self.db[item.id] = item;
callback(null, clone(item));
Expand Down
23 changes: 22 additions & 1 deletion test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,26 @@ describe('CLI', function () {
});
});

describe('-H, --case-sensitive-headers', function () {
it('should return default if no flag provided', function () {
const expected = false;
const actual = sut.getArgs([]);
assert.strictEqual(actual['case-sensitive-headers'], expected);
});

it('should return supplied value when provided', function () {
const expected = true;
const actual = sut.getArgs(['-H', expected]);
assert.strictEqual(actual['case-sensitive-headers'], expected);
});

it('should return supplied value when provided with full flag', function () {
const expected = true;
const actual = sut.getArgs(['--case-sensitive-headers', expected]);
assert.strictEqual(actual['case-sensitive-headers'], expected);
});
});

describe('getArgs', function () {
it('should gather all arguments', function () {
var actual;
Expand All @@ -247,6 +267,7 @@ describe('CLI', function () {
quiet: true,
watch: filename,
datadir: process.cwd(),
'case-sensitive-headers': true,
help: undefined, // eslint-disable-line no-undefined
version: (require('../package.json')).version
};
Expand All @@ -255,7 +276,7 @@ describe('CLI', function () {
this.sandbox.stub(sut, 'cert').returns(expected.cert);
this.sandbox.stub(sut, 'pfx').returns(expected.pfx);

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']);
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']);

assert.deepStrictEqual(actual, expected);
});
Expand Down
27 changes: 27 additions & 0 deletions test/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,33 @@ describe('Endpoint', function () {
assert.deepStrictEqual(actual.request.headers, expected.request);
});

it('should not lower case response headers properties if caseSensitiveHeaders is true', function () {
var actual, expected;
this.data.request = {
headers: {
'Content-Type': 'application/json'
}
};
this.data.response = {
headers: {
'Content-Type': 'application/json'
}
};
expected = {
request: {
'content-type': 'application/json'
},
response: {
'Content-Type': 'application/json'
}
};

actual = new Endpoint(this.data, null, true);

assert.deepStrictEqual(actual.response[0].headers, expected.response);
assert.deepStrictEqual(actual.request.headers, expected.request);
});

it('should define multiple headers with same name', function () {
var actual, expected;
this.data.request = {
Expand Down

0 comments on commit 815b0aa

Please sign in to comment.