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