http/simple_request.ts

Refusing CORS simple requests — the cross-origin POSTs a browser sends without asking first.

A cross-origin fetch or form POST skips the CORS preflight when its Content-Type is absent or one of the three CORS-safelisted types (text/plain, application/x-www-form-urlencoded, multipart/form-data). The browser sends such a request as-is, with whatever cookies apply — only the response is withheld from the page. Any other content type makes the browser send an OPTIONS preflight first, and since fuz_app answers no preflight with CORS headers, the real request is never sent.

The session cookie's SameSite=Strict doesn't close this on its own: same-site ignores the port, so a page on http://localhost:8000 is same-site with a server on http://localhost:4460 and its requests carry the cookie. The origin allowlist (http/origin.ts) refuses such a page unless a wildcard (http://localhost:*) admits it; this gate refuses it whatever the allowlist says.

So every POST route carries a content-type check, run first by apply_route_specs (is_post_content_type_refused, 415 unsupported_content_type): a JSON route takes only application/json, bodyless or not, and a raw_body route takes any type that isn't safelisted. Only POST needs it: PUT, PATCH, and DELETE are never simple methods, so the browser always preflights them.

The check rests on the server answering no preflight. A consumer that adds CORS middleware granting cross-origin requests reopens what this closes.

Twin of the Rust spine's fuz_http::simple_request: the same predicates, is_json_content_type checked in each spine JSON handler and is_cors_simple_content_type by the simple_request_layer consumers mount over routes with other bodies.

view source

Declarations
#

3 declarations

is_cors_simple_content_type
#

http/simple_request.ts view source

(content_type: string | undefined): boolean import {is_cors_simple_content_type} from '@fuzdev/fuz_app/http/simple_request.js';

True when a browser could have sent a POST with this Content-Type cross-origin without a preflight: none, or a CORS-safelisted one.

Matches more than browsers do (a value they'd refuse to call safelisted — one with odd bytes, say — counts as safelisted here), so it only ever refuses too much, never too little.

content_type

the request's Content-Type header, if any

type string | undefined

returns

boolean

is_json_content_type
#

http/simple_request.ts view source

(content_type: string | undefined): boolean import {is_json_content_type} from '@fuzdev/fuz_app/http/simple_request.js';

True when Content-Type is application/json (any parameters, such as charset, allowed).

content_type

the request's Content-Type header, if any

type string | undefined

returns

boolean

is_post_content_type_refused
#

http/simple_request.ts view source

(content_type: string | undefined, raw_body: boolean): boolean import {is_post_content_type_refused} from '@fuzdev/fuz_app/http/simple_request.js';

True when a POST route refuses this Content-Type: a JSON route anything but application/json, a raw_body route a CORS-safelisted type or none.

content_type

the request's Content-Type header, if any

type string | undefined

raw_body

the route's RouteSpec.raw_body

type boolean

returns

boolean

Imported by
#