Skip to content

Commit 9034af8

Browse files
committed
also fallback when you get 405 status code
1 parent 67a0a24 commit 9034af8

2 files changed

Lines changed: 28 additions & 5 deletions

File tree

‎src/modules/actors.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ const DEAD_MS = 3_000;
5252
// Mint responses that mean "direct can't serve this connection, the proxy can":
5353
// 409 = legacy-family actor script, 503 = direct connections not provisioned,
5454
// 422 = no principal (e.g. anonymous outside a browser) or an id/room only the
55-
// proxy's looser validation accepts. The proxy serves migrated actors too, so
56-
// falling back is always safe.
57-
const PROXY_FALLBACK_STATUSES = new Set([409, 422, 503]);
55+
// proxy's looser validation accepts, 405 = a backend that predates the mint
56+
// endpoint (its actor deploy routes catch the path via `{handler_name:path}`
57+
// but not the POST method — and the real endpoint never 405s a POST). The
58+
// proxy serves migrated actors too, so falling back is always safe.
59+
const PROXY_FALLBACK_STATUSES = new Set([405, 409, 422, 503]);
5860

5961
// Mint responses no retry can fix (bad request / forbidden / not found): the
6062
// connection closes instead of re-minting forever; a fresh connect() re-probes.

‎tests/unit/actors.test.ts‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ describe("Actors Module — proxy fallback", () => {
264264
);
265265
});
266266

267-
test.each([503, 422])("mint %i falls back to the proxy", async (status) => {
267+
test.each([503, 422, 405])("mint %i falls back to the proxy", async (status) => {
268268
const config = makeConfig();
269269
config.mintConnectionToken.mockRejectedValueOnce(httpError(status));
270270
mod(config).GameRoom("r").connect({ id: "c" });
@@ -440,7 +440,7 @@ describe("Actors Module — mint error reporting", () => {
440440
expect(reported.message).toContain("boom");
441441
});
442442

443-
test.each([409, 422, 503])(
443+
test.each([405, 409, 422, 503])(
444444
"the expected fallback status %i is silent",
445445
async (status) => {
446446
const config = makeConfig();
@@ -599,6 +599,27 @@ describe("Actors Module — client wiring", () => {
599599
base44.cleanup();
600600
});
601601

602+
test("a 405 mint reply (backend without the endpoint) falls back to the proxy", async () => {
603+
// What a pre-direct backend actually answers: its actor deploy routes
604+
// match the path via `{handler_name:path}` but not the POST method.
605+
nock(serverUrl)
606+
.post(`/api/apps/${appId}/actors/PongGame/connection-token`)
607+
.reply(405, {
608+
error_type: "HTTPException",
609+
message: "Method Not Allowed",
610+
detail: "Method Not Allowed",
611+
});
612+
613+
const onError = vi.fn();
614+
const base44 = createClient({ serverUrl, appId, token: "tok", options: { onError } });
615+
base44.actors.PongGame("r1").connect({ id: "c1" });
616+
await expect(sockets[0].urlProvider()).resolves.toBe(
617+
"wss://base44.app/parties/PongGame/r1?_pk=c1&app_id=app-1&handler=PongGame&token=tok",
618+
);
619+
expect(onError).not.toHaveBeenCalled();
620+
base44.cleanup();
621+
});
622+
602623
test("a non-fallback mint failure reaches the client's onError as a Base44Error", async () => {
603624
nock(serverUrl)
604625
.post(`/api/apps/${appId}/actors/PongGame/connection-token`)

0 commit comments

Comments
 (0)