UNPKG

wikimedia-kad-fork

Version:

implementation of the kademlia dht for node

139 lines (112 loc) 3.13 kB
'use strict'; var assert = require('assert'); var inherits = require('util').inherits; var clarinet = require('clarinet'); var net = require('net'); var AddressPortContact = require('../contacts/address-port-contact'); var RPC = require('../rpc'); /** * Represents an RPC interface over TCP * @constructor * @extends {RPC} * @param {AddressPortContact} contact - Your node's contact instance * @param {Object} options */ function TCPTransport(contact, options) { if (!(this instanceof TCPTransport)) { return new TCPTransport(contact, options); } assert(contact instanceof AddressPortContact, 'Invalid contact supplied'); RPC.call(this, contact, options); } inherits(TCPTransport, RPC); /** * Create a TCP server * @private * @param {Function} done */ TCPTransport.prototype._open = function(done) { var self = this; this._socket = net.createServer(this._handleConnection.bind(this)); this._queuedResponses = {}; this._socket.on('error', function(err) { self._log.error('rpc encountered and error: %s', err.message); }); this._socket.on('listening', done); this._socket.listen(this._contact.port, this._contact.address); }; /** * Send a RPC to the given contact * @private * @param {Buffer} data * @param {Contact} contact */ TCPTransport.prototype._send = function(data, contact) { var self = this; var parsed = JSON.parse(data.toString()); if (this._queuedResponses[parsed.id]) { this._queuedResponses[parsed.id].end(data); delete this._queuedResponses[parsed.id]; return; } var sock = net.createConnection(contact.port, contact.address); sock.on('error', function(err) { self._log.error('error connecting to peer', err); }); this._queuedResponses[parsed.id] = sock; this._handleConnection(sock); sock.write(data); }; /** * Close the underlying socket * @private */ TCPTransport.prototype._close = function() { this._socket.close(); }; /** * Handle incoming connection * @private * @param {Object} connection */ TCPTransport.prototype._handleConnection = function(connection) { var self = this; var parser = clarinet.createStream(); var buffer = ''; var opened = 0; var closed = 0; parser.on('openobject', function() { opened++; }); parser.on('closeobject', function() { closed++; if (opened === closed) { try { var parsed = JSON.parse(buffer); if (parsed.id && !self._queuedResponses[parsed.id]) { self._queuedResponses[parsed.id] = connection; } } catch(err) { // noop } self.receive(new Buffer(buffer)); buffer = ''; opened = 0; closed = 0; } }); parser.on('error', function(err) { self._log.error(err.message); self._log.warn('failed to parse incoming message'); connection.end(); }); connection.on('error', function(err) { self._log.error(err.message); self._log.warn('error communicating with peer'); }); connection.on('data', function(data) { buffer += data.toString('utf8'); parser.write(data.toString('utf8')); }); }; module.exports = TCPTransport;