Skip to content

Commit 8b2a272

Browse files
rvaggMylesBorins
authored andcommitted
http: process headers after setting up agent
Added tests to clarify the implicit behaviour of array header setting vs object header setting PR-URL: #16568 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 6b74064 commit 8b2a272

File tree

2 files changed

+108
-43
lines changed

2 files changed

+108
-43
lines changed

lib/_http_client.js

+48-43
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,40 @@ function ClientRequest(options, cb) {
164164
this.once('response', cb);
165165
}
166166

167+
if (method === 'GET' ||
168+
method === 'HEAD' ||
169+
method === 'DELETE' ||
170+
method === 'OPTIONS' ||
171+
method === 'CONNECT') {
172+
this.useChunkedEncodingByDefault = false;
173+
} else {
174+
this.useChunkedEncodingByDefault = true;
175+
}
176+
177+
this._ended = false;
178+
this.res = null;
179+
this.aborted = undefined;
180+
this.timeoutCb = null;
181+
this.upgradeOrConnect = false;
182+
this.parser = null;
183+
this.maxHeadersCount = null;
184+
185+
var called = false;
186+
187+
if (this.agent) {
188+
// If there is an agent we should default to Connection:keep-alive,
189+
// but only if the Agent will actually reuse the connection!
190+
// If it's not a keepAlive agent, and the maxSockets==Infinity, then
191+
// there's never a case where this socket will actually be reused
192+
if (!this.agent.keepAlive && !Number.isFinite(this.agent.maxSockets)) {
193+
this._last = true;
194+
this.shouldKeepAlive = false;
195+
} else {
196+
this._last = false;
197+
this.shouldKeepAlive = true;
198+
}
199+
}
200+
167201
var headersArray = Array.isArray(options.headers);
168202
if (!headersArray) {
169203
if (options.headers) {
@@ -173,6 +207,7 @@ function ClientRequest(options, cb) {
173207
this.setHeader(key, options.headers[key]);
174208
}
175209
}
210+
176211
if (host && !this.getHeader('host') && setHost) {
177212
var hostHeader = host;
178213

@@ -191,45 +226,25 @@ function ClientRequest(options, cb) {
191226
}
192227
this.setHeader('Host', hostHeader);
193228
}
194-
}
195229

196-
if (options.auth && !this.getHeader('Authorization')) {
197-
this.setHeader('Authorization', 'Basic ' +
198-
Buffer.from(options.auth).toString('base64'));
199-
}
230+
if (options.auth && !this.getHeader('Authorization')) {
231+
this.setHeader('Authorization', 'Basic ' +
232+
Buffer.from(options.auth).toString('base64'));
233+
}
200234

201-
if (method === 'GET' ||
202-
method === 'HEAD' ||
203-
method === 'DELETE' ||
204-
method === 'OPTIONS' ||
205-
method === 'CONNECT') {
206-
this.useChunkedEncodingByDefault = false;
207-
} else {
208-
this.useChunkedEncodingByDefault = true;
209-
}
235+
if (this.getHeader('expect')) {
236+
if (this._header) {
237+
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'render');
238+
}
210239

211-
if (headersArray) {
212-
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
213-
options.headers);
214-
} else if (this.getHeader('expect')) {
215-
if (this._header) {
216-
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'render');
240+
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
241+
this[outHeadersKey]);
217242
}
218-
243+
} else {
219244
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
220-
this[outHeadersKey]);
245+
options.headers);
221246
}
222247

223-
this._ended = false;
224-
this.res = null;
225-
this.aborted = undefined;
226-
this.timeoutCb = null;
227-
this.upgradeOrConnect = false;
228-
this.parser = null;
229-
this.maxHeadersCount = null;
230-
231-
var called = false;
232-
233248
var oncreate = (err, socket) => {
234249
if (called)
235250
return;
@@ -242,18 +257,8 @@ function ClientRequest(options, cb) {
242257
this._deferToConnect(null, null, () => this._flush());
243258
};
244259

260+
// initiate connection
245261
if (this.agent) {
246-
// If there is an agent we should default to Connection:keep-alive,
247-
// but only if the Agent will actually reuse the connection!
248-
// If it's not a keepAlive agent, and the maxSockets==Infinity, then
249-
// there's never a case where this socket will actually be reused
250-
if (!this.agent.keepAlive && !Number.isFinite(this.agent.maxSockets)) {
251-
this._last = true;
252-
this.shouldKeepAlive = false;
253-
} else {
254-
this._last = false;
255-
this.shouldKeepAlive = true;
256-
}
257262
this.agent.addRequest(this, options);
258263
} else {
259264
// No agent, default to Connection:close.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const assert = require('assert');
6+
const http = require('http');
7+
8+
function execute(options) {
9+
http.createServer(function(req, res) {
10+
const expectHeaders = {
11+
'x-foo': 'boom',
12+
cookie: 'a=1; b=2; c=3',
13+
connection: 'close'
14+
};
15+
16+
// no Host header when you set headers an array
17+
if (!Array.isArray(options.headers)) {
18+
expectHeaders.host = `localhost:${this.address().port}`;
19+
}
20+
21+
// no Authorization header when you set headers an array
22+
if (options.auth && !Array.isArray(options.headers)) {
23+
expectHeaders.authorization =
24+
`Basic ${Buffer.from(options.auth).toString('base64')}`;
25+
}
26+
27+
this.close();
28+
29+
assert.deepStrictEqual(req.headers, expectHeaders);
30+
31+
res.end();
32+
}).listen(0, function() {
33+
options = Object.assign(options, {
34+
port: this.address().port,
35+
path: '/'
36+
});
37+
const req = http.request(options);
38+
req.end();
39+
});
40+
}
41+
42+
// should be the same except for implicit Host header on the first two
43+
execute({ headers: { 'x-foo': 'boom', 'cookie': 'a=1; b=2; c=3' } });
44+
execute({ headers: { 'x-foo': 'boom', 'cookie': [ 'a=1', 'b=2', 'c=3' ] } });
45+
execute({ headers: [[ 'x-foo', 'boom' ], [ 'cookie', 'a=1; b=2; c=3' ]] });
46+
execute({ headers: [
47+
[ 'x-foo', 'boom' ], [ 'cookie', [ 'a=1', 'b=2', 'c=3' ]]
48+
] });
49+
execute({ headers: [
50+
[ 'x-foo', 'boom' ], [ 'cookie', 'a=1' ],
51+
[ 'cookie', 'b=2' ], [ 'cookie', 'c=3']
52+
] });
53+
54+
// Authorization and Host header both missing from the second
55+
execute({ auth: 'foo:bar', headers:
56+
{ 'x-foo': 'boom', 'cookie': 'a=1; b=2; c=3' } });
57+
execute({ auth: 'foo:bar', headers: [
58+
[ 'x-foo', 'boom' ], [ 'cookie', 'a=1' ],
59+
[ 'cookie', 'b=2' ], [ 'cookie', 'c=3']
60+
] });

0 commit comments

Comments
 (0)