* * @param {import("@cloudflare/workers-types").WebSocket} webSocketServer * @param {string} earlyDataHeader for ws 0rtt * @param {(info: string)=> void} log for ws 0rtt
(webSocketServer, earlyDataHeader, log)
| 248 | * @param {(info: string)=> void} log for ws 0rtt |
| 249 | */ |
| 250 | function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) { |
| 251 | let readableStreamCancel = false; |
| 252 | const stream = new ReadableStream({ |
| 253 | start(controller) { |
| 254 | webSocketServer.addEventListener("message", (event) => { |
| 255 | if (readableStreamCancel) { |
| 256 | return; |
| 257 | } |
| 258 | const message = event.data; |
| 259 | controller.enqueue(message); |
| 260 | }); |
| 261 | |
| 262 | // The event means that the client closed the client -> server stream. |
| 263 | // However, the server -> client stream is still open until you call close() on the server side. |
| 264 | // The WebSocket protocol says that a separate close message must be sent in each direction to fully close the socket. |
| 265 | webSocketServer.addEventListener("close", () => { |
| 266 | // client send close, need close server |
| 267 | // if stream is cancel, skip controller.close |
| 268 | safeCloseWebSocket(webSocketServer); |
| 269 | if (readableStreamCancel) { |
| 270 | return; |
| 271 | } |
| 272 | controller.close(); |
| 273 | }); |
| 274 | webSocketServer.addEventListener("error", (err) => { |
| 275 | log("webSocketServer has error"); |
| 276 | controller.error(err); |
| 277 | }); |
| 278 | // for ws 0rtt |
| 279 | const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader); |
| 280 | if (error) { |
| 281 | controller.error(error); |
| 282 | } else if (earlyData) { |
| 283 | controller.enqueue(earlyData); |
| 284 | } |
| 285 | }, |
| 286 | |
| 287 | pull(controller) { |
| 288 | // if ws can stop read if stream is full, we can implement backpressure |
| 289 | // https://streams.spec.whatwg.org/#example-rs-push-backpressure |
| 290 | }, |
| 291 | cancel(reason) { |
| 292 | // 1. pipe WritableStream has error, this cancel will called, so ws handle server close into here |
| 293 | // 2. if readableStream is cancel, all controller.close/enqueue need skip, |
| 294 | // 3. but from testing controller.error still work even if readableStream is cancel |
| 295 | if (readableStreamCancel) { |
| 296 | return; |
| 297 | } |
| 298 | log(`ReadableStream was canceled, due to ${reason}`); |
| 299 | readableStreamCancel = true; |
| 300 | safeCloseWebSocket(webSocketServer); |
| 301 | }, |
| 302 | }); |
| 303 | |
| 304 | return stream; |
| 305 | } |
| 306 | |
| 307 | // https://xtls.github.io/development/protocols/vless.html |
no outgoing calls
no test coverage detected