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
* 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.<Array.>}
* contains discovered nodes when fulfilled (both modes).
* NOTE: Do not use nodes received by this method in `addr` messages!
* This is meaningless.
* @return {Promise.<Array>}
* @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") {

View File

@ -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;

View File

@ -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;

View File

@ -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;