144 lines
4.4 KiB
JavaScript
144 lines
4.4 KiB
JavaScript
/*
|
|
* Tencent is pleased to support the open source community by making Puerts available.
|
|
* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
|
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms.
|
|
* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package.
|
|
*/
|
|
|
|
var global = global || (function () { return this; }());
|
|
(function (global) {
|
|
const WebSocketPP = global.WebSocketPP;
|
|
//global.WebSocketPP = undefined;
|
|
|
|
class EventTarget {
|
|
constructor() {
|
|
this.listeners = {};
|
|
}
|
|
|
|
addEventListener(type, callback) {
|
|
if (!(type in this.listeners)) {
|
|
this.listeners[type] = [];
|
|
}
|
|
this.listeners[type].push(callback);
|
|
}
|
|
|
|
removeEventListener(type, callback) {
|
|
if (!(type in this.listeners)) {
|
|
return;
|
|
}
|
|
const stack = this.listeners[type];
|
|
for (let i = 0; i < stack.length; i++) {
|
|
if (stack[i] === callback) {
|
|
stack.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
dispatchEvent(ev) {
|
|
if (!(ev.type in this.listeners)) {
|
|
return true;
|
|
}
|
|
const stack = this.listeners[ev.type].slice();
|
|
|
|
for (let i = 0; i < stack.length; i++) {
|
|
stack[i].call(this, ev);
|
|
}
|
|
return !ev.defaultPrevented;
|
|
}
|
|
}
|
|
|
|
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
|
|
|
|
const poll_ws_objects = [];
|
|
|
|
class WebSocket extends EventTarget {
|
|
constructor(url, protocols) {
|
|
super();
|
|
if (protocols) throw new Error('do not support protocols argument');
|
|
this._raw = new WebSocketPP(url);
|
|
this._url = url;
|
|
// !!do not raise exception in handles.
|
|
this._raw.setHandles(
|
|
()=> {
|
|
this._readyState = WebSocket.OPEN;
|
|
this._addPendingEvent({type:'open'});
|
|
},
|
|
(data) => {
|
|
this._addPendingEvent({type:'message', data:data, origin:this._url});
|
|
},
|
|
(code, reason) => {
|
|
this._cleanup();
|
|
this._addPendingEvent({type:'close', code:code, reason: reason});
|
|
},
|
|
() => {
|
|
this._addPendingEvent({type:'error'});
|
|
this._cleanup();
|
|
this._addPendingEvent({type:'close', code:1006, reason: ""});
|
|
});
|
|
|
|
this._readyState = WebSocket.CONNECTING;
|
|
this._tid = setInterval(() => this._poll(), 1);
|
|
this._pendingEvents = [];
|
|
}
|
|
|
|
get url() {
|
|
return this._url;
|
|
}
|
|
|
|
get readyState() {
|
|
return this._readyState;
|
|
}
|
|
|
|
send(data) {
|
|
if (this._readyState !== WebSocket.OPEN) {
|
|
throw new Error(`WebSocket is not open: readyState ${this._readyState} (${readyStates[this._readyState]})`);
|
|
}
|
|
this._raw.send(data);
|
|
}
|
|
|
|
_cleanup() {
|
|
this._readyState = WebSocket.CLOSING;
|
|
}
|
|
|
|
_addPendingEvent(ev) {
|
|
this._pendingEvents.push(ev);
|
|
}
|
|
|
|
_poll() {
|
|
if (this._pendingEvents.length === 0 && this._readyState != WebSocket.CLOSING) {
|
|
this._raw.poll();
|
|
}
|
|
const ev = this._pendingEvents.shift();
|
|
if (ev) this.dispatchEvent(ev);
|
|
if (this._pendingEvents.length === 0 && this._readyState == WebSocket.CLOSING) {
|
|
this._raw = undefined;
|
|
clearInterval(this._tid);
|
|
this._readyState = WebSocket.CLOSED;
|
|
}
|
|
}
|
|
|
|
close(code, data) {
|
|
try {
|
|
this._raw.close(code, data);
|
|
} catch(e) {}
|
|
this._cleanup();
|
|
}
|
|
|
|
}
|
|
|
|
for (let i = 0; i < readyStates.length; i++) {
|
|
Object.defineProperty(WebSocket, readyStates[i], {
|
|
enumerable: true,
|
|
value: i
|
|
});
|
|
|
|
Object.defineProperty(WebSocket.prototype, readyStates[i], {
|
|
enumerable: true,
|
|
value: i
|
|
});
|
|
}
|
|
|
|
global.WebSocket = WebSocket;
|
|
|
|
}(global)); |