1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// browser dail function, return a promise which product a Session class to Read/Write
export function dial(addr) {
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket
let ws = new WebSocket(addr)
ws.binaryType = "arraybuffer"
let session = new Session(ws)
return new Promise((open_resolve, open_reject) => {
ws.onerror = (event) => {
open_reject(event)
// change read queue state
session._reader.close()
}
ws.onclose = (event) => {
open_reject(event)
// change read queue state
session._reader.close()
}
ws.onmessage = (event) => {
// push to read queue
session._reader.push(event.data)
}
ws.onopen = () => {
// return a session class
open_resolve(session)
}
})
}
// Rust bind type for browser websocket
export class Session {
constructor(ws) {
this._ws = ws
this._reader = new ReadQueue()
}
// return a promise which means when browser's write ok or not
write(data) {
if (this._ws.readyState === 1) {
this._ws.send(data)
return new Promise((resolve, reject) => {
if (this._ws.readyState !== 1) {
return reject("WebSocket is closed");
} else {
return resolve()
}
})
} else {
return Promise.reject("WebSocket is closed");
}
}
// return a promise to product a arraybuffer or null
read() {
return this._reader.next()
}
isClosed() {
return this._ws.readyState !== 1
}
close() {
this._ws.close()
}
}
class ReadQueue {
constructor() {
this.queue = []
// always false, expect ws closed
this.closed = false
// cache read resolve when queue is empty
this.cache_resolve = null
}
push(buffer) {
if (this.cache_resolve !== null) {
this.cache_resolve(buffer)
this.cache_resolve = null
} else {
this.queue.push(Promise.resolve(buffer))
}
}
close() {
this.closed = true
this.queue.push(Promise.resolve(null))
}
// return Promise<null> means websocket is closed
next() {
if (this.queue.length !== 0) {
return this.queue.shift();
} else {
if (this.closed) {
return new Promise.resolve(null)
} else {
return new Promise((resolve, _reject) => {
this.cache_resolve = resolve
})
}
}
}
}