From d90975a2dd6da1b0457a7b08506cd8d436d69839 Mon Sep 17 00:00:00 2001 From: Lev Gimelfarb Date: Mon, 30 Dec 2013 20:53:21 -0500 Subject: [PATCH] fix #40: httpsys crashes node.exe when serving static files Fixing an issue where httpsys native module crashes node.exe process with Access Violation 0xc0000005, when responding to a request for a static file (or any large response). --- .gitignore | 4 ++- lib/Socket.js | 14 ++++++++-- package.json | 3 +- test/110_static.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 test/110_static.js diff --git a/.gitignore b/.gitignore index 7c56c34..5cc5702 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,6 @@ _ReSharper* node.exe httpsys.node -msvcr110.dll \ No newline at end of file +msvcr110.dll + +test/_dummy diff --git a/lib/Socket.js b/lib/Socket.js index 8c22349..95f39a9 100644 --- a/lib/Socket.js +++ b/lib/Socket.js @@ -348,8 +348,18 @@ Socket.prototype._on_written = function () { } } - if (!this._closed && this._requestContext.chunks) - this._initiate_send_next(); + if (!this._closed && this._requestContext.chunks) { + // fix #40: httpsys native module has a problem in processing empty list of chunks, + // which happens when stream gets to an end, and .write(null, null, true) + // is issued to indicate it. The problem has to do with callback recursion, + // and so we get out of it by scheduling _initiate_send_next() to occur on + // next nodejs event-loop iteration. + + if (!this._requestContext.chunks.length) + process.nextTick(this._initiate_send_next.bind(this)); + else + this._initiate_send_next(); + } }; Socket.prototype._initiate_send_next = function () { diff --git a/package.json b/package.json index 6ded8c6..6d4bf9a 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "mocha": "1.8.1", "ws": "0.4.27", "socket.io": "0.9.16", - "socket.io-client": "0.9.16" + "socket.io-client": "0.9.16", + "connect": "~2.12.0" }, "homepage": "https://github.com/tjanczuk/httpsys", "repository": { diff --git a/test/110_static.js b/test/110_static.js new file mode 100644 index 0000000..f510961 --- /dev/null +++ b/test/110_static.js @@ -0,0 +1,69 @@ +require('../lib/httpsys').slipstream(); + +var http = require('http') + , assert = require('assert') + , connect = require('connect') + , fs = require('fs'); + +var port = process.env.PORT || 3103; +var server; + +describe('110_static.js: static', function () { + + afterEach(function (done) { + if (server) { + server.close(function () { + done(); + server = undefined; + }); + } + else { + done(); + } + }); + + it('works', function () { + assert.equal(typeof http.httpsys_version, 'string'); + }); + + it('serve static file works', function (done) { + // Create a random file, about 35KB will do + var buf = new Buffer(35 * 1024); + buf.fill("t"); + var fd = fs.openSync(__dirname + '/_dummy', 'w'); + fs.writeSync(fd, buf, 0, buf.length); + fs.close(fd); + + // Setup server to serve static files from + // the folder (i.e. the _dummy file we just created) + server = http.createServer(connect() + .use(connect.static(__dirname))); + + server.on('close', function() { + fs.unlinkSync(__dirname + '/_dummy'); + }); + + server.listen(port); + + var request = http.request({ + hostname: 'localhost', + port: port, + path: '/_dummy', + method: 'GET' + }, function (res) { + // Check that we could retrieve the file + assert.equal(res.statusCode, 200); + assert.equal(res.headers['content-type'], 'application/octet-stream'); + var body = ''; + res.on('data', function (chunk) { body += chunk; }); + res.on('end', function () { + assert.ok(body.indexOf('tttttttttttttt') >= 0, "Unexpected response file contents"); + done(); + }); + }); + + request.on('error', assert.ifError); + request.end(); + }); + +}); \ No newline at end of file