Skip to content

Commit 15663ec

Browse files
author
Dom Harrington
committed
Buffer up res.body so we can send it off to the metrics server
1 parent 7eeb0c9 commit 15663ec

File tree

4 files changed

+114
-12
lines changed

4 files changed

+114
-12
lines changed

index.js

+25
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,30 @@ const config = require('config');
33

44
const constructPayload = require('./lib/construct-payload');
55

6+
// We're doing this to buffer up the response body
7+
// so we can send it off to the metrics server
8+
// It's unfortunate that this isn't accessible
9+
// natively. This may take up lots of memory on
10+
// big responses, we can make it configurable in future
11+
function patchResponse(res) {
12+
/* eslint-disable no-underscore-dangle */
13+
const { write, end } = res;
14+
15+
res._body = '';
16+
17+
res.write = (chunk, encoding, cb) => {
18+
res._body += chunk;
19+
write.call(res, chunk, encoding, cb);
20+
};
21+
22+
res.end = (chunk, encoding, cb) => {
23+
// Chunk is optional in res.end
24+
// http://nodejs.org/dist/latest/docs/api/http.html#http_response_end_data_encoding_callback
25+
if (chunk) res._body += chunk;
26+
end.call(res, chunk, encoding, cb);
27+
};
28+
}
29+
630
module.exports = (apiKey, group, options) => {
731
if (!apiKey) throw new Error('You must provide your ReadMe API key');
832
if (!group) throw new Error('You must provide a grouping function');
@@ -11,6 +35,7 @@ module.exports = (apiKey, group, options) => {
1135

1236
return (req, res, next) => {
1337
const startedDateTime = new Date();
38+
patchResponse(res);
1439

1540
function send() {
1641
request.post(`${config.host}/request`, {

lib/process-response.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,31 @@ const removeOtherProperties = require('lodash.pick');
44
const objectToArray = require('./object-to-array');
55

66
module.exports = (res, options = {}) => {
7-
if (options.blacklist) {
8-
res.body = removeProperties(res.body, options.blacklist);
7+
// Here we have to reconstruct the body
8+
// from the string that we've buffered up
9+
// We have to do this so we can strip out
10+
// any whitelist/blacklist properties
11+
let body;
12+
try {
13+
body = JSON.parse(res._body); // eslint-disable-line no-underscore-dangle
14+
} catch (e) {
15+
// Non JSON body, don't attempt to send it off
916
}
1017

11-
if (options.whitelist) {
12-
res.body = removeOtherProperties(res.body, options.whitelist);
18+
if (options.blacklist && body) {
19+
body = removeProperties(body, options.blacklist);
20+
}
21+
22+
if (options.whitelist && body) {
23+
body = removeOtherProperties(body, options.whitelist);
1324
}
1425

1526
return {
1627
status: res.statusCode,
1728
statusText: res.statusMessage,
1829
headers: objectToArray(res.getHeaders()),
1930
content: {
20-
text: JSON.stringify(res.body),
31+
text: JSON.stringify(body),
2132
size: res.get('content-length'),
2233
mimeType: res.get('content-type'),
2334
},

test/index.test.js

+67-5
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,7 @@ describe('@readme/metrics', () => {
4343
.reply(200);
4444

4545
const app = express();
46-
app.use((req, res, next) => {
47-
req.user = { group };
48-
return next();
49-
});
50-
app.use(middleware(apiKey, req => req.user.group));
46+
app.use(middleware(apiKey, () => group));
5147
app.get('/test', (req, res) => res.sendStatus(200));
5248

5349
await request(app)
@@ -56,4 +52,70 @@ describe('@readme/metrics', () => {
5652

5753
mock.done();
5854
});
55+
56+
describe('`res._body`', () => {
57+
const responseBody = { a: 1, b: 2, c: 3 };
58+
function createMock() {
59+
return nock(config.host)
60+
.post('/request', ([body]) => {
61+
assert.equal(
62+
body.request.log.entries[0].response.content.text,
63+
JSON.stringify(responseBody),
64+
);
65+
return true;
66+
})
67+
.basicAuth({ user: apiKey })
68+
.reply(200);
69+
}
70+
71+
it('should buffer up res.write() calls', async function test() {
72+
this.timeout(5000);
73+
74+
const mock = createMock();
75+
const app = express();
76+
app.use(middleware(apiKey, () => '123'));
77+
app.get('/test', (req, res) => {
78+
res.write('{"a":1,');
79+
res.write('"b":2,');
80+
res.write('"c":3}');
81+
res.status(200).end();
82+
});
83+
84+
await request(app)
85+
.get('/test')
86+
.expect(200);
87+
88+
mock.done();
89+
});
90+
91+
it('should buffer up res.end() calls', async function test() {
92+
this.timeout(5000);
93+
94+
const mock = createMock();
95+
const app = express();
96+
app.use(middleware(apiKey, () => '123'));
97+
app.get('/test', (req, res) => res.end(JSON.stringify(responseBody)));
98+
99+
await request(app)
100+
.get('/test')
101+
.expect(200);
102+
103+
mock.done();
104+
});
105+
106+
it('should work for res.send() calls', async function test() {
107+
this.timeout(5000);
108+
109+
const mock = createMock();
110+
const app = express();
111+
app.use(middleware(apiKey, () => '123'));
112+
app.get('/test', (req, res) => res.send(responseBody));
113+
114+
await request(app)
115+
.get('/test')
116+
.expect(200);
117+
118+
mock.done();
119+
});
120+
});
59121
});

test/process-response.test.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ function testResponse(assertion, response) {
1212
app.post('/*', (req, res) => {
1313
res.once('finish', assertion.bind(null, res));
1414

15+
// This is done in the main middleware by
16+
// overwriting res.write/end
17+
res._body = JSON.stringify(response); // eslint-disable-line no-underscore-dangle
18+
1519
res.json(response);
1620
});
1721

@@ -22,7 +26,7 @@ function testResponse(assertion, response) {
2226
}
2327

2428
describe('processResponse()', () => {
25-
describe.skip('options', () => {
29+
describe('options', () => {
2630
it('should strip blacklisted properties', done => {
2731
testResponse(
2832
res => {
@@ -90,7 +94,7 @@ describe('processResponse()', () => {
9094
return done();
9195
}));
9296

93-
it.skip('#text', done => {
97+
it('#text', done => {
9498
const body = { a: 1, b: 2, c: 3 };
9599
testResponse(res => {
96100
assert.deepEqual(processResponse(res).content.text, JSON.stringify(body));

0 commit comments

Comments
 (0)