@@ -4,6 +4,7 @@ This code is borrowed from Cloudflare Sandbox-sdk's npm package
44
55import { createObjectLogger } from "../../logger" ;
66import { getSandbox , type Sandbox } from "@cloudflare/sandbox" ;
7+ import { switchPort } from '@cloudflare/containers' ;
78
89const logger = createObjectLogger ( {
910 component : 'sandbox-do' ,
@@ -36,6 +37,17 @@ export async function proxyToSandbox<E extends SandboxEnv>(
3637 const { sandboxId, port, path, token } = routeInfo ;
3738 const sandbox = getSandbox ( env . Sandbox , sandboxId ) ;
3839
40+ logger . info ( "[Proxy] Sandbox" , sandbox , "Port" , port , "Path" , path , "Token" , token ) ;
41+
42+ // Detect WebSocket upgrade request
43+ const upgradeHeader = request . headers . get ( 'Upgrade' ) ;
44+ if ( upgradeHeader ?. toLowerCase ( ) === 'websocket' ) {
45+ logger . info ( "[Proxy] WebSocket upgrade request" , upgradeHeader , "Port" , port , "Path" , path , "Token" , token ) ;
46+ // WebSocket path: Must use fetch() not containerFetch()
47+ // This bypasses JSRPC serialization boundary which cannot handle WebSocket upgrades
48+ return await sandbox . fetch ( switchPort ( request , port ) ) ;
49+ }
50+
3951 // Build proxy request with proper headers
4052 let proxyUrl : string ;
4153
@@ -55,7 +67,7 @@ export async function proxyToSandbox<E extends SandboxEnv>(
5567 'X-Original-URL' : request . url ,
5668 'X-Forwarded-Host' : url . hostname ,
5769 'X-Forwarded-Proto' : url . protocol . replace ( ':' , '' ) ,
58- 'X-Sandbox-Name' : sandboxId , // Pass the friendly name
70+ 'X-Sandbox-Name' : sandboxId // Pass the friendly name
5971 } ,
6072 body : request . body ,
6173 // @ts -expect-error - duplex required for body streaming in modern runtimes
@@ -70,17 +82,22 @@ export async function proxyToSandbox<E extends SandboxEnv>(
7082 proxyUrl,
7183 } ) ;
7284
73- return sandbox . containerFetch ( proxyRequest , port ) ;
85+ return await sandbox . containerFetch ( proxyRequest , port ) ;
7486 } catch ( error ) {
75- logger . error ( 'Proxy routing error' , error instanceof Error ? error : new Error ( String ( error ) ) ) ;
87+ logger . error (
88+ 'Proxy routing error' ,
89+ error instanceof Error ? error : new Error ( String ( error ) )
90+ ) ;
7691 return new Response ( 'Proxy routing error' , { status : 500 } ) ;
7792 }
7893}
7994
8095function extractSandboxRoute ( url : URL ) : RouteInfo | null {
8196 // Parse subdomain pattern: port-sandboxId-token.domain (tokens mandatory)
8297 // Token is always exactly 16 chars (generated by generatePortToken)
83- const subdomainMatch = url . hostname . match ( / ^ ( \d { 4 , 5 } ) - ( [ ^ . - ] [ ^ . ] * ?[ ^ . - ] | [ ^ . - ] ) - ( [ a - z 0 - 9 _ - ] { 16 } ) \. ( .+ ) $ / ) ;
98+ const subdomainMatch = url . hostname . match (
99+ / ^ ( \d { 4 , 5 } ) - ( [ ^ . - ] [ ^ . ] * ?[ ^ . - ] | [ ^ . - ] ) - ( [ a - z 0 - 9 _ - ] { 16 } ) \. ( .+ ) $ /
100+ ) ;
84101
85102 if ( ! subdomainMatch ) {
86103 return null ;
@@ -117,18 +134,18 @@ export function isLocalhostPattern(hostname: string): boolean {
117134 return hostname === '[::1]' ;
118135 }
119136 }
120-
137+
121138 // Handle bare IPv6 without brackets
122139 if ( hostname === '::1' ) {
123140 return true ;
124141 }
125-
142+
126143 // For IPv4 and regular hostnames, split on colon to remove port
127- const hostPart = hostname . split ( ":" ) [ 0 ] ;
128-
144+ const hostPart = hostname . split ( ':' ) [ 0 ] ;
145+
129146 return (
130- hostPart === " localhost" ||
131- hostPart === " 127.0.0.1" ||
132- hostPart === " 0.0.0.0"
147+ hostPart === ' localhost' ||
148+ hostPart === ' 127.0.0.1' ||
149+ hostPart === ' 0.0.0.0'
133150 ) ;
134151}
0 commit comments