* Handles VLESS over WebSocket requests by creating a WebSocket pair, accepting the WebSocket connection, and processing the VLESS header. * @param {import("@cloudflare/workers-types").Request} request The incoming request object. * @returns {Promise } A Promise that resolves to a WebSo
(request)
| 167 | * @returns {Promise<Response>} A Promise that resolves to a WebSocket response object. |
| 168 | */ |
| 169 | async function vlessOverWSHandler(request) { |
| 170 | const webSocketPair = new WebSocketPair(); |
| 171 | const [client, webSocket] = Object.values(webSocketPair); |
| 172 | webSocket.accept(); |
| 173 | |
| 174 | let address = ''; |
| 175 | let portWithRandomLog = ''; |
| 176 | const log = (/** @type {string} */ info, /** @type {string | undefined} */ event) => { |
| 177 | console.log(`[${address}:${portWithRandomLog}] ${info}`, event || ''); |
| 178 | }; |
| 179 | const earlyDataHeader = request.headers.get('sec-websocket-protocol') || ''; |
| 180 | |
| 181 | const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log); |
| 182 | |
| 183 | /** @type {{ value: import("@cloudflare/workers-types").Socket | null}}*/ |
| 184 | let remoteSocketWapper = { |
| 185 | value: null, |
| 186 | }; |
| 187 | let udpStreamWrite = null; |
| 188 | let isDns = false; |
| 189 | |
| 190 | // ws --> remote |
| 191 | readableWebSocketStream.pipeTo(new WritableStream({ |
| 192 | async write(chunk, controller) { |
| 193 | if (isDns && udpStreamWrite) { |
| 194 | return udpStreamWrite(chunk); |
| 195 | } |
| 196 | if (remoteSocketWapper.value) { |
| 197 | const writer = remoteSocketWapper.value.writable.getWriter() |
| 198 | await writer.write(chunk); |
| 199 | writer.releaseLock(); |
| 200 | return; |
| 201 | } |
| 202 | |
| 203 | const { |
| 204 | hasError, |
| 205 | message, |
| 206 | portRemote = 443, |
| 207 | addressRemote = '', |
| 208 | rawDataIndex, |
| 209 | vlessVersion = new Uint8Array([0, 0]), |
| 210 | isUDP, |
| 211 | } = processVlessHeader(chunk, userID); |
| 212 | address = addressRemote; |
| 213 | portWithRandomLog = `${portRemote} ${isUDP ? 'udp' : 'tcp'} `; |
| 214 | if (hasError) { |
| 215 | // controller.error(message); |
| 216 | throw new Error(message); // cf seems has bug, controller.error will not end stream |
| 217 | // webSocket.close(1000, message); |
| 218 | return; |
| 219 | } |
| 220 | |
| 221 | // If UDP and not DNS port, close it |
| 222 | if (isUDP && portRemote !== 53) { |
| 223 | throw new Error('UDP proxy only enabled for DNS which is port 53'); |
| 224 | // cf seems has bug, controller.error will not end stream |
| 225 | } |
| 226 |
no test coverage detected