Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
titaniumnetwork-dev
GitHub Repository: titaniumnetwork-dev/Ultraviolet
Path: blob/main/src/client/requests/websocket.js
305 views
1
import EventEmitter from "events";
2
import HookEvent from "../hook.js";
3
4
/**
5
* @typedef {import('../index').default} UVClient
6
*/
7
8
class WebSocketApi extends EventEmitter {
9
/**
10
*
11
* @param {UVClient} ctx
12
*/
13
constructor(ctx) {
14
super();
15
this.ctx = ctx;
16
this.window = ctx.window;
17
this.WebSocket = this.window.WebSocket || {};
18
this.wsProto = this.WebSocket.prototype;
19
this.CONNECTING = WebSocket.CONNECTING;
20
this.OPEN = WebSocket.OPEN;
21
this.CLOSING = WebSocket.CLOSING;
22
this.CLOSED = WebSocket.CLOSED;
23
this.socketmap = new WeakMap();
24
}
25
overrideWebSocket(client) {
26
this.ctx.override(
27
this.window,
28
"WebSocket",
29
(target, that, args) => {
30
const fakeWebSocket = new EventTarget();
31
Object.setPrototypeOf(fakeWebSocket, this.wsProto);
32
fakeWebSocket.constructor = this.WebSocket;
33
34
const barews = client.createWebSocket(args[0], args[1], null, {
35
"User-Agent": navigator.userAgent,
36
Origin: __uv.meta.url.origin,
37
});
38
39
const state = {
40
extensions: "",
41
protocol: "",
42
url: args[0],
43
binaryType: "blob",
44
barews,
45
46
onclose: null,
47
onerror: null,
48
onmessage: null,
49
onopen: null,
50
};
51
52
function fakeEventSend(fakeev) {
53
state["on" + fakeev.type]?.(fakeev);
54
fakeWebSocket.dispatchEvent(fakeev);
55
}
56
57
barews.addEventListener("open", () => {
58
fakeEventSend(new Event("open"));
59
});
60
barews.addEventListener("close", (ev) => {
61
fakeEventSend(new CloseEvent("close", ev));
62
});
63
barews.addEventListener("message", async (ev) => {
64
let payload = ev.data;
65
if (typeof payload === "string") {
66
// DO NOTHING
67
} else if ("byteLength" in payload) {
68
// arraybuffer, convert to blob if needed or set the proper prototype
69
if (state.binaryType === "blob") {
70
payload = new Blob([payload]);
71
} else {
72
Object.setPrototypeOf(payload, ArrayBuffer.prototype);
73
}
74
} else if ("arrayBuffer" in payload) {
75
// blob, convert to arraybuffer if neccesary.
76
if (state.binaryType === "arraybuffer") {
77
payload = await payload.arrayBuffer();
78
Object.setPrototypeOf(payload, ArrayBuffer.prototype);
79
}
80
}
81
const fakeev = new MessageEvent("message", {
82
data: payload,
83
origin: ev.origin,
84
lastEventId: ev.lastEventId,
85
source: ev.source,
86
ports: ev.ports,
87
});
88
89
fakeEventSend(fakeev);
90
});
91
barews.addEventListener("error", () => {
92
fakeEventSend(new Event("error"));
93
});
94
95
this.socketmap.set(fakeWebSocket, state);
96
return fakeWebSocket;
97
},
98
true
99
);
100
101
this.ctx.overrideDescriptor(this.wsProto, "binaryType", {
102
get: (target, that) => {
103
const ws = this.socketmap.get(that);
104
105
return ws.binaryType;
106
},
107
set: (target, that, value) => {
108
const ws = this.socketmap.get(that);
109
if (value[0] === "blob" || value[0] === "arraybuffer")
110
ws.binaryType = value[0];
111
},
112
});
113
114
this.ctx.overrideDescriptor(this.wsProto, "bufferedAmount", {
115
get: (target, that) => {
116
return 0;
117
},
118
});
119
120
this.ctx.overrideDescriptor(this.wsProto, "extensions", {
121
get: (target, that) => {
122
const ws = this.socketmap.get(that); // get the WebSocket from socketmap
123
124
return ws.extensions;
125
},
126
});
127
128
this.ctx.overrideDescriptor(this.wsProto, "onclose", {
129
get: (target, that) => {
130
const ws = this.socketmap.get(that);
131
132
return ws.onclose;
133
},
134
set: (target, that, value) => {
135
const ws = this.socketmap.get(that);
136
137
ws.onclose = value[0];
138
},
139
});
140
141
this.ctx.overrideDescriptor(this.wsProto, "onerror", {
142
get: (target, that) => {
143
const ws = this.socketmap.get(that);
144
145
return ws.onerror;
146
},
147
set: (target, that, value) => {
148
const ws = this.socketmap.get(that);
149
150
ws.onerror = value[0];
151
},
152
});
153
154
this.ctx.overrideDescriptor(this.wsProto, "onmessage", {
155
get: (target, that) => {
156
const ws = this.socketmap.get(that);
157
158
return ws.onmessage;
159
},
160
set: (target, that, value) => {
161
const ws = this.socketmap.get(that);
162
163
ws.onmessage = value[0];
164
},
165
});
166
167
this.ctx.overrideDescriptor(this.wsProto, "onopen", {
168
get: (target, that) => {
169
const ws = this.socketmap.get(that);
170
171
return ws.onopen;
172
},
173
set: (target, that, value) => {
174
const ws = this.socketmap.get(that);
175
176
ws.onopen = value[0];
177
},
178
});
179
180
this.ctx.overrideDescriptor(this.wsProto, "url", {
181
get: (target, that) => {
182
const ws = this.socketmap.get(that);
183
184
return ws.url;
185
},
186
});
187
188
this.ctx.overrideDescriptor(this.wsProto, "protocol", {
189
get: (target, that) => {
190
const ws = this.socketmap.get(that);
191
192
return ws.protocol;
193
},
194
});
195
196
this.ctx.overrideDescriptor(this.wsProto, "readyState", {
197
get: (target, that) => {
198
const ws = this.socketmap.get(that);
199
200
return ws.barews.readyState;
201
},
202
});
203
204
this.ctx.override(
205
this.wsProto,
206
"send",
207
(target, that, args) => {
208
const ws = this.socketmap.get(that);
209
210
return ws.barews.send(args[0]);
211
},
212
false
213
);
214
this.ctx.override(
215
this.wsProto,
216
"close",
217
(target, that, args) => {
218
const ws = this.socketmap.get(that);
219
if (args[0] === undefined) args[0] = 1000;
220
if (args[1] === undefined) args[1] = "";
221
return ws.barews.close(args[0], args[1]);
222
},
223
false
224
);
225
}
226
}
227
228
export default WebSocketApi;
229
230