diff --git a/lib/http.js b/lib/http.js index 89e2d81a09..7df210a7ba 100644 --- a/lib/http.js +++ b/lib/http.js @@ -8,24 +8,24 @@ var HTTPParser = process.binding('http_parser').HTTPParser; var debug; var debugLevel = parseInt(process.env.NODE_DEBUG, 16); if (debugLevel & 0x4) { - debug = function (x) { console.error('HTTP: %s', x); }; + debug = function(x) { console.error('HTTP: %s', x); }; } else { - debug = function () { }; + debug = function() { }; } -var parsers = new FreeList('parsers', 1000, function () { +var parsers = new FreeList('parsers', 1000, function() { var parser = new HTTPParser('request'); - parser.onMessageBegin = function () { + 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); + parser.onURL = function(b, start, len) { + var slice = b.toString('ascii', start, start + len); if (parser.incoming.url) { parser.incoming.url += slice; } else { @@ -34,8 +34,8 @@ var parsers = new FreeList('parsers', 1000, function () { } }; - parser.onHeaderField = function (b, start, len) { - var slice = b.toString('ascii', start, start+len).toLowerCase(); + parser.onHeaderField = function(b, start, len) { + var slice = b.toString('ascii', start, start + len).toLowerCase(); if (parser.value != undefined) { parser.incoming._addHeaderLine(parser.field, parser.value); parser.field = null; @@ -48,8 +48,8 @@ var parsers = new FreeList('parsers', 1000, function () { } }; - parser.onHeaderValue = function (b, start, len) { - var slice = b.toString('ascii', start, start+len); + parser.onHeaderValue = function(b, start, len) { + var slice = b.toString('ascii', start, start + len); if (parser.value) { parser.value += slice; } else { @@ -57,7 +57,7 @@ var parsers = new FreeList('parsers', 1000, function () { } }; - parser.onHeadersComplete = function (info) { + parser.onHeadersComplete = function(info) { if (parser.field && (parser.value != undefined)) { parser.incoming._addHeaderLine(parser.field, parser.value); parser.field = null; @@ -89,9 +89,9 @@ var parsers = new FreeList('parsers', 1000, function () { return isHeadResponse; }; - parser.onBody = function (b, start, len) { + parser.onBody = function(b, start, len) { // TODO body encoding? - var slice = b.slice(start, start+len); + var slice = b.slice(start, start + len); if (parser.incoming._decoder) { var string = parser.incoming._decoder.write(slice); if (string.length) parser.incoming.emit('data', string); @@ -100,14 +100,14 @@ var parsers = new FreeList('parsers', 1000, function () { } }; - parser.onMessageComplete = function () { + parser.onMessageComplete = function() { this.incoming.complete = true; if (parser.field && (parser.value != undefined)) { parser.incoming._addHeaderLine(parser.field, parser.value); } if (!parser.incoming.upgrade) { // For upgraded connections, also emit this after parser.execute - parser.incoming.emit("end"); + parser.incoming.emit('end'); } }; @@ -116,7 +116,7 @@ var parsers = new FreeList('parsers', 1000, function () { exports.parsers = parsers; -var CRLF = "\r\n"; +var CRLF = '\r\n'; var STATUS_CODES = exports.STATUS_CODES = { 100 : 'Continue', 101 : 'Switching Protocols', @@ -183,7 +183,7 @@ var continueExpression = /100-continue/i; /* Abstract base class for ServerRequest and ClientResponse. */ -function IncomingMessage (socket) { +function IncomingMessage(socket) { stream.Stream.call(this); // TODO Remove one of these eventually. @@ -198,7 +198,7 @@ function IncomingMessage (socket) { this.readable = true; // request (server) only - this.url = ""; + this.url = ''; this.method = null; @@ -212,23 +212,23 @@ util.inherits(IncomingMessage, stream.Stream); exports.IncomingMessage = IncomingMessage; -IncomingMessage.prototype.destroy = function (error) { +IncomingMessage.prototype.destroy = function(error) { this.socket.destroy(error); }; -IncomingMessage.prototype.setEncoding = function (encoding) { - var StringDecoder = require("string_decoder").StringDecoder; // lazy load +IncomingMessage.prototype.setEncoding = function(encoding) { + var StringDecoder = require('string_decoder').StringDecoder; // lazy load this._decoder = new StringDecoder(encoding); }; -IncomingMessage.prototype.pause = function () { +IncomingMessage.prototype.pause = function() { this.socket.pause(); }; -IncomingMessage.prototype.resume = function () { +IncomingMessage.prototype.resume = function() { this.socket.resume(); }; @@ -240,13 +240,9 @@ IncomingMessage.prototype.resume = function () { // multiple values this way. If not, we declare the first instance the winner // and drop the second. Extended header fields (those beginning with 'x-') are // always joined. -IncomingMessage.prototype._addHeaderLine = function (field, value) { - var dest; - if (this.complete) { - dest = this.trailers; - } else { - dest = this.headers; - } +IncomingMessage.prototype._addHeaderLine = function(field, value) { + var dest = this.complete ? this.trailers : this.headers; + switch (field) { // Array headers: case 'set-cookie': @@ -273,7 +269,7 @@ IncomingMessage.prototype._addHeaderLine = function (field, value) { default: - if (field.slice(0,2) == 'x-') { + if (field.slice(0, 2) == 'x-') { // except for x- if (field in dest) { dest[field] += ', ' + value; @@ -289,7 +285,7 @@ IncomingMessage.prototype._addHeaderLine = function (field, value) { }; -function OutgoingMessage (socket) { +function OutgoingMessage(socket) { stream.Stream.call(this); // TODO Remove one of these eventually. @@ -317,13 +313,13 @@ util.inherits(OutgoingMessage, stream.Stream); exports.OutgoingMessage = OutgoingMessage; -OutgoingMessage.prototype.destroy = function (error) { +OutgoingMessage.prototype.destroy = function(error) { this.socket.destroy(error); }; // This abstract either writing directly to the socket or buffering it. -OutgoingMessage.prototype._send = function (data, encoding) { +OutgoingMessage.prototype._send = function(data, encoding) { // This is a shameful hack to get the headers and first body chunk onto // the same packet. Future versions of Node are going to take care of // this at a lower level and in a more general way. @@ -362,49 +358,49 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding) { }; -OutgoingMessage.prototype._buffer = function (data, encoding) { +OutgoingMessage.prototype._buffer = function(data, encoding) { if (data.length === 0) return; var length = this.output.length; if (length === 0 || typeof data != 'string') { this.output.push(data); - encoding = encoding || "ascii"; + encoding = encoding || 'ascii'; this.outputEncodings.push(encoding); return false; } - var lastEncoding = this.outputEncodings[length-1]; - var lastData = this.output[length-1]; + var lastEncoding = this.outputEncodings[length - 1]; + var lastData = this.output[length - 1]; if ((lastEncoding === encoding) || (!encoding && data.constructor === lastData.constructor)) { - this.output[length-1] = lastData + data; + this.output[length - 1] = lastData + data; return false; } this.output.push(data); - encoding = encoding || "ascii"; + encoding = encoding || 'ascii'; this.outputEncodings.push(encoding); return false; }; -OutgoingMessage.prototype._storeHeader = function (firstLine, headers) { +OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { var sentConnectionHeader = false; var sentContentLengthHeader = false; var sentTransferEncodingHeader = false; var sentExpect = false; - // firstLine in the case of request is: "GET /index.html HTTP/1.1\r\n" - // in the case of response it is: "HTTP/1.1 200 OK\r\n" + // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n' + // in the case of response it is: 'HTTP/1.1 200 OK\r\n' var messageHeader = firstLine; var field, value; var self = this; function store(field, value) { - messageHeader += field + ": " + value + CRLF; + messageHeader += field + ': ' + value + CRLF; if (connectionExpression.test(field)) { sentConnectionHeader = true; @@ -455,17 +451,17 @@ OutgoingMessage.prototype._storeHeader = function (firstLine, headers) { if (sentConnectionHeader == false) { if (this.shouldKeepAlive && (sentContentLengthHeader || this.useChunkedEncodingByDefault)) { - messageHeader += "Connection: keep-alive\r\n"; + messageHeader += 'Connection: keep-alive\r\n'; } else { this._last = true; - messageHeader += "Connection: close\r\n"; + messageHeader += 'Connection: close\r\n'; } } if (sentContentLengthHeader == false && sentTransferEncodingHeader == false) { if (this._hasBody) { if (this.useChunkedEncodingByDefault) { - messageHeader += "Transfer-Encoding: chunked\r\n"; + messageHeader += 'Transfer-Encoding: chunked\r\n'; this.chunkedEncoding = true; } else { this._last = true; @@ -481,23 +477,24 @@ OutgoingMessage.prototype._storeHeader = function (firstLine, headers) { // wait until the first body chunk, or close(), is sent to flush, // UNLESS we're sending Expect: 100-continue. - if (sentExpect) this._send(""); + if (sentExpect) this._send(''); }; -OutgoingMessage.prototype.write = function (chunk, encoding) { +OutgoingMessage.prototype.write = function(chunk, encoding) { if (!this._header) { - throw new Error("You have to call writeHead() before write()"); + throw new Error('You have to call writeHead() before write()'); } if (!this._hasBody) { - console.error("This type of response MUST NOT have a body. Ignoring write() calls."); + console.error('This type of response MUST NOT have a body. ' + + 'Ignoring write() calls.'); return true; } - if (typeof chunk !== "string" && !Buffer.isBuffer(chunk) && + if (typeof chunk !== 'string' && !Buffer.isBuffer(chunk) && !Array.isArray(chunk)) { - throw new TypeError("first argument must be a string, Array, or Buffer"); + throw new TypeError('first argument must be a string, Array, or Buffer'); } if (chunk.length === 0) return false; @@ -525,8 +522,8 @@ OutgoingMessage.prototype.write = function (chunk, encoding) { }; -OutgoingMessage.prototype.addTrailers = function (headers) { - this._trailer = ""; +OutgoingMessage.prototype.addTrailers = function(headers) { + this._trailer = ''; var keys = Object.keys(headers); var isArray = (Array.isArray(headers)); for (var i = 0, l = keys.length; i < l; i++) { @@ -539,16 +536,16 @@ OutgoingMessage.prototype.addTrailers = function (headers) { value = headers[key]; } - this._trailer += field + ": " + value + CRLF; + this._trailer += field + ': ' + value + CRLF; } }; -OutgoingMessage.prototype.end = function (data, encoding) { +OutgoingMessage.prototype.end = function(data, encoding) { var ret; var hot = this._headerSent === false && - typeof(data) === "string" && + typeof(data) === 'string' && data.length > 0 && this.output.length === 0 && this.connection.writable && @@ -562,8 +559,8 @@ OutgoingMessage.prototype.end = function (data, encoding) { if (this.chunkedEncoding) { var l = Buffer.byteLength(data, encoding).toString(16); ret = this.connection.write(this._header + l + CRLF + - data + "\r\n0\r\n" + - this._trailer + "\r\n", encoding); + data + '\r\n0\r\n' + + this._trailer + '\r\n', encoding); } else { ret = this.connection.write(this._header + data, encoding); } @@ -596,7 +593,7 @@ OutgoingMessage.prototype.end = function (data, encoding) { }; -function ServerResponse (req) { +function ServerResponse(req) { OutgoingMessage.call(this, req.socket); if (req.method === 'HEAD') this._hasBody = false; @@ -612,20 +609,20 @@ util.inherits(ServerResponse, OutgoingMessage); exports.ServerResponse = ServerResponse; -ServerResponse.prototype.writeContinue = function () { - this._writeRaw("HTTP/1.1 100 Continue" + CRLF + CRLF, 'ascii'); +ServerResponse.prototype.writeContinue = function() { + this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii'); this._sent100 = true; }; -ServerResponse.prototype.writeHead = function (statusCode) { +ServerResponse.prototype.writeHead = function(statusCode) { var reasonPhrase, headers, headerIndex; if (typeof arguments[1] == 'string') { reasonPhrase = arguments[1]; headerIndex = 2; } else { - reasonPhrase = STATUS_CODES[statusCode] || "unknown"; + reasonPhrase = STATUS_CODES[statusCode] || 'unknown'; headerIndex = 1; } @@ -635,7 +632,7 @@ ServerResponse.prototype.writeHead = function (statusCode) { headers = {}; } - var statusLine = "HTTP/1.1 " + statusCode.toString() + " " + + var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' + reasonPhrase + CRLF; if (statusCode === 204 || statusCode === 304 || @@ -663,24 +660,24 @@ ServerResponse.prototype.writeHead = function (statusCode) { }; -ServerResponse.prototype.writeHeader = function () { +ServerResponse.prototype.writeHeader = function() { this.writeHead.apply(this, arguments); }; -function ClientRequest (socket, method, url, headers) { +function ClientRequest(socket, method, url, headers) { OutgoingMessage.call(this, socket); this.method = method = method.toUpperCase(); this.shouldKeepAlive = false; - if (method === "GET" || method === "HEAD") { + if (method === 'GET' || method === 'HEAD') { this.useChunkedEncodingByDefault = false; } else { this.useChunkedEncodingByDefault = true; } this._last = true; - this._storeHeader(method + " " + url + " HTTP/1.1\r\n", headers); + this._storeHeader(method + ' ' + url + ' HTTP/1.1\r\n', headers); } util.inherits(ClientRequest, OutgoingMessage); @@ -688,7 +685,7 @@ util.inherits(ClientRequest, OutgoingMessage); exports.ClientRequest = ClientRequest; -function outgoingFlush (socket) { +function outgoingFlush(socket) { // This logic is probably a bit confusing. Let me explain a bit: // // In both HTTP servers and clients it is possible to queue up several @@ -700,7 +697,7 @@ function outgoingFlush (socket) { // // When the user does // - // req2.write("hello world\n"); + // req2.write('hello world\n'); // // it's possible that the first request has not been completely flushed to // the socket yet. Thus the outgoing messages need to be prepared to queue @@ -731,14 +728,14 @@ function outgoingFlush (socket) { } -function httpSocketSetup (socket) { +function httpSocketSetup(socket) { // An array of outgoing messages for the socket. In pipelined connections // we need to keep track of the order they were sent. socket._outgoing = []; socket.__destroyOnDrain = false; // NOTE: be sure not to use ondrain elsewhere in this file! - socket.ondrain = function () { + socket.ondrain = function() { var message = socket._outgoing[0]; if (message) message.emit('drain'); if (socket.__destroyOnDrain) socket.destroy(); @@ -746,15 +743,15 @@ function httpSocketSetup (socket) { } -function Server (requestListener) { +function Server(requestListener) { if (!(this instanceof Server)) return new Server(requestListener); net.Server.call(this, { allowHalfOpen: true }); if (requestListener) { - this.addListener("request", requestListener); + this.addListener('request', requestListener); } - this.addListener("connection", connectionListener); + this.addListener('connection', connectionListener); } util.inherits(Server, net.Server); @@ -762,20 +759,20 @@ util.inherits(Server, net.Server); exports.Server = Server; -exports.createServer = function (requestListener) { +exports.createServer = function(requestListener) { return new Server(requestListener); }; -function connectionListener (socket) { +function connectionListener(socket) { var self = this; - debug("SERVER new http connection"); + debug('SERVER new http connection'); httpSocketSetup(socket); - socket.setTimeout(2*60*1000); // 2 minute timeout - socket.addListener('timeout', function () { + socket.setTimeout(2 * 60 * 1000); // 2 minute timeout + socket.addListener('timeout', function() { socket.destroy(); }); @@ -783,14 +780,14 @@ function connectionListener (socket) { parser.reinitialize('request'); parser.socket = socket; - socket.addListener('error', function (e) { + socket.addListener('error', function(e) { self.emit('clientError', e); }); - socket.ondata = function (d, start, end) { + socket.ondata = function(d, start, end) { var ret = parser.execute(d, start, end - start); if (ret instanceof Error) { - debug("parse error"); + debug('parse error'); socket.destroy(ret); } else if (parser.incoming && parser.incoming.upgrade) { var bytesParsed = ret; @@ -803,7 +800,7 @@ function connectionListener (socket) { // in the upgradeHead from the closing lines of the headers var upgradeHead = d.slice(start + bytesParsed + 1, end); - if (self.listeners("upgrade").length) { + if (self.listeners('upgrade').length) { self.emit('upgrade', req, req.socket, upgradeHead); } else { // Got upgrade header, but have no handler. @@ -812,25 +809,25 @@ function connectionListener (socket) { } }; - socket.onend = function () { + socket.onend = function() { parser.finish(); if (socket._outgoing.length) { - socket._outgoing[socket._outgoing.length-1]._last = true; + socket._outgoing[socket._outgoing.length - 1]._last = true; outgoingFlush(socket); } else { socket.end(); } }; - socket.addListener('close', function () { + socket.addListener('close', function() { // unref the parser for easy gc parsers.free(parser); }); // At the end of each response message, after it has been flushed to the // socket. Here we insert logic about what to do next. - socket._onOutgoingSent = function (message) { + socket._onOutgoingSent = function(message) { var message = socket._outgoing.shift(); if (message._last) { // No more messages to be pushed out. @@ -851,7 +848,7 @@ function connectionListener (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. - parser.onIncoming = function (req, shouldKeepAlive) { + parser.onIncoming = function(req, shouldKeepAlive) { var res = new ServerResponse(req); debug('server response shouldKeepAlive: ' + shouldKeepAlive); res.shouldKeepAlive = shouldKeepAlive; @@ -861,8 +858,8 @@ function connectionListener (socket) { (req.httpVersionMajor == 1 && req.httpVersionMinor == 1) && continueExpression.test(req.headers['expect'])) { res._expect_continue = true; - if (self.listeners("checkContinue").length) { - self.emit("checkContinue", req, res); + if (self.listeners('checkContinue').length) { + self.emit('checkContinue', req, res); } else { res.writeContinue(); self.emit('request', req, res); @@ -875,7 +872,7 @@ function connectionListener (socket) { } -function Client ( ) { +function Client() { if (!(this instanceof Client)) return new Client(); net.Stream.call(this, { allowHalfOpen: true }); var self = this; @@ -890,7 +887,7 @@ function Client ( ) { function onData(d, start, end) { if (!self.parser) { - throw new Error("parser not initialized prior to Client.ondata call"); + throw new Error('parser not initialized prior to Client.ondata call'); } var ret = self.parser.execute(d, start, end - start); if (ret instanceof Error) { @@ -912,30 +909,29 @@ function Client ( ) { } }; - self.addListener("connect", function () { + self.addListener('connect', function() { debug('CLIENT connected'); self.ondata = onData; self.onend = onEnd; - self._state = "connected"; + self._state = 'connected'; self._initParser(); - debug('CLIENT requests: ' + util.inspect(self._outgoing.map(function (r) { return r.method; }))); outgoingFlush(self); }); function onEnd() { if (self.parser) self.parser.finish(); - debug("CLIENT got end closing. state = " + self._state); + debug('CLIENT got end closing. state = ' + self._state); self.end(); }; - self.addListener("close", function (e) { - self._state = "disconnected"; + self.addListener('close', function(e) { + self._state = 'disconnected'; if (e) return; - debug("CLIENT onClose. state = " + self._state); + debug('CLIENT onClose. state = ' + self._state); // finally done with the request self._outgoing.shift(); @@ -948,14 +944,14 @@ function Client ( ) { self.parser = null; } }); -}; +} util.inherits(Client, net.Stream); exports.Client = Client; -exports.createClient = function (port, host, https, credentials) { +exports.createClient = function(port, host, https, credentials) { var c = new Client(); c.port = port; c.host = host; @@ -965,24 +961,24 @@ exports.createClient = function (port, host, https, credentials) { }; -Client.prototype._initParser = function () { +Client.prototype._initParser = function() { var self = this; if (!self.parser) self.parser = parsers.alloc(); self.parser.reinitialize('response'); self.parser.socket = self; - self.parser.onIncoming = function (res) { - debug("CLIENT incoming response!"); + self.parser.onIncoming = function(res) { + debug('CLIENT incoming response!'); var req = self._outgoing[0]; // Responses to HEAD requests are AWFUL. Ask Ryan. // A major oversight in HTTP. Hence this nastiness. - var isHeadResponse = req.method == "HEAD"; + var isHeadResponse = req.method == 'HEAD'; debug('CLIENT isHeadResponse ' + isHeadResponse); if (res.statusCode == 100) { // restart the parser, as this is a continue message. - req.emit("continue"); + req.emit('continue'); return true; } @@ -990,8 +986,8 @@ Client.prototype._initParser = function () { req.shouldKeepAlive = false; } - res.addListener('end', function ( ) { - debug("CLIENT request complete disconnecting. state = " + self._state); + res.addListener('end', function() { + debug('CLIENT request complete disconnecting. state = ' + self._state); // For the moment we reconnect for every request. FIXME! // All that should be required for keep-alive is to not reconnect, // but outgoingFlush instead. @@ -1004,7 +1000,7 @@ Client.prototype._initParser = function () { } }); - req.emit("response", res); + req.emit('response', res); return isHeadResponse; }; @@ -1022,7 +1018,7 @@ Client.prototype._initParser = function () { // each request/response cycle so we cannot shift off the request from // client._outgoing until we're completely disconnected after the response // comes back. -Client.prototype._onOutgoingSent = function (message) { +Client.prototype._onOutgoingSent = function(message) { // We've just finished a message. We don't end/shutdown the connection here // because HTTP servers typically cannot handle half-closed connections // (Node servers can). @@ -1030,27 +1026,27 @@ Client.prototype._onOutgoingSent = function (message) { // Instead, we just check if the connection is closed, and if so // reconnect if we have pending messages. if (this._outgoing.length) { - debug("CLIENT request flush. ensure connection. state = " + this._state); + debug('CLIENT request flush. ensure connection. state = ' + this._state); this._ensureConnection(); } }; -Client.prototype._ensureConnection = function () { +Client.prototype._ensureConnection = function() { if (this._state == 'disconnected') { - debug("CLIENT reconnecting state = " + this._state); + debug('CLIENT reconnecting state = ' + this._state); this.connect(this.port, this.host); - this._state = "connecting"; + this._state = 'connecting'; } }; -Client.prototype.request = function (method, url, headers) { - if (typeof(url) != "string") { +Client.prototype.request = function(method, url, headers) { + if (typeof(url) != 'string') { // assume method was omitted, shift arguments headers = url; url = method; - method = "GET"; + method = 'GET'; } var req = new ClientRequest(this, method, url, headers); this._outgoing.push(req); @@ -1059,7 +1055,7 @@ Client.prototype.request = function (method, url, headers) { }; -exports.cat = function (url, encoding_, headers_) { +exports.cat = function(url, encoding_, headers_) { var encoding = 'utf8', headers = {}, callback = null; @@ -1083,7 +1079,7 @@ exports.cat = function (url, encoding_, headers_) { } } - var url = require("url").parse(url); + var url = require('url').parse(url); var hasHost = false; if (Array.isArray(headers)) { @@ -1093,7 +1089,7 @@ exports.cat = function (url, encoding_, headers_) { break; } } - } else if (typeof headers === "Object") { + } else if (typeof headers === 'Object') { var keys = Object.keys(headers); for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; @@ -1103,20 +1099,23 @@ exports.cat = function (url, encoding_, headers_) { } } } - if (!hasHost) headers["Host"] = url.hostname; + if (!hasHost) headers['Host'] = url.hostname; - var content = ""; + var content = ''; var client = exports.createClient(url.port || 80, url.hostname); - var req = client.request((url.pathname || "/")+(url.search || "")+(url.hash || ""), headers); + var req = client.request((url.pathname || '/') + + (url.search || '') + + (url.hash || ''), + headers); - if (url.protocol=="https:") { - client.https = true; + if (url.protocol == 'https:') { + client.https = true; } var callbackSent = false; - req.addListener('response', function (res) { + req.addListener('response', function(res) { if (res.statusCode < 200 || res.statusCode >= 300) { if (callback && !callbackSent) { callback(res.statusCode); @@ -1126,8 +1125,8 @@ exports.cat = function (url, encoding_, headers_) { return; } res.setEncoding(encoding); - res.addListener('data', function (chunk) { content += chunk; }); - res.addListener('end', function () { + res.addListener('data', function(chunk) { content += chunk; }); + res.addListener('end', function() { if (callback && !callbackSent) { callback(null, content); callbackSent = true; @@ -1135,14 +1134,14 @@ exports.cat = function (url, encoding_, headers_) { }); }); - client.addListener("error", function (err) { + client.addListener('error', function(err) { if (callback && !callbackSent) { callback(err); callbackSent = true; } }); - client.addListener("close", function () { + client.addListener('close', function() { if (callback && !callbackSent) { callback(new Error('Connection closed unexpectedly')); callbackSent = true; diff --git a/lib/net.js b/lib/net.js index 20907532b3..0cd5bb18ef 100644 --- a/lib/net.js +++ b/lib/net.js @@ -1,14 +1,19 @@ -var util = require("util"); -var events = require("events"); -var stream = require("stream"); +var util = require('util'); +var events = require('events'); +var stream = require('stream'); var kMinPoolSpace = 128; -var kPoolSize = 40*1024; +var kPoolSize = 40 * 1024; var debugLevel = parseInt(process.env.NODE_DEBUG, 16); -function debug () { - if (debugLevel & 0x2) util.error.apply(this, arguments); +var debug; +if (debugLevel & 0x2) { + debug = function(x) { util.error.apply(this, arguments); }; +} else { + debug = function() { }; } + + var binding = process.binding('net'); // Note about Buffer interface: @@ -21,49 +26,49 @@ var binding = process.binding('net'); // waiting to implement it until I get more experience with this. var FreeList = require('freelist').FreeList; -var IOWatcher = process.binding('io_watcher').IOWatcher; -var constants = process.binding('constants'); -var assert = process.assert; - -var socket = binding.socket; -var bind = binding.bind; -var connect = binding.connect; -var listen = binding.listen; -var accept = binding.accept; -var close = binding.close; -var shutdown = binding.shutdown; -var read = binding.read; -var write = binding.write; -var toRead = binding.toRead; -var setNoDelay = binding.setNoDelay; -var setKeepAlive= binding.setKeepAlive; +var IOWatcher = process.binding('io_watcher').IOWatcher; +var constants = process.binding('constants'); +var assert = process.assert; + +var socket = binding.socket; +var bind = binding.bind; +var connect = binding.connect; +var listen = binding.listen; +var accept = binding.accept; +var close = binding.close; +var shutdown = binding.shutdown; +var read = binding.read; +var write = binding.write; +var toRead = binding.toRead; +var setNoDelay = binding.setNoDelay; +var setKeepAlive = binding.setKeepAlive; var socketError = binding.socketError; var getsockname = binding.getsockname; var errnoException = binding.errnoException; -var sendMsg = binding.sendMsg; -var recvMsg = binding.recvMsg; +var sendMsg = binding.sendMsg; +var recvMsg = binding.recvMsg; var EINPROGRESS = constants.EINPROGRESS; -var ENOENT = constants.ENOENT; -var EMFILE = constants.EMFILE; +var ENOENT = constants.ENOENT; +var EMFILE = constants.EMFILE; var END_OF_FILE = 42; -var ioWatchers = new FreeList("iowatcher", 100, function () { +var ioWatchers = new FreeList('iowatcher', 100, function() { return new IOWatcher(); }); exports.isIP = binding.isIP; -exports.isIPv4 = function (input) { +exports.isIPv4 = function(input) { if (binding.isIP(input) === 4) { return true; } return false; }; -exports.isIPv6 = function (input) { +exports.isIPv6 = function(input) { if (binding.isIP(input) === 6) { return true; } @@ -72,19 +77,19 @@ exports.isIPv6 = function (input) { // Allocated on demand. var pool = null; -function allocNewPool () { +function allocNewPool() { pool = new Buffer(kPoolSize); pool.used = 0; } var emptyBuffer = null; -function allocEmptyBuffer () { - emptyBuffer = new Buffer(1); - emptyBuffer.sent = 0; - emptyBuffer.length = 0; +function allocEmptyBuffer() { + emptyBuffer = new Buffer(1); + emptyBuffer.sent = 0; + emptyBuffer.length = 0; } -function setImplmentationMethods (self) { +function setImplmentationMethods(self) { function noData(buf, off, len) { return !buf || (off != undefined && off >= buf.length) || @@ -92,7 +97,7 @@ function setImplmentationMethods (self) { }; if (self.type == 'unix') { - self._writeImpl = function (buf, off, len, fd, flags) { + self._writeImpl = function(buf, off, len, fd, flags) { // Detect and disallow zero-byte writes wth an attached file // descriptor. This is an implementation limitation of sendmsg(2). if (fd && noData(buf, off, len)) { @@ -102,7 +107,7 @@ function setImplmentationMethods (self) { return sendMsg(self.fd, buf, off, len, fd, flags); }; - self._readImpl = function (buf, off, len) { + self._readImpl = function(buf, off, len) { var bytesRead = recvMsg(self.fd, buf, off, len); // Do not emit this in the same stack, otherwise we risk corrupting our @@ -115,7 +120,7 @@ function setImplmentationMethods (self) { // emitting null (or another FD). if (recvMsg.fd !== null) { var fd = recvMsg.fd; - process.nextTick(function () { + process.nextTick(function() { self.emit('fd', fd); }); } @@ -123,7 +128,7 @@ function setImplmentationMethods (self) { return bytesRead; }; } else { - self._writeImpl = function (buf, off, len, fd, flags) { + self._writeImpl = function(buf, off, len, fd, flags) { // XXX: TLS support requires that 0-byte writes get processed // by the kernel for some reason. Otherwise, we'd just // fast-path return here. @@ -133,26 +138,26 @@ function setImplmentationMethods (self) { return write(self.fd, buf, off, len); }; - self._readImpl = function (buf, off, len) { + self._readImpl = function(buf, off, len) { return read(self.fd, buf, off, len); }; } - self._shutdownImpl = function () { + self._shutdownImpl = function() { shutdown(self.fd, 'write'); }; -}; +} -function onReadable (readable, writable) { +function onReadable(readable, writable) { assert(this.socket); var socket = this.socket; socket._onReadable(); } -function onWritable (readable, writable) { +function onWritable(readable, writable) { assert(this.socket); var socket = this.socket; if (socket._connecting) { @@ -163,7 +168,7 @@ function onWritable (readable, writable) { } } -function initStream (self) { +function initStream(self) { self._readWatcher = ioWatchers.alloc(); self._readWatcher.socket = self; self._readWatcher.callback = onReadable; @@ -181,8 +186,8 @@ function initStream (self) { } // Deprecated API: Stream(fd, type) -// New API: Stream({ fd: 10, type: "unix", allowHalfOpen: true }) -function Stream (options) { +// New API: Stream({ fd: 10, type: 'unix', allowHalfOpen: true }) +function Stream(options) { if (!(this instanceof Stream)) return new Stream(arguments[0], arguments[1]); stream.Stream.call(this); @@ -190,11 +195,11 @@ function Stream (options) { this.type = null; this.allowHalfOpen = false; - if (typeof options == "object") { + if (typeof options == 'object') { this.fd = options.fd !== undefined ? parseInt(options.fd, 10) : null; this.type = options.type || null; this.allowHalfOpen = options.allowHalfOpen || false; - } else if (typeof options == "number") { + } else if (typeof options == 'number') { this.fd = arguments[0]; this.type = arguments[1]; } @@ -204,17 +209,17 @@ function Stream (options) { } else { setImplmentationMethods(this); } -}; +} util.inherits(Stream, stream.Stream); exports.Stream = Stream; -Stream.prototype._onTimeout = function () { +Stream.prototype._onTimeout = function() { this.emit('timeout'); }; -Stream.prototype.open = function (fd, type) { +Stream.prototype.open = function(fd, type) { initStream(this); this.fd = fd; @@ -228,7 +233,7 @@ Stream.prototype.open = function (fd, type) { }; -exports.createConnection = function (port, host) { +exports.createConnection = function(port, host) { var s = new Stream(); s.connect(port, host); return s; @@ -236,16 +241,16 @@ exports.createConnection = function (port, host) { Object.defineProperty(Stream.prototype, 'readyState', { - get: function () { + get: function() { if (this._connecting) { return 'opening'; } else if (this.readable && this.writable) { assert(typeof this.fd == 'number'); return 'open'; - } else if (this.readable && !this.writable){ + } else if (this.readable && !this.writable) { assert(typeof this.fd == 'number'); return 'readOnly'; - } else if (!this.readable && this.writable){ + } else if (!this.readable && this.writable) { assert(typeof this.fd == 'number'); return 'writeOnly'; } else { @@ -257,9 +262,9 @@ Object.defineProperty(Stream.prototype, 'readyState', { // Returns true if all the data was flushed to socket. Returns false if -// something was queued. If data was queued, then the "drain" event will +// something was queued. If data was queued, then the 'drain' event will // signal when it has been finally flushed to socket. -Stream.prototype.write = function (data, encoding, fd) { +Stream.prototype.write = function(data, encoding, fd) { if (this._connecting || (this._writeQueue && this._writeQueue.length)) { if (!this._writeQueue) { this._writeQueue = []; @@ -272,12 +277,14 @@ Stream.prototype.write = function (data, encoding, fd) { throw new Error('Stream.end() called already; cannot write.'); } + var last = this._writeQueue.length - 1; + if (typeof data == 'string' && this._writeQueue.length && - typeof this._writeQueue[this._writeQueue.length-1] === 'string' && - this._writeQueueEncoding[this._writeQueueEncoding.length-1] === encoding) { + typeof this._writeQueue[last] === 'string' && + this._writeQueueEncoding[last] === encoding) { // optimization - concat onto last - this._writeQueue[this._writeQueue.length-1] += data; + this._writeQueue[last] += data; } else { this._writeQueue.push(data); this._writeQueueEncoding.push(encoding); @@ -304,7 +311,7 @@ Stream.prototype.write = function (data, encoding, fd) { // 2. Write data to socket. Return true if flushed. // 3. Slice out remaining // 4. Unshift remaining onto _writeQueue. Return false. -Stream.prototype._writeOut = function (data, encoding, fd) { +Stream.prototype._writeOut = function(data, encoding, fd) { if (!this.writable) { throw new Error('Stream is not writable'); } @@ -349,7 +356,9 @@ Stream.prototype._writeOut = function (data, encoding, fd) { debug('wrote ' + bytesWritten + ' bytes to pool'); if (charsWritten != data.length) { - //debug("couldn't fit " + (data.length - charsWritten) + " bytes into the pool\n"); + // debug('couldn't fit ' + + // (data.length - charsWritten) + + // ' bytes into the pool\n'); // Unshift whatever didn't fit onto the buffer this._writeQueue.unshift(data.slice(charsWritten)); this._writeQueueEncoding.unshift(encoding); @@ -365,7 +374,8 @@ Stream.prototype._writeOut = function (data, encoding, fd) { return false; } - debug('wrote ' + bytesWritten + ' to socket. [fd, off, len] = ' + JSON.stringify([this.fd, off, len]) + "\n"); + debug('wrote ' + bytesWritten + ' to socket. [fd, off, len] = ' + + JSON.stringify([this.fd, off, len]) + '\n'); require('timers').active(this); @@ -410,7 +420,7 @@ Stream.prototype._writeOut = function (data, encoding, fd) { // Flushes the write buffer out. // Returns true if the entire buffer was flushed. -Stream.prototype.flush = function () { +Stream.prototype.flush = function() { while (this._writeQueue && this._writeQueue.length) { var data = this._writeQueue.shift(); var encoding = this._writeQueueEncoding.shift(); @@ -421,7 +431,7 @@ Stream.prototype.flush = function () { return true; } - var flushed = this._writeOut(data,encoding,fd); + var flushed = this._writeOut(data, encoding, fd); if (!flushed) return false; } if (this._writeWatcher) this._writeWatcher.stop(); @@ -429,19 +439,19 @@ Stream.prototype.flush = function () { }; -Stream.prototype._writeQueueLast = function () { - return this._writeQueue.length > 0 ? this._writeQueue[this._writeQueue.length-1] - : null; +Stream.prototype._writeQueueLast = function() { + return this._writeQueue.length > 0 ? + this._writeQueue[this._writeQueue.length - 1] : null; }; -Stream.prototype.setEncoding = function (encoding) { - var StringDecoder = require("string_decoder").StringDecoder; // lazy load +Stream.prototype.setEncoding = function(encoding) { + var StringDecoder = require('string_decoder').StringDecoder; // lazy load this._decoder = new StringDecoder(encoding); }; -function doConnect (socket, port, host) { +function doConnect(socket, port, host) { try { connect(socket.fd, port, host); } catch (e) { @@ -464,10 +474,10 @@ function doConnect (socket, port, host) { } -function toPort (x) { return (x = Number(x)) >= 0 ? x : false; } +function toPort(x) { return (x = Number(x)) >= 0 ? x : false; } -Stream.prototype._onConnect = function () { +Stream.prototype._onConnect = function() { var errno = socketError(this.fd); if (errno == 0) { // connection established @@ -494,17 +504,17 @@ Stream.prototype._onConnect = function () { }; -Stream.prototype._onWritable = function () { +Stream.prototype._onWritable = function() { // Stream becomes writable on connect() but don't flush if there's // nothing actually to write if (this.flush()) { - if (this._events && this._events['drain']) this.emit("drain"); + if (this._events && this._events['drain']) this.emit('drain'); if (this.ondrain) this.ondrain(); // Optimization } }; -Stream.prototype._onReadable = function () { +Stream.prototype._onReadable = function() { var self = this; // If this is the first recv (pool doesn't exist) or we've used up @@ -572,7 +582,7 @@ Stream.prototype._onReadable = function () { // stream.connect(80) - TCP connect to port 80 on the localhost // stream.connect(80, 'nodejs.org') - TCP connect to port 80 on nodejs.org // stream.connect('/tmp/socket') - UNIX connect to socket specified by path -Stream.prototype.connect = function () { +Stream.prototype.connect = function() { var self = this; initStream(self); if (self.fd) throw new Error('Stream already opened'); @@ -593,7 +603,7 @@ Stream.prototype.connect = function () { doConnect(self, arguments[0]); } else { // TCP - require('dns').lookup(arguments[1], function (err, ip, addressType) { + require('dns').lookup(arguments[1], function(err, ip, addressType) { if (err) { self.emit('error', err); } else { @@ -606,25 +616,25 @@ Stream.prototype.connect = function () { }; -Stream.prototype.address = function () { +Stream.prototype.address = function() { return getsockname(this.fd); }; -Stream.prototype.setNoDelay = function (v) { - if ((this.type == 'tcp4')||(this.type == 'tcp6')) { +Stream.prototype.setNoDelay = function(v) { + if ((this.type == 'tcp4') || (this.type == 'tcp6')) { setNoDelay(this.fd, v); } }; -Stream.prototype.setKeepAlive = function (enable, time) { - if ((this.type == 'tcp4')||(this.type == 'tcp6')) { - var secondDelay = Math.ceil(time/1000); +Stream.prototype.setKeepAlive = function(enable, time) { + if ((this.type == 'tcp4') || (this.type == 'tcp6')) { + var secondDelay = Math.ceil(time / 1000); setKeepAlive(this.fd, enable, secondDelay); } }; -Stream.prototype.setTimeout = function (msecs) { +Stream.prototype.setTimeout = function(msecs) { if (msecs > 0) { require('timers').enroll(this, msecs); if (this.fd) { require('timers').active(this); } @@ -634,12 +644,12 @@ Stream.prototype.setTimeout = function (msecs) { }; -Stream.prototype.pause = function () { +Stream.prototype.pause = function() { this._readWatcher.stop(); }; -Stream.prototype.resume = function () { +Stream.prototype.resume = function() { if (this.fd === null) throw new Error('Cannot resume() closed Stream.'); this._readWatcher.stop(); this._readWatcher.set(this.fd, true, false); @@ -647,7 +657,7 @@ Stream.prototype.resume = function () { }; -Stream.prototype.destroy = function (exception) { +Stream.prototype.destroy = function(exception) { // pool is shared between sockets, so don't need to free it here. var self = this; @@ -681,7 +691,7 @@ Stream.prototype.destroy = function (exception) { if (typeof this.fd == 'number') { close(this.fd); this.fd = null; - process.nextTick(function () { + process.nextTick(function() { if (exception) self.emit('error', exception); self.emit('close', exception ? true : false); }); @@ -689,7 +699,7 @@ Stream.prototype.destroy = function (exception) { }; -Stream.prototype._shutdown = function () { +Stream.prototype._shutdown = function() { if (!this.writable) { throw new Error('The connection is not writable'); } else { @@ -711,7 +721,7 @@ Stream.prototype._shutdown = function () { }; -Stream.prototype.end = function (data, encoding) { +Stream.prototype.end = function(data, encoding) { if (this.writable) { if (this._writeQueueLast() !== END_OF_FILE) { if (data) this.write(data, encoding); @@ -724,19 +734,19 @@ Stream.prototype.end = function (data, encoding) { }; -function Server (/* [ options, ] listener */) { +function Server(/* [ options, ] listener */) { if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]); events.EventEmitter.call(this); var self = this; var options = {}; - if (typeof arguments[0] == "object") { + if (typeof arguments[0] == 'object') { options = arguments[0]; } // listener: find the last argument that is a function for (var l = arguments.length - 1; l >= 0; l--) { - if (typeof arguments[l] == "function") { + if (typeof arguments[l] == 'function') { self.addListener('connection', arguments[l]); } if (arguments[l] !== undefined) break; @@ -748,7 +758,7 @@ function Server (/* [ options, ] listener */) { self.watcher = new IOWatcher(); self.watcher.host = self; - self.watcher.callback = function () { + self.watcher.callback = function() { // Just in case we don't have a dummy fd. getDummyFD(); @@ -783,9 +793,10 @@ function Server (/* [ options, ] listener */) { self.connections++; - var s = new Stream({ fd: peerInfo.fd, - type: self.type, - allowHalfOpen: self.allowHalfOpen }); + var options = { fd: peerInfo.fd, + type: self.type, + allowHalfOpen: self.allowHalfOpen }; + var s = new Stream(options); s.remoteAddress = peerInfo.address; s.remotePort = peerInfo.port; s.type = self.type; @@ -809,14 +820,14 @@ util.inherits(Server, events.EventEmitter); exports.Server = Server; -exports.createServer = function () { +exports.createServer = function() { return new Server(arguments[0], arguments[1]); }; // Just stop trying to accepting connections for a while. // Useful for throttling against DoS attacks. -Server.prototype.pause = function (msecs) { +Server.prototype.pause = function(msecs) { // We're already paused. if (this._pauseTimer) return; @@ -826,7 +837,7 @@ Server.prototype.pause = function (msecs) { this.watcher.stop(); // Wait a second before accepting more. - this._pauseTimer = setTimeout(function () { + this._pauseTimer = setTimeout(function() { // Our fd should still be there. If someone calls server.close() then // the pauseTimer should be cleared. assert(parseInt(self.fd) >= 0); @@ -836,7 +847,7 @@ Server.prototype.pause = function (msecs) { }; -Server.prototype._rejectPending = function () { +Server.prototype._rejectPending = function() { var self = this; var acceptCount = 0; // Accept and close the waiting clients one at a time. @@ -856,14 +867,14 @@ Server.prototype._rejectPending = function () { // Listen on a UNIX socket -// server.listen("/tmp/socket"); +// server.listen('/tmp/socket'); // // Listen on port 8000, accept connections from INADDR_ANY. // server.listen(8000); // -// Listen on port 8000, accept connections to "192.168.1.2" -// server.listen(8000, "192.168.1.2"); -Server.prototype.listen = function () { +// Listen on port 8000, accept connections to '192.168.1.2' +// server.listen(8000, '192.168.1.2'); +Server.prototype.listen = function() { var self = this; if (self.fd) throw new Error('Server already opened'); @@ -887,7 +898,7 @@ Server.prototype.listen = function () { var path = arguments[0]; self.path = path; // unlink sockfile if it exists - require('fs').stat(path, function (err, r) { + require('fs').stat(path, function(err, r) { if (err) { if (err.errno == ENOENT) { self._doListen(path); @@ -896,9 +907,9 @@ Server.prototype.listen = function () { } } else { if (!r.isSocket()) { - throw new Error("Non-socket exists at " + path); + throw new Error('Non-socket exists at ' + path); } else { - require('fs').unlink(path, function (err) { + require('fs').unlink(path, function(err) { if (err) throw err; self._doListen(path); }); @@ -907,7 +918,7 @@ Server.prototype.listen = function () { }); } else { // the first argument is the port, the second an IP - require('dns').lookup(arguments[1], function (err, ip, addressType) { + require('dns').lookup(arguments[1], function(err, ip, addressType) { if (err) { self.emit('error', err); } else { @@ -919,7 +930,7 @@ Server.prototype.listen = function () { } }; -Server.prototype.listenFD = function (fd, type) { +Server.prototype.listenFD = function(fd, type) { if (this.fd) { throw new Error('Server already opened'); } @@ -929,13 +940,13 @@ Server.prototype.listenFD = function (fd, type) { this._startWatcher(); }; -Server.prototype._startWatcher = function () { +Server.prototype._startWatcher = function() { this.watcher.set(this.fd, true, false); this.watcher.start(); - this.emit("listening"); + this.emit('listening'); }; -Server.prototype._doListen = function () { +Server.prototype._doListen = function() { var self = this; // Ensure we have a dummy fd for EMFILE conditions. @@ -950,7 +961,7 @@ Server.prototype._doListen = function () { // Need to the listening in the nextTick so that people potentially have // time to register 'listening' listeners. - process.nextTick(function () { + process.nextTick(function() { // It could be that server.close() was called between the time the // original listen command was issued and this. Bail if that's the case. // See test/simple/test-net-eaddrinuse.js @@ -965,15 +976,15 @@ Server.prototype._doListen = function () { self._startWatcher(); }); -} +}; -Server.prototype.address = function () { +Server.prototype.address = function() { return getsockname(this.fd); }; -Server.prototype.close = function () { +Server.prototype.close = function() { var self = this; if (!self.fd) throw new Error('Not running'); @@ -987,12 +998,12 @@ Server.prototype.close = function () { self._pauseTimer = null; } - if (self.type === "unix") { - require('fs').unlink(self.path, function () { - self.emit("close"); + if (self.type === 'unix') { + require('fs').unlink(self.path, function() { + self.emit('close'); }); } else { - self.emit("close"); + self.emit('close'); } }; @@ -1005,7 +1016,7 @@ function rescueEMFILE(callback) { // Output a warning, but only at most every 5 seconds. var now = new Date(); if (now - lastEMFILEWarning > 5000) { - console.error("(node) Hit max file limit. Increase 'ulimit -n'."); + console.error('(node) Hit max file limit. Increase "ulimit - n"'); lastEMFILEWarning = now; } @@ -1020,7 +1031,7 @@ function rescueEMFILE(callback) { function getDummyFD() { if (!dummyFD) { try { - dummyFD = socket("tcp"); + dummyFD = socket('tcp'); } catch (e) { dummyFD = null; } diff --git a/lib/path.js b/lib/path.js index 9e530084c7..fc7f4c3ba6 100644 --- a/lib/path.js +++ b/lib/path.js @@ -1,28 +1,28 @@ -function validPathPart (p, keepBlanks) { - return typeof p === "string" && (p || keepBlanks); +function validPathPart(p, keepBlanks) { + return typeof p === 'string' && (p || keepBlanks); } -exports.join = function () { +exports.join = function() { var args = Array.prototype.slice.call(arguments); // edge case flag to switch into url-resolve-mode var keepBlanks = false; - if (args[ args.length - 1 ] === true) { + if (args[args.length - 1] === true) { keepBlanks = args.pop(); } // return exports.split(args.join("/"), keepBlanks).join("/"); - var joined = exports.normalizeArray(args, keepBlanks).join("/"); + var joined = exports.normalizeArray(args, keepBlanks).join('/'); return joined; }; -exports.split = function (path, keepBlanks) { +exports.split = function(path, keepBlanks) { // split based on /, but only if that / is not at the start or end. return exports.normalizeArray(path.split(/^|\/(?!$)/), keepBlanks); }; -function cleanArray (parts, keepBlanks) { +function cleanArray(parts, keepBlanks) { var i = 0; var l = parts.length - 1; var stripped = false; @@ -30,13 +30,13 @@ function cleanArray (parts, keepBlanks) { // strip leading empty args while (i < l && !validPathPart(parts[i], keepBlanks)) { stripped = true; - i ++; + i++; } // strip tailing empty args while (l >= i && !validPathPart(parts[l], keepBlanks)) { stripped = true; - l --; + l--; } if (stripped) { @@ -44,52 +44,46 @@ function cleanArray (parts, keepBlanks) { parts = Array.prototype.slice.call(parts, i, l + 1); } - return parts.filter(function (p) { return validPathPart(p, keepBlanks) }) + return parts.filter(function(p) { return validPathPart(p, keepBlanks) }) .join('/') .split(/^|\/(?!$)/); } -exports.normalizeArray = function (original, keepBlanks) { +exports.normalizeArray = function(original, keepBlanks) { var parts = cleanArray(original, keepBlanks); - if (!parts.length || (parts.length === 1 && !parts[0])) return ["."]; + if (!parts.length || (parts.length === 1 && !parts[0])) return ['.']; // now we're fully ready to rock. // leading/trailing invalids have been stripped off. // if it comes in starting with a slash, or ending with a slash, - var leadingSlash = (parts[0].charAt(0) === "/"); + var leadingSlash = (parts[0].charAt(0) === '/'); if (leadingSlash) parts[0] = parts[0].substr(1); var last = parts.slice(-1)[0]; - var tailingSlash = (last.substr(-1) === "/"); + var tailingSlash = (last.substr(-1) === '/'); if (tailingSlash) parts[parts.length - 1] = last.slice(0, -1); var directories = []; var prev; - for (var i = 0, l = parts.length - 1 ; i <= l ; i ++) { + for (var i = 0, l = parts.length - 1; i <= l; i++) { var directory = parts[i]; // if it's blank, and we're not keeping blanks, then skip it. - if (directory === "" && !keepBlanks) continue; + if (directory === '' && !keepBlanks) continue; // if it's a dot, then skip it - if (directory === "." && ( - directories.length - || (i === 0 && !(tailingSlash && i === l)) - || (i === 0 && leadingSlash) - )) continue; + if (directory === '.' && (directories.length || + (i === 0 && !(tailingSlash && i === l)) || + (i === 0 && leadingSlash))) continue; // if we're dealing with an absolute path, then discard ..s that go // above that the base. - if (leadingSlash && directories.length === 0 && directory === "..") { + if (leadingSlash && directories.length === 0 && directory === '..') { continue; } // trying to go up a dir - if ( - directory === ".." - && directories.length - && prev !== ".." - && prev !== undefined - ) { + if (directory === '..' && directories.length && prev !== '..' && + prev !== undefined) { directories.pop(); prev = directories.slice(-1)[0]; } else { @@ -98,27 +92,27 @@ exports.normalizeArray = function (original, keepBlanks) { } } if (!directories.length) { - directories = [ leadingSlash || tailingSlash ? "" : "." ]; + directories = [leadingSlash || tailingSlash ? '' : '.']; } var last = directories.slice(-1)[0]; - if (tailingSlash && last.substr(-1) !== "/") { - directories[directories.length-1] += "/"; + if (tailingSlash && last.substr(-1) !== '/') { + directories[directories.length - 1] += '/'; } - if (leadingSlash && directories[0].charAt(0) !== "/") { - if (directories[0] === ".") directories[0] = ""; - directories[0] = "/" + directories[0]; + if (leadingSlash && directories[0].charAt(0) !== '/') { + if (directories[0] === '.') directories[0] = ''; + directories[0] = '/' + directories[0]; } return directories; }; -exports.normalize = function (path, keepBlanks) { +exports.normalize = function(path, keepBlanks) { return exports.join(path, keepBlanks || false); }; -exports.dirname = function (path) { - if (path.length > 1 && '/' === path[path.length-1]) { +exports.dirname = function(path) { + if (path.length > 1 && '/' === path[path.length - 1]) { path = path.replace(/\/+$/, ''); } var lastSlash = path.lastIndexOf('/'); @@ -133,8 +127,8 @@ exports.dirname = function (path) { }; -exports.basename = function (path, ext) { - var f = path.substr(path.lastIndexOf("/") + 1); +exports.basename = function(path, ext) { + var f = path.substr(path.lastIndexOf('/') + 1); if (ext && f.substr(-1 * ext.length) === ext) { f = f.substr(0, f.length - ext.length); } @@ -142,7 +136,7 @@ exports.basename = function (path, ext) { }; -exports.extname = function (path) { +exports.extname = function(path) { var dot = path.lastIndexOf('.'), slash = path.lastIndexOf('/'); // The last dot must be in the last path component, and it (the last dot) must @@ -152,18 +146,18 @@ exports.extname = function (path) { }; -exports.exists = function (path, callback) { - process.binding('fs').stat(path, function (err, stats) { +exports.exists = function(path, callback) { + process.binding('fs').stat(path, function(err, stats) { if (callback) callback(err ? false : true); }); }; -exports.existsSync = function (path) { +exports.existsSync = function(path) { try { process.binding('fs').stat(path); return true; - } catch(e){ + } catch (e) { return false; } }; diff --git a/lib/querystring.js b/lib/querystring.js index 8015469636..eeeada0467 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -1,18 +1,18 @@ // Query String Utilities var QueryString = exports; -var urlDecode = process.binding("http_parser").urlDecode; +var urlDecode = process.binding('http_parser').urlDecode; -function charCode (c) { +function charCode(c) { return c.charCodeAt(0); } // a safe fast alternative to decodeURIComponent -QueryString.unescapeBuffer = function (s, decodeSpaces) { +QueryString.unescapeBuffer = function(s, decodeSpaces) { var out = new Buffer(s.length); - var state = "CHAR"; // states: CHAR, HEX0, HEX1 + var state = 'CHAR'; // states: CHAR, HEX0, HEX1 var n, m, hexchar; for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) { @@ -65,61 +65,50 @@ QueryString.unescapeBuffer = function (s, decodeSpaces) { out[outIndex++] = c; break; } - out[outIndex++] = 16*n + m; + out[outIndex++] = 16 * n + m; break; } } // TODO support returning arbitrary buffers. - return out.slice(0, outIndex-1); + return out.slice(0, outIndex - 1); }; -QueryString.unescape = function (s, decodeSpaces) { +QueryString.unescape = function(s, decodeSpaces) { return QueryString.unescapeBuffer(s, decodeSpaces).toString(); }; -QueryString.escape = function (str) { +QueryString.escape = function(str) { return encodeURIComponent(str); }; var stringifyPrimitive = function(v) { switch (typeof v) { - case "string": + case 'string': return v; - case "boolean": - return v ? "true" : "false"; + case 'boolean': + return v ? 'true' : 'false'; - case "number": - return isFinite(v) ? v : ""; + case 'number': + return isFinite(v) ? v : ''; default: - return ""; + return ''; } }; -/** - *
Converts an arbitrary value to a Query String representation.
- * - *Objects with cyclical references will trigger an exception.
- * - * @method stringify - * @param obj {Variant} any arbitrary value to convert to query string - * @param sep {String} (optional) Character that should join param k=v pairs together. Default: "&" - * @param eq {String} (optional) Character that should join keys to their values. Default: "=" - * @param name {String} (optional) Name of the current key, for handling children recursively. - * @static - */ -QueryString.stringify = QueryString.encode = function (obj, sep, eq, name) { - sep = sep || "&"; - eq = eq || "="; + +QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) { + sep = sep || '&'; + eq = eq || '='; obj = (obj === null) ? undefined : obj; switch (typeof obj) { - case "object": + case 'object': return Object.keys(obj).map(function(k) { if (Array.isArray(obj[k])) { return obj[k].map(function(v) { @@ -128,24 +117,23 @@ QueryString.stringify = QueryString.encode = function (obj, sep, eq, name) { QueryString.escape(stringifyPrimitive(v)); }).join(sep); } else { - return QueryString.escape(stringifyPrimitive(k)) + + return QueryString.escape(stringifyPrimitive(k)) + eq + QueryString.escape(stringifyPrimitive(obj[k])); } }).join(sep); default: - return (name) ? - QueryString.escape(stringifyPrimitive(name)) + eq + - QueryString.escape(stringifyPrimitive(obj)) : - ""; + if (!name) return ''; + return QueryString.escape(stringifyPrimitive(name)) + eq + + QueryString.escape(stringifyPrimitive(obj)); } }; // Parse a key=val string. -QueryString.parse = QueryString.decode = function (qs, sep, eq) { - sep = sep || "&"; - eq = eq || "="; +QueryString.parse = QueryString.decode = function(qs, sep, eq) { + sep = sep || '&'; + eq = eq || '='; var obj = {}; if (typeof qs !== 'string') { @@ -158,11 +146,11 @@ QueryString.parse = QueryString.decode = function (qs, sep, eq) { var v = QueryString.unescape(x.slice(1).join(eq), true); if (!(k in obj)) { - obj[k] = v; + obj[k] = v; } else if (!Array.isArray(obj[k])) { - obj[k] = [obj[k], v]; + obj[k] = [obj[k], v]; } else { - obj[k].push(v); + obj[k].push(v); } }); diff --git a/lib/readline.js b/lib/readline.js index 60e0f07410..b96499e228 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -5,7 +5,7 @@ // * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html var kHistorySize = 30; -var kBufSize = 10*1024; +var kBufSize = 10 * 1024; var util = require('util'); var inherits = require('util').inherits; @@ -13,15 +13,15 @@ var EventEmitter = require('events').EventEmitter; var stdio = process.binding('stdio'); -exports.createInterface = function (output, completer) { +exports.createInterface = function(output, completer) { return new Interface(output, completer); }; -function writeFilter (stream) { +function writeFilter(stream) { if (stream._writeFiltered) return; stream._writeFiltered = true; stream._normalWrite = stream.write; - stream.write = function (d) { + stream.write = function(d) { var args = Array.prototype.slice.call(arguments); if (typeof d == 'string') { args[0] = d.replace(/([^\r])\n|^\n/g, '$1\r\n'); @@ -31,14 +31,14 @@ function writeFilter (stream) { } } -function Interface (output, completer) { +function Interface(output, completer) { if (!(this instanceof Interface)) return new Interface(output, completer); EventEmitter.call(this); this.output = output; this.completer = completer; - this.setPrompt("> "); + this.setPrompt('> '); this.enabled = stdio.isatty(output.fd); @@ -53,7 +53,7 @@ function Interface (output, completer) { writeFilter(process.stdout); // Current line - this.line = ""; + this.line = ''; // Check process.env.TERM ? stdio.setRawMode(true); @@ -67,8 +67,8 @@ function Interface (output, completer) { exports.columns = process.binding('stdio').getColumns(); - if (process.listeners("SIGWINCH").length === 0) { - process.on("SIGWINCH", function () { + if (process.listeners('SIGWINCH').length === 0) { + process.on('SIGWINCH', function() { exports.columns = process.binding('stdio').getColumns(); }); } @@ -77,17 +77,17 @@ function Interface (output, completer) { inherits(Interface, EventEmitter); -Interface.prototype.__defineGetter__("columns", function () { +Interface.prototype.__defineGetter__('columns', function() { return exports.columns; }); -Interface.prototype.setPrompt = function (prompt, length) { +Interface.prototype.setPrompt = function(prompt, length) { this._prompt = prompt; this._promptLength = length ? length : Buffer.byteLength(prompt); }; -Interface.prototype.prompt = function () { +Interface.prototype.prompt = function() { if (this.enabled) { this.cursor = 0; this._refreshLine(); @@ -97,11 +97,11 @@ Interface.prototype.prompt = function () { }; -Interface.prototype._addHistory = function () { - if (this.line.length === 0) return ""; +Interface.prototype._addHistory = function() { + if (this.line.length === 0) return ''; this.history.unshift(this.line); - this.line = ""; + this.line = ''; this.historyIndex = -1; this.cursor = 0; @@ -113,7 +113,7 @@ Interface.prototype._addHistory = function () { }; -Interface.prototype._refreshLine = function () { +Interface.prototype._refreshLine = function() { if (this._closed) return; // Cursor to left edge. @@ -131,7 +131,7 @@ Interface.prototype._refreshLine = function () { }; -Interface.prototype.close = function (d) { +Interface.prototype.close = function(d) { if (this.enabled) { stdio.setRawMode(false); } @@ -140,19 +140,19 @@ Interface.prototype.close = function (d) { }; -Interface.prototype.write = function (d) { +Interface.prototype.write = function(d) { if (this._closed) return; return this.enabled ? this._ttyWrite(d) : this._normalWrite(d); }; -Interface.prototype._normalWrite = function (b) { +Interface.prototype._normalWrite = function(b) { // Very simple implementation right now. Should try to break on // new lines. this.emit('line', b.toString()); }; -Interface.prototype._insertString = function (c) { +Interface.prototype._insertString = function(c) { //BUG: Problem when adding tabs with following content. // Perhaps the bug is in _refreshLine(). Not sure. // A hack would be to insert spaces instead of literal '\t'. @@ -169,7 +169,7 @@ Interface.prototype._insertString = function (c) { } }; -Interface.prototype._tabComplete = function () { +Interface.prototype._tabComplete = function() { var self = this; var rv = this.completer(self.line.slice(0, self.cursor)); @@ -181,7 +181,7 @@ Interface.prototype._tabComplete = function () { self._insertString(completions[0].slice(completeOn.length)); self._refreshLine(); } else { - self.output.write("\r\n"); + self.output.write('\r\n'); var width = completions.reduce(function(a, b) { return a.length > b.length ? a : b; }).length + 2; // 2 space padding @@ -214,7 +214,7 @@ Interface.prototype._tabComplete = function () { var group = [], c; for (var i = 0, compLen = completions.length; i < compLen; i++) { c = completions[i]; - if (c === "") { + if (c === '') { handleGroup(group); group = []; } else { @@ -225,8 +225,8 @@ Interface.prototype._tabComplete = function () { // If there is a common prefix to all matches, then apply that // portion. - var prefix = commonPrefix( - completions.filter(function(e) { if (e) return e; })); + var f = completions.filter(function(e) { if (e) return e; }); + var prefix = commonPrefix(f); if (prefix.length > completeOn.length) { self._insertString(prefix.slice(completeOn.length)); } @@ -238,7 +238,7 @@ Interface.prototype._tabComplete = function () { function commonPrefix(strings) { if (!strings || strings.length == 0) { - return ""; + return ''; } var sorted = strings.slice().sort(); var min = sorted[0]; @@ -251,7 +251,7 @@ function commonPrefix(strings) { return min; } -Interface.prototype._historyNext = function () { +Interface.prototype._historyNext = function() { if (this.historyIndex > 0) { this.historyIndex--; this.line = this.history[this.historyIndex]; @@ -266,7 +266,7 @@ Interface.prototype._historyNext = function () { } }; -Interface.prototype._historyPrev = function () { +Interface.prototype._historyPrev = function() { if (this.historyIndex + 1 < this.history.length) { this.historyIndex++; this.line = this.history[this.historyIndex]; @@ -277,7 +277,7 @@ Interface.prototype._historyPrev = function () { }; // handle a write from the tty -Interface.prototype._ttyWrite = function (b) { +Interface.prototype._ttyWrite = function(b) { switch (b[0]) { /* ctrl+c */ case 3: @@ -294,9 +294,9 @@ Interface.prototype._ttyWrite = function (b) { if (this.cursor === 0 && this.line.length === 0) { this.close(); } else if (this.cursor < this.line.length) { - this.line = this.line.slice(0, this.cursor) - + this.line.slice(this.cursor+1, this.line.length) - ; + this.line = this.line.slice(0, this.cursor) + + this.line.slice(this.cursor + 1, this.line.length); + this._refreshLine(); } break; @@ -310,9 +310,9 @@ Interface.prototype._ttyWrite = function (b) { case 127: /* backspace */ case 8: /* ctrl+h */ if (this.cursor > 0 && this.line.length > 0) { - this.line = this.line.slice(0, this.cursor-1) - + this.line.slice(this.cursor, this.line.length) - ; + this.line = this.line.slice(0, this.cursor - 1) + + this.line.slice(this.cursor, this.line.length); + this.cursor--; this._refreshLine(); } @@ -379,7 +379,7 @@ Interface.prototype._ttyWrite = function (b) { break; case 26: /* ctrl+z */ - process.kill(process.pid, "SIGTSTP"); + process.kill(process.pid, 'SIGTSTP'); return; case 27: /* escape sequence */ @@ -407,9 +407,8 @@ Interface.prototype._ttyWrite = function (b) { // meta-f - forward word next_word = this.line.slice(this.cursor, this.line.length).search(/\w/); if (next_word !== -1) { - next_non_word = - this.line.slice(this.cursor + next_word, this.line.length) - .search(/\W/); + next_non_word = this.line.slice(this.cursor + next_word, + this.line.length).search(/\W/); if (next_non_word !== -1) { this.cursor += next_word + next_non_word; this._refreshLine(); @@ -423,12 +422,12 @@ Interface.prototype._ttyWrite = function (b) { // meta-d delete forward word next_word = this.line.slice(this.cursor, this.line.length).search(/\w/); if (next_word !== -1) { - next_non_word = - this.line.slice(this.cursor + next_word, this.line.length) - .search(/\W/); + next_non_word = this.line.slice(this.cursor + next_word, + this.line.length).search(/\W/); if (next_non_word !== -1) { - this.line = - this.line.slice(this.cursor + next_word + next_non_word); + this.line = this.line.slice(this.cursor + + next_word + + next_non_word); this.cursor = 0; this._refreshLine(); break; @@ -478,7 +477,7 @@ Interface.prototype._ttyWrite = function (b) { } else if (b[1] === 91 && b[2] === 51 && this.cursor < this.line.length) { // delete right this.line = this.line.slice(0, this.cursor) + - this.line.slice(this.cursor+1, this.line.length); + this.line.slice(this.cursor + 1, this.line.length); this._refreshLine(); } diff --git a/lib/repl.js b/lib/repl.js index 3ad63bc6b3..fb08593b29 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -1,29 +1,36 @@ -// A repl library that you can include in your own code to get a runtime -// interface to your program. -// -// var repl = require("/repl.js"); -// repl.start("prompt> "); // start repl on stdin -// net.createServer(function (socket) { // listen for unix socket connections and start repl on them -// repl.start("node via Unix socket> ", socket); -// }).listen("/tmp/node-repl-sock"); -// net.createServer(function (socket) { // listen for TCP socket connections and start repl on them -// repl.start("node via TCP socket> ", socket); -// }).listen(5001); - -// repl.start("node > ").context.foo = "stdin is fun"; // expose foo to repl context +/* A repl library that you can include in your own code to get a runtime + * interface to your program. + * + * var repl = require("/repl.js"); + * // start repl on stdin + * repl.start("prompt> "); + * + * // listen for unix socket connections and start repl on them + * net.createServer(function (socket) { + * repl.start("node via Unix socket> ", socket); + * }).listen("/tmp/node-repl-sock"); + * + * // listen for TCP socket connections and start repl on them + * net.createServer(function (socket) { + * repl.start("node via TCP socket> ", socket); + * }).listen(5001); + * + * // expose foo to repl context + * repl.start("node > ").context.foo = "stdin is fun"; + */ var util = require('util'); var Script = process.binding('evals').Script; var evalcx = Script.runInContext; -var path = require("path"); -var fs = require("fs"); +var path = require('path'); +var fs = require('fs'); var rl = require('readline'); var context; var disableColors = process.env.NODE_DISABLE_COLORS ? true : false; // hack for require.resolve("./relative") to work properly. -module.filename = process.cwd() + "/repl"; +module.filename = process.cwd() + '/repl'; function resetContext() { context = Script.createContext(); @@ -44,9 +51,9 @@ function REPLServer(prompt, stream) { self.buffered_cmd = ''; self.stream = stream || process.openStdin(); - self.prompt = prompt || "> "; + self.prompt = prompt || '> '; - var rli = self.rli = rl.createInterface(self.stream, function (text) { + var rli = self.rli = rl.createInterface(self.stream, function(text) { return self.complete(text); }); @@ -62,9 +69,9 @@ function REPLServer(prompt, stream) { rli.setPrompt(self.prompt); - rli.on("SIGINT", function () { + rli.on('SIGINT', function() { if (self.buffered_cmd && self.buffered_cmd.length > 0) { - rli.write("\n"); + rli.write('\n'); self.buffered_cmd = ''; self.displayPrompt(); } else { @@ -72,11 +79,11 @@ function REPLServer(prompt, stream) { } }); - self.stream.addListener("data", function (chunk) { + self.stream.addListener('data', function(chunk) { rli.write(chunk); }); - rli.addListener('line', function (cmd) { + rli.addListener('line', function(cmd) { cmd = trimWhitespace(cmd); // Check to see if a REPL keyword was used. If it returns true, @@ -95,30 +102,31 @@ function REPLServer(prompt, stream) { // continue onto the next line. try { // Use evalcx to supply the global context - var ret = evalcx(self.buffered_cmd, context, "repl"); + var ret = evalcx(self.buffered_cmd, context, 'repl'); if (ret !== undefined) { context._ = ret; - self.stream.write(exports.writer(ret) + "\n"); + self.stream.write(exports.writer(ret) + '\n'); } self.buffered_cmd = ''; } catch (e) { // instanceof doesn't work across context switches. - if (!(e && e.constructor && e.constructor.name === "SyntaxError")) { + if (!(e && e.constructor && e.constructor.name === 'SyntaxError')) { throw e; // It could also be an error from JSON.parse - } else if(e && e.stack - && e.stack.match('Unexpected token ILLEGAL') - && e.stack.match(/Object.parse \(native\)/)) { + } else if (e && + e.stack && + e.stack.match('Unexpected token ILLEGAL') && + e.stack.match(/Object.parse \(native\)/)) { throw e; } } } catch (e) { // On error: Print the error and clear the buffer if (e.stack) { - self.stream.write(e.stack + "\n"); + self.stream.write(e.stack + '\n'); } else { - self.stream.write(e.toString() + "\n"); + self.stream.write(e.toString() + '\n'); } self.buffered_cmd = ''; } @@ -126,7 +134,7 @@ function REPLServer(prompt, stream) { self.displayPrompt(); }); - rli.addListener('close', function () { + rli.addListener('close', function() { self.stream.destroy(); }); @@ -136,41 +144,36 @@ exports.REPLServer = REPLServer; // prompt is a string to print on each line for the prompt, // source is a stream to use for I/O, defaulting to stdin/stdout. -exports.start = function (prompt, source) { +exports.start = function(prompt, source) { return new REPLServer(prompt, source); }; -REPLServer.prototype.displayPrompt = function () { +REPLServer.prototype.displayPrompt = function() { this.rli.setPrompt(this.buffered_cmd.length ? '... ' : this.prompt); this.rli.prompt(); }; // read a line from the stream, then eval it -REPLServer.prototype.readline = function (cmd) { +REPLServer.prototype.readline = function(cmd) { }; -/** - * Provide a list of completions for the given leading text. This is - * given to the readline interface for handling tab completion. - * - * @param {line} The text (preceding the cursor) to complete - * @returns {Array} Two elements: (1) an array of completions; and - * (2) the leading text completed. - * - * Example: - * complete('var foo = util.') - * -> [['util.print', 'util.debug', 'util.log', 'util.inspect', 'util.pump'], - * 'util.' ] - * - * Warning: This eval's code like "foo.bar.baz", so it will run property - * getter code. - */ +// Provide a list of completions for the given leading text. This is +// given to the readline interface for handling tab completion. +// +// Example: +// complete('var foo = util.') +// -> [['util.print', 'util.debug', 'util.log', 'util.inspect', 'util.pump'], +// 'util.' ] +// +// Warning: This eval's code like "foo.bar.baz", so it will run property +// getter code. +REPLServer.prototype.complete = function(line) { + var completions; + + // list of completion lists, one for each inheritance "level" + var completionGroups = []; -REPLServer.prototype.complete = function (line) { - var completions, - completionGroups = [], // list of completion lists, one for each inheritance "level" - completeOn, - match, filter, i, j, group, c; + var completeOn, match, filter, i, j, group, c; // REPL commands (e.g. ".break"). var match = null; @@ -181,17 +184,19 @@ REPLServer.prototype.complete = function (line) { if (match[1].length > 1) { filter = match[1]; } - } - - // require('...