Commit 1ee75c7
authored
server: inject CORS headers on Fastify-internal errors (JavaScriptSolidServer#376) (JavaScriptSolidServer#421)
* server: inject CORS headers on Fastify-internal errors (JavaScriptSolidServer#376)
Surfaced during JavaScriptSolidServer#374 review. When Fastify itself rejects a
request before any handler runs (most commonly FST_ERR_BAD_URL
for malformed percent-encoding like `%g1`, truncated `%E0%`,
invalid UTF-8 sequences), the 400 response carries no
`Access-Control-Allow-*` headers — browsers surface it as a
generic CORS / network error instead of the real status,
which is confusing for debugging and undermines the per-handler
CORS work in JavaScriptSolidServer#371 / JavaScriptSolidServer#374.
## Root cause
`FST_ERR_BAD_URL` is thrown by Fastify's URL parser BEFORE the
request enters the normal handler chain. Confirmed by direct
probe: neither `setErrorHandler` nor `onSend` hooks fire for
this code path. Fastify writes the 400 response directly via
`res.writeHead({...})` / `res.end(body)` in `onBadUrl()`
(node_modules/fastify/fastify.js:752–771) when no
`frameworkErrors` option is configured.
## Fix
Configure Fastify's `frameworkErrors` option in `createServer`.
This is the explicit hook Fastify provides for framework-level
errors (FST_ERR_BAD_URL and FST_ERR_ASYNC_CONSTRAINT). When set,
Fastify routes the error through this function instead of the
direct-write path, giving us a Reply object to attach headers to.
The handler:
- Reads `request.headers.origin`; if present, sets the full
JSS CORS header set via `getCorsHeaders(origin)` (ACAO
mirrors the request Origin, plus Allow-Methods / Allow-
Headers / Expose-Headers / Credentials / Max-Age).
- Sends a JSON body matching the prior shape (`error`, `code`,
`message`, `statusCode`) — minimal change for clients that
were parsing the old format.
## Test plan
Three new integration tests against a live JSS server:
1. `%g1` with Origin → 400, ACAO mirrors Origin, full CORS set
2. `%g1` without Origin → 400, JSON body still well-shaped
(CORS not enforced when no Origin was sent)
3. Normal 404 path (no FST_ERR) → CORS still injected by the
existing wildcard handler — confirms the new hook didn't
displace existing behavior
Test count: 777 → 780 in full suite.
* Address copilot pass 1 on JavaScriptSolidServer#421
Three findings, all valid.
1. The `if (origin)` guard made the frameworkErrors path
inconsistent with the rest of the server, which sets CORS
on EVERY response via the global onRequest hook (with ACAO
defaulting to `*` when no Origin was sent). Removed the
guard — getCorsHeaders is now called unconditionally.
2. `error: err.name` produced "FastifyError" (unhelpful),
while Fastify's default error body uses the HTTP status
text ("Bad Request"). Switched to
`STATUS_CODES[statusCode] || 'Error'` from node:http —
400 → "Bad Request", 500 → "Internal Server Error", etc.
Matches Fastify's default body shape so any pre-fix client
that was parsing `error` keeps working.
3. The "no Origin" test name implied CORS was set with
ACAO=* but the assertions only checked the body. Now
asserts ACAO=*, ACAM contains GET, ACAH is set, and the
body's `error` field is "Bad Request". The "with Origin"
test also gained a body-shape assertion on the new
`error: "Bad Request"` field so any future regression
(like reverting to err.name) is caught.
Test count: same 780.
* Address copilot pass 2 on JavaScriptSolidServer#421 — doc cleanups
Two stale-docs findings, both real.
1. The test file's block comment said the fix attaches CORS
"whenever the bad request carried an Origin header" — that
was the pass-0 behavior. The pass-1 review (correctly)
removed that guard so CORS is now set unconditionally (with
ACAO=* when no Origin was sent). Updated the comment to
match the actual behavior.
2. The PR description's "After" example body still showed
`error: "FastifyError"` from my initial draft, before the
pass-1 fix that switched to `STATUS_CODES[statusCode]`
("Bad Request" for 400). Updated the PR body via REST
patch so the example matches the shipped response shape.
No code or test changes — pure documentation alignment.
Test count: same 780.1 parent b39c6d9 commit 1ee75c7
2 files changed
Lines changed: 109 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
169 | 170 | | |
170 | 171 | | |
171 | 172 | | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
172 | 199 | | |
173 | 200 | | |
174 | 201 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
0 commit comments