-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsocketPyProbe.html
More file actions
81 lines (77 loc) · 4.81 KB
/
Copy pathsocketPyProbe.html
File metadata and controls
81 lines (77 loc) · 4.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<!--
결과(append): 진짜 파이썬(Pyodide) socket이 WS->TCP 릴레이 통해 진짜 인터넷 바이트를 받는가.
파이썬 socket.socket()을 릴레이 소켓으로 심하고, Python이 동기 connect/send/recv로 example.com:80에
raw HTTP를 왕복한다. 블로킹 recv는 워커에서 Atomics.wait, main이 WS/릴레이를 서비스. 릴레이 없으면 SKIP.
-->
<html lang="ko">
<head><meta charset="utf-8"><title>probe: python socket over relay</title>
<style>body{font:14px/1.5 system-ui;margin:2rem;max-width:52rem} pre{background:#f4f4f5;padding:1rem;border-radius:6px;white-space:pre-wrap}</style></head>
<body>
<h1>probe: 진짜 파이썬 socket이 릴레이 통해 진짜 TCP (벽2 파이썬 배선)</h1>
<p>Pyodide의 Python <code>socket.socket()</code>을 릴레이 소켓으로 심한다. Python이 동기
<code>connect/send/recv</code>로 example.com:80에 raw HTTP를 보내고 raw 응답을 받는다. recv 블로킹은
워커 Atomics.wait, main은 WS와 릴레이를 서비스. requests/urllib3도 이 socket 심을 그대로 쓴다.
릴레이 없으면 SKIP.</p>
<pre id="out">실행 중...</pre>
<script type="module">
const out = document.getElementById("out");
const checks = []; const timings = {};
const check = (name, pass, info = "") => { checks.push({ name, pass: !!pass, info: String(info) }); out.textContent += `\n${pass ? "PASS" : "FAIL"} ${name}${info ? " (" + info + ")" : ""}`; };
const report = async () => {
const ok = checks.length > 0 && checks.every((c) => c.pass);
try { await fetch("/gateReport", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ok, checks, timings }) }); } catch (e) {}
};
const RELAY = "ws://127.0.0.1:8791";
const PYODIDE_INDEX = "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/"; // pyproc 기본, 워커 import(pyodide.mjs)
try {
let up = false;
try { await new Promise((res, rej) => { const w = new WebSocket(RELAY); w.onopen = () => { up = true; w.close(); res(); }; w.onerror = () => rej(); setTimeout(rej, 2500); }); } catch (e) { up = false; }
if (!up) { check("SKIP: 릴레이 미기동(node tests/attempts/socketBridge/relay.mjs 8791)", true); await report(); throw new Error("__skip__"); }
const ctl = new Int32Array(new SharedArrayBuffer(16));
const data = new Uint8Array(new SharedArrayBuffer(256 * 1024));
let ws = null; const queue = [];
const pump = () => {
if (Atomics.load(ctl, 0) !== 0 || !queue.length) return;
const ev = queue.shift();
data.set(ev.bytes);
Atomics.store(ctl, 2, ev.bytes.length);
Atomics.store(ctl, 1, ev.type);
Atomics.store(ctl, 0, 1);
Atomics.notify(ctl, 0);
};
const push = (type, bytes = new Uint8Array(0)) => {
if (type === 2 && bytes.length > data.length) { for (let o = 0; o < bytes.length; o += data.length) queue.push({ type: 2, bytes: bytes.subarray(o, o + data.length) }); }
else queue.push({ type, bytes });
pump();
};
const worker = new Worker(new URL("./socketPyWorker.js", import.meta.url), { type: "module" });
let t0 = performance.now();
const done = new Promise((resolve) => {
worker.onmessage = (m) => {
const d = m.data;
if (d.type === "drained") pump();
else if (d.type === "dial") {
ws = new WebSocket(RELAY); ws.binaryType = "arraybuffer";
ws.onopen = () => ws.send(JSON.stringify({ host: d.host, port: d.port }));
ws.onmessage = (ev) => { if (typeof ev.data === "string") { const mm = JSON.parse(ev.data); if (mm.type === "connected") push(1); else if (mm.type === "closed") push(3); else if (mm.type === "error") push(4); } else push(2, new Uint8Array(ev.data)); };
ws.onerror = () => push(4);
ws.onclose = () => push(3);
} else if (d.type === "send") { if (ws) ws.send(d.bytes); }
else if (d.type === "result") resolve(d);
};
worker.onerror = (e) => resolve({ ok: false, error: "worker: " + (e.message || e) });
});
worker.postMessage({ type: "boot", indexURL: PYODIDE_INDEX, ctlSab: ctl.buffer, dataSab: data.buffer });
const r = await Promise.race([done, new Promise((res) => setTimeout(() => res({ ok: false, error: "timeout" }), 90000))]);
timings.totalMs = Math.round(performance.now() - t0);
check("Pyodide Python socket -> 릴레이 -> 진짜 TCP 왕복", r.ok === true && /HTTP\/1\.[01] 200/.test(r.status || ""), r.error || `status="${r.status}", ${r.len}바이트, ${timings.totalMs}ms`);
check("파이썬이 받은 응답이 진짜 200 + <html> 바디", r.ok && /HTTP\/1\.[01] 200/.test(r.status || "") && r.hasBody === true, `status="${r.status}", body=${r.hasBody}`);
worker.terminate();
} catch (e) {
if (!String(e).includes("__skip__")) check("예외 없음", false, String(e).slice(-300));
}
await report();
</script>
</body>
</html>