Browse Source

Fix a bug in HTTP server when receiving half-closes.

v0.7.4-release
Ryan 16 years ago
parent
commit
6a172d7119
  1. 30
      src/http.js
  2. 2
      src/net.cc
  3. 25
      test/test-http.js
  4. 17
      test/test-pingpong.js
  5. 23
      test_client.js

30
src/http.js

@ -105,6 +105,7 @@ function toRaw(string) {
node.http.ServerResponse = function (connection, responses) { node.http.ServerResponse = function (connection, responses) {
responses.push(this); responses.push(this);
this.connection = connection; this.connection = connection;
this.closeOnFinish = false;
var output = []; var output = [];
// The send method appends data onto the output array. The deal is, // The send method appends data onto the output array. The deal is,
@ -151,14 +152,7 @@ node.http.ServerResponse = function (connection, responses) {
output.push(data); output.push(data);
}; };
this.flush = function () {
if (responses.length > 0 && responses[0] === this)
while (output.length > 0)
connection.send(output.shift());
};
var chunked_encoding = false; var chunked_encoding = false;
var connection_close = false;
this.sendHeader = function (status_code, headers) { this.sendHeader = function (status_code, headers) {
var sent_connection_header = false; var sent_connection_header = false;
@ -225,6 +219,14 @@ node.http.ServerResponse = function (connection, responses) {
this.flush(); this.flush();
}; };
this.flush = function () {
if (responses.length > 0 && responses[0] === this)
while (output.length > 0) {
var out = output.shift();
connection.send(out);
}
};
this.finished = false; this.finished = false;
this.finish = function () { this.finish = function () {
if (chunked_encoding) if (chunked_encoding)
@ -235,12 +237,10 @@ node.http.ServerResponse = function (connection, responses) {
while (responses.length > 0 && responses[0].finished) { while (responses.length > 0 && responses[0].finished) {
var res = responses[0]; var res = responses[0];
res.flush(); res.flush();
if (res.closeOnFinish)
connection.fullClose();
responses.shift(); responses.shift();
} }
if (responses.length == 0 && connection_close) {
connection.fullClose();
}
}; };
}; };
@ -322,7 +322,11 @@ node.http.Server = function (RequestHandler, options) {
// is this really needed? // is this really needed?
connection.onEOF = function () { connection.onEOF = function () {
connection.close(); puts("HTTP SERVER got eof");
if (responses.length == 0)
connection.close();
else
responses[responses.length-1].closeOnFinish = true;
}; };
} }
@ -355,7 +359,7 @@ node.http.Client = function (port, host) {
if (connection_expression.exec(field)) { if (connection_expression.exec(field)) {
sent_connection_header = true; sent_connection_header = true;
if (close_expression.exec(value)) if (close_expression.exec(value))
connection_close = true; this.closeOnFinish = true;
} else if (transfer_encoding_expression.exec(field)) { } else if (transfer_encoding_expression.exec(field)) {
sent_transfer_encoding_header = true; sent_transfer_encoding_header = true;
if (chunk_expression.exec(value)) if (chunk_expression.exec(value))

2
src/net.cc

@ -440,7 +440,7 @@ void name () \
TryCatch try_catch; \ TryCatch try_catch; \
callback->Call(handle_, 0, NULL); \ callback->Call(handle_, 0, NULL); \
if (try_catch.HasCaught()) \ if (try_catch.HasCaught()) \
fatal_exception(try_catch); \ node::fatal_exception(try_catch); \
} }
DEFINE_SIMPLE_CALLBACK(Connection::OnConnect, ON_CONNECT_SYMBOL) DEFINE_SIMPLE_CALLBACK(Connection::OnConnect, ON_CONNECT_SYMBOL)

25
test/test-http.js

@ -7,24 +7,26 @@ function onLoad() {
var request_number = 0; var request_number = 0;
new node.http.Server(function (req, res) { new node.http.Server(function (req, res) {
var server = this; res.id = request_number;
req.id = request_number++; req.id = request_number++;
if (req.id == 0) { if (req.id == 0) {
puts("get req"); //puts("get req");
assertEquals("GET", req.method); assertEquals("GET", req.method);
assertEquals("/hello", req.uri.path); assertEquals("/hello", req.uri.path);
} }
if (req.id == 1) { if (req.id == 1) {
puts("post req"); //puts("post req");
assertEquals("POST", req.method); assertEquals("POST", req.method);
assertEquals("/quit", req.uri.path); assertEquals("/quit", req.uri.path);
server.close(); this.close();
puts("server closed"); //puts("server closed");
} }
setTimeout(function () { setTimeout(function () {
//puts("send response");
res.sendHeader(200, [["Content-Type", "text/plain"]]); res.sendHeader(200, [["Content-Type", "text/plain"]]);
res.sendBody(req.uri.path); res.sendBody(req.uri.path);
res.finish(); res.finish();
@ -35,13 +37,14 @@ function onLoad() {
var c = new node.tcp.Connection(); var c = new node.tcp.Connection();
var req_sent = 0; var req_sent = 0;
c.onConnect = function () { c.onConnect = function () {
puts("send get"); //puts("send get");
c.send( "GET /hello HTTP/1.1\r\n\r\n" ); c.send( "GET /hello HTTP/1.1\r\n\r\n" );
req_sent += 1; req_sent += 1;
}; };
var total = ""; var total = "";
c.onReceive = function (chunk) { c.onReceive = function (chunk) {
//puts("client recv");
total += chunk.encodeUtf8(); total += chunk.encodeUtf8();
puts("total: " + JSON.stringify(total)); puts("total: " + JSON.stringify(total));
@ -49,12 +52,20 @@ function onLoad() {
puts("send post"); puts("send post");
c.send("POST /quit HTTP/1.1\r\n\r\n"); c.send("POST /quit HTTP/1.1\r\n\r\n");
c.close(); c.close();
puts("client half close");
assertEquals(c.readyState, "readOnly");
req_sent += 1; req_sent += 1;
} }
}; };
c.onEOF = function () {
puts("client got eof");
};
c.onDisconnect = function () { c.onDisconnect = function () {
puts("client disocnnected"); puts("client disconnected");
assertEquals(c.readyState, "closed");
var hello = new RegExp("/hello"); var hello = new RegExp("/hello");
assertTrue(hello.exec(total) != null); assertTrue(hello.exec(total) != null);

17
test/test-pingpong.js

@ -48,23 +48,34 @@ function onLoad() {
client.send("PING"); client.send("PING");
}; };
var sent_final_ping = false;
client.onReceive = function (data) { client.onReceive = function (data) {
assertEquals("open", client.readyState);
//puts("client recved data: " + JSON.stringify(data)); //puts("client recved data: " + JSON.stringify(data));
stdout.print("."); stdout.print(".");
assertEquals("PONG", data); assertEquals("PONG", data);
count += 1; count += 1;
if (sent_final_ping) {
assertEquals("readOnly", client.readyState);
return;
} else {
assertEquals("open", client.readyState);
}
if (count < N) { if (count < N) {
client.send("PING"); client.send("PING");
} else { } else {
puts("sending FIN"); puts("sending final ping");
sent_final_ping = true;
client.send("PING");
client.close(); client.close();
} }
}; };
client.onEOF = function () { client.onEOF = function () {
puts("pinger: onEOF"); puts("pinger: onEOF");
assertEquals(N, count); assertEquals(N+1, count);
}; };
client.connect(port); client.connect(port);

23
test_client.js

@ -2,15 +2,32 @@ var c = new node.http.Client(8000, "localhost")
var req = c.get("/hello/world", [["Accept", "*/*"]]); var req = c.get("/hello/world", [["Accept", "*/*"]]);
req.finish(function (res) { req.finish(function (res) {
puts("got response: " + res.status_code.toString()); puts("response 1: " + res.status_code.toString());
res.onBody = function (chunk) { res.onBody = function (chunk) {
puts("got response body <" + chunk.encodeUtf8() + ">"); puts("response 1 body <" + chunk.encodeUtf8() + ">");
return true; return true;
}; };
res.onBodyComplete = function () { res.onBodyComplete = function () {
puts("response complete!"); puts("response 1 complete!");
return true; return true;
}; };
}); });
/*
var req = c.get("/something/else", []);
req.finish(function (res) {
puts("response 2: " + res.status_code.toString());
res.onBody = function (chunk) {
puts("response 2 body <" + chunk.encodeUtf8() + ">");
return true;
};
res.onBodyComplete = function () {
puts("response 2 complete!");
return true;
};
});
*/

Loading…
Cancel
Save