Fix HTTP agent disconnection problem
authorRyan Dahl <ry@tinyclouds.org>
Mon, 14 Mar 2011 21:42:01 +0000 (14:42 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Mon, 14 Mar 2011 21:47:41 +0000 (14:47 -0700)
https://groups.google.com/forum/#!topic/nodejs-dev/V5fB69hFa9o

Closes GH-787.

lib/http.js
test/fixtures/person.jpg [new file with mode: 0644]
test/simple/test-http-get-pipeline-problem.js [new file with mode: 0644]

index 98a9ab9..8cc37ed 100644 (file)
@@ -1289,8 +1289,10 @@ Agent.prototype._establishNewConnection = function() {
       // All that should be required for keep-alive is to not reconnect,
       // but outgoingFlush instead.
       if (!req.shouldKeepAlive) {
-        debug('AGENT socket.end()');
-        if (socket.writable) socket.end();
+        if (socket.writable) {
+          debug('AGENT socket.destroySoon()');
+          socket.destroySoon();
+        }
         assert(!socket.writable);
       } else {
         debug('AGENT socket keep-alive');
diff --git a/test/fixtures/person.jpg b/test/fixtures/person.jpg
new file mode 100644 (file)
index 0000000..4f71881
Binary files /dev/null and b/test/fixtures/person.jpg differ
diff --git a/test/simple/test-http-get-pipeline-problem.js b/test/simple/test-http-get-pipeline-problem.js
new file mode 100644 (file)
index 0000000..5742372
--- /dev/null
@@ -0,0 +1,87 @@
+// We are demonstrating a problem with http.get when queueing up many
+// transfers. The server simply introduces some delay and sends a file.
+// Note this is demonstarted with connection: close.
+var common = require('../common');
+var assert = require('assert');
+var http = require('http');
+var fs = require('fs');
+
+var image = fs.readFileSync(common.fixturesDir + '/person.jpg');
+
+console.log("image.length = " + image.length);
+
+var total = 100;
+var requests = 0, responses = 0;
+
+var server = http.Server(function(req, res) {
+  if (++requests == total) {
+    server.close();
+  }
+
+  setTimeout(function() {
+    res.writeHead(200, {
+      'content-type': 'image/jpeg',
+      'connection': 'close',
+      'content-length': image.length
+    });
+    res.end(image);
+  }, 1);
+});
+
+
+server.listen(common.PORT, function() {
+  for (var i = 0; i < total; i++) {
+    (function() {
+      var x = i;
+
+      var opts = {
+        port: common.PORT,
+        headers: { connection: 'close' }
+      };
+
+      http.get(opts, function(res) {
+        console.error("recv " + x);
+        var s = fs.createWriteStream(common.tmpDir + '/' + x + ".jpg");
+        res.pipe(s);
+
+        // TODO there should be a callback to pipe() that will allow
+        // us to get a callback when the pipe is finished.
+        res.on('end', function() {
+          console.error("done " + x);
+          if (++responses == total) {
+            s.on('close', checkFiles);
+          }
+        });
+      }).on('error', function(e) {
+        console.error('error! ', e.message)
+        throw e;
+      });
+    })();
+  }
+});
+
+
+var checkedFiles = false;
+function checkFiles() {
+  // Should see 1.jpg, 2.jpg, ..., 100.jpg in tmpDir
+  var files = fs.readdirSync(common.tmpDir);
+  assert.equal(total, files.length);
+
+  for (var i = 0; i < total; i++) {
+    var fn = i + '.jpg';
+    assert.ok(files.indexOf(fn) >= 0, "couldn't find '" + fn + "'");
+    var stat = fs.statSync(common.tmpDir + '/' + fn);
+    assert.equal(image.length, stat.size,
+                 "size doesn't match on '" + fn +
+                 "'. Got " + stat.size + " bytes");
+  }
+
+  checkedFiles = true;
+}
+
+
+process.on('exit', function() {
+  assert.equal(total, requests);
+  assert.equal(total, responses);
+  assert.ok(checkedFiles);
+});