Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/net/websocket.js
2868 views
1
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Definition of the WebSocket class. A WebSocket provides a
17
* bi-directional, full-duplex communications channel, over a single TCP socket.
18
*
19
* See http://dev.w3.org/html5/websockets/
20
* for the full HTML5 WebSocket API.
21
*
22
* Typical usage will look like this:
23
*
24
* var ws = new goog.net.WebSocket();
25
*
26
* var handler = new goog.events.EventHandler();
27
* handler.listen(ws, goog.net.WebSocket.EventType.OPENED, onOpen);
28
* handler.listen(ws, goog.net.WebSocket.EventType.MESSAGE, onMessage);
29
*
30
* try {
31
* ws.open('ws://127.0.0.1:4200');
32
* } catch (e) {
33
* ...
34
* }
35
*
36
*/
37
38
goog.provide('goog.net.WebSocket');
39
goog.provide('goog.net.WebSocket.ErrorEvent');
40
goog.provide('goog.net.WebSocket.EventType');
41
goog.provide('goog.net.WebSocket.MessageEvent');
42
43
goog.require('goog.Timer');
44
goog.require('goog.asserts');
45
goog.require('goog.debug.entryPointRegistry');
46
goog.require('goog.events');
47
goog.require('goog.events.Event');
48
goog.require('goog.events.EventTarget');
49
goog.require('goog.log');
50
51
52
53
/**
54
* Class encapsulating the logic for using a WebSocket.
55
*
56
* @param {boolean=} opt_autoReconnect True if the web socket should
57
* automatically reconnect or not. This is true by default.
58
* @param {function(number):number=} opt_getNextReconnect A function for
59
* obtaining the time until the next reconnect attempt. Given the reconnect
60
* attempt count (which is a positive integer), the function should return a
61
* positive integer representing the milliseconds to the next reconnect
62
* attempt. The default function used is an exponential back-off. Note that
63
* this function is never called if auto reconnect is disabled.
64
* @constructor
65
* @extends {goog.events.EventTarget}
66
*/
67
goog.net.WebSocket = function(opt_autoReconnect, opt_getNextReconnect) {
68
goog.net.WebSocket.base(this, 'constructor');
69
70
/**
71
* True if the web socket should automatically reconnect or not.
72
* @type {boolean}
73
* @private
74
*/
75
this.autoReconnect_ =
76
goog.isDef(opt_autoReconnect) ? opt_autoReconnect : true;
77
78
/**
79
* A function for obtaining the time until the next reconnect attempt.
80
* Given the reconnect attempt count (which is a positive integer), the
81
* function should return a positive integer representing the milliseconds to
82
* the next reconnect attempt.
83
* @type {function(number):number}
84
* @private
85
*/
86
this.getNextReconnect_ =
87
opt_getNextReconnect || goog.net.WebSocket.EXPONENTIAL_BACKOFF_;
88
89
/**
90
* The time, in milliseconds, that must elapse before the next attempt to
91
* reconnect.
92
* @type {number}
93
* @private
94
*/
95
this.nextReconnect_ = this.getNextReconnect_(this.reconnectAttempt_);
96
};
97
goog.inherits(goog.net.WebSocket, goog.events.EventTarget);
98
99
100
/**
101
* The actual web socket that will be used to send/receive messages.
102
* @type {WebSocket}
103
* @private
104
*/
105
goog.net.WebSocket.prototype.webSocket_ = null;
106
107
108
/**
109
* The URL to which the web socket will connect.
110
* @type {?string}
111
* @private
112
*/
113
goog.net.WebSocket.prototype.url_ = null;
114
115
116
/**
117
* The subprotocol name used when establishing the web socket connection.
118
* @type {string|undefined}
119
* @private
120
*/
121
goog.net.WebSocket.prototype.protocol_ = undefined;
122
123
124
/**
125
* True if a call to the close callback is expected or not.
126
* @type {boolean}
127
* @private
128
*/
129
goog.net.WebSocket.prototype.closeExpected_ = false;
130
131
132
/**
133
* Keeps track of the number of reconnect attempts made since the last
134
* successful connection.
135
* @type {number}
136
* @private
137
*/
138
goog.net.WebSocket.prototype.reconnectAttempt_ = 0;
139
140
141
/** @private {?number} */
142
goog.net.WebSocket.prototype.reconnectTimer_ = null;
143
144
145
/**
146
* The logger for this class.
147
* @type {goog.log.Logger}
148
* @private
149
*/
150
goog.net.WebSocket.prototype.logger_ = goog.log.getLogger('goog.net.WebSocket');
151
152
153
/**
154
* The events fired by the web socket.
155
* @enum {string} The event types for the web socket.
156
*/
157
goog.net.WebSocket.EventType = {
158
159
/**
160
* Fired when an attempt to open the WebSocket fails or there is a connection
161
* failure after a successful connection has been established.
162
*/
163
CLOSED: goog.events.getUniqueId('closed'),
164
165
/**
166
* Fired when the WebSocket encounters an error.
167
*/
168
ERROR: goog.events.getUniqueId('error'),
169
170
/**
171
* Fired when a new message arrives from the WebSocket.
172
*/
173
MESSAGE: goog.events.getUniqueId('message'),
174
175
/**
176
* Fired when the WebSocket connection has been established.
177
*/
178
OPENED: goog.events.getUniqueId('opened')
179
};
180
181
182
/**
183
* The various states of the web socket.
184
* @enum {number} The states of the web socket.
185
* @private
186
*/
187
goog.net.WebSocket.ReadyState_ = {
188
// This is the initial state during construction.
189
CONNECTING: 0,
190
// This is when the socket is actually open and ready for data.
191
OPEN: 1,
192
// This is when the socket is in the middle of a close handshake.
193
// Note that this is a valid state even if the OPEN state was never achieved.
194
CLOSING: 2,
195
// This is when the socket is actually closed.
196
CLOSED: 3
197
};
198
199
200
/**
201
* The maximum amount of time between reconnect attempts for the exponential
202
* back-off in milliseconds.
203
* @type {number}
204
* @private
205
*/
206
goog.net.WebSocket.EXPONENTIAL_BACKOFF_CEILING_ = 60 * 1000;
207
208
209
/**
210
* Computes the next reconnect time given the number of reconnect attempts since
211
* the last successful connection.
212
*
213
* @param {number} attempt The number of reconnect attempts since the last
214
* connection.
215
* @return {number} The time, in milliseconds, until the next reconnect attempt.
216
* @const
217
* @private
218
*/
219
goog.net.WebSocket.EXPONENTIAL_BACKOFF_ = function(attempt) {
220
var time = Math.pow(2, attempt) * 1000;
221
return Math.min(time, goog.net.WebSocket.EXPONENTIAL_BACKOFF_CEILING_);
222
};
223
224
225
/**
226
* Installs exception protection for all entry points introduced by
227
* goog.net.WebSocket instances which are not protected by
228
* {@link goog.debug.ErrorHandler#protectWindowSetTimeout},
229
* {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or
230
* {@link goog.events.protectBrowserEventEntryPoint}.
231
*
232
* @param {!goog.debug.ErrorHandler} errorHandler Error handler with which to
233
* protect the entry points.
234
*/
235
goog.net.WebSocket.protectEntryPoints = function(errorHandler) {
236
goog.net.WebSocket.prototype.onOpen_ =
237
errorHandler.protectEntryPoint(goog.net.WebSocket.prototype.onOpen_);
238
goog.net.WebSocket.prototype.onClose_ =
239
errorHandler.protectEntryPoint(goog.net.WebSocket.prototype.onClose_);
240
goog.net.WebSocket.prototype.onMessage_ =
241
errorHandler.protectEntryPoint(goog.net.WebSocket.prototype.onMessage_);
242
goog.net.WebSocket.prototype.onError_ =
243
errorHandler.protectEntryPoint(goog.net.WebSocket.prototype.onError_);
244
};
245
246
247
/**
248
* Creates and opens the actual WebSocket. Only call this after attaching the
249
* appropriate listeners to this object. If listeners aren't registered, then
250
* the {@code goog.net.WebSocket.EventType.OPENED} event might be missed.
251
*
252
* @param {string} url The URL to which to connect.
253
* @param {string=} opt_protocol The subprotocol to use. The connection will
254
* only be established if the server reports that it has selected this
255
* subprotocol. The subprotocol name must all be a non-empty ASCII string
256
* with no control characters and no spaces in them (i.e. only characters
257
* in the range U+0021 to U+007E).
258
*/
259
goog.net.WebSocket.prototype.open = function(url, opt_protocol) {
260
// Sanity check. This works only in modern browsers.
261
goog.asserts.assert(
262
goog.global['WebSocket'], 'This browser does not support WebSocket');
263
264
// Don't do anything if the web socket is already open.
265
goog.asserts.assert(!this.isOpen(), 'The WebSocket is already open');
266
267
// Clear any pending attempts to reconnect.
268
this.clearReconnectTimer_();
269
270
// Construct the web socket.
271
this.url_ = url;
272
this.protocol_ = opt_protocol;
273
274
// This check has to be made otherwise you get protocol mismatch exceptions
275
// for passing undefined, null, '', or [].
276
if (this.protocol_) {
277
goog.log.info(
278
this.logger_, 'Opening the WebSocket on ' + this.url_ +
279
' with protocol ' + this.protocol_);
280
this.webSocket_ = new WebSocket(this.url_, this.protocol_);
281
} else {
282
goog.log.info(this.logger_, 'Opening the WebSocket on ' + this.url_);
283
this.webSocket_ = new WebSocket(this.url_);
284
}
285
286
// Register the event handlers. Note that it is not possible for these
287
// callbacks to be missed because it is registered after the web socket is
288
// instantiated. Because of the synchronous nature of JavaScript, this code
289
// will execute before the browser creates the resource and makes any calls
290
// to these callbacks.
291
this.webSocket_.onopen = goog.bind(this.onOpen_, this);
292
this.webSocket_.onclose = goog.bind(this.onClose_, this);
293
this.webSocket_.onmessage = goog.bind(this.onMessage_, this);
294
this.webSocket_.onerror = goog.bind(this.onError_, this);
295
};
296
297
298
/**
299
* Closes the web socket connection.
300
*/
301
goog.net.WebSocket.prototype.close = function() {
302
303
// Clear any pending attempts to reconnect.
304
this.clearReconnectTimer_();
305
306
// Attempt to close only if the web socket was created.
307
if (this.webSocket_) {
308
goog.log.info(this.logger_, 'Closing the WebSocket.');
309
310
// Close is expected here since it was a direct call. Close is considered
311
// unexpected when opening the connection fails or there is some other form
312
// of connection loss after being connected.
313
this.closeExpected_ = true;
314
this.webSocket_.close();
315
this.webSocket_ = null;
316
}
317
};
318
319
320
/**
321
* Sends the message over the web socket.
322
*
323
* @param {string|!ArrayBuffer|!ArrayBufferView} message The message to send.
324
*/
325
goog.net.WebSocket.prototype.send = function(message) {
326
// Make sure the socket is ready to go before sending a message.
327
goog.asserts.assert(this.isOpen(), 'Cannot send without an open socket');
328
329
// Send the message and let onError_ be called if it fails thereafter.
330
this.webSocket_.send(message);
331
};
332
333
334
/**
335
* Checks to see if the web socket is open or not.
336
*
337
* @return {boolean} True if the web socket is open, false otherwise.
338
*/
339
goog.net.WebSocket.prototype.isOpen = function() {
340
return !!this.webSocket_ &&
341
this.webSocket_.readyState == goog.net.WebSocket.ReadyState_.OPEN;
342
};
343
344
345
/**
346
* Gets the number of bytes of data that have been queued using calls to send()
347
* but not yet transmitted to the network.
348
*
349
* @return {number} Number of bytes of data that have been queued.
350
*/
351
goog.net.WebSocket.prototype.getBufferedAmount = function() {
352
return this.webSocket_.bufferedAmount;
353
};
354
355
356
/**
357
* Called when the web socket has connected.
358
*
359
* @private
360
*/
361
goog.net.WebSocket.prototype.onOpen_ = function() {
362
goog.log.info(this.logger_, 'WebSocket opened on ' + this.url_);
363
this.dispatchEvent(goog.net.WebSocket.EventType.OPENED);
364
365
// Set the next reconnect interval.
366
this.reconnectAttempt_ = 0;
367
this.nextReconnect_ = this.getNextReconnect_(this.reconnectAttempt_);
368
};
369
370
371
/**
372
* Called when the web socket has closed.
373
*
374
* @param {!Event} event The close event.
375
* @private
376
*/
377
goog.net.WebSocket.prototype.onClose_ = function(event) {
378
goog.log.info(this.logger_, 'The WebSocket on ' + this.url_ + ' closed.');
379
380
// Firing this event allows handlers to query the URL.
381
this.dispatchEvent(goog.net.WebSocket.EventType.CLOSED);
382
383
// Always clear out the web socket on a close event.
384
this.webSocket_ = null;
385
386
// See if this is an expected call to onClose_.
387
if (this.closeExpected_) {
388
goog.log.info(this.logger_, 'The WebSocket closed normally.');
389
// Only clear out the URL if this is a normal close.
390
this.url_ = null;
391
this.protocol_ = undefined;
392
} else {
393
// Unexpected, so try to reconnect.
394
goog.log.error(
395
this.logger_, 'The WebSocket disconnected unexpectedly: ' + event.data);
396
397
// Only try to reconnect if it is enabled.
398
if (this.autoReconnect_) {
399
// Log the reconnect attempt.
400
var seconds = Math.floor(this.nextReconnect_ / 1000);
401
goog.log.info(
402
this.logger_, 'Seconds until next reconnect attempt: ' + seconds);
403
404
// Actually schedule the timer.
405
this.reconnectTimer_ = goog.Timer.callOnce(
406
goog.bind(this.open, this, this.url_, this.protocol_),
407
this.nextReconnect_, this);
408
409
// Set the next reconnect interval.
410
this.reconnectAttempt_++;
411
this.nextReconnect_ = this.getNextReconnect_(this.reconnectAttempt_);
412
}
413
}
414
this.closeExpected_ = false;
415
};
416
417
418
/**
419
* Called when a new message arrives from the server.
420
*
421
* @param {MessageEvent<string>} event The web socket message event.
422
* @private
423
*/
424
goog.net.WebSocket.prototype.onMessage_ = function(event) {
425
var message = event.data;
426
this.dispatchEvent(new goog.net.WebSocket.MessageEvent(message));
427
};
428
429
430
/**
431
* Called when there is any error in communication.
432
*
433
* @param {Event} event The error event containing the error data.
434
* @private
435
*/
436
goog.net.WebSocket.prototype.onError_ = function(event) {
437
var data = /** @type {string} */ (event.data);
438
goog.log.error(this.logger_, 'An error occurred: ' + data);
439
this.dispatchEvent(new goog.net.WebSocket.ErrorEvent(data));
440
};
441
442
443
/**
444
* Clears the reconnect timer.
445
*
446
* @private
447
*/
448
goog.net.WebSocket.prototype.clearReconnectTimer_ = function() {
449
if (goog.isDefAndNotNull(this.reconnectTimer_)) {
450
goog.Timer.clear(this.reconnectTimer_);
451
}
452
this.reconnectTimer_ = null;
453
};
454
455
456
/** @override */
457
goog.net.WebSocket.prototype.disposeInternal = function() {
458
goog.net.WebSocket.base(this, 'disposeInternal');
459
this.close();
460
};
461
462
463
464
/**
465
* Object representing a new incoming message event.
466
*
467
* @param {string} message The raw message coming from the web socket.
468
* @extends {goog.events.Event}
469
* @constructor
470
* @final
471
*/
472
goog.net.WebSocket.MessageEvent = function(message) {
473
goog.net.WebSocket.MessageEvent.base(
474
this, 'constructor', goog.net.WebSocket.EventType.MESSAGE);
475
476
/**
477
* The new message from the web socket.
478
* @type {string}
479
*/
480
this.message = message;
481
};
482
goog.inherits(goog.net.WebSocket.MessageEvent, goog.events.Event);
483
484
485
486
/**
487
* Object representing an error event. This is fired whenever an error occurs
488
* on the web socket.
489
*
490
* @param {string} data The error data.
491
* @extends {goog.events.Event}
492
* @constructor
493
* @final
494
*/
495
goog.net.WebSocket.ErrorEvent = function(data) {
496
goog.net.WebSocket.ErrorEvent.base(
497
this, 'constructor', goog.net.WebSocket.EventType.ERROR);
498
499
/**
500
* The error data coming from the web socket.
501
* @type {string}
502
*/
503
this.data = data;
504
};
505
goog.inherits(goog.net.WebSocket.ErrorEvent, goog.events.Event);
506
507
508
// Register the WebSocket as an entry point, so that it can be monitored for
509
// exception handling, etc.
510
goog.debug.entryPointRegistry.register(
511
/**
512
* @param {function(!Function): !Function} transformer The transforming
513
* function.
514
*/
515
function(transformer) {
516
goog.net.WebSocket.prototype.onOpen_ =
517
transformer(goog.net.WebSocket.prototype.onOpen_);
518
goog.net.WebSocket.prototype.onClose_ =
519
transformer(goog.net.WebSocket.prototype.onClose_);
520
goog.net.WebSocket.prototype.onMessage_ =
521
transformer(goog.net.WebSocket.prototype.onMessage_);
522
goog.net.WebSocket.prototype.onError_ =
523
transformer(goog.net.WebSocket.prototype.onError_);
524
});
525
526