forked from drashland/wocket
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.js
152 lines (142 loc) · 4.9 KB
/
client.js
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* @description
* This SocketClient class uses the native WebSocket object to create a connection to a socket
* server. It creates a connection, handles messages received from the socket server, and can
* send messages to the socket server it is connected to.
*
* There are two methods that allow this class to interact with the socket server it is connected
* to. Those methods are:
*
* - on(); and
* - to()
*
* Use these methods to interact with the connected socket server.
*
* There are methods in this class that are prefixed with an underscore. A method prefixed with
* an underscore means it is not meant to be used publicly. Although these methods are not
* private, these methods are mainly for the SocketClient class' use.
*
* Helpful links:
* - Learn more about the WebSocket API at the following address:
* https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
* - Learn more about writing web socket client applications at the following address:
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
*/
class SocketClient {
/**
* @description
* Construct an object of this class.
*
* @return {SocketClient}
*/
constructor(options) {
this.configs = {
hostname: options.hostname || "localhost",
port: options.port || "3000",
protocol: options.protocol || "ws",
};
this.decoder = new TextDecoder();
this.listening_to = {};
this.message_queue = [];
this.ready = true;
this._connectToSocketServer();
this._listenToSocketServerMessages();
return this;
}
// FILE MARKER - METHODS FOR PUBLIC USE //////////////////////////////////////////////////////////
/**
* @description
* On receipt of a message in the following channel or on the following event, perform the
* specified callback.
*
* @param {String} channelOrEvent
* The name of the channel or event.
* @param {Function} callback
* The callback to execute on receipt of a message from the channel or event.
*/
on(channelOrEvent, callback) {
if (this.connection.readyState === 1) {
if (!this.listening_to[channelOrEvent]) {
this.listening_to[channelOrEvent] = null;
}
this.listening_to[channelOrEvent] = callback;
const message = JSON.stringify({ listening_to: channelOrEvent });
const encoded = new TextEncoder().encode(message);
this.message_queue.push(encoded);
this._sendMessagesToSocketServer();
} else {
setTimeout(() => this.on(channelOrEvent, callback), 5);
}
}
/**
* @description
* Send a message to a channel or an event.
*
* @param {String} channelOrEvent
* The name of the channel or event.
* @param {String} message
* The message to send to the channel or event.
*/
to(channelOrEvent, message) {
if (channelOrEvent) {
message = JSON.stringify({ [channelOrEvent]: message });
message = new TextEncoder().encode(message);
}
this.message_queue.push(message);
this._sendMessagesToSocketServer();
}
// FILE MARKER - METHODS FOR INTERNAL USE ////////////////////////////////////////////////////////
/**
* Connect to the socket server at the hostname and port specified in the configs.
*/
_connectToSocketServer() {
this.connection = new WebSocket(
`${this.configs.protocol}://${this.configs.hostname}:${this.configs.port}`,
);
}
/**
* @description
* Listen to messages from sent by the socket server.
*/
_listenToSocketServerMessages() {
this.connection.addEventListener("message", (event) => {
this._handleEncodedMessage(event.data);
});
}
/**
* @description
* All messages received by the socket server will be handled by this method.
*
* @param {Body}
* The encoded message. See https://developer.mozilla.org/en-US/docs/Web/API/Body for more
* information about the Body mixin.
*/
_handleEncodedMessage(encodedMessage) {
encodedMessage.arrayBuffer().then((buffer) => {
const decodedMessage = this.decoder.decode(buffer);
const parsedMessage = JSON.parse(decodedMessage);
Object.keys(parsedMessage).forEach((channelOrEvent) => {
if (this.listening_to[channelOrEvent]) {
this.listening_to[channelOrEvent](parsedMessage[channelOrEvent]);
}
});
});
}
/**
* @description
* Send all messages in the message queue to the socket server.
*/
_sendMessagesToSocketServer() {
if (this.ready && this.message_queue.length) {
this.ready = false;
let message = null;
while (this.message_queue.length) {
message = new Uint8Array(this.message_queue[0].length);
message.set(this.message_queue.pop());
}
this.connection.send(message);
this.ready = true;
this._sendMessagesToSocketServer();
}
}
}