Skip to content

Commit cda037b

Browse files
authored
fix: add e2e and return 404 when a server-function is not found (#1785)
1 parent 0be2bf8 commit cda037b

File tree

3 files changed

+67
-36
lines changed

3 files changed

+67
-36
lines changed

.changeset/heavy-shrimps-study.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solidjs/start": patch
3+
---
4+
5+
Return `404` when server-function is not found

packages/start/src/runtime/server-handler.ts

+45-27
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ import {
1616
import { sharedConfig } from "solid-js";
1717
import { renderToString } from "solid-js/web";
1818
import { provideRequestEvent } from "solid-js/web/storage";
19-
import { eventHandler, parseCookies, setHeader, setResponseStatus, type HTTPEvent } from "vinxi/http";
19+
import {
20+
eventHandler,
21+
parseCookies,
22+
setHeader,
23+
setResponseStatus,
24+
type HTTPEvent
25+
} from "vinxi/http";
2026
import invariant from "vinxi/lib/invariant";
2127
import { cloneEvent, getFetchEvent, mergeResponseHeaders } from "../server/fetchEvent";
2228
import { getExpectedRedirectStatus } from "../server/handler";
@@ -87,14 +93,23 @@ async function handleServerFunction(h3Event: HTTPEvent) {
8793
} else {
8894
functionId = url.searchParams.get("id");
8995
name = url.searchParams.get("name");
90-
if (!functionId || !name) return new Response("Server function not found", { status: 404 });
96+
97+
if (!functionId || !name) {
98+
return process.env.NODE_ENV === "development"
99+
? new Response("Server function not found", { status: 404 })
100+
: new Response(null, { status: 404 });
101+
}
91102
}
92103

93104
const serverFnInfo = serverFnManifest[functionId];
94105
let fnModule: undefined | { [key: string]: any };
95106

96-
if (!serverFnInfo) return new Response("Server function not found", { status: 404 });
97-
107+
if (!serverFnInfo) {
108+
return process.env.NODE_ENV === "development"
109+
? new Response("Server function not found", { status: 404 })
110+
: new Response(null, { status: 404 });
111+
}
112+
98113
if (process.env.NODE_ENV === "development") {
99114
// In dev, we use Vinxi to get the "server" server-side router
100115
// Then we use that router's devServer.ssrLoadModule to get the serverFn
@@ -118,19 +133,19 @@ async function handleServerFunction(h3Event: HTTPEvent) {
118133
const json = JSON.parse(args);
119134
(json.t
120135
? (fromJSON(json, {
121-
plugins: [
122-
CustomEventPlugin,
123-
DOMExceptionPlugin,
124-
EventPlugin,
125-
FormDataPlugin,
126-
HeadersPlugin,
127-
ReadableStreamPlugin,
128-
RequestPlugin,
129-
ResponsePlugin,
130-
URLSearchParamsPlugin,
131-
URLPlugin
132-
]
133-
}) as any)
136+
plugins: [
137+
CustomEventPlugin,
138+
DOMExceptionPlugin,
139+
EventPlugin,
140+
FormDataPlugin,
141+
HeadersPlugin,
142+
ReadableStreamPlugin,
143+
RequestPlugin,
144+
ResponsePlugin,
145+
URLSearchParamsPlugin,
146+
URLPlugin
147+
]
148+
}) as any)
134149
: json
135150
).forEach((arg: any) => parsed.push(arg));
136151
}
@@ -148,7 +163,8 @@ async function handleServerFunction(h3Event: HTTPEvent) {
148163
const isReadableStream = h3Request instanceof ReadableStream;
149164
const hasReadableStream = (h3Request as EdgeIncomingMessage).body instanceof ReadableStream;
150165
const isH3EventBodyStreamLocked =
151-
(isReadableStream && h3Request.locked) || (hasReadableStream && ((h3Request as EdgeIncomingMessage).body as ReadableStream).locked);
166+
(isReadableStream && h3Request.locked) ||
167+
(hasReadableStream && ((h3Request as EdgeIncomingMessage).body as ReadableStream).locked);
152168
const requestBody = isReadableStream ? h3Request : h3Request.body;
153169

154170
if (
@@ -272,13 +288,15 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole
272288
if (result) {
273289
headers.append(
274290
"Set-Cookie",
275-
`flash=${encodeURIComponent(JSON.stringify({
276-
url: url.pathname + url.search,
277-
result: isError ? result.message : result,
278-
thrown: thrown,
279-
error: isError,
280-
input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]]
281-
}))}; Secure; HttpOnly;`
291+
`flash=${encodeURIComponent(
292+
JSON.stringify({
293+
url: url.pathname + url.search,
294+
result: isError ? result.message : result,
295+
thrown: thrown,
296+
error: isError,
297+
input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]]
298+
})
299+
)}; Secure; HttpOnly;`
282300
);
283301
}
284302
return new Response(null, {
@@ -302,7 +320,7 @@ function createSingleFlightHeaders(sourceEvent: FetchEvent) {
302320
useH3Internals = true;
303321
sourceEvent.nativeEvent.node.req.headers.cookie = "";
304322
}
305-
SetCookies.forEach((cookie) => {
323+
SetCookies.forEach(cookie => {
306324
if (!cookie) return;
307325
const keyValue = cookie.split(";")[0]!;
308326
const [key, value] = keyValue.split("=");
@@ -311,7 +329,7 @@ function createSingleFlightHeaders(sourceEvent: FetchEvent) {
311329
Object.entries(cookies).forEach(([key, value]) => {
312330
headers.append("cookie", `${key}=${value}`);
313331
useH3Internals && (sourceEvent.nativeEvent.node.req.headers.cookie += `${key}=${value};`);
314-
})
332+
});
315333

316334
return headers;
317335
}

packages/tests/cypress/e2e/server-function.cy.ts

+17-9
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,46 @@ describe("server-function", () => {
22
it("should have isServer false in the client", () => {
33
cy.visit("/");
44
cy.get("#server-fn-test").contains('{"clientWithIsServer":false}');
5-
})
5+
});
66
it("should have isServer true in the server function - nested", () => {
77
cy.visit("/is-server-nested");
88
cy.get("#server-fn-test").contains('{"serverFnWithIsServer":true}');
9-
})
9+
});
1010
it("should have an id of type string in the server function meta - nested", () => {
1111
cy.visit("/server-function-meta-nested");
1212
cy.get("#server-fn-test").contains('{"serverFnWithMeta":"string"}');
13-
})
13+
});
1414
it("should externalize node builtin in server function - nested", () => {
1515
cy.visit("/node-builtin-nested");
1616
cy.get("#server-fn-test").contains('{"serverFnWithNodeBuiltin":"can/externalize"}');
17-
})
17+
});
1818
it("should externalize npm module in server function - nested", () => {
1919
cy.visit("npm-module-nested");
2020
cy.get("#server-fn-test").contains('{"serverFnWithNpmModule":[2,4,6]}');
21-
})
21+
});
2222

2323
it("should have isServer true in the server function - toplevel", () => {
2424
cy.visit("/is-server-toplevel");
2525
cy.get("#server-fn-test").contains('{"serverFnWithIsServer":true}');
26-
})
26+
});
2727
it("should have an id of type string in the server function meta - toplevel", () => {
2828
cy.visit("/server-function-meta");
2929
cy.get("#server-fn-test").contains('{"serverFnWithMeta":"string"}');
30-
})
30+
});
3131
it("should externalize node builtin in server function - toplevel", () => {
3232
cy.visit("/node-builtin-toplevel");
3333
cy.get("#server-fn-test").contains('{"serverFnWithNodeBuiltin":"can/externalize"}');
34-
})
34+
});
3535
it("should externalize npm module in server function - toplevel", () => {
3636
cy.visit("npm-module-toplevel");
3737
cy.get("#server-fn-test").contains('{"serverFnWithNpmModule":[2,4,6]}');
38-
})
38+
});
39+
it("should throw a 404 if function is not found", () => {
40+
cy.request({
41+
url: "/_server/?name='not-found-function'",
42+
failOnStatusCode: false
43+
})
44+
.its("status")
45+
.should("eq", 404);
46+
});
3947
});

0 commit comments

Comments
 (0)