diff --git a/lib/freelist.js b/lib/freelist.js new file mode 100644 index 0000000000..a09fb4b21e --- /dev/null +++ b/lib/freelist.js @@ -0,0 +1,22 @@ +// This is a free list to avoid creating so many of the same object. +exports.FreeList = function(name, max, constructor) { + this.name = name; + this.constructor = constructor; + this.max = max; + this.list = []; +} + + +exports.FreeList.prototype.alloc = function () { + //debug("alloc " + this.name + " " + this.list.length); + return this.list.length ? this.list.shift() + : this.constructor.apply(this, arguments); +}; + + +exports.FreeList.prototype.free = function (obj) { + //debug("free " + this.name + " " + this.list.length); + if (this.list.length < this.max) { + this.list.push(obj); + } +}; diff --git a/lib/http.js b/lib/http.js index 8523bd6301..28e3e3e249 100644 --- a/lib/http.js +++ b/lib/http.js @@ -11,101 +11,91 @@ var sys = require('sys'); var net = require('net'); var events = require('events'); +var FreeList = require('freelist').FreeList; var HTTPParser = process.binding('http_parser').HTTPParser; -var parserFreeList = []; +var parsers = new FreeList('parsers', 1000, function () { + var parser = new HTTPParser('request'); -function newParser (type) { - var parser; - if (parserFreeList.length) { - parser = parserFreeList.shift(); - parser.reinitialize(type); - } else { - parser = new HTTPParser(type); + parser.onMessageBegin = function () { + parser.incoming = new IncomingMessage(parser.socket); + parser.field = null; + parser.value = null; + }; + + // Only servers will get URL events. + parser.onURL = function (b, start, len) { + var slice = b.toString('ascii', start, start+len); + if (parser.incoming.url) { + parser.incoming.url += slice; + } else { + // Almost always will branch here. + parser.incoming.url = slice; + } + }; - parser.onMessageBegin = function () { - parser.incoming = new IncomingMessage(parser.socket); + parser.onHeaderField = function (b, start, len) { + var slice = b.toString('ascii', start, start+len).toLowerCase(); + if (parser.value) { + parser.incoming._addHeaderLine(parser.field, parser.value); parser.field = null; parser.value = null; - }; + } + if (parser.field) { + parser.field += slice; + } else { + parser.field = slice; + } + }; - // Only servers will get URL events. - parser.onURL = function (b, start, len) { - var slice = b.toString('ascii', start, start+len); - if (parser.incoming.url) { - parser.incoming.url += slice; - } else { - // Almost always will branch here. - parser.incoming.url = slice; - } - }; - - parser.onHeaderField = function (b, start, len) { - var slice = b.toString('ascii', start, start+len).toLowerCase(); - if (parser.value) { - parser.incoming._addHeaderLine(parser.field, parser.value); - parser.field = null; - parser.value = null; - } - if (parser.field) { - parser.field += slice; - } else { - parser.field = slice; - } - }; + parser.onHeaderValue = function (b, start, len) { + var slice = b.toString('ascii', start, start+len); + if (parser.value) { + parser.value += slice; + } else { + parser.value = slice; + } + }; - parser.onHeaderValue = function (b, start, len) { - var slice = b.toString('ascii', start, start+len); - if (parser.value) { - parser.value += slice; - } else { - parser.value = slice; - } - }; + parser.onHeadersComplete = function (info) { + if (parser.field && parser.value) { + parser.incoming._addHeaderLine(parser.field, parser.value); + } - parser.onHeadersComplete = function (info) { - if (parser.field && parser.value) { - parser.incoming._addHeaderLine(parser.field, parser.value); - } + parser.incoming.httpVersionMajor = info.versionMajor; + parser.incoming.httpVersionMinor = info.versionMinor; + parser.incoming.httpVersion = info.versionMajor + + '.' + + info.versionMinor ; - parser.incoming.httpVersionMajor = info.versionMajor; - parser.incoming.httpVersionMinor = info.versionMinor; - parser.incoming.httpVersion = info.versionMajor - + '.' - + info.versionMinor ; + if (info.method) { + // server only + parser.incoming.method = info.method; + } else { + // client only + parser.incoming.statusCode = info.statusCode; + } - if (info.method) { - // server only - parser.incoming.method = info.method; - } else { - // client only - parser.incoming.statusCode = info.statusCode; - } + parser.onIncoming(parser.incoming, info.shouldKeepAlive); + }; - parser.onIncoming(parser.incoming, info.shouldKeepAlive); - }; + parser.onBody = function (b, start, len) { + // TODO body encoding? + var enc = parser.incoming._encoding; + if (!enc) { + parser.incoming.emit('data', b.slice(start, start+len)); + } else { + var string = b.toString(enc, start, start+len); + parser.incoming.emit('data', string); + } + }; - parser.onBody = function (b, start, len) { - // TODO body encoding? - var enc = parser.incoming._encoding; - if (!enc) { - parser.incoming.emit('data', b.slice(start, start+len)); - } else { - var string = b.toString(enc, start, start+len); - parser.incoming.emit('data', string); - } - }; + parser.onMessageComplete = function () { + parser.incoming.emit("end"); + }; - parser.onMessageComplete = function () { - parser.incoming.emit("end"); - }; - } return parser; -} - -function freeParser (parser) { - if (parserFreeList.length < 1000) parserFreeList.push(parser); -} +}); var CRLF = "\r\n"; @@ -517,7 +507,9 @@ function connectionListener (socket) { // we need to keep track of the order they were sent. var responses = []; - var parser = newParser('request'); + var parser = parsers.alloc(); + parser.reinitialize('request'); + parser.socket = socket; socket.ondata = function (d, start, end) { parser.execute(d, start, end - start); @@ -526,7 +518,7 @@ function connectionListener (socket) { socket.onend = function () { parser.finish(); // unref the parser for easy gc - freeParser(parser); + parsers.free(parser); if (responses.length == 0) { socket.end(); @@ -535,7 +527,6 @@ function connectionListener (socket) { } }; - parser.socket = socket; // The following callback is issued after the headers have been read on a // new message. In this callback we setup the response object and pass it // to the user. @@ -563,7 +554,8 @@ function Client ( ) { var requests = []; var currentRequest; - var parser = newParser('response'); + var parser = parsers.alloc(); + parser.reinitialize('response'); parser.socket = this; self._reconnect = function () { @@ -617,7 +609,7 @@ function Client ( ) { if (requests.length > 0) { self._reconnect(); } else { - freeParser(parser); + parsers.free(parser); } }); diff --git a/lib/net.js b/lib/net.js index bf7caf64c7..72eb878c8e 100644 --- a/lib/net.js +++ b/lib/net.js @@ -22,6 +22,7 @@ var binding = process.binding('net'); // yet convinced that every use-case can be fit into that abstraction, so // waiting to implement it until I get more experience with this. var Buffer = require('buffer').Buffer; +var FreeList = require('freelist').FreeList; var IOWatcher = process.IOWatcher; var assert = process.assert; @@ -205,35 +206,6 @@ var timeout = new (function () { }; })(); - - - - -// This is a free list to avoid creating so many of the same object. - -function FreeList (name, max, constructor) { - this.name = name; - this.constructor = constructor; - this.max = max; - this.list = []; -} - - -FreeList.prototype.alloc = function () { - //debug("alloc " + this.name + " " + this.list.length); - return this.list.length ? this.list.shift() - : this.constructor.apply(this, arguments); -}; - - -FreeList.prototype.free = function (obj) { - //debug("free " + this.name + " " + this.list.length); - if (this.list.length < this.max) { - this.list.push(obj); - } -}; - - var ioWatchers = new FreeList("iowatcher", 100, function () { return new IOWatcher(); }); diff --git a/src/node.cc b/src/node.cc index 131b8ca667..28b97ce95f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1268,6 +1268,7 @@ static Handle Binding(const Arguments& args) { exports->Set(String::New("dns"), String::New(native_dns)); exports->Set(String::New("events"), String::New(native_events)); exports->Set(String::New("file"), String::New(native_file)); + exports->Set(String::New("freelist"), String::New(native_freelist)); exports->Set(String::New("fs"), String::New(native_fs)); exports->Set(String::New("http"), String::New(native_http)); exports->Set(String::New("http_old"), String::New(native_http_old));