more lint
This commit is contained in:
parent
ddbaeef1b3
commit
e232f6e735
265
lib/http.js
265
lib/http.js
@ -8,24 +8,24 @@ var HTTPParser = process.binding('http_parser').HTTPParser;
|
|||||||
var debug;
|
var debug;
|
||||||
var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
|
var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
|
||||||
if (debugLevel & 0x4) {
|
if (debugLevel & 0x4) {
|
||||||
debug = function (x) { console.error('HTTP: %s', x); };
|
debug = function(x) { console.error('HTTP: %s', x); };
|
||||||
} else {
|
} else {
|
||||||
debug = function () { };
|
debug = function() { };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var parsers = new FreeList('parsers', 1000, function () {
|
var parsers = new FreeList('parsers', 1000, function() {
|
||||||
var parser = new HTTPParser('request');
|
var parser = new HTTPParser('request');
|
||||||
|
|
||||||
parser.onMessageBegin = function () {
|
parser.onMessageBegin = function() {
|
||||||
parser.incoming = new IncomingMessage(parser.socket);
|
parser.incoming = new IncomingMessage(parser.socket);
|
||||||
parser.field = null;
|
parser.field = null;
|
||||||
parser.value = null;
|
parser.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only servers will get URL events.
|
// Only servers will get URL events.
|
||||||
parser.onURL = function (b, start, len) {
|
parser.onURL = function(b, start, len) {
|
||||||
var slice = b.toString('ascii', start, start+len);
|
var slice = b.toString('ascii', start, start + len);
|
||||||
if (parser.incoming.url) {
|
if (parser.incoming.url) {
|
||||||
parser.incoming.url += slice;
|
parser.incoming.url += slice;
|
||||||
} else {
|
} else {
|
||||||
@ -34,8 +34,8 @@ var parsers = new FreeList('parsers', 1000, function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
parser.onHeaderField = function (b, start, len) {
|
parser.onHeaderField = function(b, start, len) {
|
||||||
var slice = b.toString('ascii', start, start+len).toLowerCase();
|
var slice = b.toString('ascii', start, start + len).toLowerCase();
|
||||||
if (parser.value != undefined) {
|
if (parser.value != undefined) {
|
||||||
parser.incoming._addHeaderLine(parser.field, parser.value);
|
parser.incoming._addHeaderLine(parser.field, parser.value);
|
||||||
parser.field = null;
|
parser.field = null;
|
||||||
@ -48,8 +48,8 @@ var parsers = new FreeList('parsers', 1000, function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
parser.onHeaderValue = function (b, start, len) {
|
parser.onHeaderValue = function(b, start, len) {
|
||||||
var slice = b.toString('ascii', start, start+len);
|
var slice = b.toString('ascii', start, start + len);
|
||||||
if (parser.value) {
|
if (parser.value) {
|
||||||
parser.value += slice;
|
parser.value += slice;
|
||||||
} else {
|
} 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)) {
|
if (parser.field && (parser.value != undefined)) {
|
||||||
parser.incoming._addHeaderLine(parser.field, parser.value);
|
parser.incoming._addHeaderLine(parser.field, parser.value);
|
||||||
parser.field = null;
|
parser.field = null;
|
||||||
@ -89,9 +89,9 @@ var parsers = new FreeList('parsers', 1000, function () {
|
|||||||
return isHeadResponse;
|
return isHeadResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
parser.onBody = function (b, start, len) {
|
parser.onBody = function(b, start, len) {
|
||||||
// TODO body encoding?
|
// TODO body encoding?
|
||||||
var slice = b.slice(start, start+len);
|
var slice = b.slice(start, start + len);
|
||||||
if (parser.incoming._decoder) {
|
if (parser.incoming._decoder) {
|
||||||
var string = parser.incoming._decoder.write(slice);
|
var string = parser.incoming._decoder.write(slice);
|
||||||
if (string.length) parser.incoming.emit('data', string);
|
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;
|
this.incoming.complete = true;
|
||||||
if (parser.field && (parser.value != undefined)) {
|
if (parser.field && (parser.value != undefined)) {
|
||||||
parser.incoming._addHeaderLine(parser.field, parser.value);
|
parser.incoming._addHeaderLine(parser.field, parser.value);
|
||||||
}
|
}
|
||||||
if (!parser.incoming.upgrade) {
|
if (!parser.incoming.upgrade) {
|
||||||
// For upgraded connections, also emit this after parser.execute
|
// 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;
|
exports.parsers = parsers;
|
||||||
|
|
||||||
|
|
||||||
var CRLF = "\r\n";
|
var CRLF = '\r\n';
|
||||||
var STATUS_CODES = exports.STATUS_CODES = {
|
var STATUS_CODES = exports.STATUS_CODES = {
|
||||||
100 : 'Continue',
|
100 : 'Continue',
|
||||||
101 : 'Switching Protocols',
|
101 : 'Switching Protocols',
|
||||||
@ -183,7 +183,7 @@ var continueExpression = /100-continue/i;
|
|||||||
|
|
||||||
|
|
||||||
/* Abstract base class for ServerRequest and ClientResponse. */
|
/* Abstract base class for ServerRequest and ClientResponse. */
|
||||||
function IncomingMessage (socket) {
|
function IncomingMessage(socket) {
|
||||||
stream.Stream.call(this);
|
stream.Stream.call(this);
|
||||||
|
|
||||||
// TODO Remove one of these eventually.
|
// TODO Remove one of these eventually.
|
||||||
@ -198,7 +198,7 @@ function IncomingMessage (socket) {
|
|||||||
this.readable = true;
|
this.readable = true;
|
||||||
|
|
||||||
// request (server) only
|
// request (server) only
|
||||||
this.url = "";
|
this.url = '';
|
||||||
|
|
||||||
this.method = null;
|
this.method = null;
|
||||||
|
|
||||||
@ -212,23 +212,23 @@ util.inherits(IncomingMessage, stream.Stream);
|
|||||||
exports.IncomingMessage = IncomingMessage;
|
exports.IncomingMessage = IncomingMessage;
|
||||||
|
|
||||||
|
|
||||||
IncomingMessage.prototype.destroy = function (error) {
|
IncomingMessage.prototype.destroy = function(error) {
|
||||||
this.socket.destroy(error);
|
this.socket.destroy(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
IncomingMessage.prototype.setEncoding = function (encoding) {
|
IncomingMessage.prototype.setEncoding = function(encoding) {
|
||||||
var StringDecoder = require("string_decoder").StringDecoder; // lazy load
|
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
|
||||||
this._decoder = new StringDecoder(encoding);
|
this._decoder = new StringDecoder(encoding);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
IncomingMessage.prototype.pause = function () {
|
IncomingMessage.prototype.pause = function() {
|
||||||
this.socket.pause();
|
this.socket.pause();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
IncomingMessage.prototype.resume = function () {
|
IncomingMessage.prototype.resume = function() {
|
||||||
this.socket.resume();
|
this.socket.resume();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -240,13 +240,9 @@ IncomingMessage.prototype.resume = function () {
|
|||||||
// multiple values this way. If not, we declare the first instance the winner
|
// 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
|
// and drop the second. Extended header fields (those beginning with 'x-') are
|
||||||
// always joined.
|
// always joined.
|
||||||
IncomingMessage.prototype._addHeaderLine = function (field, value) {
|
IncomingMessage.prototype._addHeaderLine = function(field, value) {
|
||||||
var dest;
|
var dest = this.complete ? this.trailers : this.headers;
|
||||||
if (this.complete) {
|
|
||||||
dest = this.trailers;
|
|
||||||
} else {
|
|
||||||
dest = this.headers;
|
|
||||||
}
|
|
||||||
switch (field) {
|
switch (field) {
|
||||||
// Array headers:
|
// Array headers:
|
||||||
case 'set-cookie':
|
case 'set-cookie':
|
||||||
@ -273,7 +269,7 @@ IncomingMessage.prototype._addHeaderLine = function (field, value) {
|
|||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (field.slice(0,2) == 'x-') {
|
if (field.slice(0, 2) == 'x-') {
|
||||||
// except for x-
|
// except for x-
|
||||||
if (field in dest) {
|
if (field in dest) {
|
||||||
dest[field] += ', ' + value;
|
dest[field] += ', ' + value;
|
||||||
@ -289,7 +285,7 @@ IncomingMessage.prototype._addHeaderLine = function (field, value) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function OutgoingMessage (socket) {
|
function OutgoingMessage(socket) {
|
||||||
stream.Stream.call(this);
|
stream.Stream.call(this);
|
||||||
|
|
||||||
// TODO Remove one of these eventually.
|
// TODO Remove one of these eventually.
|
||||||
@ -317,13 +313,13 @@ util.inherits(OutgoingMessage, stream.Stream);
|
|||||||
exports.OutgoingMessage = OutgoingMessage;
|
exports.OutgoingMessage = OutgoingMessage;
|
||||||
|
|
||||||
|
|
||||||
OutgoingMessage.prototype.destroy = function (error) {
|
OutgoingMessage.prototype.destroy = function(error) {
|
||||||
this.socket.destroy(error);
|
this.socket.destroy(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// This abstract either writing directly to the socket or buffering it.
|
// 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
|
// 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
|
// the same packet. Future versions of Node are going to take care of
|
||||||
// this at a lower level and in a more general way.
|
// 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;
|
if (data.length === 0) return;
|
||||||
|
|
||||||
var length = this.output.length;
|
var length = this.output.length;
|
||||||
|
|
||||||
if (length === 0 || typeof data != 'string') {
|
if (length === 0 || typeof data != 'string') {
|
||||||
this.output.push(data);
|
this.output.push(data);
|
||||||
encoding = encoding || "ascii";
|
encoding = encoding || 'ascii';
|
||||||
this.outputEncodings.push(encoding);
|
this.outputEncodings.push(encoding);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastEncoding = this.outputEncodings[length-1];
|
var lastEncoding = this.outputEncodings[length - 1];
|
||||||
var lastData = this.output[length-1];
|
var lastData = this.output[length - 1];
|
||||||
|
|
||||||
if ((lastEncoding === encoding) ||
|
if ((lastEncoding === encoding) ||
|
||||||
(!encoding && data.constructor === lastData.constructor)) {
|
(!encoding && data.constructor === lastData.constructor)) {
|
||||||
this.output[length-1] = lastData + data;
|
this.output[length - 1] = lastData + data;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.output.push(data);
|
this.output.push(data);
|
||||||
encoding = encoding || "ascii";
|
encoding = encoding || 'ascii';
|
||||||
this.outputEncodings.push(encoding);
|
this.outputEncodings.push(encoding);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
OutgoingMessage.prototype._storeHeader = function (firstLine, headers) {
|
OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
|
||||||
var sentConnectionHeader = false;
|
var sentConnectionHeader = false;
|
||||||
var sentContentLengthHeader = false;
|
var sentContentLengthHeader = false;
|
||||||
var sentTransferEncodingHeader = false;
|
var sentTransferEncodingHeader = false;
|
||||||
var sentExpect = false;
|
var sentExpect = false;
|
||||||
|
|
||||||
// firstLine in the case of request is: "GET /index.html HTTP/1.1\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"
|
// in the case of response it is: 'HTTP/1.1 200 OK\r\n'
|
||||||
var messageHeader = firstLine;
|
var messageHeader = firstLine;
|
||||||
var field, value;
|
var field, value;
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
function store(field, value) {
|
function store(field, value) {
|
||||||
messageHeader += field + ": " + value + CRLF;
|
messageHeader += field + ': ' + value + CRLF;
|
||||||
|
|
||||||
if (connectionExpression.test(field)) {
|
if (connectionExpression.test(field)) {
|
||||||
sentConnectionHeader = true;
|
sentConnectionHeader = true;
|
||||||
@ -455,17 +451,17 @@ OutgoingMessage.prototype._storeHeader = function (firstLine, headers) {
|
|||||||
if (sentConnectionHeader == false) {
|
if (sentConnectionHeader == false) {
|
||||||
if (this.shouldKeepAlive &&
|
if (this.shouldKeepAlive &&
|
||||||
(sentContentLengthHeader || this.useChunkedEncodingByDefault)) {
|
(sentContentLengthHeader || this.useChunkedEncodingByDefault)) {
|
||||||
messageHeader += "Connection: keep-alive\r\n";
|
messageHeader += 'Connection: keep-alive\r\n';
|
||||||
} else {
|
} else {
|
||||||
this._last = true;
|
this._last = true;
|
||||||
messageHeader += "Connection: close\r\n";
|
messageHeader += 'Connection: close\r\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sentContentLengthHeader == false && sentTransferEncodingHeader == false) {
|
if (sentContentLengthHeader == false && sentTransferEncodingHeader == false) {
|
||||||
if (this._hasBody) {
|
if (this._hasBody) {
|
||||||
if (this.useChunkedEncodingByDefault) {
|
if (this.useChunkedEncodingByDefault) {
|
||||||
messageHeader += "Transfer-Encoding: chunked\r\n";
|
messageHeader += 'Transfer-Encoding: chunked\r\n';
|
||||||
this.chunkedEncoding = true;
|
this.chunkedEncoding = true;
|
||||||
} else {
|
} else {
|
||||||
this._last = true;
|
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,
|
// wait until the first body chunk, or close(), is sent to flush,
|
||||||
// UNLESS we're sending Expect: 100-continue.
|
// 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) {
|
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) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof chunk !== "string" && !Buffer.isBuffer(chunk) &&
|
if (typeof chunk !== 'string' && !Buffer.isBuffer(chunk) &&
|
||||||
!Array.isArray(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;
|
if (chunk.length === 0) return false;
|
||||||
@ -525,8 +522,8 @@ OutgoingMessage.prototype.write = function (chunk, encoding) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
OutgoingMessage.prototype.addTrailers = function (headers) {
|
OutgoingMessage.prototype.addTrailers = function(headers) {
|
||||||
this._trailer = "";
|
this._trailer = '';
|
||||||
var keys = Object.keys(headers);
|
var keys = Object.keys(headers);
|
||||||
var isArray = (Array.isArray(headers));
|
var isArray = (Array.isArray(headers));
|
||||||
for (var i = 0, l = keys.length; i < l; i++) {
|
for (var i = 0, l = keys.length; i < l; i++) {
|
||||||
@ -539,16 +536,16 @@ OutgoingMessage.prototype.addTrailers = function (headers) {
|
|||||||
value = headers[key];
|
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 ret;
|
||||||
|
|
||||||
var hot = this._headerSent === false &&
|
var hot = this._headerSent === false &&
|
||||||
typeof(data) === "string" &&
|
typeof(data) === 'string' &&
|
||||||
data.length > 0 &&
|
data.length > 0 &&
|
||||||
this.output.length === 0 &&
|
this.output.length === 0 &&
|
||||||
this.connection.writable &&
|
this.connection.writable &&
|
||||||
@ -562,8 +559,8 @@ OutgoingMessage.prototype.end = function (data, encoding) {
|
|||||||
if (this.chunkedEncoding) {
|
if (this.chunkedEncoding) {
|
||||||
var l = Buffer.byteLength(data, encoding).toString(16);
|
var l = Buffer.byteLength(data, encoding).toString(16);
|
||||||
ret = this.connection.write(this._header + l + CRLF +
|
ret = this.connection.write(this._header + l + CRLF +
|
||||||
data + "\r\n0\r\n" +
|
data + '\r\n0\r\n' +
|
||||||
this._trailer + "\r\n", encoding);
|
this._trailer + '\r\n', encoding);
|
||||||
} else {
|
} else {
|
||||||
ret = this.connection.write(this._header + data, encoding);
|
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);
|
OutgoingMessage.call(this, req.socket);
|
||||||
|
|
||||||
if (req.method === 'HEAD') this._hasBody = false;
|
if (req.method === 'HEAD') this._hasBody = false;
|
||||||
@ -612,20 +609,20 @@ util.inherits(ServerResponse, OutgoingMessage);
|
|||||||
exports.ServerResponse = ServerResponse;
|
exports.ServerResponse = ServerResponse;
|
||||||
|
|
||||||
|
|
||||||
ServerResponse.prototype.writeContinue = function () {
|
ServerResponse.prototype.writeContinue = function() {
|
||||||
this._writeRaw("HTTP/1.1 100 Continue" + CRLF + CRLF, 'ascii');
|
this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii');
|
||||||
this._sent100 = true;
|
this._sent100 = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ServerResponse.prototype.writeHead = function (statusCode) {
|
ServerResponse.prototype.writeHead = function(statusCode) {
|
||||||
var reasonPhrase, headers, headerIndex;
|
var reasonPhrase, headers, headerIndex;
|
||||||
|
|
||||||
if (typeof arguments[1] == 'string') {
|
if (typeof arguments[1] == 'string') {
|
||||||
reasonPhrase = arguments[1];
|
reasonPhrase = arguments[1];
|
||||||
headerIndex = 2;
|
headerIndex = 2;
|
||||||
} else {
|
} else {
|
||||||
reasonPhrase = STATUS_CODES[statusCode] || "unknown";
|
reasonPhrase = STATUS_CODES[statusCode] || 'unknown';
|
||||||
headerIndex = 1;
|
headerIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,7 +632,7 @@ ServerResponse.prototype.writeHead = function (statusCode) {
|
|||||||
headers = {};
|
headers = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var statusLine = "HTTP/1.1 " + statusCode.toString() + " " +
|
var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' +
|
||||||
reasonPhrase + CRLF;
|
reasonPhrase + CRLF;
|
||||||
|
|
||||||
if (statusCode === 204 || statusCode === 304 ||
|
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);
|
this.writeHead.apply(this, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function ClientRequest (socket, method, url, headers) {
|
function ClientRequest(socket, method, url, headers) {
|
||||||
OutgoingMessage.call(this, socket);
|
OutgoingMessage.call(this, socket);
|
||||||
|
|
||||||
this.method = method = method.toUpperCase();
|
this.method = method = method.toUpperCase();
|
||||||
this.shouldKeepAlive = false;
|
this.shouldKeepAlive = false;
|
||||||
if (method === "GET" || method === "HEAD") {
|
if (method === 'GET' || method === 'HEAD') {
|
||||||
this.useChunkedEncodingByDefault = false;
|
this.useChunkedEncodingByDefault = false;
|
||||||
} else {
|
} else {
|
||||||
this.useChunkedEncodingByDefault = true;
|
this.useChunkedEncodingByDefault = true;
|
||||||
}
|
}
|
||||||
this._last = 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);
|
util.inherits(ClientRequest, OutgoingMessage);
|
||||||
|
|
||||||
@ -688,7 +685,7 @@ util.inherits(ClientRequest, OutgoingMessage);
|
|||||||
exports.ClientRequest = ClientRequest;
|
exports.ClientRequest = ClientRequest;
|
||||||
|
|
||||||
|
|
||||||
function outgoingFlush (socket) {
|
function outgoingFlush(socket) {
|
||||||
// This logic is probably a bit confusing. Let me explain a bit:
|
// 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
|
// In both HTTP servers and clients it is possible to queue up several
|
||||||
@ -700,7 +697,7 @@ function outgoingFlush (socket) {
|
|||||||
//
|
//
|
||||||
// When the user does
|
// 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
|
// 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
|
// 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
|
// An array of outgoing messages for the socket. In pipelined connections
|
||||||
// we need to keep track of the order they were sent.
|
// we need to keep track of the order they were sent.
|
||||||
socket._outgoing = [];
|
socket._outgoing = [];
|
||||||
socket.__destroyOnDrain = false;
|
socket.__destroyOnDrain = false;
|
||||||
|
|
||||||
// NOTE: be sure not to use ondrain elsewhere in this file!
|
// NOTE: be sure not to use ondrain elsewhere in this file!
|
||||||
socket.ondrain = function () {
|
socket.ondrain = function() {
|
||||||
var message = socket._outgoing[0];
|
var message = socket._outgoing[0];
|
||||||
if (message) message.emit('drain');
|
if (message) message.emit('drain');
|
||||||
if (socket.__destroyOnDrain) socket.destroy();
|
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);
|
if (!(this instanceof Server)) return new Server(requestListener);
|
||||||
net.Server.call(this, { allowHalfOpen: true });
|
net.Server.call(this, { allowHalfOpen: true });
|
||||||
|
|
||||||
if (requestListener) {
|
if (requestListener) {
|
||||||
this.addListener("request", requestListener);
|
this.addListener('request', requestListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addListener("connection", connectionListener);
|
this.addListener('connection', connectionListener);
|
||||||
}
|
}
|
||||||
util.inherits(Server, net.Server);
|
util.inherits(Server, net.Server);
|
||||||
|
|
||||||
@ -762,20 +759,20 @@ util.inherits(Server, net.Server);
|
|||||||
exports.Server = Server;
|
exports.Server = Server;
|
||||||
|
|
||||||
|
|
||||||
exports.createServer = function (requestListener) {
|
exports.createServer = function(requestListener) {
|
||||||
return new Server(requestListener);
|
return new Server(requestListener);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function connectionListener (socket) {
|
function connectionListener(socket) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
debug("SERVER new http connection");
|
debug('SERVER new http connection');
|
||||||
|
|
||||||
httpSocketSetup(socket);
|
httpSocketSetup(socket);
|
||||||
|
|
||||||
socket.setTimeout(2*60*1000); // 2 minute timeout
|
socket.setTimeout(2 * 60 * 1000); // 2 minute timeout
|
||||||
socket.addListener('timeout', function () {
|
socket.addListener('timeout', function() {
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -783,14 +780,14 @@ function connectionListener (socket) {
|
|||||||
parser.reinitialize('request');
|
parser.reinitialize('request');
|
||||||
parser.socket = socket;
|
parser.socket = socket;
|
||||||
|
|
||||||
socket.addListener('error', function (e) {
|
socket.addListener('error', function(e) {
|
||||||
self.emit('clientError', 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);
|
var ret = parser.execute(d, start, end - start);
|
||||||
if (ret instanceof Error) {
|
if (ret instanceof Error) {
|
||||||
debug("parse error");
|
debug('parse error');
|
||||||
socket.destroy(ret);
|
socket.destroy(ret);
|
||||||
} else if (parser.incoming && parser.incoming.upgrade) {
|
} else if (parser.incoming && parser.incoming.upgrade) {
|
||||||
var bytesParsed = ret;
|
var bytesParsed = ret;
|
||||||
@ -803,7 +800,7 @@ function connectionListener (socket) {
|
|||||||
// in the upgradeHead from the closing lines of the headers
|
// in the upgradeHead from the closing lines of the headers
|
||||||
var upgradeHead = d.slice(start + bytesParsed + 1, end);
|
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);
|
self.emit('upgrade', req, req.socket, upgradeHead);
|
||||||
} else {
|
} else {
|
||||||
// Got upgrade header, but have no handler.
|
// Got upgrade header, but have no handler.
|
||||||
@ -812,25 +809,25 @@ function connectionListener (socket) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onend = function () {
|
socket.onend = function() {
|
||||||
parser.finish();
|
parser.finish();
|
||||||
|
|
||||||
if (socket._outgoing.length) {
|
if (socket._outgoing.length) {
|
||||||
socket._outgoing[socket._outgoing.length-1]._last = true;
|
socket._outgoing[socket._outgoing.length - 1]._last = true;
|
||||||
outgoingFlush(socket);
|
outgoingFlush(socket);
|
||||||
} else {
|
} else {
|
||||||
socket.end();
|
socket.end();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.addListener('close', function () {
|
socket.addListener('close', function() {
|
||||||
// unref the parser for easy gc
|
// unref the parser for easy gc
|
||||||
parsers.free(parser);
|
parsers.free(parser);
|
||||||
});
|
});
|
||||||
|
|
||||||
// At the end of each response message, after it has been flushed to the
|
// 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. Here we insert logic about what to do next.
|
||||||
socket._onOutgoingSent = function (message) {
|
socket._onOutgoingSent = function(message) {
|
||||||
var message = socket._outgoing.shift();
|
var message = socket._outgoing.shift();
|
||||||
if (message._last) {
|
if (message._last) {
|
||||||
// No more messages to be pushed out.
|
// 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
|
// 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
|
// new message. In this callback we setup the response object and pass it
|
||||||
// to the user.
|
// to the user.
|
||||||
parser.onIncoming = function (req, shouldKeepAlive) {
|
parser.onIncoming = function(req, shouldKeepAlive) {
|
||||||
var res = new ServerResponse(req);
|
var res = new ServerResponse(req);
|
||||||
debug('server response shouldKeepAlive: ' + shouldKeepAlive);
|
debug('server response shouldKeepAlive: ' + shouldKeepAlive);
|
||||||
res.shouldKeepAlive = shouldKeepAlive;
|
res.shouldKeepAlive = shouldKeepAlive;
|
||||||
@ -861,8 +858,8 @@ function connectionListener (socket) {
|
|||||||
(req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
|
(req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
|
||||||
continueExpression.test(req.headers['expect'])) {
|
continueExpression.test(req.headers['expect'])) {
|
||||||
res._expect_continue = true;
|
res._expect_continue = true;
|
||||||
if (self.listeners("checkContinue").length) {
|
if (self.listeners('checkContinue').length) {
|
||||||
self.emit("checkContinue", req, res);
|
self.emit('checkContinue', req, res);
|
||||||
} else {
|
} else {
|
||||||
res.writeContinue();
|
res.writeContinue();
|
||||||
self.emit('request', req, res);
|
self.emit('request', req, res);
|
||||||
@ -875,7 +872,7 @@ function connectionListener (socket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Client ( ) {
|
function Client() {
|
||||||
if (!(this instanceof Client)) return new Client();
|
if (!(this instanceof Client)) return new Client();
|
||||||
net.Stream.call(this, { allowHalfOpen: true });
|
net.Stream.call(this, { allowHalfOpen: true });
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -890,7 +887,7 @@ function Client ( ) {
|
|||||||
|
|
||||||
function onData(d, start, end) {
|
function onData(d, start, end) {
|
||||||
if (!self.parser) {
|
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);
|
var ret = self.parser.execute(d, start, end - start);
|
||||||
if (ret instanceof Error) {
|
if (ret instanceof Error) {
|
||||||
@ -912,30 +909,29 @@ function Client ( ) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.addListener("connect", function () {
|
self.addListener('connect', function() {
|
||||||
debug('CLIENT connected');
|
debug('CLIENT connected');
|
||||||
|
|
||||||
self.ondata = onData;
|
self.ondata = onData;
|
||||||
self.onend = onEnd;
|
self.onend = onEnd;
|
||||||
|
|
||||||
self._state = "connected";
|
self._state = 'connected';
|
||||||
|
|
||||||
self._initParser();
|
self._initParser();
|
||||||
debug('CLIENT requests: ' + util.inspect(self._outgoing.map(function (r) { return r.method; })));
|
|
||||||
outgoingFlush(self);
|
outgoingFlush(self);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onEnd() {
|
function onEnd() {
|
||||||
if (self.parser) self.parser.finish();
|
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.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
self.addListener("close", function (e) {
|
self.addListener('close', function(e) {
|
||||||
self._state = "disconnected";
|
self._state = 'disconnected';
|
||||||
if (e) return;
|
if (e) return;
|
||||||
|
|
||||||
debug("CLIENT onClose. state = " + self._state);
|
debug('CLIENT onClose. state = ' + self._state);
|
||||||
|
|
||||||
// finally done with the request
|
// finally done with the request
|
||||||
self._outgoing.shift();
|
self._outgoing.shift();
|
||||||
@ -948,14 +944,14 @@ function Client ( ) {
|
|||||||
self.parser = null;
|
self.parser = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
util.inherits(Client, net.Stream);
|
util.inherits(Client, net.Stream);
|
||||||
|
|
||||||
|
|
||||||
exports.Client = Client;
|
exports.Client = Client;
|
||||||
|
|
||||||
|
|
||||||
exports.createClient = function (port, host, https, credentials) {
|
exports.createClient = function(port, host, https, credentials) {
|
||||||
var c = new Client();
|
var c = new Client();
|
||||||
c.port = port;
|
c.port = port;
|
||||||
c.host = host;
|
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;
|
var self = this;
|
||||||
if (!self.parser) self.parser = parsers.alloc();
|
if (!self.parser) self.parser = parsers.alloc();
|
||||||
self.parser.reinitialize('response');
|
self.parser.reinitialize('response');
|
||||||
self.parser.socket = self;
|
self.parser.socket = self;
|
||||||
self.parser.onIncoming = function (res) {
|
self.parser.onIncoming = function(res) {
|
||||||
debug("CLIENT incoming response!");
|
debug('CLIENT incoming response!');
|
||||||
|
|
||||||
var req = self._outgoing[0];
|
var req = self._outgoing[0];
|
||||||
|
|
||||||
// Responses to HEAD requests are AWFUL. Ask Ryan.
|
// Responses to HEAD requests are AWFUL. Ask Ryan.
|
||||||
// A major oversight in HTTP. Hence this nastiness.
|
// A major oversight in HTTP. Hence this nastiness.
|
||||||
var isHeadResponse = req.method == "HEAD";
|
var isHeadResponse = req.method == 'HEAD';
|
||||||
debug('CLIENT isHeadResponse ' + isHeadResponse);
|
debug('CLIENT isHeadResponse ' + isHeadResponse);
|
||||||
|
|
||||||
if (res.statusCode == 100) {
|
if (res.statusCode == 100) {
|
||||||
// restart the parser, as this is a continue message.
|
// restart the parser, as this is a continue message.
|
||||||
req.emit("continue");
|
req.emit('continue');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,8 +986,8 @@ Client.prototype._initParser = function () {
|
|||||||
req.shouldKeepAlive = false;
|
req.shouldKeepAlive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.addListener('end', function ( ) {
|
res.addListener('end', function() {
|
||||||
debug("CLIENT request complete disconnecting. state = " + self._state);
|
debug('CLIENT request complete disconnecting. state = ' + self._state);
|
||||||
// For the moment we reconnect for every request. FIXME!
|
// For the moment we reconnect for every request. FIXME!
|
||||||
// All that should be required for keep-alive is to not reconnect,
|
// All that should be required for keep-alive is to not reconnect,
|
||||||
// but outgoingFlush instead.
|
// but outgoingFlush instead.
|
||||||
@ -1004,7 +1000,7 @@ Client.prototype._initParser = function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
req.emit("response", res);
|
req.emit('response', res);
|
||||||
|
|
||||||
return isHeadResponse;
|
return isHeadResponse;
|
||||||
};
|
};
|
||||||
@ -1022,7 +1018,7 @@ Client.prototype._initParser = function () {
|
|||||||
// each request/response cycle so we cannot shift off the request from
|
// each request/response cycle so we cannot shift off the request from
|
||||||
// client._outgoing until we're completely disconnected after the response
|
// client._outgoing until we're completely disconnected after the response
|
||||||
// comes back.
|
// 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
|
// We've just finished a message. We don't end/shutdown the connection here
|
||||||
// because HTTP servers typically cannot handle half-closed connections
|
// because HTTP servers typically cannot handle half-closed connections
|
||||||
// (Node servers can).
|
// (Node servers can).
|
||||||
@ -1030,27 +1026,27 @@ Client.prototype._onOutgoingSent = function (message) {
|
|||||||
// Instead, we just check if the connection is closed, and if so
|
// Instead, we just check if the connection is closed, and if so
|
||||||
// reconnect if we have pending messages.
|
// reconnect if we have pending messages.
|
||||||
if (this._outgoing.length) {
|
if (this._outgoing.length) {
|
||||||
debug("CLIENT request flush. ensure connection. state = " + this._state);
|
debug('CLIENT request flush. ensure connection. state = ' + this._state);
|
||||||
this._ensureConnection();
|
this._ensureConnection();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Client.prototype._ensureConnection = function () {
|
Client.prototype._ensureConnection = function() {
|
||||||
if (this._state == 'disconnected') {
|
if (this._state == 'disconnected') {
|
||||||
debug("CLIENT reconnecting state = " + this._state);
|
debug('CLIENT reconnecting state = ' + this._state);
|
||||||
this.connect(this.port, this.host);
|
this.connect(this.port, this.host);
|
||||||
this._state = "connecting";
|
this._state = 'connecting';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Client.prototype.request = function (method, url, headers) {
|
Client.prototype.request = function(method, url, headers) {
|
||||||
if (typeof(url) != "string") {
|
if (typeof(url) != 'string') {
|
||||||
// assume method was omitted, shift arguments
|
// assume method was omitted, shift arguments
|
||||||
headers = url;
|
headers = url;
|
||||||
url = method;
|
url = method;
|
||||||
method = "GET";
|
method = 'GET';
|
||||||
}
|
}
|
||||||
var req = new ClientRequest(this, method, url, headers);
|
var req = new ClientRequest(this, method, url, headers);
|
||||||
this._outgoing.push(req);
|
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',
|
var encoding = 'utf8',
|
||||||
headers = {},
|
headers = {},
|
||||||
callback = null;
|
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;
|
var hasHost = false;
|
||||||
if (Array.isArray(headers)) {
|
if (Array.isArray(headers)) {
|
||||||
@ -1093,7 +1089,7 @@ exports.cat = function (url, encoding_, headers_) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (typeof headers === "Object") {
|
} else if (typeof headers === 'Object') {
|
||||||
var keys = Object.keys(headers);
|
var keys = Object.keys(headers);
|
||||||
for (var i = 0, l = keys.length; i < l; i++) {
|
for (var i = 0, l = keys.length; i < l; i++) {
|
||||||
var key = keys[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 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:") {
|
if (url.protocol == 'https:') {
|
||||||
client.https = true;
|
client.https = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var callbackSent = false;
|
var callbackSent = false;
|
||||||
|
|
||||||
req.addListener('response', function (res) {
|
req.addListener('response', function(res) {
|
||||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||||
if (callback && !callbackSent) {
|
if (callback && !callbackSent) {
|
||||||
callback(res.statusCode);
|
callback(res.statusCode);
|
||||||
@ -1126,8 +1125,8 @@ exports.cat = function (url, encoding_, headers_) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.setEncoding(encoding);
|
res.setEncoding(encoding);
|
||||||
res.addListener('data', function (chunk) { content += chunk; });
|
res.addListener('data', function(chunk) { content += chunk; });
|
||||||
res.addListener('end', function () {
|
res.addListener('end', function() {
|
||||||
if (callback && !callbackSent) {
|
if (callback && !callbackSent) {
|
||||||
callback(null, content);
|
callback(null, content);
|
||||||
callbackSent = true;
|
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) {
|
if (callback && !callbackSent) {
|
||||||
callback(err);
|
callback(err);
|
||||||
callbackSent = true;
|
callbackSent = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.addListener("close", function () {
|
client.addListener('close', function() {
|
||||||
if (callback && !callbackSent) {
|
if (callback && !callbackSent) {
|
||||||
callback(new Error('Connection closed unexpectedly'));
|
callback(new Error('Connection closed unexpectedly'));
|
||||||
callbackSent = true;
|
callbackSent = true;
|
||||||
|
259
lib/net.js
259
lib/net.js
@ -1,14 +1,19 @@
|
|||||||
var util = require("util");
|
var util = require('util');
|
||||||
var events = require("events");
|
var events = require('events');
|
||||||
var stream = require("stream");
|
var stream = require('stream');
|
||||||
|
|
||||||
var kMinPoolSpace = 128;
|
var kMinPoolSpace = 128;
|
||||||
var kPoolSize = 40*1024;
|
var kPoolSize = 40 * 1024;
|
||||||
|
|
||||||
var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
|
var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
|
||||||
function debug () {
|
var debug;
|
||||||
if (debugLevel & 0x2) util.error.apply(this, arguments);
|
if (debugLevel & 0x2) {
|
||||||
|
debug = function(x) { util.error.apply(this, arguments); };
|
||||||
|
} else {
|
||||||
|
debug = function() { };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var binding = process.binding('net');
|
var binding = process.binding('net');
|
||||||
|
|
||||||
// Note about Buffer interface:
|
// Note about Buffer interface:
|
||||||
@ -21,49 +26,49 @@ var binding = process.binding('net');
|
|||||||
// waiting to implement it until I get more experience with this.
|
// waiting to implement it until I get more experience with this.
|
||||||
var FreeList = require('freelist').FreeList;
|
var FreeList = require('freelist').FreeList;
|
||||||
|
|
||||||
var IOWatcher = process.binding('io_watcher').IOWatcher;
|
var IOWatcher = process.binding('io_watcher').IOWatcher;
|
||||||
var constants = process.binding('constants');
|
var constants = process.binding('constants');
|
||||||
var assert = process.assert;
|
var assert = process.assert;
|
||||||
|
|
||||||
var socket = binding.socket;
|
var socket = binding.socket;
|
||||||
var bind = binding.bind;
|
var bind = binding.bind;
|
||||||
var connect = binding.connect;
|
var connect = binding.connect;
|
||||||
var listen = binding.listen;
|
var listen = binding.listen;
|
||||||
var accept = binding.accept;
|
var accept = binding.accept;
|
||||||
var close = binding.close;
|
var close = binding.close;
|
||||||
var shutdown = binding.shutdown;
|
var shutdown = binding.shutdown;
|
||||||
var read = binding.read;
|
var read = binding.read;
|
||||||
var write = binding.write;
|
var write = binding.write;
|
||||||
var toRead = binding.toRead;
|
var toRead = binding.toRead;
|
||||||
var setNoDelay = binding.setNoDelay;
|
var setNoDelay = binding.setNoDelay;
|
||||||
var setKeepAlive= binding.setKeepAlive;
|
var setKeepAlive = binding.setKeepAlive;
|
||||||
var socketError = binding.socketError;
|
var socketError = binding.socketError;
|
||||||
var getsockname = binding.getsockname;
|
var getsockname = binding.getsockname;
|
||||||
var errnoException = binding.errnoException;
|
var errnoException = binding.errnoException;
|
||||||
var sendMsg = binding.sendMsg;
|
var sendMsg = binding.sendMsg;
|
||||||
var recvMsg = binding.recvMsg;
|
var recvMsg = binding.recvMsg;
|
||||||
|
|
||||||
var EINPROGRESS = constants.EINPROGRESS;
|
var EINPROGRESS = constants.EINPROGRESS;
|
||||||
var ENOENT = constants.ENOENT;
|
var ENOENT = constants.ENOENT;
|
||||||
var EMFILE = constants.EMFILE;
|
var EMFILE = constants.EMFILE;
|
||||||
|
|
||||||
var END_OF_FILE = 42;
|
var END_OF_FILE = 42;
|
||||||
|
|
||||||
|
|
||||||
var ioWatchers = new FreeList("iowatcher", 100, function () {
|
var ioWatchers = new FreeList('iowatcher', 100, function() {
|
||||||
return new IOWatcher();
|
return new IOWatcher();
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.isIP = binding.isIP;
|
exports.isIP = binding.isIP;
|
||||||
|
|
||||||
exports.isIPv4 = function (input) {
|
exports.isIPv4 = function(input) {
|
||||||
if (binding.isIP(input) === 4) {
|
if (binding.isIP(input) === 4) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.isIPv6 = function (input) {
|
exports.isIPv6 = function(input) {
|
||||||
if (binding.isIP(input) === 6) {
|
if (binding.isIP(input) === 6) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -72,19 +77,19 @@ exports.isIPv6 = function (input) {
|
|||||||
|
|
||||||
// Allocated on demand.
|
// Allocated on demand.
|
||||||
var pool = null;
|
var pool = null;
|
||||||
function allocNewPool () {
|
function allocNewPool() {
|
||||||
pool = new Buffer(kPoolSize);
|
pool = new Buffer(kPoolSize);
|
||||||
pool.used = 0;
|
pool.used = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var emptyBuffer = null;
|
var emptyBuffer = null;
|
||||||
function allocEmptyBuffer () {
|
function allocEmptyBuffer() {
|
||||||
emptyBuffer = new Buffer(1);
|
emptyBuffer = new Buffer(1);
|
||||||
emptyBuffer.sent = 0;
|
emptyBuffer.sent = 0;
|
||||||
emptyBuffer.length = 0;
|
emptyBuffer.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setImplmentationMethods (self) {
|
function setImplmentationMethods(self) {
|
||||||
function noData(buf, off, len) {
|
function noData(buf, off, len) {
|
||||||
return !buf ||
|
return !buf ||
|
||||||
(off != undefined && off >= buf.length) ||
|
(off != undefined && off >= buf.length) ||
|
||||||
@ -92,7 +97,7 @@ function setImplmentationMethods (self) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (self.type == 'unix') {
|
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
|
// Detect and disallow zero-byte writes wth an attached file
|
||||||
// descriptor. This is an implementation limitation of sendmsg(2).
|
// descriptor. This is an implementation limitation of sendmsg(2).
|
||||||
if (fd && noData(buf, off, len)) {
|
if (fd && noData(buf, off, len)) {
|
||||||
@ -102,7 +107,7 @@ function setImplmentationMethods (self) {
|
|||||||
return sendMsg(self.fd, buf, off, len, fd, flags);
|
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);
|
var bytesRead = recvMsg(self.fd, buf, off, len);
|
||||||
|
|
||||||
// Do not emit this in the same stack, otherwise we risk corrupting our
|
// 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).
|
// emitting null (or another FD).
|
||||||
if (recvMsg.fd !== null) {
|
if (recvMsg.fd !== null) {
|
||||||
var fd = recvMsg.fd;
|
var fd = recvMsg.fd;
|
||||||
process.nextTick(function () {
|
process.nextTick(function() {
|
||||||
self.emit('fd', fd);
|
self.emit('fd', fd);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -123,7 +128,7 @@ function setImplmentationMethods (self) {
|
|||||||
return bytesRead;
|
return bytesRead;
|
||||||
};
|
};
|
||||||
} else {
|
} 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
|
// XXX: TLS support requires that 0-byte writes get processed
|
||||||
// by the kernel for some reason. Otherwise, we'd just
|
// by the kernel for some reason. Otherwise, we'd just
|
||||||
// fast-path return here.
|
// fast-path return here.
|
||||||
@ -133,26 +138,26 @@ function setImplmentationMethods (self) {
|
|||||||
return write(self.fd, buf, off, len);
|
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);
|
return read(self.fd, buf, off, len);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
self._shutdownImpl = function () {
|
self._shutdownImpl = function() {
|
||||||
shutdown(self.fd, 'write');
|
shutdown(self.fd, 'write');
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
function onReadable (readable, writable) {
|
function onReadable(readable, writable) {
|
||||||
assert(this.socket);
|
assert(this.socket);
|
||||||
var socket = this.socket;
|
var socket = this.socket;
|
||||||
socket._onReadable();
|
socket._onReadable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function onWritable (readable, writable) {
|
function onWritable(readable, writable) {
|
||||||
assert(this.socket);
|
assert(this.socket);
|
||||||
var socket = this.socket;
|
var socket = this.socket;
|
||||||
if (socket._connecting) {
|
if (socket._connecting) {
|
||||||
@ -163,7 +168,7 @@ function onWritable (readable, writable) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initStream (self) {
|
function initStream(self) {
|
||||||
self._readWatcher = ioWatchers.alloc();
|
self._readWatcher = ioWatchers.alloc();
|
||||||
self._readWatcher.socket = self;
|
self._readWatcher.socket = self;
|
||||||
self._readWatcher.callback = onReadable;
|
self._readWatcher.callback = onReadable;
|
||||||
@ -181,8 +186,8 @@ function initStream (self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated API: Stream(fd, type)
|
// Deprecated API: Stream(fd, type)
|
||||||
// New API: Stream({ fd: 10, type: "unix", allowHalfOpen: true })
|
// New API: Stream({ fd: 10, type: 'unix', allowHalfOpen: true })
|
||||||
function Stream (options) {
|
function Stream(options) {
|
||||||
if (!(this instanceof Stream)) return new Stream(arguments[0], arguments[1]);
|
if (!(this instanceof Stream)) return new Stream(arguments[0], arguments[1]);
|
||||||
stream.Stream.call(this);
|
stream.Stream.call(this);
|
||||||
|
|
||||||
@ -190,11 +195,11 @@ function Stream (options) {
|
|||||||
this.type = null;
|
this.type = null;
|
||||||
this.allowHalfOpen = false;
|
this.allowHalfOpen = false;
|
||||||
|
|
||||||
if (typeof options == "object") {
|
if (typeof options == 'object') {
|
||||||
this.fd = options.fd !== undefined ? parseInt(options.fd, 10) : null;
|
this.fd = options.fd !== undefined ? parseInt(options.fd, 10) : null;
|
||||||
this.type = options.type || null;
|
this.type = options.type || null;
|
||||||
this.allowHalfOpen = options.allowHalfOpen || false;
|
this.allowHalfOpen = options.allowHalfOpen || false;
|
||||||
} else if (typeof options == "number") {
|
} else if (typeof options == 'number') {
|
||||||
this.fd = arguments[0];
|
this.fd = arguments[0];
|
||||||
this.type = arguments[1];
|
this.type = arguments[1];
|
||||||
}
|
}
|
||||||
@ -204,17 +209,17 @@ function Stream (options) {
|
|||||||
} else {
|
} else {
|
||||||
setImplmentationMethods(this);
|
setImplmentationMethods(this);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
util.inherits(Stream, stream.Stream);
|
util.inherits(Stream, stream.Stream);
|
||||||
exports.Stream = Stream;
|
exports.Stream = Stream;
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype._onTimeout = function () {
|
Stream.prototype._onTimeout = function() {
|
||||||
this.emit('timeout');
|
this.emit('timeout');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype.open = function (fd, type) {
|
Stream.prototype.open = function(fd, type) {
|
||||||
initStream(this);
|
initStream(this);
|
||||||
|
|
||||||
this.fd = fd;
|
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();
|
var s = new Stream();
|
||||||
s.connect(port, host);
|
s.connect(port, host);
|
||||||
return s;
|
return s;
|
||||||
@ -236,16 +241,16 @@ exports.createConnection = function (port, host) {
|
|||||||
|
|
||||||
|
|
||||||
Object.defineProperty(Stream.prototype, 'readyState', {
|
Object.defineProperty(Stream.prototype, 'readyState', {
|
||||||
get: function () {
|
get: function() {
|
||||||
if (this._connecting) {
|
if (this._connecting) {
|
||||||
return 'opening';
|
return 'opening';
|
||||||
} else if (this.readable && this.writable) {
|
} else if (this.readable && this.writable) {
|
||||||
assert(typeof this.fd == 'number');
|
assert(typeof this.fd == 'number');
|
||||||
return 'open';
|
return 'open';
|
||||||
} else if (this.readable && !this.writable){
|
} else if (this.readable && !this.writable) {
|
||||||
assert(typeof this.fd == 'number');
|
assert(typeof this.fd == 'number');
|
||||||
return 'readOnly';
|
return 'readOnly';
|
||||||
} else if (!this.readable && this.writable){
|
} else if (!this.readable && this.writable) {
|
||||||
assert(typeof this.fd == 'number');
|
assert(typeof this.fd == 'number');
|
||||||
return 'writeOnly';
|
return 'writeOnly';
|
||||||
} else {
|
} else {
|
||||||
@ -257,9 +262,9 @@ Object.defineProperty(Stream.prototype, 'readyState', {
|
|||||||
|
|
||||||
|
|
||||||
// Returns true if all the data was flushed to socket. Returns false if
|
// 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.
|
// 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._connecting || (this._writeQueue && this._writeQueue.length)) {
|
||||||
if (!this._writeQueue) {
|
if (!this._writeQueue) {
|
||||||
this._writeQueue = [];
|
this._writeQueue = [];
|
||||||
@ -272,12 +277,14 @@ Stream.prototype.write = function (data, encoding, fd) {
|
|||||||
throw new Error('Stream.end() called already; cannot write.');
|
throw new Error('Stream.end() called already; cannot write.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var last = this._writeQueue.length - 1;
|
||||||
|
|
||||||
if (typeof data == 'string' &&
|
if (typeof data == 'string' &&
|
||||||
this._writeQueue.length &&
|
this._writeQueue.length &&
|
||||||
typeof this._writeQueue[this._writeQueue.length-1] === 'string' &&
|
typeof this._writeQueue[last] === 'string' &&
|
||||||
this._writeQueueEncoding[this._writeQueueEncoding.length-1] === encoding) {
|
this._writeQueueEncoding[last] === encoding) {
|
||||||
// optimization - concat onto last
|
// optimization - concat onto last
|
||||||
this._writeQueue[this._writeQueue.length-1] += data;
|
this._writeQueue[last] += data;
|
||||||
} else {
|
} else {
|
||||||
this._writeQueue.push(data);
|
this._writeQueue.push(data);
|
||||||
this._writeQueueEncoding.push(encoding);
|
this._writeQueueEncoding.push(encoding);
|
||||||
@ -304,7 +311,7 @@ Stream.prototype.write = function (data, encoding, fd) {
|
|||||||
// 2. Write data to socket. Return true if flushed.
|
// 2. Write data to socket. Return true if flushed.
|
||||||
// 3. Slice out remaining
|
// 3. Slice out remaining
|
||||||
// 4. Unshift remaining onto _writeQueue. Return false.
|
// 4. Unshift remaining onto _writeQueue. Return false.
|
||||||
Stream.prototype._writeOut = function (data, encoding, fd) {
|
Stream.prototype._writeOut = function(data, encoding, fd) {
|
||||||
if (!this.writable) {
|
if (!this.writable) {
|
||||||
throw new Error('Stream is not 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');
|
debug('wrote ' + bytesWritten + ' bytes to pool');
|
||||||
|
|
||||||
if (charsWritten != data.length) {
|
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
|
// Unshift whatever didn't fit onto the buffer
|
||||||
this._writeQueue.unshift(data.slice(charsWritten));
|
this._writeQueue.unshift(data.slice(charsWritten));
|
||||||
this._writeQueueEncoding.unshift(encoding);
|
this._writeQueueEncoding.unshift(encoding);
|
||||||
@ -365,7 +374,8 @@ Stream.prototype._writeOut = function (data, encoding, fd) {
|
|||||||
return false;
|
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);
|
require('timers').active(this);
|
||||||
|
|
||||||
@ -410,7 +420,7 @@ Stream.prototype._writeOut = function (data, encoding, fd) {
|
|||||||
|
|
||||||
// Flushes the write buffer out.
|
// Flushes the write buffer out.
|
||||||
// Returns true if the entire buffer was flushed.
|
// Returns true if the entire buffer was flushed.
|
||||||
Stream.prototype.flush = function () {
|
Stream.prototype.flush = function() {
|
||||||
while (this._writeQueue && this._writeQueue.length) {
|
while (this._writeQueue && this._writeQueue.length) {
|
||||||
var data = this._writeQueue.shift();
|
var data = this._writeQueue.shift();
|
||||||
var encoding = this._writeQueueEncoding.shift();
|
var encoding = this._writeQueueEncoding.shift();
|
||||||
@ -421,7 +431,7 @@ Stream.prototype.flush = function () {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var flushed = this._writeOut(data,encoding,fd);
|
var flushed = this._writeOut(data, encoding, fd);
|
||||||
if (!flushed) return false;
|
if (!flushed) return false;
|
||||||
}
|
}
|
||||||
if (this._writeWatcher) this._writeWatcher.stop();
|
if (this._writeWatcher) this._writeWatcher.stop();
|
||||||
@ -429,19 +439,19 @@ Stream.prototype.flush = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype._writeQueueLast = function () {
|
Stream.prototype._writeQueueLast = function() {
|
||||||
return this._writeQueue.length > 0 ? this._writeQueue[this._writeQueue.length-1]
|
return this._writeQueue.length > 0 ?
|
||||||
: null;
|
this._writeQueue[this._writeQueue.length - 1] : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype.setEncoding = function (encoding) {
|
Stream.prototype.setEncoding = function(encoding) {
|
||||||
var StringDecoder = require("string_decoder").StringDecoder; // lazy load
|
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
|
||||||
this._decoder = new StringDecoder(encoding);
|
this._decoder = new StringDecoder(encoding);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function doConnect (socket, port, host) {
|
function doConnect(socket, port, host) {
|
||||||
try {
|
try {
|
||||||
connect(socket.fd, port, host);
|
connect(socket.fd, port, host);
|
||||||
} catch (e) {
|
} 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);
|
var errno = socketError(this.fd);
|
||||||
if (errno == 0) {
|
if (errno == 0) {
|
||||||
// connection established
|
// 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
|
// Stream becomes writable on connect() but don't flush if there's
|
||||||
// nothing actually to write
|
// nothing actually to write
|
||||||
if (this.flush()) {
|
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
|
if (this.ondrain) this.ondrain(); // Optimization
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype._onReadable = function () {
|
Stream.prototype._onReadable = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// If this is the first recv (pool doesn't exist) or we've used up
|
// 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) - TCP connect to port 80 on the localhost
|
||||||
// stream.connect(80, 'nodejs.org') - TCP connect to port 80 on nodejs.org
|
// 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.connect('/tmp/socket') - UNIX connect to socket specified by path
|
||||||
Stream.prototype.connect = function () {
|
Stream.prototype.connect = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
initStream(self);
|
initStream(self);
|
||||||
if (self.fd) throw new Error('Stream already opened');
|
if (self.fd) throw new Error('Stream already opened');
|
||||||
@ -593,7 +603,7 @@ Stream.prototype.connect = function () {
|
|||||||
doConnect(self, arguments[0]);
|
doConnect(self, arguments[0]);
|
||||||
} else {
|
} else {
|
||||||
// TCP
|
// TCP
|
||||||
require('dns').lookup(arguments[1], function (err, ip, addressType) {
|
require('dns').lookup(arguments[1], function(err, ip, addressType) {
|
||||||
if (err) {
|
if (err) {
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
} else {
|
} else {
|
||||||
@ -606,25 +616,25 @@ Stream.prototype.connect = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype.address = function () {
|
Stream.prototype.address = function() {
|
||||||
return getsockname(this.fd);
|
return getsockname(this.fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype.setNoDelay = function (v) {
|
Stream.prototype.setNoDelay = function(v) {
|
||||||
if ((this.type == 'tcp4')||(this.type == 'tcp6')) {
|
if ((this.type == 'tcp4') || (this.type == 'tcp6')) {
|
||||||
setNoDelay(this.fd, v);
|
setNoDelay(this.fd, v);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Stream.prototype.setKeepAlive = function (enable, time) {
|
Stream.prototype.setKeepAlive = function(enable, time) {
|
||||||
if ((this.type == 'tcp4')||(this.type == 'tcp6')) {
|
if ((this.type == 'tcp4') || (this.type == 'tcp6')) {
|
||||||
var secondDelay = Math.ceil(time/1000);
|
var secondDelay = Math.ceil(time / 1000);
|
||||||
setKeepAlive(this.fd, enable, secondDelay);
|
setKeepAlive(this.fd, enable, secondDelay);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Stream.prototype.setTimeout = function (msecs) {
|
Stream.prototype.setTimeout = function(msecs) {
|
||||||
if (msecs > 0) {
|
if (msecs > 0) {
|
||||||
require('timers').enroll(this, msecs);
|
require('timers').enroll(this, msecs);
|
||||||
if (this.fd) { require('timers').active(this); }
|
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();
|
this._readWatcher.stop();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Stream.prototype.resume = function () {
|
Stream.prototype.resume = function() {
|
||||||
if (this.fd === null) throw new Error('Cannot resume() closed Stream.');
|
if (this.fd === null) throw new Error('Cannot resume() closed Stream.');
|
||||||
this._readWatcher.stop();
|
this._readWatcher.stop();
|
||||||
this._readWatcher.set(this.fd, true, false);
|
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.
|
// pool is shared between sockets, so don't need to free it here.
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -681,7 +691,7 @@ Stream.prototype.destroy = function (exception) {
|
|||||||
if (typeof this.fd == 'number') {
|
if (typeof this.fd == 'number') {
|
||||||
close(this.fd);
|
close(this.fd);
|
||||||
this.fd = null;
|
this.fd = null;
|
||||||
process.nextTick(function () {
|
process.nextTick(function() {
|
||||||
if (exception) self.emit('error', exception);
|
if (exception) self.emit('error', exception);
|
||||||
self.emit('close', exception ? true : false);
|
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) {
|
if (!this.writable) {
|
||||||
throw new Error('The connection is not writable');
|
throw new Error('The connection is not writable');
|
||||||
} else {
|
} 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.writable) {
|
||||||
if (this._writeQueueLast() !== END_OF_FILE) {
|
if (this._writeQueueLast() !== END_OF_FILE) {
|
||||||
if (data) this.write(data, encoding);
|
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]);
|
if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]);
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var options = {};
|
var options = {};
|
||||||
if (typeof arguments[0] == "object") {
|
if (typeof arguments[0] == 'object') {
|
||||||
options = arguments[0];
|
options = arguments[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// listener: find the last argument that is a function
|
// listener: find the last argument that is a function
|
||||||
for (var l = arguments.length - 1; l >= 0; l--) {
|
for (var l = arguments.length - 1; l >= 0; l--) {
|
||||||
if (typeof arguments[l] == "function") {
|
if (typeof arguments[l] == 'function') {
|
||||||
self.addListener('connection', arguments[l]);
|
self.addListener('connection', arguments[l]);
|
||||||
}
|
}
|
||||||
if (arguments[l] !== undefined) break;
|
if (arguments[l] !== undefined) break;
|
||||||
@ -748,7 +758,7 @@ function Server (/* [ options, ] listener */) {
|
|||||||
|
|
||||||
self.watcher = new IOWatcher();
|
self.watcher = new IOWatcher();
|
||||||
self.watcher.host = self;
|
self.watcher.host = self;
|
||||||
self.watcher.callback = function () {
|
self.watcher.callback = function() {
|
||||||
// Just in case we don't have a dummy fd.
|
// Just in case we don't have a dummy fd.
|
||||||
getDummyFD();
|
getDummyFD();
|
||||||
|
|
||||||
@ -783,9 +793,10 @@ function Server (/* [ options, ] listener */) {
|
|||||||
|
|
||||||
self.connections++;
|
self.connections++;
|
||||||
|
|
||||||
var s = new Stream({ fd: peerInfo.fd,
|
var options = { fd: peerInfo.fd,
|
||||||
type: self.type,
|
type: self.type,
|
||||||
allowHalfOpen: self.allowHalfOpen });
|
allowHalfOpen: self.allowHalfOpen };
|
||||||
|
var s = new Stream(options);
|
||||||
s.remoteAddress = peerInfo.address;
|
s.remoteAddress = peerInfo.address;
|
||||||
s.remotePort = peerInfo.port;
|
s.remotePort = peerInfo.port;
|
||||||
s.type = self.type;
|
s.type = self.type;
|
||||||
@ -809,14 +820,14 @@ util.inherits(Server, events.EventEmitter);
|
|||||||
exports.Server = Server;
|
exports.Server = Server;
|
||||||
|
|
||||||
|
|
||||||
exports.createServer = function () {
|
exports.createServer = function() {
|
||||||
return new Server(arguments[0], arguments[1]);
|
return new Server(arguments[0], arguments[1]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Just stop trying to accepting connections for a while.
|
// Just stop trying to accepting connections for a while.
|
||||||
// Useful for throttling against DoS attacks.
|
// Useful for throttling against DoS attacks.
|
||||||
Server.prototype.pause = function (msecs) {
|
Server.prototype.pause = function(msecs) {
|
||||||
// We're already paused.
|
// We're already paused.
|
||||||
if (this._pauseTimer) return;
|
if (this._pauseTimer) return;
|
||||||
|
|
||||||
@ -826,7 +837,7 @@ Server.prototype.pause = function (msecs) {
|
|||||||
this.watcher.stop();
|
this.watcher.stop();
|
||||||
|
|
||||||
// Wait a second before accepting more.
|
// 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
|
// Our fd should still be there. If someone calls server.close() then
|
||||||
// the pauseTimer should be cleared.
|
// the pauseTimer should be cleared.
|
||||||
assert(parseInt(self.fd) >= 0);
|
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 self = this;
|
||||||
var acceptCount = 0;
|
var acceptCount = 0;
|
||||||
// Accept and close the waiting clients one at a time.
|
// Accept and close the waiting clients one at a time.
|
||||||
@ -856,14 +867,14 @@ Server.prototype._rejectPending = function () {
|
|||||||
|
|
||||||
|
|
||||||
// Listen on a UNIX socket
|
// Listen on a UNIX socket
|
||||||
// server.listen("/tmp/socket");
|
// server.listen('/tmp/socket');
|
||||||
//
|
//
|
||||||
// Listen on port 8000, accept connections from INADDR_ANY.
|
// Listen on port 8000, accept connections from INADDR_ANY.
|
||||||
// server.listen(8000);
|
// server.listen(8000);
|
||||||
//
|
//
|
||||||
// Listen on port 8000, accept connections to "192.168.1.2"
|
// Listen on port 8000, accept connections to '192.168.1.2'
|
||||||
// server.listen(8000, "192.168.1.2");
|
// server.listen(8000, '192.168.1.2');
|
||||||
Server.prototype.listen = function () {
|
Server.prototype.listen = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (self.fd) throw new Error('Server already opened');
|
if (self.fd) throw new Error('Server already opened');
|
||||||
|
|
||||||
@ -887,7 +898,7 @@ Server.prototype.listen = function () {
|
|||||||
var path = arguments[0];
|
var path = arguments[0];
|
||||||
self.path = path;
|
self.path = path;
|
||||||
// unlink sockfile if it exists
|
// unlink sockfile if it exists
|
||||||
require('fs').stat(path, function (err, r) {
|
require('fs').stat(path, function(err, r) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.errno == ENOENT) {
|
if (err.errno == ENOENT) {
|
||||||
self._doListen(path);
|
self._doListen(path);
|
||||||
@ -896,9 +907,9 @@ Server.prototype.listen = function () {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!r.isSocket()) {
|
if (!r.isSocket()) {
|
||||||
throw new Error("Non-socket exists at " + path);
|
throw new Error('Non-socket exists at ' + path);
|
||||||
} else {
|
} else {
|
||||||
require('fs').unlink(path, function (err) {
|
require('fs').unlink(path, function(err) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
self._doListen(path);
|
self._doListen(path);
|
||||||
});
|
});
|
||||||
@ -907,7 +918,7 @@ Server.prototype.listen = function () {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// the first argument is the port, the second an IP
|
// 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) {
|
if (err) {
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
} else {
|
} else {
|
||||||
@ -919,7 +930,7 @@ Server.prototype.listen = function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype.listenFD = function (fd, type) {
|
Server.prototype.listenFD = function(fd, type) {
|
||||||
if (this.fd) {
|
if (this.fd) {
|
||||||
throw new Error('Server already opened');
|
throw new Error('Server already opened');
|
||||||
}
|
}
|
||||||
@ -929,13 +940,13 @@ Server.prototype.listenFD = function (fd, type) {
|
|||||||
this._startWatcher();
|
this._startWatcher();
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype._startWatcher = function () {
|
Server.prototype._startWatcher = function() {
|
||||||
this.watcher.set(this.fd, true, false);
|
this.watcher.set(this.fd, true, false);
|
||||||
this.watcher.start();
|
this.watcher.start();
|
||||||
this.emit("listening");
|
this.emit('listening');
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype._doListen = function () {
|
Server.prototype._doListen = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Ensure we have a dummy fd for EMFILE conditions.
|
// 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
|
// Need to the listening in the nextTick so that people potentially have
|
||||||
// time to register 'listening' listeners.
|
// time to register 'listening' listeners.
|
||||||
process.nextTick(function () {
|
process.nextTick(function() {
|
||||||
// It could be that server.close() was called between the time the
|
// 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.
|
// original listen command was issued and this. Bail if that's the case.
|
||||||
// See test/simple/test-net-eaddrinuse.js
|
// See test/simple/test-net-eaddrinuse.js
|
||||||
@ -965,15 +976,15 @@ Server.prototype._doListen = function () {
|
|||||||
|
|
||||||
self._startWatcher();
|
self._startWatcher();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
Server.prototype.address = function () {
|
Server.prototype.address = function() {
|
||||||
return getsockname(this.fd);
|
return getsockname(this.fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Server.prototype.close = function () {
|
Server.prototype.close = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!self.fd) throw new Error('Not running');
|
if (!self.fd) throw new Error('Not running');
|
||||||
|
|
||||||
@ -987,12 +998,12 @@ Server.prototype.close = function () {
|
|||||||
self._pauseTimer = null;
|
self._pauseTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.type === "unix") {
|
if (self.type === 'unix') {
|
||||||
require('fs').unlink(self.path, function () {
|
require('fs').unlink(self.path, function() {
|
||||||
self.emit("close");
|
self.emit('close');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.emit("close");
|
self.emit('close');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1005,7 +1016,7 @@ function rescueEMFILE(callback) {
|
|||||||
// Output a warning, but only at most every 5 seconds.
|
// Output a warning, but only at most every 5 seconds.
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
if (now - lastEMFILEWarning > 5000) {
|
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;
|
lastEMFILEWarning = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,7 +1031,7 @@ function rescueEMFILE(callback) {
|
|||||||
function getDummyFD() {
|
function getDummyFD() {
|
||||||
if (!dummyFD) {
|
if (!dummyFD) {
|
||||||
try {
|
try {
|
||||||
dummyFD = socket("tcp");
|
dummyFD = socket('tcp');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dummyFD = null;
|
dummyFD = null;
|
||||||
}
|
}
|
||||||
|
82
lib/path.js
82
lib/path.js
@ -1,28 +1,28 @@
|
|||||||
function validPathPart (p, keepBlanks) {
|
function validPathPart(p, keepBlanks) {
|
||||||
return typeof p === "string" && (p || keepBlanks);
|
return typeof p === 'string' && (p || keepBlanks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.join = function () {
|
exports.join = function() {
|
||||||
var args = Array.prototype.slice.call(arguments);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
// edge case flag to switch into url-resolve-mode
|
// edge case flag to switch into url-resolve-mode
|
||||||
var keepBlanks = false;
|
var keepBlanks = false;
|
||||||
if (args[ args.length - 1 ] === true) {
|
if (args[args.length - 1] === true) {
|
||||||
keepBlanks = args.pop();
|
keepBlanks = args.pop();
|
||||||
}
|
}
|
||||||
// return exports.split(args.join("/"), keepBlanks).join("/");
|
// return exports.split(args.join("/"), keepBlanks).join("/");
|
||||||
var joined = exports.normalizeArray(args, keepBlanks).join("/");
|
var joined = exports.normalizeArray(args, keepBlanks).join('/');
|
||||||
return joined;
|
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.
|
// split based on /, but only if that / is not at the start or end.
|
||||||
return exports.normalizeArray(path.split(/^|\/(?!$)/), keepBlanks);
|
return exports.normalizeArray(path.split(/^|\/(?!$)/), keepBlanks);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function cleanArray (parts, keepBlanks) {
|
function cleanArray(parts, keepBlanks) {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var l = parts.length - 1;
|
var l = parts.length - 1;
|
||||||
var stripped = false;
|
var stripped = false;
|
||||||
@ -30,13 +30,13 @@ function cleanArray (parts, keepBlanks) {
|
|||||||
// strip leading empty args
|
// strip leading empty args
|
||||||
while (i < l && !validPathPart(parts[i], keepBlanks)) {
|
while (i < l && !validPathPart(parts[i], keepBlanks)) {
|
||||||
stripped = true;
|
stripped = true;
|
||||||
i ++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip tailing empty args
|
// strip tailing empty args
|
||||||
while (l >= i && !validPathPart(parts[l], keepBlanks)) {
|
while (l >= i && !validPathPart(parts[l], keepBlanks)) {
|
||||||
stripped = true;
|
stripped = true;
|
||||||
l --;
|
l--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stripped) {
|
if (stripped) {
|
||||||
@ -44,52 +44,46 @@ function cleanArray (parts, keepBlanks) {
|
|||||||
parts = Array.prototype.slice.call(parts, i, l + 1);
|
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('/')
|
.join('/')
|
||||||
.split(/^|\/(?!$)/);
|
.split(/^|\/(?!$)/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.normalizeArray = function (original, keepBlanks) {
|
exports.normalizeArray = function(original, keepBlanks) {
|
||||||
var parts = cleanArray(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.
|
// now we're fully ready to rock.
|
||||||
// leading/trailing invalids have been stripped off.
|
// leading/trailing invalids have been stripped off.
|
||||||
// if it comes in starting with a slash, or ending with a slash,
|
// 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);
|
if (leadingSlash) parts[0] = parts[0].substr(1);
|
||||||
var last = parts.slice(-1)[0];
|
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);
|
if (tailingSlash) parts[parts.length - 1] = last.slice(0, -1);
|
||||||
var directories = [];
|
var directories = [];
|
||||||
var prev;
|
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];
|
var directory = parts[i];
|
||||||
|
|
||||||
// if it's blank, and we're not keeping blanks, then skip it.
|
// 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 it's a dot, then skip it
|
||||||
if (directory === "." && (
|
if (directory === '.' && (directories.length ||
|
||||||
directories.length
|
(i === 0 && !(tailingSlash && i === l)) ||
|
||||||
|| (i === 0 && !(tailingSlash && i === l))
|
(i === 0 && leadingSlash))) continue;
|
||||||
|| (i === 0 && leadingSlash)
|
|
||||||
)) continue;
|
|
||||||
|
|
||||||
// if we're dealing with an absolute path, then discard ..s that go
|
// if we're dealing with an absolute path, then discard ..s that go
|
||||||
// above that the base.
|
// above that the base.
|
||||||
if (leadingSlash && directories.length === 0 && directory === "..") {
|
if (leadingSlash && directories.length === 0 && directory === '..') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// trying to go up a dir
|
// trying to go up a dir
|
||||||
if (
|
if (directory === '..' && directories.length && prev !== '..' &&
|
||||||
directory === ".."
|
prev !== undefined) {
|
||||||
&& directories.length
|
|
||||||
&& prev !== ".."
|
|
||||||
&& prev !== undefined
|
|
||||||
) {
|
|
||||||
directories.pop();
|
directories.pop();
|
||||||
prev = directories.slice(-1)[0];
|
prev = directories.slice(-1)[0];
|
||||||
} else {
|
} else {
|
||||||
@ -98,27 +92,27 @@ exports.normalizeArray = function (original, keepBlanks) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!directories.length) {
|
if (!directories.length) {
|
||||||
directories = [ leadingSlash || tailingSlash ? "" : "." ];
|
directories = [leadingSlash || tailingSlash ? '' : '.'];
|
||||||
}
|
}
|
||||||
var last = directories.slice(-1)[0];
|
var last = directories.slice(-1)[0];
|
||||||
if (tailingSlash && last.substr(-1) !== "/") {
|
if (tailingSlash && last.substr(-1) !== '/') {
|
||||||
directories[directories.length-1] += "/";
|
directories[directories.length - 1] += '/';
|
||||||
}
|
}
|
||||||
if (leadingSlash && directories[0].charAt(0) !== "/") {
|
if (leadingSlash && directories[0].charAt(0) !== '/') {
|
||||||
if (directories[0] === ".") directories[0] = "";
|
if (directories[0] === '.') directories[0] = '';
|
||||||
directories[0] = "/" + directories[0];
|
directories[0] = '/' + directories[0];
|
||||||
}
|
}
|
||||||
return directories;
|
return directories;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.normalize = function (path, keepBlanks) {
|
exports.normalize = function(path, keepBlanks) {
|
||||||
return exports.join(path, keepBlanks || false);
|
return exports.join(path, keepBlanks || false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.dirname = function (path) {
|
exports.dirname = function(path) {
|
||||||
if (path.length > 1 && '/' === path[path.length-1]) {
|
if (path.length > 1 && '/' === path[path.length - 1]) {
|
||||||
path = path.replace(/\/+$/, '');
|
path = path.replace(/\/+$/, '');
|
||||||
}
|
}
|
||||||
var lastSlash = path.lastIndexOf('/');
|
var lastSlash = path.lastIndexOf('/');
|
||||||
@ -133,8 +127,8 @@ exports.dirname = function (path) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.basename = function (path, ext) {
|
exports.basename = function(path, ext) {
|
||||||
var f = path.substr(path.lastIndexOf("/") + 1);
|
var f = path.substr(path.lastIndexOf('/') + 1);
|
||||||
if (ext && f.substr(-1 * ext.length) === ext) {
|
if (ext && f.substr(-1 * ext.length) === ext) {
|
||||||
f = f.substr(0, f.length - ext.length);
|
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('.'),
|
var dot = path.lastIndexOf('.'),
|
||||||
slash = path.lastIndexOf('/');
|
slash = path.lastIndexOf('/');
|
||||||
// The last dot must be in the last path component, and it (the last dot) must
|
// 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) {
|
exports.exists = function(path, callback) {
|
||||||
process.binding('fs').stat(path, function (err, stats) {
|
process.binding('fs').stat(path, function(err, stats) {
|
||||||
if (callback) callback(err ? false : true);
|
if (callback) callback(err ? false : true);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.existsSync = function (path) {
|
exports.existsSync = function(path) {
|
||||||
try {
|
try {
|
||||||
process.binding('fs').stat(path);
|
process.binding('fs').stat(path);
|
||||||
return true;
|
return true;
|
||||||
} catch(e){
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
// Query String Utilities
|
// Query String Utilities
|
||||||
|
|
||||||
var QueryString = exports;
|
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);
|
return c.charCodeAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// a safe fast alternative to decodeURIComponent
|
// a safe fast alternative to decodeURIComponent
|
||||||
QueryString.unescapeBuffer = function (s, decodeSpaces) {
|
QueryString.unescapeBuffer = function(s, decodeSpaces) {
|
||||||
var out = new Buffer(s.length);
|
var out = new Buffer(s.length);
|
||||||
var state = "CHAR"; // states: CHAR, HEX0, HEX1
|
var state = 'CHAR'; // states: CHAR, HEX0, HEX1
|
||||||
var n, m, hexchar;
|
var n, m, hexchar;
|
||||||
|
|
||||||
for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
|
for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
|
||||||
@ -65,61 +65,50 @@ QueryString.unescapeBuffer = function (s, decodeSpaces) {
|
|||||||
out[outIndex++] = c;
|
out[outIndex++] = c;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
out[outIndex++] = 16*n + m;
|
out[outIndex++] = 16 * n + m;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO support returning arbitrary buffers.
|
// 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();
|
return QueryString.unescapeBuffer(s, decodeSpaces).toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
QueryString.escape = function (str) {
|
QueryString.escape = function(str) {
|
||||||
return encodeURIComponent(str);
|
return encodeURIComponent(str);
|
||||||
};
|
};
|
||||||
|
|
||||||
var stringifyPrimitive = function(v) {
|
var stringifyPrimitive = function(v) {
|
||||||
switch (typeof v) {
|
switch (typeof v) {
|
||||||
case "string":
|
case 'string':
|
||||||
return v;
|
return v;
|
||||||
|
|
||||||
case "boolean":
|
case 'boolean':
|
||||||
return v ? "true" : "false";
|
return v ? 'true' : 'false';
|
||||||
|
|
||||||
case "number":
|
case 'number':
|
||||||
return isFinite(v) ? v : "";
|
return isFinite(v) ? v : '';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Converts an arbitrary value to a Query String representation.</p>
|
QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) {
|
||||||
*
|
sep = sep || '&';
|
||||||
* <p>Objects with cyclical references will trigger an exception.</p>
|
eq = eq || '=';
|
||||||
*
|
|
||||||
* @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 || "=";
|
|
||||||
obj = (obj === null) ? undefined : obj;
|
obj = (obj === null) ? undefined : obj;
|
||||||
|
|
||||||
switch (typeof obj) {
|
switch (typeof obj) {
|
||||||
case "object":
|
case 'object':
|
||||||
return Object.keys(obj).map(function(k) {
|
return Object.keys(obj).map(function(k) {
|
||||||
if (Array.isArray(obj[k])) {
|
if (Array.isArray(obj[k])) {
|
||||||
return obj[k].map(function(v) {
|
return obj[k].map(function(v) {
|
||||||
@ -128,24 +117,23 @@ QueryString.stringify = QueryString.encode = function (obj, sep, eq, name) {
|
|||||||
QueryString.escape(stringifyPrimitive(v));
|
QueryString.escape(stringifyPrimitive(v));
|
||||||
}).join(sep);
|
}).join(sep);
|
||||||
} else {
|
} else {
|
||||||
return QueryString.escape(stringifyPrimitive(k)) +
|
return QueryString.escape(stringifyPrimitive(k)) +
|
||||||
eq +
|
eq +
|
||||||
QueryString.escape(stringifyPrimitive(obj[k]));
|
QueryString.escape(stringifyPrimitive(obj[k]));
|
||||||
}
|
}
|
||||||
}).join(sep);
|
}).join(sep);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return (name) ?
|
if (!name) return '';
|
||||||
QueryString.escape(stringifyPrimitive(name)) + eq +
|
return QueryString.escape(stringifyPrimitive(name)) + eq +
|
||||||
QueryString.escape(stringifyPrimitive(obj)) :
|
QueryString.escape(stringifyPrimitive(obj));
|
||||||
"";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse a key=val string.
|
// Parse a key=val string.
|
||||||
QueryString.parse = QueryString.decode = function (qs, sep, eq) {
|
QueryString.parse = QueryString.decode = function(qs, sep, eq) {
|
||||||
sep = sep || "&";
|
sep = sep || '&';
|
||||||
eq = eq || "=";
|
eq = eq || '=';
|
||||||
var obj = {};
|
var obj = {};
|
||||||
|
|
||||||
if (typeof qs !== 'string') {
|
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);
|
var v = QueryString.unescape(x.slice(1).join(eq), true);
|
||||||
|
|
||||||
if (!(k in obj)) {
|
if (!(k in obj)) {
|
||||||
obj[k] = v;
|
obj[k] = v;
|
||||||
} else if (!Array.isArray(obj[k])) {
|
} else if (!Array.isArray(obj[k])) {
|
||||||
obj[k] = [obj[k], v];
|
obj[k] = [obj[k], v];
|
||||||
} else {
|
} else {
|
||||||
obj[k].push(v);
|
obj[k].push(v);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
|
// * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
|
||||||
|
|
||||||
var kHistorySize = 30;
|
var kHistorySize = 30;
|
||||||
var kBufSize = 10*1024;
|
var kBufSize = 10 * 1024;
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
@ -13,15 +13,15 @@ var EventEmitter = require('events').EventEmitter;
|
|||||||
var stdio = process.binding('stdio');
|
var stdio = process.binding('stdio');
|
||||||
|
|
||||||
|
|
||||||
exports.createInterface = function (output, completer) {
|
exports.createInterface = function(output, completer) {
|
||||||
return new Interface(output, completer);
|
return new Interface(output, completer);
|
||||||
};
|
};
|
||||||
|
|
||||||
function writeFilter (stream) {
|
function writeFilter(stream) {
|
||||||
if (stream._writeFiltered) return;
|
if (stream._writeFiltered) return;
|
||||||
stream._writeFiltered = true;
|
stream._writeFiltered = true;
|
||||||
stream._normalWrite = stream.write;
|
stream._normalWrite = stream.write;
|
||||||
stream.write = function (d) {
|
stream.write = function(d) {
|
||||||
var args = Array.prototype.slice.call(arguments);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
if (typeof d == 'string') {
|
if (typeof d == 'string') {
|
||||||
args[0] = d.replace(/([^\r])\n|^\n/g, '$1\r\n');
|
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);
|
if (!(this instanceof Interface)) return new Interface(output, completer);
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.completer = completer;
|
this.completer = completer;
|
||||||
|
|
||||||
this.setPrompt("> ");
|
this.setPrompt('> ');
|
||||||
|
|
||||||
this.enabled = stdio.isatty(output.fd);
|
this.enabled = stdio.isatty(output.fd);
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ function Interface (output, completer) {
|
|||||||
writeFilter(process.stdout);
|
writeFilter(process.stdout);
|
||||||
|
|
||||||
// Current line
|
// Current line
|
||||||
this.line = "";
|
this.line = '';
|
||||||
|
|
||||||
// Check process.env.TERM ?
|
// Check process.env.TERM ?
|
||||||
stdio.setRawMode(true);
|
stdio.setRawMode(true);
|
||||||
@ -67,8 +67,8 @@ function Interface (output, completer) {
|
|||||||
|
|
||||||
exports.columns = process.binding('stdio').getColumns();
|
exports.columns = process.binding('stdio').getColumns();
|
||||||
|
|
||||||
if (process.listeners("SIGWINCH").length === 0) {
|
if (process.listeners('SIGWINCH').length === 0) {
|
||||||
process.on("SIGWINCH", function () {
|
process.on('SIGWINCH', function() {
|
||||||
exports.columns = process.binding('stdio').getColumns();
|
exports.columns = process.binding('stdio').getColumns();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -77,17 +77,17 @@ function Interface (output, completer) {
|
|||||||
|
|
||||||
inherits(Interface, EventEmitter);
|
inherits(Interface, EventEmitter);
|
||||||
|
|
||||||
Interface.prototype.__defineGetter__("columns", function () {
|
Interface.prototype.__defineGetter__('columns', function() {
|
||||||
return exports.columns;
|
return exports.columns;
|
||||||
});
|
});
|
||||||
|
|
||||||
Interface.prototype.setPrompt = function (prompt, length) {
|
Interface.prototype.setPrompt = function(prompt, length) {
|
||||||
this._prompt = prompt;
|
this._prompt = prompt;
|
||||||
this._promptLength = length ? length : Buffer.byteLength(prompt);
|
this._promptLength = length ? length : Buffer.byteLength(prompt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype.prompt = function () {
|
Interface.prototype.prompt = function() {
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
this.cursor = 0;
|
this.cursor = 0;
|
||||||
this._refreshLine();
|
this._refreshLine();
|
||||||
@ -97,11 +97,11 @@ Interface.prototype.prompt = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype._addHistory = function () {
|
Interface.prototype._addHistory = function() {
|
||||||
if (this.line.length === 0) return "";
|
if (this.line.length === 0) return '';
|
||||||
|
|
||||||
this.history.unshift(this.line);
|
this.history.unshift(this.line);
|
||||||
this.line = "";
|
this.line = '';
|
||||||
this.historyIndex = -1;
|
this.historyIndex = -1;
|
||||||
|
|
||||||
this.cursor = 0;
|
this.cursor = 0;
|
||||||
@ -113,7 +113,7 @@ Interface.prototype._addHistory = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype._refreshLine = function () {
|
Interface.prototype._refreshLine = function() {
|
||||||
if (this._closed) return;
|
if (this._closed) return;
|
||||||
|
|
||||||
// Cursor to left edge.
|
// 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) {
|
if (this.enabled) {
|
||||||
stdio.setRawMode(false);
|
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;
|
if (this._closed) return;
|
||||||
return this.enabled ? this._ttyWrite(d) : this._normalWrite(d);
|
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
|
// Very simple implementation right now. Should try to break on
|
||||||
// new lines.
|
// new lines.
|
||||||
this.emit('line', b.toString());
|
this.emit('line', b.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
Interface.prototype._insertString = function (c) {
|
Interface.prototype._insertString = function(c) {
|
||||||
//BUG: Problem when adding tabs with following content.
|
//BUG: Problem when adding tabs with following content.
|
||||||
// Perhaps the bug is in _refreshLine(). Not sure.
|
// Perhaps the bug is in _refreshLine(). Not sure.
|
||||||
// A hack would be to insert spaces instead of literal '\t'.
|
// 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 self = this;
|
||||||
|
|
||||||
var rv = this.completer(self.line.slice(0, self.cursor));
|
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._insertString(completions[0].slice(completeOn.length));
|
||||||
self._refreshLine();
|
self._refreshLine();
|
||||||
} else {
|
} else {
|
||||||
self.output.write("\r\n");
|
self.output.write('\r\n');
|
||||||
var width = completions.reduce(function(a, b) {
|
var width = completions.reduce(function(a, b) {
|
||||||
return a.length > b.length ? a : b;
|
return a.length > b.length ? a : b;
|
||||||
}).length + 2; // 2 space padding
|
}).length + 2; // 2 space padding
|
||||||
@ -214,7 +214,7 @@ Interface.prototype._tabComplete = function () {
|
|||||||
var group = [], c;
|
var group = [], c;
|
||||||
for (var i = 0, compLen = completions.length; i < compLen; i++) {
|
for (var i = 0, compLen = completions.length; i < compLen; i++) {
|
||||||
c = completions[i];
|
c = completions[i];
|
||||||
if (c === "") {
|
if (c === '') {
|
||||||
handleGroup(group);
|
handleGroup(group);
|
||||||
group = [];
|
group = [];
|
||||||
} else {
|
} else {
|
||||||
@ -225,8 +225,8 @@ Interface.prototype._tabComplete = function () {
|
|||||||
|
|
||||||
// If there is a common prefix to all matches, then apply that
|
// If there is a common prefix to all matches, then apply that
|
||||||
// portion.
|
// portion.
|
||||||
var prefix = commonPrefix(
|
var f = completions.filter(function(e) { if (e) return e; });
|
||||||
completions.filter(function(e) { if (e) return e; }));
|
var prefix = commonPrefix(f);
|
||||||
if (prefix.length > completeOn.length) {
|
if (prefix.length > completeOn.length) {
|
||||||
self._insertString(prefix.slice(completeOn.length));
|
self._insertString(prefix.slice(completeOn.length));
|
||||||
}
|
}
|
||||||
@ -238,7 +238,7 @@ Interface.prototype._tabComplete = function () {
|
|||||||
|
|
||||||
function commonPrefix(strings) {
|
function commonPrefix(strings) {
|
||||||
if (!strings || strings.length == 0) {
|
if (!strings || strings.length == 0) {
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
var sorted = strings.slice().sort();
|
var sorted = strings.slice().sort();
|
||||||
var min = sorted[0];
|
var min = sorted[0];
|
||||||
@ -251,7 +251,7 @@ function commonPrefix(strings) {
|
|||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface.prototype._historyNext = function () {
|
Interface.prototype._historyNext = function() {
|
||||||
if (this.historyIndex > 0) {
|
if (this.historyIndex > 0) {
|
||||||
this.historyIndex--;
|
this.historyIndex--;
|
||||||
this.line = this.history[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) {
|
if (this.historyIndex + 1 < this.history.length) {
|
||||||
this.historyIndex++;
|
this.historyIndex++;
|
||||||
this.line = this.history[this.historyIndex];
|
this.line = this.history[this.historyIndex];
|
||||||
@ -277,7 +277,7 @@ Interface.prototype._historyPrev = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// handle a write from the tty
|
// handle a write from the tty
|
||||||
Interface.prototype._ttyWrite = function (b) {
|
Interface.prototype._ttyWrite = function(b) {
|
||||||
switch (b[0]) {
|
switch (b[0]) {
|
||||||
/* ctrl+c */
|
/* ctrl+c */
|
||||||
case 3:
|
case 3:
|
||||||
@ -294,9 +294,9 @@ Interface.prototype._ttyWrite = function (b) {
|
|||||||
if (this.cursor === 0 && this.line.length === 0) {
|
if (this.cursor === 0 && this.line.length === 0) {
|
||||||
this.close();
|
this.close();
|
||||||
} else if (this.cursor < this.line.length) {
|
} else if (this.cursor < this.line.length) {
|
||||||
this.line = this.line.slice(0, this.cursor)
|
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();
|
this._refreshLine();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -310,9 +310,9 @@ Interface.prototype._ttyWrite = function (b) {
|
|||||||
case 127: /* backspace */
|
case 127: /* backspace */
|
||||||
case 8: /* ctrl+h */
|
case 8: /* ctrl+h */
|
||||||
if (this.cursor > 0 && this.line.length > 0) {
|
if (this.cursor > 0 && this.line.length > 0) {
|
||||||
this.line = this.line.slice(0, this.cursor-1)
|
this.line = this.line.slice(0, this.cursor - 1) +
|
||||||
+ this.line.slice(this.cursor, this.line.length)
|
this.line.slice(this.cursor, this.line.length);
|
||||||
;
|
|
||||||
this.cursor--;
|
this.cursor--;
|
||||||
this._refreshLine();
|
this._refreshLine();
|
||||||
}
|
}
|
||||||
@ -379,7 +379,7 @@ Interface.prototype._ttyWrite = function (b) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 26: /* ctrl+z */
|
case 26: /* ctrl+z */
|
||||||
process.kill(process.pid, "SIGTSTP");
|
process.kill(process.pid, 'SIGTSTP');
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 27: /* escape sequence */
|
case 27: /* escape sequence */
|
||||||
@ -407,9 +407,8 @@ Interface.prototype._ttyWrite = function (b) {
|
|||||||
// meta-f - forward word
|
// meta-f - forward word
|
||||||
next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
|
next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
|
||||||
if (next_word !== -1) {
|
if (next_word !== -1) {
|
||||||
next_non_word =
|
next_non_word = this.line.slice(this.cursor + next_word,
|
||||||
this.line.slice(this.cursor + next_word, this.line.length)
|
this.line.length).search(/\W/);
|
||||||
.search(/\W/);
|
|
||||||
if (next_non_word !== -1) {
|
if (next_non_word !== -1) {
|
||||||
this.cursor += next_word + next_non_word;
|
this.cursor += next_word + next_non_word;
|
||||||
this._refreshLine();
|
this._refreshLine();
|
||||||
@ -423,12 +422,12 @@ Interface.prototype._ttyWrite = function (b) {
|
|||||||
// meta-d delete forward word
|
// meta-d delete forward word
|
||||||
next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
|
next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
|
||||||
if (next_word !== -1) {
|
if (next_word !== -1) {
|
||||||
next_non_word =
|
next_non_word = this.line.slice(this.cursor + next_word,
|
||||||
this.line.slice(this.cursor + next_word, this.line.length)
|
this.line.length).search(/\W/);
|
||||||
.search(/\W/);
|
|
||||||
if (next_non_word !== -1) {
|
if (next_non_word !== -1) {
|
||||||
this.line =
|
this.line = this.line.slice(this.cursor +
|
||||||
this.line.slice(this.cursor + next_word + next_non_word);
|
next_word +
|
||||||
|
next_non_word);
|
||||||
this.cursor = 0;
|
this.cursor = 0;
|
||||||
this._refreshLine();
|
this._refreshLine();
|
||||||
break;
|
break;
|
||||||
@ -478,7 +477,7 @@ Interface.prototype._ttyWrite = function (b) {
|
|||||||
} else if (b[1] === 91 && b[2] === 51 && this.cursor < this.line.length) {
|
} else if (b[1] === 91 && b[2] === 51 && this.cursor < this.line.length) {
|
||||||
// delete right
|
// delete right
|
||||||
this.line = this.line.slice(0, this.cursor) +
|
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();
|
this._refreshLine();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
228
lib/repl.js
228
lib/repl.js
@ -1,29 +1,36 @@
|
|||||||
// A repl library that you can include in your own code to get a runtime
|
/* A repl library that you can include in your own code to get a runtime
|
||||||
// interface to your program.
|
* interface to your program.
|
||||||
//
|
*
|
||||||
// var repl = require("/repl.js");
|
* var repl = require("/repl.js");
|
||||||
// repl.start("prompt> "); // start repl on stdin
|
* // start repl on stdin
|
||||||
// net.createServer(function (socket) { // listen for unix socket connections and start repl on them
|
* repl.start("prompt> ");
|
||||||
// repl.start("node via Unix socket> ", socket);
|
*
|
||||||
// }).listen("/tmp/node-repl-sock");
|
* // listen for unix socket connections and start repl on them
|
||||||
// net.createServer(function (socket) { // listen for TCP socket connections and start repl on them
|
* net.createServer(function (socket) {
|
||||||
// repl.start("node via TCP socket> ", socket);
|
* repl.start("node via Unix socket> ", socket);
|
||||||
// }).listen(5001);
|
* }).listen("/tmp/node-repl-sock");
|
||||||
|
*
|
||||||
// repl.start("node > ").context.foo = "stdin is fun"; // expose foo to repl context
|
* // 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 util = require('util');
|
||||||
var Script = process.binding('evals').Script;
|
var Script = process.binding('evals').Script;
|
||||||
var evalcx = Script.runInContext;
|
var evalcx = Script.runInContext;
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var fs = require("fs");
|
var fs = require('fs');
|
||||||
var rl = require('readline');
|
var rl = require('readline');
|
||||||
var context;
|
var context;
|
||||||
|
|
||||||
var disableColors = process.env.NODE_DISABLE_COLORS ? true : false;
|
var disableColors = process.env.NODE_DISABLE_COLORS ? true : false;
|
||||||
|
|
||||||
// hack for require.resolve("./relative") to work properly.
|
// hack for require.resolve("./relative") to work properly.
|
||||||
module.filename = process.cwd() + "/repl";
|
module.filename = process.cwd() + '/repl';
|
||||||
|
|
||||||
function resetContext() {
|
function resetContext() {
|
||||||
context = Script.createContext();
|
context = Script.createContext();
|
||||||
@ -44,9 +51,9 @@ function REPLServer(prompt, stream) {
|
|||||||
self.buffered_cmd = '';
|
self.buffered_cmd = '';
|
||||||
|
|
||||||
self.stream = stream || process.openStdin();
|
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);
|
return self.complete(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -62,9 +69,9 @@ function REPLServer(prompt, stream) {
|
|||||||
|
|
||||||
rli.setPrompt(self.prompt);
|
rli.setPrompt(self.prompt);
|
||||||
|
|
||||||
rli.on("SIGINT", function () {
|
rli.on('SIGINT', function() {
|
||||||
if (self.buffered_cmd && self.buffered_cmd.length > 0) {
|
if (self.buffered_cmd && self.buffered_cmd.length > 0) {
|
||||||
rli.write("\n");
|
rli.write('\n');
|
||||||
self.buffered_cmd = '';
|
self.buffered_cmd = '';
|
||||||
self.displayPrompt();
|
self.displayPrompt();
|
||||||
} else {
|
} 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.write(chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
rli.addListener('line', function (cmd) {
|
rli.addListener('line', function(cmd) {
|
||||||
cmd = trimWhitespace(cmd);
|
cmd = trimWhitespace(cmd);
|
||||||
|
|
||||||
// Check to see if a REPL keyword was used. If it returns true,
|
// 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.
|
// continue onto the next line.
|
||||||
try {
|
try {
|
||||||
// Use evalcx to supply the global context
|
// 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) {
|
if (ret !== undefined) {
|
||||||
context._ = ret;
|
context._ = ret;
|
||||||
self.stream.write(exports.writer(ret) + "\n");
|
self.stream.write(exports.writer(ret) + '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
self.buffered_cmd = '';
|
self.buffered_cmd = '';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// instanceof doesn't work across context switches.
|
// 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;
|
throw e;
|
||||||
// It could also be an error from JSON.parse
|
// It could also be an error from JSON.parse
|
||||||
} else if(e && e.stack
|
} else if (e &&
|
||||||
&& e.stack.match('Unexpected token ILLEGAL')
|
e.stack &&
|
||||||
&& e.stack.match(/Object.parse \(native\)/)) {
|
e.stack.match('Unexpected token ILLEGAL') &&
|
||||||
|
e.stack.match(/Object.parse \(native\)/)) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// On error: Print the error and clear the buffer
|
// On error: Print the error and clear the buffer
|
||||||
if (e.stack) {
|
if (e.stack) {
|
||||||
self.stream.write(e.stack + "\n");
|
self.stream.write(e.stack + '\n');
|
||||||
} else {
|
} else {
|
||||||
self.stream.write(e.toString() + "\n");
|
self.stream.write(e.toString() + '\n');
|
||||||
}
|
}
|
||||||
self.buffered_cmd = '';
|
self.buffered_cmd = '';
|
||||||
}
|
}
|
||||||
@ -126,7 +134,7 @@ function REPLServer(prompt, stream) {
|
|||||||
self.displayPrompt();
|
self.displayPrompt();
|
||||||
});
|
});
|
||||||
|
|
||||||
rli.addListener('close', function () {
|
rli.addListener('close', function() {
|
||||||
self.stream.destroy();
|
self.stream.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -136,41 +144,36 @@ exports.REPLServer = REPLServer;
|
|||||||
|
|
||||||
// prompt is a string to print on each line for the prompt,
|
// 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.
|
// 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);
|
return new REPLServer(prompt, source);
|
||||||
};
|
};
|
||||||
|
|
||||||
REPLServer.prototype.displayPrompt = function () {
|
REPLServer.prototype.displayPrompt = function() {
|
||||||
this.rli.setPrompt(this.buffered_cmd.length ? '... ' : this.prompt);
|
this.rli.setPrompt(this.buffered_cmd.length ? '... ' : this.prompt);
|
||||||
this.rli.prompt();
|
this.rli.prompt();
|
||||||
};
|
};
|
||||||
|
|
||||||
// read a line from the stream, then eval it
|
// 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
|
||||||
* Provide a list of completions for the given leading text. This is
|
// given to the readline interface for handling tab completion.
|
||||||
* given to the readline interface for handling tab completion.
|
//
|
||||||
*
|
// Example:
|
||||||
* @param {line} The text (preceding the cursor) to complete
|
// complete('var foo = util.')
|
||||||
* @returns {Array} Two elements: (1) an array of completions; and
|
// -> [['util.print', 'util.debug', 'util.log', 'util.inspect', 'util.pump'],
|
||||||
* (2) the leading text completed.
|
// 'util.' ]
|
||||||
*
|
//
|
||||||
* Example:
|
// Warning: This eval's code like "foo.bar.baz", so it will run property
|
||||||
* complete('var foo = util.')
|
// getter code.
|
||||||
* -> [['util.print', 'util.debug', 'util.log', 'util.inspect', 'util.pump'],
|
REPLServer.prototype.complete = function(line) {
|
||||||
* 'util.' ]
|
var completions;
|
||||||
*
|
|
||||||
* Warning: This eval's code like "foo.bar.baz", so it will run property
|
|
||||||
* getter code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
REPLServer.prototype.complete = function (line) {
|
// list of completion lists, one for each inheritance "level"
|
||||||
var completions,
|
var completionGroups = [];
|
||||||
completionGroups = [], // list of completion lists, one for each inheritance "level"
|
|
||||||
completeOn,
|
var completeOn, match, filter, i, j, group, c;
|
||||||
match, filter, i, j, group, c;
|
|
||||||
|
|
||||||
// REPL commands (e.g. ".break").
|
// REPL commands (e.g. ".break").
|
||||||
var match = null;
|
var match = null;
|
||||||
@ -181,17 +184,19 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
if (match[1].length > 1) {
|
if (match[1].length > 1) {
|
||||||
filter = match[1];
|
filter = match[1];
|
||||||
}
|
}
|
||||||
}
|
} else if (match =
|
||||||
|
line.match(/\brequire\s*\(['"](([\w\.\/-]+\/)?([\w\.\/-]*))/)) {
|
||||||
// require('...<Tab>')
|
// require('...<Tab>')
|
||||||
else if (match = line.match(/\brequire\s*\(['"](([\w\.\/-]+\/)?([\w\.\/-]*))/)) {
|
//TODO: suggest require.exts be exposed to be introspec registered
|
||||||
//TODO: suggest require.exts be exposed to be introspec registered extensions?
|
//extensions?
|
||||||
//TODO: suggest include the '.' in exts in internal repr: parity with `path.extname`.
|
//TODO: suggest include the '.' in exts in internal repr: parity with
|
||||||
var exts = [".js", ".node"];
|
//`path.extname`.
|
||||||
var indexRe = new RegExp('^index(' + exts.map(regexpEscape).join('|') + ')$');
|
var exts = ['.js', '.node'];
|
||||||
|
var indexRe = new RegExp('^index(' + exts.map(regexpEscape).join('|') +
|
||||||
|
')$');
|
||||||
|
|
||||||
completeOn = match[1];
|
completeOn = match[1];
|
||||||
var subdir = match[2] || "";
|
var subdir = match[2] || '';
|
||||||
var filter = match[1];
|
var filter = match[1];
|
||||||
var dir, files, f, name, base, ext, abs, subfiles, s;
|
var dir, files, f, name, base, ext, abs, subfiles, s;
|
||||||
group = [];
|
group = [];
|
||||||
@ -211,12 +216,12 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
name = files[f];
|
name = files[f];
|
||||||
ext = path.extname(name);
|
ext = path.extname(name);
|
||||||
base = name.slice(0, -ext.length);
|
base = name.slice(0, -ext.length);
|
||||||
if (base.match(/-\d+\.\d+(\.\d+)?/) || name === ".npm") {
|
if (base.match(/-\d+\.\d+(\.\d+)?/) || name === '.npm') {
|
||||||
// Exclude versioned names that 'npm' installs.
|
// Exclude versioned names that 'npm' installs.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (exts.indexOf(ext) !== -1) {
|
if (exts.indexOf(ext) !== -1) {
|
||||||
if (!subdir || base !== "index") {
|
if (!subdir || base !== 'index') {
|
||||||
group.push(subdir + base);
|
group.push(subdir + base);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -231,7 +236,7 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +249,8 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
// Intentionally excluding moved modules: posix, utils.
|
// Intentionally excluding moved modules: posix, utils.
|
||||||
var builtinLibs = ['assert', 'buffer', 'child_process', 'crypto', 'dgram',
|
var builtinLibs = ['assert', 'buffer', 'child_process', 'crypto', 'dgram',
|
||||||
'dns', 'events', 'file', 'freelist', 'fs', 'http', 'net', 'path',
|
'dns', 'events', 'file', 'freelist', 'fs', 'http', 'net', 'path',
|
||||||
'querystring', 'readline', 'repl', 'string_decoder', 'util', 'tcp', 'url'];
|
'querystring', 'readline', 'repl', 'string_decoder', 'util', 'tcp',
|
||||||
|
'url'];
|
||||||
completionGroups.push(builtinLibs);
|
completionGroups.push(builtinLibs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,24 +265,27 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
// spam.eggs.<|> # completions for 'spam.eggs' with filter ''
|
// spam.eggs.<|> # completions for 'spam.eggs' with filter ''
|
||||||
// foo<|> # all scope vars with filter 'foo'
|
// foo<|> # all scope vars with filter 'foo'
|
||||||
// foo.<|> # completions for 'foo' with filter ''
|
// foo.<|> # completions for 'foo' with filter ''
|
||||||
else if (line.length === 0 || line[line.length-1].match(/\w|\.|\$/)) {
|
else if (line.length === 0 || line[line.length - 1].match(/\w|\.|\$/)) {
|
||||||
var simpleExpressionPat = /(([a-zA-Z_$](?:\w|\$)*)\.)*([a-zA-Z_$](?:\w|\$)*)\.?$/;
|
var simpleExpressionPat =
|
||||||
|
/(([a-zA-Z_$](?:\w|\$)*)\.)*([a-zA-Z_$](?:\w|\$)*)\.?$/;
|
||||||
match = simpleExpressionPat.exec(line);
|
match = simpleExpressionPat.exec(line);
|
||||||
if (line.length === 0 || match) {
|
if (line.length === 0 || match) {
|
||||||
var expr;
|
var expr;
|
||||||
completeOn = (match ? match[0] : "");
|
completeOn = (match ? match[0] : '');
|
||||||
if (line.length === 0) {
|
if (line.length === 0) {
|
||||||
filter = "";
|
filter = '';
|
||||||
expr = "";
|
expr = '';
|
||||||
} else if (line[line.length-1] === '.') {
|
} else if (line[line.length - 1] === '.') {
|
||||||
filter = "";
|
filter = '';
|
||||||
expr = match[0].slice(0, match[0].length-1);
|
expr = match[0].slice(0, match[0].length - 1);
|
||||||
} else {
|
} else {
|
||||||
var bits = match[0].split('.');
|
var bits = match[0].split('.');
|
||||||
filter = bits.pop();
|
filter = bits.pop();
|
||||||
expr = bits.join('.');
|
expr = bits.join('.');
|
||||||
}
|
}
|
||||||
//console.log("expression completion: completeOn='"+completeOn+"' expr='"+expr+"'");
|
|
||||||
|
// console.log("expression completion: completeOn='" + completeOn +
|
||||||
|
// "' expr='" + expr + "'");
|
||||||
|
|
||||||
// Resolve expr and get its completions.
|
// Resolve expr and get its completions.
|
||||||
var obj, memberGroups = [];
|
var obj, memberGroups = [];
|
||||||
@ -284,31 +293,31 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
completionGroups.push(Object.getOwnPropertyNames(this.context));
|
completionGroups.push(Object.getOwnPropertyNames(this.context));
|
||||||
// Global object properties
|
// Global object properties
|
||||||
// (http://www.ecma-international.org/publications/standards/Ecma-262.htm)
|
// (http://www.ecma-international.org/publications/standards/Ecma-262.htm)
|
||||||
completionGroups.push(["NaN", "Infinity", "undefined",
|
completionGroups.push(['NaN', 'Infinity', 'undefined',
|
||||||
"eval", "parseInt", "parseFloat", "isNaN", "isFinite", "decodeURI",
|
'eval', 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'decodeURI',
|
||||||
"decodeURIComponent", "encodeURI", "encodeURIComponent",
|
'decodeURIComponent', 'encodeURI', 'encodeURIComponent',
|
||||||
"Object", "Function", "Array", "String", "Boolean", "Number",
|
'Object', 'Function', 'Array', 'String', 'Boolean', 'Number',
|
||||||
"Date", "RegExp", "Error", "EvalError", "RangeError",
|
'Date', 'RegExp', 'Error', 'EvalError', 'RangeError',
|
||||||
"ReferenceError", "SyntaxError", "TypeError", "URIError",
|
'ReferenceError', 'SyntaxError', 'TypeError', 'URIError',
|
||||||
"Math", "JSON"]);
|
'Math', 'JSON']);
|
||||||
// Common keywords. Exclude for completion on the empty string, b/c
|
// Common keywords. Exclude for completion on the empty string, b/c
|
||||||
// they just get in the way.
|
// they just get in the way.
|
||||||
if (filter) {
|
if (filter) {
|
||||||
completionGroups.push(["break", "case", "catch", "const",
|
completionGroups.push(['break', 'case', 'catch', 'const',
|
||||||
"continue", "debugger", "default", "delete", "do", "else", "export",
|
'continue', 'debugger', 'default', 'delete', 'do', 'else', 'export',
|
||||||
"false", "finally", "for", "function", "if", "import", "in",
|
'false', 'finally', 'for', 'function', 'if', 'import', 'in',
|
||||||
"instanceof", "let", "new", "null", "return", "switch", "this",
|
'instanceof', 'let', 'new', 'null', 'return', 'switch', 'this',
|
||||||
"throw", "true", "try", "typeof", "undefined", "var", "void",
|
'throw', 'true', 'try', 'typeof', 'undefined', 'var', 'void',
|
||||||
"while", "with", "yield"]);
|
'while', 'with', 'yield']);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
obj = evalcx(expr, this.context, "repl");
|
obj = evalcx(expr, this.context, 'repl');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//console.log("completion eval error, expr='"+expr+"': "+e);
|
//console.log("completion eval error, expr='"+expr+"': "+e);
|
||||||
}
|
}
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
if (typeof obj === "object" || typeof obj === "function") {
|
if (typeof obj === 'object' || typeof obj === 'function') {
|
||||||
memberGroups.push(Object.getOwnPropertyNames(obj));
|
memberGroups.push(Object.getOwnPropertyNames(obj));
|
||||||
}
|
}
|
||||||
// works for non-objects
|
// works for non-objects
|
||||||
@ -371,9 +380,9 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
uniq[c] = true;
|
uniq[c] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
completions.push(""); // separator btwn groups
|
completions.push(''); // separator btwn groups
|
||||||
}
|
}
|
||||||
while (completions.length && completions[completions.length-1] === "") {
|
while (completions.length && completions[completions.length - 1] === '') {
|
||||||
completions.pop();
|
completions.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,11 +393,11 @@ REPLServer.prototype.complete = function (line) {
|
|||||||
/**
|
/**
|
||||||
* Used to parse and execute the Node REPL commands.
|
* Used to parse and execute the Node REPL commands.
|
||||||
*
|
*
|
||||||
* @param {cmd} cmd The command entered to check
|
* @param {keyword} keyword The command entered to check.
|
||||||
* @returns {Boolean} If true it means don't continue parsing the command
|
* @return {Boolean} If true it means don't continue parsing the command.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
REPLServer.prototype.parseREPLKeyword = function (keyword, rest) {
|
REPLServer.prototype.parseREPLKeyword = function(keyword, rest) {
|
||||||
var cmd = this.commands[keyword];
|
var cmd = this.commands[keyword];
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
cmd.action.call(this, rest);
|
cmd.action.call(this, rest);
|
||||||
@ -418,7 +427,7 @@ function defineDefaultCommands(repl) {
|
|||||||
repl.defineCommand('clear', {
|
repl.defineCommand('clear', {
|
||||||
help: 'Break, and also clear the local context',
|
help: 'Break, and also clear the local context',
|
||||||
action: function() {
|
action: function() {
|
||||||
this.stream.write("Clearing context...\n");
|
this.stream.write('Clearing context...\n');
|
||||||
this.buffered_cmd = '';
|
this.buffered_cmd = '';
|
||||||
resetContext();
|
resetContext();
|
||||||
this.displayPrompt();
|
this.displayPrompt();
|
||||||
@ -438,16 +447,16 @@ function defineDefaultCommands(repl) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
Object.keys(this.commands).sort().forEach(function(name) {
|
Object.keys(this.commands).sort().forEach(function(name) {
|
||||||
var cmd = self.commands[name];
|
var cmd = self.commands[name];
|
||||||
self.stream.write(name + "\t" + (cmd.help || '') + "\n");
|
self.stream.write(name + '\t' + (cmd.help || '') + '\n');
|
||||||
});
|
});
|
||||||
this.displayPrompt();
|
this.displayPrompt();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function trimWhitespace (cmd) {
|
function trimWhitespace(cmd) {
|
||||||
var trimmer = /^\s*(.+)\s*$/m,
|
var trimmer = /^\s*(.+)\s*$/m,
|
||||||
matches = trimmer.exec(cmd);
|
matches = trimmer.exec(cmd);
|
||||||
|
|
||||||
if (matches && matches.length === 2) {
|
if (matches && matches.length === 2) {
|
||||||
return matches[1];
|
return matches[1];
|
||||||
@ -455,32 +464,33 @@ function trimWhitespace (cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function regexpEscape(s) {
|
function regexpEscape(s) {
|
||||||
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts commands that use var and function <name>() to use the
|
* Converts commands that use var and function <name>() to use the
|
||||||
* local exports.context when evaled. This provides a local context
|
* local exports.context when evaled. This provides a local context
|
||||||
* on the REPL.
|
* on the REPL.
|
||||||
*
|
*
|
||||||
* @param {String} cmd The cmd to convert
|
* @param {String} cmd The cmd to convert.
|
||||||
* @returns {String} The converted command
|
* @return {String} The converted command.
|
||||||
*/
|
*/
|
||||||
REPLServer.prototype.convertToContext = function (cmd) {
|
REPLServer.prototype.convertToContext = function(cmd) {
|
||||||
var self = this, matches,
|
var self = this, matches,
|
||||||
scopeVar = /^\s*var\s*([_\w\$]+)(.*)$/m,
|
scopeVar = /^\s*var\s*([_\w\$]+)(.*)$/m,
|
||||||
scopeFunc = /^\s*function\s*([_\w\$]+)/;
|
scopeFunc = /^\s*function\s*([_\w\$]+)/;
|
||||||
|
|
||||||
// Replaces: var foo = "bar"; with: self.context.foo = bar;
|
// Replaces: var foo = "bar"; with: self.context.foo = bar;
|
||||||
matches = scopeVar.exec(cmd);
|
matches = scopeVar.exec(cmd);
|
||||||
if (matches && matches.length === 3) {
|
if (matches && matches.length === 3) {
|
||||||
return "self.context." + matches[1] + matches[2];
|
return 'self.context.' + matches[1] + matches[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replaces: function foo() {}; with: foo = function foo() {};
|
// Replaces: function foo() {}; with: foo = function foo() {};
|
||||||
matches = scopeFunc.exec(self.buffered_cmd);
|
matches = scopeFunc.exec(self.buffered_cmd);
|
||||||
if (matches && matches.length === 2) {
|
if (matches && matches.length === 2) {
|
||||||
return matches[1] + " = " + self.buffered_cmd;
|
return matches[1] + ' = ' + self.buffered_cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd;
|
return cmd;
|
||||||
|
@ -5,10 +5,11 @@ var assert = process.assert;
|
|||||||
|
|
||||||
|
|
||||||
var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
|
var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
|
||||||
function debug () {
|
var debug;
|
||||||
if (debugLevel & 0x2) {
|
if (debugLevel & 0x2) {
|
||||||
util.error.apply(this, arguments);
|
debug = function() { util.error.apply(this, arguments); };
|
||||||
}
|
} else {
|
||||||
|
debug = function() { };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
this._clearWriteState = true;
|
this._clearWriteState = true;
|
||||||
this._done = false;
|
this._done = false;
|
||||||
|
|
||||||
var crypto = require("crypto");
|
var crypto = require('crypto');
|
||||||
|
|
||||||
if (!credentials) {
|
if (!credentials) {
|
||||||
this.credentials = crypto.createCredentials();
|
this.credentials = crypto.createCredentials();
|
||||||
@ -50,7 +51,8 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._isServer) {
|
if (!this._isServer) {
|
||||||
/* For clients, we will always have either a given ca list or be using default one */
|
// For clients, we will always have either a given ca list or be using
|
||||||
|
// default one
|
||||||
shouldVerifyPeer = true;
|
shouldVerifyPeer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +75,7 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
this.encrypted.readable = true;
|
this.encrypted.readable = true;
|
||||||
this.encrypted.writable = true;
|
this.encrypted.writable = true;
|
||||||
|
|
||||||
this.cleartext.write = function (data) {
|
this.cleartext.write = function(data) {
|
||||||
if (typeof data == 'string') data = Buffer(data);
|
if (typeof data == 'string') data = Buffer(data);
|
||||||
debug('clearIn data');
|
debug('clearIn data');
|
||||||
self._clearInPending.push(data);
|
self._clearInPending.push(data);
|
||||||
@ -81,17 +83,17 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
return self._cleartextWriteState;
|
return self._cleartextWriteState;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cleartext.pause = function () {
|
this.cleartext.pause = function() {
|
||||||
debug('paused cleartext');
|
debug('paused cleartext');
|
||||||
self._cleartextWriteState = false;
|
self._cleartextWriteState = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cleartext.resume = function () {
|
this.cleartext.resume = function() {
|
||||||
debug('resumed cleartext');
|
debug('resumed cleartext');
|
||||||
self._cleartextWriteState = true;
|
self._cleartextWriteState = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cleartext.end = function (err) {
|
this.cleartext.end = function(err) {
|
||||||
debug('cleartext end');
|
debug('cleartext end');
|
||||||
if (!self._done) {
|
if (!self._done) {
|
||||||
self._ssl.shutdown();
|
self._ssl.shutdown();
|
||||||
@ -100,25 +102,25 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
self._destroy(err);
|
self._destroy(err);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.encrypted.write = function (data) {
|
this.encrypted.write = function(data) {
|
||||||
debug('encIn data');
|
debug('encIn data');
|
||||||
self._encInPending.push(data);
|
self._encInPending.push(data);
|
||||||
self._cycle();
|
self._cycle();
|
||||||
return self._encryptedWriteState;
|
return self._encryptedWriteState;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.encrypted.pause = function () {
|
this.encrypted.pause = function() {
|
||||||
if (typeof data == 'string') data = Buffer(data);
|
if (typeof data == 'string') data = Buffer(data);
|
||||||
debug('pause encrypted');
|
debug('pause encrypted');
|
||||||
self._encryptedWriteState = false;
|
self._encryptedWriteState = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.encrypted.resume = function () {
|
this.encrypted.resume = function() {
|
||||||
debug('resume encrypted');
|
debug('resume encrypted');
|
||||||
self._encryptedWriteState = true;
|
self._encryptedWriteState = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.encrypted.end = function (err) {
|
this.encrypted.end = function(err) {
|
||||||
debug('encrypted end');
|
debug('encrypted end');
|
||||||
if (!self._done) {
|
if (!self._done) {
|
||||||
self._ssl.shutdown();
|
self._ssl.shutdown();
|
||||||
@ -127,7 +129,7 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
self._destroy(err);
|
self._destroy(err);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cleartext.on('end', function (err) {
|
this.cleartext.on('end', function(err) {
|
||||||
debug('clearIn end');
|
debug('clearIn end');
|
||||||
if (!self._done) {
|
if (!self._done) {
|
||||||
self._ssl.shutdown();
|
self._ssl.shutdown();
|
||||||
@ -136,37 +138,39 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
self._destroy(err);
|
self._destroy(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.cleartext.on('close', function () {
|
this.cleartext.on('close', function() {
|
||||||
debug('source close');
|
debug('source close');
|
||||||
self.emit('close');
|
self.emit('close');
|
||||||
self._destroy();
|
self._destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.encrypted.on('end', function () {
|
this.encrypted.on('end', function() {
|
||||||
if (!self._done) {
|
if (!self._done) {
|
||||||
self._error(new Error('Encrypted stream ended before secure pair was done'));
|
self._error(
|
||||||
|
new Error('Encrypted stream ended before secure pair was done'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.encrypted.on('close', function () {
|
this.encrypted.on('close', function() {
|
||||||
if (!self._done) {
|
if (!self._done) {
|
||||||
self._error(new Error('Encrypted stream closed before secure pair was done'));
|
self._error(
|
||||||
|
new Error('Encrypted stream closed before secure pair was done'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.cleartext.on('drain', function () {
|
this.cleartext.on('drain', function() {
|
||||||
debug('source drain');
|
debug('source drain');
|
||||||
self._cycle();
|
self._cycle();
|
||||||
self.encrypted.resume();
|
self.encrypted.resume();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.encrypted.on('drain', function () {
|
this.encrypted.on('drain', function() {
|
||||||
debug('target drain');
|
debug('target drain');
|
||||||
self._cycle();
|
self._cycle();
|
||||||
self.cleartext.resume();
|
self.cleartext.resume();
|
||||||
});
|
});
|
||||||
|
|
||||||
process.nextTick(function () {
|
process.nextTick(function() {
|
||||||
self._ssl.start();
|
self._ssl.start();
|
||||||
self._cycle();
|
self._cycle();
|
||||||
});
|
});
|
||||||
@ -175,11 +179,12 @@ function SecurePair(credentials, isServer, shouldVerifyPeer) {
|
|||||||
util.inherits(SecurePair, events.EventEmitter);
|
util.inherits(SecurePair, events.EventEmitter);
|
||||||
|
|
||||||
|
|
||||||
exports.createSecurePair = function (credentials, isServer, shouldVerifyPeer) {
|
exports.createSecurePair = function(credentials, isServer, shouldVerifyPeer) {
|
||||||
var pair = new SecurePair(credentials, isServer, shouldVerifyPeer);
|
var pair = new SecurePair(credentials, isServer, shouldVerifyPeer);
|
||||||
return pair;
|
return pair;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to cycle OpenSSLs buffers in various directions.
|
* Attempt to cycle OpenSSLs buffers in various directions.
|
||||||
*
|
*
|
||||||
@ -189,24 +194,26 @@ exports.createSecurePair = function (credentials, isServer, shouldVerifyPeer) {
|
|||||||
* but consider things like mid-stream renegotiation of
|
* but consider things like mid-stream renegotiation of
|
||||||
* the ciphers.
|
* the ciphers.
|
||||||
*
|
*
|
||||||
* The four pipelines, using terminology of the client (server is just reversed):
|
* The four pipelines, using terminology of the client (server is just
|
||||||
|
* reversed):
|
||||||
* (1) Encrypted Output stream (Writing encrypted data to peer)
|
* (1) Encrypted Output stream (Writing encrypted data to peer)
|
||||||
* (2) Encrypted Input stream (Reading encrypted data from peer)
|
* (2) Encrypted Input stream (Reading encrypted data from peer)
|
||||||
* (3) Cleartext Output stream (Decrypted content from the peer)
|
* (3) Cleartext Output stream (Decrypted content from the peer)
|
||||||
* (4) Cleartext Input stream (Cleartext content to send to the peer)
|
* (4) Cleartext Input stream (Cleartext content to send to the peer)
|
||||||
*
|
*
|
||||||
* This function attempts to pull any available data out of the Cleartext
|
* This function attempts to pull any available data out of the Cleartext
|
||||||
* input stream (4), and the Encrypted input stream (2). Then it pushes
|
* input stream (4), and the Encrypted input stream (2). Then it pushes any
|
||||||
* any data available from the cleartext output stream (3), and finally
|
* data available from the cleartext output stream (3), and finally from the
|
||||||
* from the Encrypted output stream (1)
|
* Encrypted output stream (1)
|
||||||
*
|
*
|
||||||
* It is called whenever we do something with OpenSSL -- post reciving content,
|
* It is called whenever we do something with OpenSSL -- post reciving
|
||||||
* trying to flush, trying to change ciphers, or shutting down the connection.
|
* content, trying to flush, trying to change ciphers, or shutting down the
|
||||||
|
* connection.
|
||||||
*
|
*
|
||||||
* Because it is also called everywhere, we also check if the connection
|
* Because it is also called everywhere, we also check if the connection has
|
||||||
* has completed negotiation and emit 'secure' from here if it has.
|
* completed negotiation and emit 'secure' from here if it has.
|
||||||
*/
|
*/
|
||||||
SecurePair.prototype._cycle = function () {
|
SecurePair.prototype._cycle = function() {
|
||||||
if (this._done) {
|
if (this._done) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -281,7 +288,7 @@ SecurePair.prototype._cycle = function () {
|
|||||||
try {
|
try {
|
||||||
chunkBytes = reader(pool,
|
chunkBytes = reader(pool,
|
||||||
pool.used + bytesRead,
|
pool.used + bytesRead,
|
||||||
pool.length - pool.used - bytesRead)
|
pool.length - pool.used - bytesRead);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return self._error(e);
|
return self._error(e);
|
||||||
}
|
}
|
||||||
@ -301,16 +308,16 @@ SecurePair.prototype._cycle = function () {
|
|||||||
// From the user's perspective this occurs as a 'data' event
|
// From the user's perspective this occurs as a 'data' event
|
||||||
// on the pair.cleartext.
|
// on the pair.cleartext.
|
||||||
mover(
|
mover(
|
||||||
function (pool, offset, length) {
|
function(pool, offset, length) {
|
||||||
debug('reading from clearOut');
|
debug('reading from clearOut');
|
||||||
return self._ssl.clearOut(pool, offset, length);
|
return self._ssl.clearOut(pool, offset, length);
|
||||||
},
|
},
|
||||||
function (chunk) {
|
function(chunk) {
|
||||||
self.cleartext.emit('data', chunk);
|
self.cleartext.emit('data', chunk);
|
||||||
},
|
},
|
||||||
function (bytesRead) {
|
function(bytesRead) {
|
||||||
return bytesRead > 0 && self._cleartextWriteState === true;
|
return bytesRead > 0 && self._cleartextWriteState === true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Move encrypted data to the stream. From the user's perspective this
|
// Move encrypted data to the stream. From the user's perspective this
|
||||||
// occurs as a 'data' event on the pair.encrypted. Usually the application
|
// occurs as a 'data' event on the pair.encrypted. Usually the application
|
||||||
@ -321,16 +328,16 @@ SecurePair.prototype._cycle = function () {
|
|||||||
// });
|
// });
|
||||||
//
|
//
|
||||||
mover(
|
mover(
|
||||||
function (pool, offset, length) {
|
function(pool, offset, length) {
|
||||||
debug('reading from encOut');
|
debug('reading from encOut');
|
||||||
return self._ssl.encOut(pool, offset, length);
|
return self._ssl.encOut(pool, offset, length);
|
||||||
},
|
},
|
||||||
function (chunk) {
|
function(chunk) {
|
||||||
self.encrypted.emit('data', chunk);
|
self.encrypted.emit('data', chunk);
|
||||||
},
|
},
|
||||||
function (bytesRead) {
|
function(bytesRead) {
|
||||||
return bytesRead > 0 && self._encryptedWriteState === true;
|
return bytesRead > 0 && self._encryptedWriteState === true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this._secureEstablished && this._ssl.isInitFinished()) {
|
if (!this._secureEstablished && this._ssl.isInitFinished()) {
|
||||||
this._secureEstablished = true;
|
this._secureEstablished = true;
|
||||||
@ -341,7 +348,7 @@ SecurePair.prototype._cycle = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
SecurePair.prototype._destroy = function (err) {
|
SecurePair.prototype._destroy = function(err) {
|
||||||
if (!this._done) {
|
if (!this._done) {
|
||||||
this._done = true;
|
this._done = true;
|
||||||
this._ssl.close();
|
this._ssl.close();
|
||||||
@ -351,12 +358,12 @@ SecurePair.prototype._destroy = function (err) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
SecurePair.prototype._error = function (err) {
|
SecurePair.prototype._error = function(err) {
|
||||||
this.emit('error', err);
|
this.emit('error', err);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
SecurePair.prototype.getPeerCertificate = function (err) {
|
SecurePair.prototype.getPeerCertificate = function(err) {
|
||||||
if (this._ssl) {
|
if (this._ssl) {
|
||||||
return this._ssl.getPeerCertificate();
|
return this._ssl.getPeerCertificate();
|
||||||
} else {
|
} else {
|
||||||
@ -365,7 +372,7 @@ SecurePair.prototype.getPeerCertificate = function (err) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
SecurePair.prototype.getCipher = function (err) {
|
SecurePair.prototype.getCipher = function(err) {
|
||||||
if (this._ssl) {
|
if (this._ssl) {
|
||||||
return this._ssl.getCurrentCipher();
|
return this._ssl.getCurrentCipher();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
var events = require('events');
|
var events = require('events');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
function Stream () {
|
function Stream() {
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
}
|
}
|
||||||
util.inherits(Stream, events.EventEmitter);
|
util.inherits(Stream, events.EventEmitter);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user