diff --git a/lib/net/base.js b/lib/net/base.js index b23a05c..be2a2d1 100644 --- a/lib/net/base.js +++ b/lib/net/base.js @@ -1,6 +1,9 @@ /** - * Networking base module. You should import some transport instead in - * order to connect/accept connections to/from other nodes. + * Networking base module. Defines base transport interface, useful for + * implementing new transports. End-users should import some transport + * instead in order to connect/accept connections to/from other nodes. + * **NOTE**: `BaseTransport` is exported as a module. + * @example var BaseTransport = require("bitmessage/net/base"); * @module bitmessage/net/base */ // TODO(Kagami): Write some sort of tutorial. @@ -13,7 +16,9 @@ var PPromise = require("../platform").Promise; var structs = require("../structs"); /** - * Network transport base class. + * Base transport class. Allows to use single class for both client and + * server modes (as separate instances). + * @param {?Object} opts - Transport options * @constructor * @static */ @@ -25,8 +30,10 @@ inherits(BaseTransport, EventEmitter); /** * Do the transport-specific bootstrap process and return promise that - * contains discovered nodes when fulfilled. - * @return {Promise.} + * contains discovered nodes when fulfilled (both modes). + * NOTE: Do not use nodes received by this method in `addr` messages! + * This is meaningless. + * @return {Promise.} * @abstract */ BaseTransport.prototype.bootstrap = function() { @@ -34,9 +41,9 @@ BaseTransport.prototype.bootstrap = function() { }; /** - * Connect to the transport-specific address. - * Should emit `open` event after successful connect and `established` - * event after `verack` messages exchange. + * Connect to the transport-specific address. Enters client mode. Should + * emit `open` event after successful connect and `established` event + * after `verack` messages exchange. * @abstract */ BaseTransport.prototype.connect = function() { @@ -44,9 +51,9 @@ BaseTransport.prototype.connect = function() { }; /** - * Listen for the transport-specific incoming connections. - * Should emit `connection` event with a transport instance for each new - * connection. + * Listen for the transport-specific incoming connections. Enters server + * mode. Should emit `connection` event with a transport instance for + * each new connection. * @abstract */ BaseTransport.prototype.listen = function() { @@ -55,9 +62,9 @@ BaseTransport.prototype.listen = function() { /** * Send [message]{@link module:bitmessage/structs.message} over the - * wire (client mode). + * wire (client mode only). * @param {(Buffer|string)} msg - Encoded message or command string - * @param (?Buffer} payload - Message payload (used if the first + * @param {?Buffer} payload - Message payload (used if the first * argument is a string) * @abstract */ @@ -67,9 +74,9 @@ BaseTransport.prototype.send = function() { /** * Send [message]{@link module:bitmessage/structs.message} to all - * connected clients (server mode). + * connected clients (server mode only). * @param {(Buffer|string)} msg - Encoded message or command string - * @param (?Buffer} payload - Message payload (used if the first + * @param {?Buffer} payload - Message payload (used if the first * argument is a string) * @abstract */ @@ -78,14 +85,14 @@ BaseTransport.prototype.broadcast = function() { }; /** - * Close connection(s) and/or stop listening. + * Close connection(s) and/or stop listening (both modes). * @abstract */ BaseTransport.prototype.close = function() { throw new Error("Not implemented"); }; -// Static helpers. +// Static private helpers. BaseTransport._getmsg = function(args) { if (typeof args[0] === "string") { diff --git a/lib/net/tcp.js b/lib/net/tcp.js index e9fc4eb..58d70e2 100644 --- a/lib/net/tcp.js +++ b/lib/net/tcp.js @@ -1,6 +1,8 @@ /** - * TCP transport. Should be compatible with PyBitmessage. Available only - * for Node.js. + * TCP transport compatible with PyBitmessage. Available only for Node + * platform. + * **NOTE**: `TcpTransport` is exported as a module. + * @example var TcpTransport = require("bitmessage/net/tcp"); * @module bitmessage/net/tcp */ @@ -20,25 +22,26 @@ var getmsg = BaseTransport._getmsg; var unmap = BaseTransport._unmap; /** - * TCP transport constructor. + * TCP transport class. Implements + * [base transport interface]{@link module:bitmessage/net/base.BaseTransport}. * @constructor * @static */ -function Transport(opts) { - Transport.super_.call(this); +function TcpTransport(opts) { + TcpTransport.super_.call(this); objectAssign(this, opts); this.seeds = this.seeds || []; this.dnsSeeds = this.dnsSeeds || []; this._clients = {}; } -inherits(Transport, BaseTransport); +inherits(TcpTransport, BaseTransport); function getfrom(client) { return unmap(client.remoteAddress) + ":" + client.remotePort; } -Transport.prototype.sendVersion = function() { +TcpTransport.prototype._sendVersion = function() { return this.send(messages.version.encode({ services: this.services, userAgent: this.userAgent, @@ -49,7 +52,7 @@ Transport.prototype.sendVersion = function() { })); }; -Transport.prototype._setupClient = function(client, accepted) { +TcpTransport.prototype._setupClient = function(client, accepted) { var self = this; self._client = client; var cache = Buffer(0); @@ -68,7 +71,7 @@ Transport.prototype._setupClient = function(client, accepted) { // accepted sockets but let's be sure. if (!accepted) { self.emit("open"); - self.sendVersion(); + self._sendVersion(); } }); @@ -113,7 +116,7 @@ Transport.prototype._setupClient = function(client, accepted) { self.send("verack"); verackSent = true; if (accepted) { - self.sendVersion(); + self._sendVersion(); } else if (verackReceived) { self.emit("established"); } @@ -176,7 +179,7 @@ function resolveDnsSeed(seed) { }); } -Transport.prototype.bootstrap = function() { +TcpTransport.prototype.bootstrap = function() { var promises = this.dnsSeeds.map(resolveDnsSeed); var hardcodedNodes = this.seeds; // FIXME(Kagami): Filter incorrect/private IP range nodes? @@ -190,13 +193,22 @@ Transport.prototype.bootstrap = function() { }); }; -Transport.prototype.connect = function() { +/** + * Connect to a TCP node. Connection arguments are the same as for + * [net.connect](http://nodejs.org/api/net.html#net_net_connect_port_host_connectlistener). + */ +TcpTransport.prototype.connect = function() { assert(!this._client, "Already connected"); assert(!this._server, "Already listening"); this._setupClient(net.connect.apply(null, arguments)); }; -Transport.prototype.listen = function() { +/** + * Listen for incoming TCP connections. Listen arguments are the same as + * for + * [server.listen](http://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback). + */ +TcpTransport.prototype.listen = function() { assert(!this._client, "Already connected"); assert(!this._server, "Already listening"); @@ -241,7 +253,7 @@ Transport.prototype.listen = function() { }); }; -Transport.prototype.send = function() { +TcpTransport.prototype.send = function() { if (this._client) { this._client.write(getmsg(arguments)); } else { @@ -249,7 +261,7 @@ Transport.prototype.send = function() { } }; -Transport.prototype.broadcast = function() { +TcpTransport.prototype.broadcast = function() { var data = getmsg(arguments); if (this._server) { Object.keys(this._clients).forEach(function(ip) { @@ -260,7 +272,7 @@ Transport.prototype.broadcast = function() { } }; -Transport.prototype.close = function() { +TcpTransport.prototype.close = function() { if (this._client) { this._client.end(); } else if (this._server) { @@ -271,4 +283,4 @@ Transport.prototype.close = function() { } }; -module.exports = Transport; +module.exports = TcpTransport; diff --git a/lib/net/ws.browser.js b/lib/net/ws.browser.js index 3daab67..7832c45 100644 --- a/lib/net/ws.browser.js +++ b/lib/net/ws.browser.js @@ -12,19 +12,19 @@ var structs = require("../structs"); var messages = require("../messages"); var BaseTransport = require("./base"); -function Transport(opts) { - Transport.super_.call(this); +function WsTransport(opts) { + WsTransport.super_.call(this); objectAssign(this, opts); this.seeds = this.seeds || []; } -inherits(Transport, BaseTransport); +inherits(WsTransport, BaseTransport); -Transport.prototype.bootstrap = function() { +WsTransport.prototype.bootstrap = function() { return Promise.resolve([].concat(this.seeds)); }; -Transport.prototype.connect = function(url, protocols) { +WsTransport.prototype.connect = function(url, protocols) { var self = this; assert(!self._client, "Already connected"); @@ -95,7 +95,7 @@ Transport.prototype.connect = function(url, protocols) { }; }; -Transport.prototype.send = function() { +WsTransport.prototype.send = function() { if (this._client) { this._client.send(BaseTransport._getmsg(arguments)); } else { @@ -103,10 +103,10 @@ Transport.prototype.send = function() { } }; -Transport.prototype.close = function() { +WsTransport.prototype.close = function() { if (this._client) { this._client.close(); } }; -module.exports = Transport; +module.exports = WsTransport; diff --git a/lib/net/ws.js b/lib/net/ws.js index 737c51c..2ce0357 100644 --- a/lib/net/ws.js +++ b/lib/net/ws.js @@ -1,7 +1,9 @@ /** * WebSocket transport. Needed because browsers can't handle TCP sockets * so we use separate WebSocket server to proxy messages into TCP data - * packets. + * packets. Available for both Node.js and Browser platforms. + * **NOTE**: `WsTransport` is exported as a module. + * @example var WsTransport = require("bitmessage/net/ws"); * @module bitmessage/net/ws */ @@ -21,23 +23,24 @@ var getmsg = BaseTransport._getmsg; var unmap = BaseTransport._unmap; /** - * WebSocket transport constructor. + * WebSocket transport class. Implements + * [base transport interface]{@link module:bitmessage/net/base.BaseTransport}. * @constructor * @static */ -function Transport(opts) { - Transport.super_.call(this); +function WsTransport(opts) { + WsTransport.super_.call(this); objectAssign(this, opts); this.seeds = this.seeds || []; } -inherits(Transport, BaseTransport); +inherits(WsTransport, BaseTransport); function getfrom(client) { return unmap(client._socket.remoteAddress) + ":" + client._socket.remotePort; } -Transport.prototype.sendVersion = function() { +WsTransport.prototype._sendVersion = function() { return this.send(messages.version.encode({ services: this.services, userAgent: this.userAgent, @@ -48,7 +51,7 @@ Transport.prototype.sendVersion = function() { })); }; -Transport.prototype._handleTimeout = function() { +WsTransport.prototype._handleTimeout = function() { var client = this._client; // TODO(Kagami): We may also want to close connection if it wasn't // established within minute. @@ -63,7 +66,7 @@ Transport.prototype._handleTimeout = function() { }); }; -Transport.prototype._setupClient = function(client, accepted) { +WsTransport.prototype._setupClient = function(client, accepted) { var self = this; self._client = client; var verackSent = false; @@ -79,7 +82,7 @@ Transport.prototype._setupClient = function(client, accepted) { // `_setupClient` is called. self._handleTimeout(); self.emit("open"); - self.sendVersion(); + self._sendVersion(); } }); @@ -113,7 +116,7 @@ Transport.prototype._setupClient = function(client, accepted) { self.send("verack"); verackSent = true; if (accepted) { - self.sendVersion(); + self._sendVersion(); } else if (verackReceived) { established = true; self.emit("established"); @@ -138,11 +141,15 @@ Transport.prototype._setupClient = function(client, accepted) { }); }; -Transport.prototype.bootstrap = function() { +WsTransport.prototype.bootstrap = function() { return PPromise.resolve([].concat(this.seeds)); }; -Transport.prototype.connect = function(address, protocols, options) { +/** + * Connect to a WebSocket node. Connection arguments are the same as for + * [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). + */ +WsTransport.prototype.connect = function(address, protocols, options) { assert(!this._client, "Already connected"); assert(!this._server, "Already listening"); // `new` doesn't work with `apply`, so passing all possible arguments @@ -150,7 +157,13 @@ Transport.prototype.connect = function(address, protocols, options) { this._setupClient(new WebSocket(address, protocols, options)); }; -Transport.prototype.listen = function(options, callback) { +/** + * Listen for incoming WebSocket connections. Listen arguments are the + * same as for + * [WebSocketServer](https://github.com/websockets/ws#server-example). + * Available only for Node platform. + */ +WsTransport.prototype.listen = function(options, callback) { assert(!this._client, "Already connected"); assert(!this._server, "Already listening"); @@ -195,7 +208,7 @@ Transport.prototype.listen = function(options, callback) { }); }; -Transport.prototype.send = function() { +WsTransport.prototype.send = function() { if (this._client) { // TODO(Kagami): `mask: true` doesn't work with Chromium 40. File a // bug to ws bugtracker. @@ -205,7 +218,7 @@ Transport.prototype.send = function() { } }; -Transport.prototype.broadcast = function() { +WsTransport.prototype.broadcast = function() { var data = getmsg(arguments); if (this._server) { this._server.clients.forEach(function(client) { @@ -216,7 +229,7 @@ Transport.prototype.broadcast = function() { } }; -Transport.prototype.close = function() { +WsTransport.prototype.close = function() { if (this._client) { this._client.close(); } else if (this._server) { @@ -227,4 +240,4 @@ Transport.prototype.close = function() { } }; -module.exports = Transport; +module.exports = WsTransport;