Skip to content
This repository was archived by the owner on Aug 10, 2023. It is now read-only.

Commit f6eed41

Browse files
misterdjulesmicahr
authored andcommitted
remove content-length header on HEAD requests only for curl user agents (#40)
* remove content-length header on HEAD requests only for curl user agents * add debug output for troubleshooting travis-ci issue * use node's HTTP client instead of wget
1 parent 159407f commit f6eed41

2 files changed

Lines changed: 131 additions & 4 deletions

File tree

lib/pre/userAgent.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ function userAgentConnection(options) {
4141

4242
if (ua && re.test(ua)) {
4343
res.setHeader('Connection', 'close');
44-
}
4544

46-
if (req.method === 'HEAD') {
47-
res.once('header',
48-
res.removeHeader.bind(res, 'content-length'));
45+
if (req.method === 'HEAD') {
46+
res.once('header',
47+
res.removeHeader.bind(res, 'content-length'));
48+
}
4949
}
5050

5151
next();

test/userAgent.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
'use strict';
2+
3+
// core requires
4+
var child_process = require('child_process');
5+
var http = require('http');
6+
7+
// external requires
8+
var assert = require('chai').assert;
9+
var restify = require('restify');
10+
11+
// local files
12+
var helper = require('./lib/helper');
13+
var plugins = require('../lib');
14+
15+
// local globals
16+
var SERVER;
17+
var SERVER_PORT;
18+
var SERVER_ADDRESS = '127.0.0.1';
19+
var SERVER_ENDPOINT;
20+
var TEST_ENDPOINT;
21+
var TEST_RESPONSE_DATA = 'foobar';
22+
var TEST_RESPONSE_DATA_LENGTH = TEST_RESPONSE_DATA.length;
23+
var TEST_PATH = '/test/userAgent';
24+
25+
describe('userAgent pre-route handler', function () {
26+
27+
beforeEach(function (done) {
28+
SERVER = restify.createServer({
29+
dtrace: helper.dtrace,
30+
log: helper.getLog('server')
31+
});
32+
33+
// Enable the user agent pre-route handler, since this is the component
34+
// under test.
35+
SERVER.use(plugins.pre.userAgentConnection());
36+
37+
SERVER.head('/test/:name', function (req, res, next) {
38+
// Explicitly set Content-Length response header so that we can test
39+
// for its removal (or lack thereof) by the userAgentConnection
40+
// pre-route handler in tests below.
41+
res.setHeader('Content-Length', TEST_RESPONSE_DATA_LENGTH);
42+
res.send(200, TEST_RESPONSE_DATA);
43+
next();
44+
});
45+
46+
47+
SERVER.listen(0, SERVER_ADDRESS, function () {
48+
SERVER_PORT = SERVER.address().port;
49+
SERVER_ENDPOINT = SERVER_ADDRESS + ':' + SERVER_PORT;
50+
TEST_ENDPOINT = SERVER_ENDPOINT + TEST_PATH;
51+
done();
52+
});
53+
});
54+
55+
afterEach(function (done) {
56+
SERVER.close(done);
57+
});
58+
59+
// By default, the userAgentConnection pre-route handler must:
60+
//
61+
// 1. set the 'connection' header to 'close'
62+
//
63+
// 2. remove the content-length header from the response
64+
//
65+
// when a HEAD request is handled and the client's user agent is curl.
66+
it('sets proper headers for HEAD requests from curl', function (done) {
67+
var CURL_CMD =
68+
['curl', '-sS', '-i', TEST_ENDPOINT, '-X', 'HEAD'].join(' ');
69+
70+
child_process.exec(CURL_CMD, function onExec(err, stdout, stderr) {
71+
assert.ifError(err);
72+
73+
var lines = stdout.split(/\n/);
74+
75+
var contentLengthHeaderNotPresent =
76+
lines.every(function checkContentLengthNotPresent(line) {
77+
return /Content-Length:.*/.test(line) === false;
78+
});
79+
var connectionCloseHeaderPresent =
80+
lines.some(function checkConnectionClosePresent(line) {
81+
return /Connection: close/.test(line);
82+
});
83+
84+
assert.ok(contentLengthHeaderNotPresent);
85+
assert.ok(connectionCloseHeaderPresent);
86+
87+
done();
88+
});
89+
});
90+
91+
// When handling a HEAD request, and if the client's user agent is not curl,
92+
// the userAgentConnection should not remove the content-length header from
93+
// the response, and it should not replace the value of the 'connection'
94+
// header by 'close'.
95+
it('sets proper headers for HEAD requests from non-curl clients',
96+
function (done) {
97+
var req = http.request({
98+
hostname: SERVER_ADDRESS,
99+
port: SERVER_PORT,
100+
path: TEST_PATH,
101+
method: 'HEAD',
102+
headers: {
103+
'user-agent': 'foobar',
104+
connection: 'keep-alive'
105+
}
106+
}, function onResponse(res) {
107+
var responseHeaders = res.headers;
108+
109+
assert.ok(responseHeaders.hasOwnProperty('content-length'));
110+
assert.equal(responseHeaders.connection, 'keep-alive');
111+
112+
// destroy the socket explicitly now since the request was
113+
// explicitly requesting to not destroy the socket by setting
114+
// its connection header to 'keep-alive'.
115+
req.abort();
116+
117+
done();
118+
});
119+
120+
req.on('error', function onReqError(err) {
121+
assert.ifError(err);
122+
done();
123+
});
124+
125+
req.end();
126+
});
127+
});

0 commit comments

Comments
 (0)