Skip to content

Commit 98f5c46

Browse files
committed
Merge pull request #407 from GUI/master
Correct keep-alive responses to HTTP 1.0 clients
2 parents 9c13ad4 + daf53bd commit 98f5c46

File tree

3 files changed

+104
-9
lines changed

3 files changed

+104
-9
lines changed

lib/node-http-proxy/http-proxy.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,16 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
248248
//
249249
// Process the `reverseProxy` `response` when it's received.
250250
//
251-
if (!response.headers.connection) {
251+
if (req.httpVersion === '1.0') {
252+
if (req.headers.connection) {
253+
response.headers.connection = req.headers.connection
254+
} else {
255+
response.headers.connection = 'close'
256+
}
257+
} else if (!response.headers.connection) {
252258
if (req.headers.connection) { response.headers.connection = req.headers.connection }
253259
else {
254-
if (req.httpVersion === '1.0') {
255-
response.headers.connection = 'close'
256-
}
257-
else if (req.httpVersion === '1.1') {
258-
response.headers.connection = 'keep-alive'
259-
}
260+
response.headers.connection = 'keep-alive'
260261
}
261262
}
262263

test/http/http-test.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ vows.describe(helpers.describe()).addBatch({
6060
targetHeaders: { connection: "keep-alive" },
6161
outputHeaders: { connection: "keep-alive" }
6262
}),
63+
"and response keep-alive connection header from http 1.0 client": macros.http.assertRawHttpProxied({
64+
rawRequest: "GET / HTTP/1.0\r\n\r\n",
65+
targetHeaders: { connection: "keep-alive" },
66+
match: /connection: close/i
67+
}),
68+
"and request keep alive from http 1.0 client": macros.http.assertRawHttpProxied({
69+
rawRequest: "GET / HTTP/1.0\r\nConnection: Keep-Alive\r\n\r\n",
70+
targetHeaders: { connection: "keep-alive" },
71+
match: /connection: keep-alive/i
72+
}),
6373
"and no connection header": macros.http.assertProxied({
6474
request: { headers: { connection: "" } }, // Must explicitly set to "" because otherwise node will automatically add a "connection: keep-alive" header
6575
outputHeaders: { connection: "keep-alive" }
@@ -89,4 +99,4 @@ vows.describe(helpers.describe()).addBatch({
8999
latency: 2000
90100
})
91101
}
92-
}).export(module);
102+
}).export(module);

test/macros/http.js

+85-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
var assert = require('assert'),
1010
fs = require('fs'),
1111
async = require('async'),
12+
net = require('net'),
1213
request = require('request'),
1314
helpers = require('../helpers/index');
1415

@@ -141,6 +142,89 @@ exports.assertProxied = function (options) {
141142
};
142143
};
143144

145+
//
146+
// ### function assertRawHttpProxied (options)
147+
// #### @options {Object} Options for this test
148+
// #### @rawRequest {string} Raw HTTP request to perform.
149+
// #### @match {RegExp} Output to match in the response.
150+
// #### @latency {number} Latency in milliseconds for the proxy server.
151+
// #### @ports {Object} Ports for the request (target, proxy).
152+
// #### @output {string} Output to assert from.
153+
// #### @forward {Object} Options for forward proxying.
154+
//
155+
// Creates a complete end-to-end test for requesting against an
156+
// http proxy.
157+
//
158+
exports.assertRawHttpProxied = function (options) {
159+
// Don't test raw requests over HTTPS since options.rawRequest won't be
160+
// encrypted.
161+
if(helpers.protocols.proxy == 'https') {
162+
return true;
163+
}
164+
165+
options = options || {};
166+
167+
var ports = options.ports || helpers.nextPortPair,
168+
output = options.output || 'hello world from ' + ports.target,
169+
outputHeaders = options.outputHeaders,
170+
targetHeaders = options.targetHeaders,
171+
proxyHeaders = options.proxyHeaders,
172+
protocol = helpers.protocols.proxy,
173+
timeout = options.timeout || null,
174+
assertFn = options.shouldFail
175+
? exports.assertFailedRequest
176+
: exports.assertRequest;
177+
178+
return {
179+
topic: function () {
180+
var topicCallback = this.callback;
181+
182+
//
183+
// Create a target server and a proxy server
184+
// using the `options` supplied.
185+
//
186+
helpers.http.createServerPair({
187+
target: {
188+
output: output,
189+
outputHeaders: targetHeaders,
190+
port: ports.target,
191+
latency: options.requestLatency
192+
},
193+
proxy: {
194+
latency: options.latency,
195+
port: ports.proxy,
196+
outputHeaders: proxyHeaders,
197+
proxy: {
198+
forward: options.forward,
199+
target: {
200+
https: helpers.protocols.target === 'https',
201+
host: '127.0.0.1',
202+
port: ports.target
203+
},
204+
timeout: timeout
205+
}
206+
}
207+
}, function() {
208+
var response = '';
209+
var client = net.connect(ports.proxy, '127.0.0.1', function() {
210+
client.write(options.rawRequest);
211+
});
212+
213+
client.on('data', function(data) {
214+
response += data.toString();
215+
});
216+
217+
client.on('end', function() {
218+
topicCallback(null, options.match, response);
219+
});
220+
});
221+
},
222+
"should succeed": function(err, match, response) {
223+
assert.match(response, match);
224+
}
225+
};
226+
};
227+
144228
//
145229
// ### function assertInvalidProxy (options)
146230
// #### @options {Object} Options for this test
@@ -444,4 +528,4 @@ exports.assertDynamicProxy = function (static, dynamic) {
444528
return exports.assertProxiedToRoutes(static, {
445529
"once the server has started": context
446530
});
447-
};
531+
};

0 commit comments

Comments
 (0)