Skip to content

Commit b19930e

Browse files
bajrangCoderclaude
andcommitted
fix: load the SDL runtime with the browser's own fetch
On a device, SDL failed with "Failed to connect to localhost/127.0.0.1:443": on Acode's main page window.fetch can be replaced (e.g. by another plugin using native HTTP to avoid CORS), and native HTTP cannot reach the plugin's files at https://localhost, which only exist inside the WebView. The worker has its own fetch, so it was unaffected. Requests for the plugin's files and Pyodide packages now go through the browser's fetch taken from a hidden same-origin iframe; all other requests still use the page's fetch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 67cd854 commit b19930e

3 files changed

Lines changed: 38 additions & 2 deletions

File tree

‎plugin.zip‎

377 Bytes
Binary file not shown.

‎src/runtime.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import pygameSource from "./python/acode_pygame.py";
99

1010
// Packages not bundled with the plugin are fetched on demand from the CDN
1111
// build matching the bundled runtime (PYODIDE_VERSION is injected at build time)
12-
const PACKAGE_BASE_URL = `https://cdn.jsdelivr.net/pyodide/v${PYODIDE_VERSION}/full/`;
12+
export const PACKAGE_BASE_URL = `https://cdn.jsdelivr.net/pyodide/v${PYODIDE_VERSION}/full/`;
1313
const NOISE = /already loaded from|^No new packages to load/;
1414

1515
/**

‎src/sdl.js‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
// SDL needs a real <canvas>, which a worker does not have, so this runtime
33
// runs on the main thread. Ordinary blocking game loops work through JSPI
44
// (see python/acode_pygame.py); without JSPI only async loops are safe.
5-
import { flushFigures, loadRuntime, prepareCode } from "./runtime.js";
5+
import {
6+
flushFigures,
7+
loadRuntime,
8+
PACKAGE_BASE_URL,
9+
prepareCode,
10+
} from "./runtime.js";
611

712
const RUNNER_SOURCE = `
813
import asyncio
@@ -47,9 +52,40 @@ let runner = null;
4752
/** the current run: its task handle and whether it was stopped */
4853
let current = null;
4954
let onFatal = () => {};
55+
let fetchRouted = false;
56+
57+
/**
58+
* Other plugins may replace `window.fetch` on Acode's page, e.g. with native
59+
* HTTP to avoid CORS. Native HTTP cannot reach the plugin's own files at
60+
* https://localhost/..., which only exist inside the WebView, so Pyodide
61+
* failed with "Failed to connect to localhost/127.0.0.1:443". Requests for the
62+
* plugin's files and Pyodide packages go through the browser's own fetch,
63+
* taken from a hidden same-origin iframe; everything else is untouched.
64+
*/
65+
function routeRuntimeFetch(baseUrl) {
66+
if (fetchRouted) return;
67+
fetchRouted = true;
68+
const $frame = document.createElement("iframe");
69+
$frame.style.display = "none";
70+
$frame.setAttribute("aria-hidden", "true");
71+
document.body.append($frame);
72+
const frameWindow = $frame.contentWindow;
73+
const browserFetch = frameWindow.fetch.bind(frameWindow);
74+
const pageFetch = window.fetch;
75+
const prefixes = [baseUrl, PACKAGE_BASE_URL];
76+
77+
window.fetch = function (input, init) {
78+
const url = typeof input === "string" ? input : (input?.url ?? `${input}`);
79+
if (prefixes.some((prefix) => url.startsWith(prefix))) {
80+
return browserFetch(input, init);
81+
}
82+
return pageFetch.call(this, input, init);
83+
};
84+
}
5085

5186
function getRuntime(baseUrl) {
5287
runtimePromise ??= (async () => {
88+
routeRuntimeFetch(baseUrl);
5389
const pyodide = await loadRuntime({
5490
baseUrl,
5591
stdout: (text) => handlers.stdout(text),

0 commit comments

Comments
 (0)