|
1 | 1 | import debug from "debug" |
2 | | -import WebSocket, { ErrorEvent } from "isomorphic-ws" |
| 2 | +import { WebSocket } from "isomorphic-ws" |
3 | 3 | import pkg from "../package.json" assert { type: "json" } |
4 | 4 | import { EventEmitter } from "./lib/EventEmitter.js" |
5 | 5 | import { isReady } from "./lib/isReady.js" |
@@ -118,86 +118,82 @@ export class Client extends EventEmitter<ClientEvents> { |
118 | 118 | const url = `${this.url}/introduction/${this.userName}` |
119 | 119 | this.log("connecting to relay server", url) |
120 | 120 |
|
121 | | - const serverConnection = new WebSocket(url) |
122 | | - |
123 | | - serverConnection.onopen = async () => { |
124 | | - await isReady(serverConnection) |
125 | | - this.retryDelay = this.minRetryDelay |
126 | | - this.shouldReconnectIfClosed = true |
127 | | - this.drainQueue() |
128 | | - this.emit("server.connect") |
129 | | - this.open = true |
130 | | - |
131 | | - this.heartbeat = setInterval( |
132 | | - () => serverConnection.send(HEARTBEAT), |
133 | | - HEARTBEAT_INTERVAL |
134 | | - ) |
135 | | - } |
136 | | - |
137 | | - serverConnection.onmessage = messageEvent => { |
138 | | - const data = messageEvent.data as Uint8Array |
139 | | - const message = unpack(data) as Message.ServerToClient |
140 | | - |
141 | | - // The only kind of message that we receive from the relay server is an introduction, which tells |
142 | | - // us that someone else is interested in the same thing we are. |
143 | | - if (message.type !== "Introduction") |
144 | | - throw new Error(`Invalid message type '${message.type}'`) |
145 | | - |
146 | | - // When we receive that message, we respond by requesting a "direct" connection to the peer |
147 | | - // (via piped sockets on the relay server) for each document ID that we have in common |
148 | | - |
149 | | - const connectToPeer = (documentId: DocumentId, userName: UserName) => { |
150 | | - const peer = this.get(userName) |
151 | | - if (peer.has(documentId)) return // don't add twice |
152 | | - peer.set(documentId, null) |
153 | | - |
154 | | - const url = `${this.url}/connection/${this.userName}/${userName}/${documentId}` |
155 | | - const peerConnection = new WebSocket(url) |
156 | | - |
157 | | - peerConnection.onopen = async () => { |
158 | | - // make sure the socket is actually in READY state |
159 | | - await isReady(peerConnection) |
160 | | - |
161 | | - // add the socket to the map for this peer |
162 | | - peer.set(documentId, peerConnection) |
163 | | - this.emit("peer.connect", { |
164 | | - userName, |
165 | | - documentId, |
166 | | - socket: peerConnection, |
167 | | - } as PeerEventPayload) |
168 | | - } |
169 | | - |
170 | | - // if the other end disconnects, we disconnect |
171 | | - peerConnection.onclose = () => { |
172 | | - this.closeSocket(userName, documentId) |
173 | | - this.emit("peer.disconnect", { |
174 | | - userName, |
175 | | - documentId, |
176 | | - socket: peerConnection, |
177 | | - } as PeerEventPayload) |
| 121 | + this.serverConnection = new WebSocket(url) |
| 122 | + |
| 123 | + .on("open", async () => { |
| 124 | + await isReady(this.serverConnection) |
| 125 | + this.retryDelay = this.minRetryDelay |
| 126 | + this.shouldReconnectIfClosed = true |
| 127 | + this.drainQueue() |
| 128 | + this.emit("server.connect") |
| 129 | + this.open = true |
| 130 | + |
| 131 | + this.heartbeat = setInterval( |
| 132 | + () => this.serverConnection.send(HEARTBEAT), |
| 133 | + HEARTBEAT_INTERVAL |
| 134 | + ) |
| 135 | + }) |
| 136 | + |
| 137 | + .on("message", data => { |
| 138 | + const message = unpack(data) as Message.ServerToClient |
| 139 | + |
| 140 | + // The only kind of message that we receive from the relay server is an introduction, which tells |
| 141 | + // us that someone else is interested in the same thing we are. |
| 142 | + if (message.type !== "Introduction") |
| 143 | + throw new Error(`Invalid message type '${message.type}'`) |
| 144 | + |
| 145 | + // When we receive that message, we respond by requesting a "direct" connection to the peer |
| 146 | + // (via piped sockets on the relay server) for each document ID that we have in common |
| 147 | + |
| 148 | + const connectToPeer = (documentId: DocumentId, userName: UserName) => { |
| 149 | + const peer = this.get(userName) |
| 150 | + if (peer.has(documentId)) return // don't add twice |
| 151 | + peer.set(documentId, null) |
| 152 | + |
| 153 | + const url = `${this.url}/connection/${this.userName}/${userName}/${documentId}` |
| 154 | + const peerConnection = new WebSocket(url) |
| 155 | + |
| 156 | + peerConnection.on("open", async () => { |
| 157 | + // make sure the socket is actually in READY state |
| 158 | + await isReady(peerConnection) |
| 159 | + |
| 160 | + // add the socket to the map for this peer |
| 161 | + peer.set(documentId, peerConnection) |
| 162 | + this.emit("peer.connect", { |
| 163 | + userName, |
| 164 | + documentId, |
| 165 | + socket: peerConnection, |
| 166 | + } as PeerEventPayload) |
| 167 | + }) |
| 168 | + |
| 169 | + // if the other end disconnects, we disconnect |
| 170 | + peerConnection.onclose = () => { |
| 171 | + this.closeSocket(userName, documentId) |
| 172 | + this.emit("peer.disconnect", { |
| 173 | + userName, |
| 174 | + documentId, |
| 175 | + socket: peerConnection, |
| 176 | + } as PeerEventPayload) |
| 177 | + } |
178 | 178 | } |
179 | | - } |
180 | | - |
181 | | - const { userName, documentIds = [] } = message |
182 | | - documentIds.forEach(documentId => connectToPeer(documentId, userName)) |
183 | | - } |
184 | 179 |
|
185 | | - serverConnection.onclose = () => { |
186 | | - this.open = false |
187 | | - this.emit("server.disconnect") |
| 180 | + const { userName, documentIds = [] } = message |
| 181 | + documentIds.forEach(documentId => connectToPeer(documentId, userName)) |
| 182 | + }) |
188 | 183 |
|
189 | | - // stop heartbeat |
190 | | - clearInterval(this.heartbeat) |
| 184 | + .on("close", () => { |
| 185 | + this.open = false |
| 186 | + this.emit("server.disconnect") |
191 | 187 |
|
192 | | - if (this.shouldReconnectIfClosed) this.tryToReopen() |
193 | | - } |
| 188 | + // stop heartbeat |
| 189 | + clearInterval(this.heartbeat) |
194 | 190 |
|
195 | | - serverConnection.onerror = ({ error }) => { |
196 | | - this.emit("error", error) |
197 | | - } |
| 191 | + if (this.shouldReconnectIfClosed) this.tryToReopen() |
| 192 | + }) |
198 | 193 |
|
199 | | - this.serverConnection = serverConnection |
200 | | - return this.serverConnection |
| 194 | + .on("error", error => { |
| 195 | + this.emit("error", error) |
| 196 | + }) |
201 | 197 | } |
202 | 198 |
|
203 | 199 | /** Try to reconnect after a delay */ |
|
0 commit comments