질문: 브라우저 탭의 파이썬이 임의 host:port로 진짜 TCP 소켓을 열 수 있는가 (HTTP fetch 근사가 아니라 socket/requests/urllib3가 원하는 소켓 층).
물리: 탭은 raw 소켓(socket()/connect())을 못 연다. 하지만 밖으로 다이얼하는 WebSocket은 연다. 그래서 아웃바운드 진짜 TCP는 얇은 릴레이(진짜 NIC를 만지는 외부 조각)로만 가능하다. 인바운드(탭이 공개 서버)는 외부 컴포넌트 없이는 물리적으로 불가(역터널 릴레이 필요). 리서치 정본: 웹 python-runtime 원장 2026-07-13 벽2 절.
- 얇은 WS->TCP 릴레이(의존성 0)로 브라우저에서 임의 host:port에 진짜 TCP 왕복이 된다.
- 파이썬 소켓의 어려운 절반(블로킹
recv()를 비동기 전송 위에서)이 pyproc이 이미 쓰는 SAB+Atomics로 성립한다(= 새 엔진 문제가 아니라 능력 배선).
- 브라우저에서 raw 바이트가 릴레이를 통해 실제 TCP로 왕복(HTTP 어댑터가 아니라 소켓 층 원문) = pass/fail
- 워커에서 동기 connect/send/recv 완주(recv가 진짜 블로킹) = pass/fail
- 승격 시: 파이썬
socket모듈을 이 브리지에 배선(Pyodide 소켓 패치 / WASI 소켓), 릴레이 계약을 소비자 교체 가능하게(Wisp 프로토콜 멀티플렉싱은 v2).
relay.mjs= zero-dep WS->TCP 릴레이(RFC 6455 핸드셰이크+프레이밍을 node:crypto/net으로 직접). 기동:node tests/attempts/socketBridge/relay.mjs 8791. 소비자 교체 가능(Wisp/websockify/자체).- probe는 릴레이 미기동 시 SKIP(게이트 green). 진짜 네트워크(example.com:80)를 타므로 CI 상시 실행 아님(수동/로컬 실측).
-
socketProbe GREEN 3/3: 브라우저 -> WS -> 릴레이 ->
example.com:80raw HTTP/1.0 ->HTTP/1.1 200 OK828바이트 원문(헤더+<html>바디, 소켓 층 그대로), 왕복 143ms. 임의 host:port 다이얼도 진짜(닫힌 127.0.0.1:9 ->ECONNREFUSED소켓 에러 전달). -
socketBlockingProbe GREEN 2/2: 워커에서 동기
connect/send/recv(블로킹 recv를 Atomics.wait으로) 완주,HTTP/1.1 200 OK828바이트, 118ms. 파이썬socket.recv()의 동기 의미가 비동기 WS 위에서 성립. -
socketPyProbe GREEN 2/2: 진짜 파이썬(Pyodide)이 소켓을 연다. 파이썬
socket.socket()을 릴레이 소켓으로 심하고(Python->js.bridge* 동기 호출, 블로킹 recv는 워커 Atomics.wait), Python 코드sock.connect(('example.com',80)); sock.sendall(b'GET...'); sock.recv()가HTTP/1.1 200 OK828바이트<html>바디를 받는다(총 3864ms, 대부분 Pyodide 부팅). JS가 아니라 파이썬이 진짜 인터넷 바이트를 받는다.requests/urllib3/http.client가 같은 socket API라 이 심을 그대로 쓴다(makefile 등 확장은 v2). -
socketCapProbe GREEN 3/3 (src 능력 승격, http + https):
Runtime.enableSocketBridge({relayURL})로 승격. 메인 스레드 Runtime에서 파이썬 socket.socket()/create_connection을 릴레이에 심하고(블로킹 recv = JSPI/run_sync, runAsync 경로),urllib.request.urlopen이 진짜 소켓으로 HTTP 200(http://example.com/559바이트 516ms) 와 HTTPS 200(https://example.com/219ms)을 수신. HTTPS는 릴레이가 port 443에서 TLS 종단하고 파이썬ssl.SSLContext.wrap_socket은 패스스루(이중 암호화 방지). http.client가 심한 socket(create_connection + makefile)을 그대로 써서 진짜 TCP로 왕복. 파이썬 HTTP 스택이 브라우저에서 진짜 소켓으로 http + https 둘 다 돈다.- 실측 특정 버그: http.client는
Connection: close응답에서 헤더만 읽고 소켓을 닫은 뒤 바디를 makefile로 읽는다(CPython 소켓은 refcount로 생존).close()가 즉시 WS를 닫으면 릴레이가 in-flight 응답을 끊어 truncation(IncompleteRead 559바이트).close()를 유예해 데이터 드레인이 끝나게 해결.
- 실측 특정 버그: http.client는
정직한 경계(실측): 이 브리지는 stdlib HTTP 스택(urllib/http.client)을 진짜 소켓으로 돌린다(http+https 실측). requests/urllib3는 Pyodide에서 socket이 아니라 **JS fetch(emscripten 백엔드)**를 써서(실측: Failed to fetch) 이 심을 안 타고 CORS 제약을 받는다. requests를 소켓으로 돌리려면 Pyodide의 urllib3-fetch 패치를 되돌려야 하는데 깊고 취약해 스코프 밖.
의미: 벽2 아웃바운드가 개념 -> 진짜 파이썬 소켓 -> **src 능력(http + https, stdlib HTTP 스택)**으로 완결됐다. SocketBridge가 공개 표면. 남은 강화 = 릴레이 Wisp 멀티플렉싱, in-tab TLS(e2e). 인바운드는 정직한 물리 벽으로 남는다(역터널 릴레이 = 별도 조각).