Improve net/ documentation

This commit is contained in:
Kagami Hiiragi 2015-02-10 15:57:55 +03:00
parent 084ddc8084
commit 2ae5d40463
4 changed files with 91 additions and 59 deletions

View File

@ -1,6 +1,9 @@
/** /**
* Networking base module. You should import some transport instead in * Networking base module. Defines base transport interface, useful for
* order to connect/accept connections to/from other nodes. * 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 * @module bitmessage/net/base
*/ */
// TODO(Kagami): Write some sort of tutorial. // TODO(Kagami): Write some sort of tutorial.
@ -13,7 +16,9 @@ var PPromise = require("../platform").Promise;
var structs = require("../structs"); 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 * @constructor
* @static * @static
*/ */
@ -25,8 +30,10 @@ inherits(BaseTransport, EventEmitter);
/** /**
* Do the transport-specific bootstrap process and return promise that * Do the transport-specific bootstrap process and return promise that
* contains discovered nodes when fulfilled. * contains discovered nodes when fulfilled (both modes).
* @return {Promise.<Array.>} * NOTE: Do not use nodes received by this method in `addr` messages!
* This is meaningless.
* @return {Promise.<Array>}
* @abstract * @abstract
*/ */
BaseTransport.prototype.bootstrap = function() { BaseTransport.prototype.bootstrap = function() {
@ -34,9 +41,9 @@ BaseTransport.prototype.bootstrap = function() {
}; };
/** /**
* Connect to the transport-specific address. * Connect to the transport-specific address. Enters client mode. Should
* Should emit `open` event after successful connect and `established` * emit `open` event after successful connect and `established` event
* event after `verack` messages exchange. * after `verack` messages exchange.
* @abstract * @abstract
*/ */
BaseTransport.prototype.connect = function() { BaseTransport.prototype.connect = function() {
@ -44,9 +51,9 @@ BaseTransport.prototype.connect = function() {
}; };
/** /**
* Listen for the transport-specific incoming connections. * Listen for the transport-specific incoming connections. Enters server
* Should emit `connection` event with a transport instance for each new * mode. Should emit `connection` event with a transport instance for
* connection. * each new connection.
* @abstract * @abstract
*/ */
BaseTransport.prototype.listen = function() { BaseTransport.prototype.listen = function() {
@ -55,9 +62,9 @@ BaseTransport.prototype.listen = function() {
/** /**
* Send [message]{@link module:bitmessage/structs.message} over the * 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|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) * argument is a string)
* @abstract * @abstract
*/ */
@ -67,9 +74,9 @@ BaseTransport.prototype.send = function() {
/** /**
* Send [message]{@link module:bitmessage/structs.message} to all * 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|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) * argument is a string)
* @abstract * @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 * @abstract
*/ */
BaseTransport.prototype.close = function() { BaseTransport.prototype.close = function() {
throw new Error("Not implemented"); throw new Error("Not implemented");
}; };
// Static helpers. // Static private helpers.
BaseTransport._getmsg = function(args) { BaseTransport._getmsg = function(args) {
if (typeof args[0] === "string") { if (typeof args[0] === "string") {

View File

@ -1,6 +1,8 @@
/** /**
* TCP transport. Should be compatible with PyBitmessage. Available only * TCP transport compatible with PyBitmessage. Available only for Node
* for Node.js. * platform.
* **NOTE**: `TcpTransport` is exported as a module.
* @example var TcpTransport = require("bitmessage/net/tcp");
* @module bitmessage/net/tcp * @module bitmessage/net/tcp
*/ */
@ -20,25 +22,26 @@ var getmsg = BaseTransport._getmsg;
var unmap = BaseTransport._unmap; var unmap = BaseTransport._unmap;
/** /**
* TCP transport constructor. * TCP transport class. Implements
* [base transport interface]{@link module:bitmessage/net/base.BaseTransport}.
* @constructor * @constructor
* @static * @static
*/ */
function Transport(opts) { function TcpTransport(opts) {
Transport.super_.call(this); TcpTransport.super_.call(this);
objectAssign(this, opts); objectAssign(this, opts);
this.seeds = this.seeds || []; this.seeds = this.seeds || [];
this.dnsSeeds = this.dnsSeeds || []; this.dnsSeeds = this.dnsSeeds || [];
this._clients = {}; this._clients = {};
} }
inherits(Transport, BaseTransport); inherits(TcpTransport, BaseTransport);
function getfrom(client) { function getfrom(client) {
return unmap(client.remoteAddress) + ":" + client.remotePort; return unmap(client.remoteAddress) + ":" + client.remotePort;
} }
Transport.prototype.sendVersion = function() { TcpTransport.prototype._sendVersion = function() {
return this.send(messages.version.encode({ return this.send(messages.version.encode({
services: this.services, services: this.services,
userAgent: this.userAgent, 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; var self = this;
self._client = client; self._client = client;
var cache = Buffer(0); var cache = Buffer(0);
@ -68,7 +71,7 @@ Transport.prototype._setupClient = function(client, accepted) {
// accepted sockets but let's be sure. // accepted sockets but let's be sure.
if (!accepted) { if (!accepted) {
self.emit("open"); self.emit("open");
self.sendVersion(); self._sendVersion();
} }
}); });
@ -113,7 +116,7 @@ Transport.prototype._setupClient = function(client, accepted) {
self.send("verack"); self.send("verack");
verackSent = true; verackSent = true;
if (accepted) { if (accepted) {
self.sendVersion(); self._sendVersion();
} else if (verackReceived) { } else if (verackReceived) {
self.emit("established"); 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 promises = this.dnsSeeds.map(resolveDnsSeed);
var hardcodedNodes = this.seeds; var hardcodedNodes = this.seeds;
// FIXME(Kagami): Filter incorrect/private IP range nodes? // 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._client, "Already connected");
assert(!this._server, "Already listening"); assert(!this._server, "Already listening");
this._setupClient(net.connect.apply(null, arguments)); 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._client, "Already connected");
assert(!this._server, "Already listening"); assert(!this._server, "Already listening");
@ -241,7 +253,7 @@ Transport.prototype.listen = function() {
}); });
}; };
Transport.prototype.send = function() { TcpTransport.prototype.send = function() {
if (this._client) { if (this._client) {
this._client.write(getmsg(arguments)); this._client.write(getmsg(arguments));
} else { } else {
@ -249,7 +261,7 @@ Transport.prototype.send = function() {
} }
}; };
Transport.prototype.broadcast = function() { TcpTransport.prototype.broadcast = function() {
var data = getmsg(arguments); var data = getmsg(arguments);
if (this._server) { if (this._server) {
Object.keys(this._clients).forEach(function(ip) { 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) { if (this._client) {
this._client.end(); this._client.end();
} else if (this._server) { } else if (this._server) {
@ -271,4 +283,4 @@ Transport.prototype.close = function() {
} }
}; };
module.exports = Transport; module.exports = TcpTransport;

View File

@ -12,19 +12,19 @@ var structs = require("../structs");
var messages = require("../messages"); var messages = require("../messages");
var BaseTransport = require("./base"); var BaseTransport = require("./base");
function Transport(opts) { function WsTransport(opts) {
Transport.super_.call(this); WsTransport.super_.call(this);
objectAssign(this, opts); objectAssign(this, opts);
this.seeds = this.seeds || []; this.seeds = this.seeds || [];
} }
inherits(Transport, BaseTransport); inherits(WsTransport, BaseTransport);
Transport.prototype.bootstrap = function() { WsTransport.prototype.bootstrap = function() {
return Promise.resolve([].concat(this.seeds)); return Promise.resolve([].concat(this.seeds));
}; };
Transport.prototype.connect = function(url, protocols) { WsTransport.prototype.connect = function(url, protocols) {
var self = this; var self = this;
assert(!self._client, "Already connected"); 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) { if (this._client) {
this._client.send(BaseTransport._getmsg(arguments)); this._client.send(BaseTransport._getmsg(arguments));
} else { } else {
@ -103,10 +103,10 @@ Transport.prototype.send = function() {
} }
}; };
Transport.prototype.close = function() { WsTransport.prototype.close = function() {
if (this._client) { if (this._client) {
this._client.close(); this._client.close();
} }
}; };
module.exports = Transport; module.exports = WsTransport;

View File

@ -1,7 +1,9 @@
/** /**
* WebSocket transport. Needed because browsers can't handle TCP sockets * WebSocket transport. Needed because browsers can't handle TCP sockets
* so we use separate WebSocket server to proxy messages into TCP data * 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 * @module bitmessage/net/ws
*/ */
@ -21,23 +23,24 @@ var getmsg = BaseTransport._getmsg;
var unmap = BaseTransport._unmap; var unmap = BaseTransport._unmap;
/** /**
* WebSocket transport constructor. * WebSocket transport class. Implements
* [base transport interface]{@link module:bitmessage/net/base.BaseTransport}.
* @constructor * @constructor
* @static * @static
*/ */
function Transport(opts) { function WsTransport(opts) {
Transport.super_.call(this); WsTransport.super_.call(this);
objectAssign(this, opts); objectAssign(this, opts);
this.seeds = this.seeds || []; this.seeds = this.seeds || [];
} }
inherits(Transport, BaseTransport); inherits(WsTransport, BaseTransport);
function getfrom(client) { function getfrom(client) {
return unmap(client._socket.remoteAddress) + ":" + client._socket.remotePort; return unmap(client._socket.remoteAddress) + ":" + client._socket.remotePort;
} }
Transport.prototype.sendVersion = function() { WsTransport.prototype._sendVersion = function() {
return this.send(messages.version.encode({ return this.send(messages.version.encode({
services: this.services, services: this.services,
userAgent: this.userAgent, userAgent: this.userAgent,
@ -48,7 +51,7 @@ Transport.prototype.sendVersion = function() {
})); }));
}; };
Transport.prototype._handleTimeout = function() { WsTransport.prototype._handleTimeout = function() {
var client = this._client; var client = this._client;
// TODO(Kagami): We may also want to close connection if it wasn't // TODO(Kagami): We may also want to close connection if it wasn't
// established within minute. // 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; var self = this;
self._client = client; self._client = client;
var verackSent = false; var verackSent = false;
@ -79,7 +82,7 @@ Transport.prototype._setupClient = function(client, accepted) {
// `_setupClient` is called. // `_setupClient` is called.
self._handleTimeout(); self._handleTimeout();
self.emit("open"); self.emit("open");
self.sendVersion(); self._sendVersion();
} }
}); });
@ -113,7 +116,7 @@ Transport.prototype._setupClient = function(client, accepted) {
self.send("verack"); self.send("verack");
verackSent = true; verackSent = true;
if (accepted) { if (accepted) {
self.sendVersion(); self._sendVersion();
} else if (verackReceived) { } else if (verackReceived) {
established = true; established = true;
self.emit("established"); 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)); 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._client, "Already connected");
assert(!this._server, "Already listening"); assert(!this._server, "Already listening");
// `new` doesn't work with `apply`, so passing all possible arguments // `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)); 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._client, "Already connected");
assert(!this._server, "Already listening"); 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) { if (this._client) {
// TODO(Kagami): `mask: true` doesn't work with Chromium 40. File a // TODO(Kagami): `mask: true` doesn't work with Chromium 40. File a
// bug to ws bugtracker. // bug to ws bugtracker.
@ -205,7 +218,7 @@ Transport.prototype.send = function() {
} }
}; };
Transport.prototype.broadcast = function() { WsTransport.prototype.broadcast = function() {
var data = getmsg(arguments); var data = getmsg(arguments);
if (this._server) { if (this._server) {
this._server.clients.forEach(function(client) { 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) { if (this._client) {
this._client.close(); this._client.close();
} else if (this._server) { } else if (this._server) {
@ -227,4 +240,4 @@ Transport.prototype.close = function() {
} }
}; };
module.exports = Transport; module.exports = WsTransport;