UNPKG

4.37 kBJavaScriptView Raw
1var EventEmitter = require('events').EventEmitter;
2var path = require('path');
3var util = require('util');
4var uuid = require('node-uuid');
5var Logger = require('./logger');
6var WebSocket = require('./web_socket');
7
8function calculatePeerUrl(url, name){
9 var wsUrl = url.replace(/^http/, 'ws');
10 var peerPath = '/peers/' + name;
11 if(wsUrl.indexOf('/', wsUrl.length - 1) === -1) {
12 wsUrl = wsUrl + peerPath;
13 } else {
14 wsUrl = wsUrl.slice(0, wsUrl.length - 1) + peerPath;
15 }
16 return wsUrl;
17}
18
19
20var PeerClient = module.exports = function(url, server) {
21 var wsUrl = calculatePeerUrl(url, server._name);
22 this.reconnect = {
23 min: 100,
24 max: 30000, // max amount of time allowed to backoff
25 maxRandomOffset: 1000, // max amount of time
26 };
27
28 this.url = wsUrl;
29
30 this.server = server.httpServer.spdyServer;
31 this.connected = false;
32 this.retryCount = 0;
33 this.log = server.log || new Logger();
34 this._backoffTimer = null;
35 this._stopped = false;
36
37 // create a unique connection id peer connection, used to associate initiaion request
38 this.connectionId = null;
39 this.ws = new WebSocket(this._createNewUrl(), {});
40
41 EventEmitter.call(this);
42};
43util.inherits(PeerClient, EventEmitter);
44
45PeerClient.calculatePeerUrl = calculatePeerUrl;
46
47PeerClient.prototype._createNewUrl = function() {
48 this.connectionId = uuid.v4();
49 return this.url + '?connectionId=' + this.connectionId;
50};
51
52PeerClient.prototype.properties = function() {
53 return {
54 url: this.url,
55 connectionId: this.connectionId,
56 };
57};
58
59PeerClient.prototype.start = function() {
60 this._stopped = false; // If previously closed, reset stopped flag
61 this._createSocket();
62};
63
64// Close and stop reconnecting
65PeerClient.prototype.close = function() {
66 clearTimeout(this._backoffTimer);
67 this._stopped = true;
68 this.ws.close();
69};
70
71PeerClient.prototype._createSocket = function() {
72 var self = this;
73
74 if (this.backoffTimer) {
75 clearTimeout(this.backoffTimer);
76 }
77
78 // once peer is closed dont create new socket
79 if (this._stopped) {
80 return;
81 }
82
83 var backoff = this.generateBackoff(this.retryCount);
84 this._backoffTimer = setTimeout(function(){
85 // create a new connection id
86 self.ws.setAddress(self._createNewUrl());
87 if (self.retryCount === 0) {
88 self.ws.on('open', function onOpen(socket) {
89 self.checkServerReq();
90 self.emit('connecting');
91 self.server.emit('connection', socket);
92 self.log.emit('log', 'peer-client', 'WebSocket to peer established (' + self.url + ')');
93 });
94
95 self.ws.on('error', function onError(err) {
96 self.connected = false;
97 self.log.emit('log', 'peer-client', 'Peer connection error (' + self.url + '): ' + err);
98 self.emit('closed');
99 reconnect(err);
100 });
101
102 self.ws.on('close', function(code, message) {
103 //if (self.retryCount > 0) throw new Error('wtf');
104 self.connected = false;
105 self.log.emit('log', 'peer-client', 'Peer connection closed (' + self.url + '): ' + code + ' - ' + message);
106 self.emit('closed');
107 reconnect();
108 });
109 }
110
111 self.ws.start();
112 }, backoff);
113
114 function reconnect(err) {
115 self.retryCount++;
116 self._createSocket();
117 }
118};
119
120PeerClient.prototype.checkServerReq = function() {
121 var self = this;
122
123 // remove any previous request listeners
124 if (self.onRequest) {
125 this.server.removeListener('request', self.onRequest);
126 }
127
128 // /_initiate_peer/{connection-id}
129 this.onRequest = function(req, res) {
130 if (req.url === '/_initiate_peer/' + self.connectionId) {
131 self.connected = true;
132 self.retryCount = 0;
133 self.emit('connected');
134 self.log.emit('log', 'peer-client', 'Peer connection established (' + self.url + ')');
135
136 res.statusCode = 200;
137 res.end();
138
139 // remove request listener
140 self.server.removeListener('request', self.onRequest);
141
142 // set up exchange of reactive queries.
143 }
144 };
145
146 this.server.on('request', this.onRequest);
147};
148
149PeerClient.prototype.generateBackoff = function(attempt) {
150 if (attempt === 0) {
151 return 0;
152 }
153
154 var random = parseInt(Math.random() * this.reconnect.maxRandomOffset);
155 var backoff = (Math.pow(2, attempt) * this.reconnect.min);
156 if (backoff > this.reconnect.max) {
157 return this.reconnect.max + random;
158 } else {
159 return backoff + random;
160 }
161};