Fix a bug in HTTP server when receiving half-closes.
authorRyan <ry@tinyclouds.org>
Tue, 19 May 2009 18:24:37 +0000 (20:24 +0200)
committerRyan <ry@tinyclouds.org>
Tue, 19 May 2009 18:24:37 +0000 (20:24 +0200)
src/http.js
src/net.cc
test/test-http.js
test/test-pingpong.js
test_client.js

index a2642cc..5934beb 100644 (file)
@@ -105,6 +105,7 @@ function toRaw(string) {
 node.http.ServerResponse = function (connection, responses) {
   responses.push(this);
   this.connection = connection;
+  this.closeOnFinish = false;
   var output = [];
 
   // 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);
   };
 
-  this.flush = function () {
-    if (responses.length > 0 && responses[0] === this)
-      while (output.length > 0)
-        connection.send(output.shift());
-  };
-
   var chunked_encoding = false;
-  var connection_close = false;
 
   this.sendHeader = function (status_code, headers) {
     var sent_connection_header = false;
@@ -225,6 +219,14 @@ node.http.ServerResponse = function (connection, responses) {
     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.finish = function () {
     if (chunked_encoding)
@@ -235,12 +237,10 @@ node.http.ServerResponse = function (connection, responses) {
     while (responses.length > 0 && responses[0].finished) {
       var res = responses[0];
       res.flush();
+      if (res.closeOnFinish)
+        connection.fullClose();
       responses.shift();
     }
-
-    if (responses.length == 0 && connection_close) {
-      connection.fullClose();
-    }
   };
 };
 
@@ -322,7 +322,11 @@ node.http.Server = function (RequestHandler, options) {
 
     // is this really needed?
     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)) {
         sent_connection_header = true;
         if (close_expression.exec(value))
-          connection_close = true;
+          this.closeOnFinish = true;
       } else if (transfer_encoding_expression.exec(field)) {
         sent_transfer_encoding_header = true;
         if (chunk_expression.exec(value))
index 5fd2f49..fbed1af 100644 (file)
@@ -440,7 +440,7 @@ void name ()                                                        \
   TryCatch try_catch;                                               \
   callback->Call(handle_, 0, NULL);                                 \
   if (try_catch.HasCaught())                                        \
-    fatal_exception(try_catch);                                     \
+    node::fatal_exception(try_catch);                               \
 }
 
 DEFINE_SIMPLE_CALLBACK(Connection::OnConnect, ON_CONNECT_SYMBOL)
index 13e52a0..c8e05bb 100644 (file)
@@ -7,24 +7,26 @@ function onLoad() {
   var request_number = 0;
 
   new node.http.Server(function (req, res) {
-    var server = this;
+    res.id = request_number;
     req.id = request_number++;
 
+
     if (req.id == 0) {
-      puts("get req");
+      //puts("get req");
       assertEquals("GET", req.method);
       assertEquals("/hello", req.uri.path);
     }
 
     if (req.id == 1) {
-      puts("post req");
+      //puts("post req");
       assertEquals("POST", req.method);
       assertEquals("/quit", req.uri.path);
-      server.close();
-      puts("server closed");
+      this.close();
+      //puts("server closed");
     }
 
     setTimeout(function () {
+      //puts("send response");
       res.sendHeader(200, [["Content-Type", "text/plain"]]);
       res.sendBody(req.uri.path);
       res.finish();
@@ -35,13 +37,14 @@ function onLoad() {
   var c = new node.tcp.Connection();
   var req_sent = 0;
   c.onConnect = function () {
-    puts("send get");
+    //puts("send get");
     c.send( "GET /hello HTTP/1.1\r\n\r\n" );
     req_sent += 1;
   };
   var total = "";
 
   c.onReceive = function (chunk) {
+    //puts("client recv");
     total += chunk.encodeUtf8();
     puts("total: " + JSON.stringify(total));
 
@@ -49,12 +52,20 @@ function onLoad() {
       puts("send post");
       c.send("POST /quit HTTP/1.1\r\n\r\n");
       c.close();
+      puts("client half close");
+      assertEquals(c.readyState, "readOnly");
       req_sent += 1;
     }
   };
 
+  c.onEOF = function () {
+    puts("client got eof");
+  };
+
   c.onDisconnect = function () {
-    puts("client disocnnected");
+    puts("client disconnected");
+
+    assertEquals(c.readyState, "closed");
 
     var hello = new RegExp("/hello");
     assertTrue(hello.exec(total) != null);
index 79cc84c..5395b59 100644 (file)
@@ -48,23 +48,34 @@ function onLoad() {
     client.send("PING");
   };
 
+  var sent_final_ping = false;
+
   client.onReceive = function (data) {
-    assertEquals("open", client.readyState);
     //puts("client recved data: " + JSON.stringify(data));
     stdout.print(".");
     assertEquals("PONG", data);
     count += 1; 
+
+    if (sent_final_ping) {
+      assertEquals("readOnly", client.readyState);
+      return;
+    } else {
+      assertEquals("open", client.readyState);
+    }
+
     if (count < N) {
       client.send("PING");
     } else {
-      puts("sending FIN");
+      puts("sending final ping");
+      sent_final_ping = true;
+      client.send("PING");
       client.close();
     }
   };
   
   client.onEOF = function () {
     puts("pinger: onEOF");
-    assertEquals(N, count);
+    assertEquals(N+1, count);
   };
 
   client.connect(port);
index 8e37f68..56fe6ac 100644 (file)
@@ -2,15 +2,32 @@ var c = new node.http.Client(8000, "localhost")
 
 var req = c.get("/hello/world", [["Accept", "*/*"]]);
 req.finish(function (res) {
-  puts("got response: " + res.status_code.toString());
+  puts("response 1: " + res.status_code.toString());
 
   res.onBody = function (chunk) {
-    puts("got response body <" + chunk.encodeUtf8() + ">");
+    puts("response 1 body <" + chunk.encodeUtf8() + ">");
     return true;
   };
 
   res.onBodyComplete = function () {
-    puts("response complete!");
+    puts("response complete!");
     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;
+  };
+});
+*/