Browse Source

Simplify state transitions in http.Client

Fixes new bug shown in test-http-allow-req-after-204-res.js pointed out by
Tom Carden <tom.carden@gmail.com>.
v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
735b9d50a3
  1. 32
      lib/http.js
  2. 43
      test/simple/test-http-allow-req-after-204-res.js

32
lib/http.js

@ -880,6 +880,12 @@ function Client ( ) {
net.Stream.call(this, { allowHalfOpen: true }); net.Stream.call(this, { allowHalfOpen: true });
var self = this; var self = this;
// Possible states:
// - disconnected
// - connecting
// - connected
this._state = 'disconnected';
httpSocketSetup(self); httpSocketSetup(self);
function onData(d, start, end) { function onData(d, start, end) {
@ -912,6 +918,8 @@ function Client ( ) {
self.ondata = onData; self.ondata = onData;
self.onend = onEnd; self.onend = onEnd;
self._state = "connected";
self._initParser(); self._initParser();
debug('CLIENT requests: ' + util.inspect(self._outgoing.map(function (r) { return r.method; }))); debug('CLIENT requests: ' + util.inspect(self._outgoing.map(function (r) { return r.method; })));
outgoingFlush(self); outgoingFlush(self);
@ -919,21 +927,22 @@ function Client ( ) {
function onEnd() { function onEnd() {
if (self.parser) self.parser.finish(); if (self.parser) self.parser.finish();
debug("CLIENT got end closing. readyState = " + self.readyState); debug("CLIENT got end closing. state = " + self._state);
self.end(); self.end();
}; };
self.addListener("close", function (e) { self.addListener("close", function (e) {
self._state = "disconnected";
if (e) return; if (e) return;
debug("CLIENT onClose. readyState = " + self.readyState); debug("CLIENT onClose. state = " + self._state);
// finally done with the request // finally done with the request
self._outgoing.shift(); self._outgoing.shift();
// If there are more requests to handle, reconnect. // If there are more requests to handle, reconnect.
if (self._outgoing.length) { if (self._outgoing.length) {
self._reconnect(); self._ensureConnection();
} else if (self.parser) { } else if (self.parser) {
parsers.free(self.parser); parsers.free(self.parser);
self.parser = null; self.parser = null;
@ -982,7 +991,7 @@ Client.prototype._initParser = function () {
} }
res.addListener('end', function ( ) { res.addListener('end', function ( ) {
debug("CLIENT request complete disconnecting. readyState = " + self.readyState); debug("CLIENT request complete disconnecting. state = " + self._state);
// For the moment we reconnect for every request. FIXME! // For the moment we reconnect for every request. FIXME!
// All that should be required for keep-alive is to not reconnect, // All that should be required for keep-alive is to not reconnect,
// but outgoingFlush instead. // but outgoingFlush instead.
@ -1020,17 +1029,18 @@ Client.prototype._onOutgoingSent = function (message) {
// //
// Instead, we just check if the connection is closed, and if so // Instead, we just check if the connection is closed, and if so
// reconnect if we have pending messages. // reconnect if we have pending messages.
if (this._outgoing.length && this.readyState == "closed") { if (this._outgoing.length) {
debug("CLIENT request flush. reconnect. readyState = " + this.readyState); debug("CLIENT request flush. ensure connection. state = " + this._state);
this._reconnect(); this._ensureConnection();
} }
}; };
Client.prototype._reconnect = function () { Client.prototype._ensureConnection = function () {
if (this.readyState === "closed") { if (this._state == 'disconnected') {
debug("CLIENT reconnecting readyState = " + this.readyState); debug("CLIENT reconnecting state = " + this._state);
this.connect(this.port, this.host); this.connect(this.port, this.host);
this._state = "connecting";
} }
}; };
@ -1044,7 +1054,7 @@ Client.prototype.request = function (method, url, headers) {
} }
var req = new ClientRequest(this, method, url, headers); var req = new ClientRequest(this, method, url, headers);
this._outgoing.push(req); this._outgoing.push(req);
if (this.readyState === 'closed') this._reconnect(); this._ensureConnection();
return req; return req;
}; };

43
test/simple/test-http-allow-req-after-204-res.js

@ -0,0 +1,43 @@
var common = require('../common');
var http = require('http');
var assert = require('assert');
// first 204 or 304 works, subsequent anything fails
var codes = [ 204, 200 ];
// Methods don't really matter, but we put in something realistic.
var methods = ['DELETE', 'DELETE'];
var server = http.createServer(function (req, res) {
var code = codes.shift();
assert.equal('number', typeof code);
assert.ok(code > 0);
console.error('writing %d response', code);
res.writeHead(code, {});
res.end();
});
var client = http.createClient(common.PORT);
function nextRequest() {
var method = methods.shift();
console.error("writing request: %s", method);
var request = client.request(method, '/');
request.on('response', function (response) {
response.on('end', function() {
if (methods.length == 0) {
console.error("close server");
server.close();
} else {
// throws error:
nextRequest();
// works just fine:
//process.nextTick(nextRequest);
}
});
});
request.end();
}
server.listen(common.PORT, nextRequest);
Loading…
Cancel
Save