← tests

cors

The framework's own CORS feature, attached to /cors with the one policy every framework configures itself: a preflight the feature answers alone, and the real request it lets through.

Comparable across every framework. The policy names its origin, because with * the feature compares nothing, and an API that sends credentials cannot use *. A framework whose feature can only cover the whole application runs it on every request, and says so in its README.

A value written {run.name} is drawn once per run and never given to the framework, so it cannot answer from a table.

testheftrequestbasewhat differs from the base
cors.disallowednot measuredOPTIONS /cors/small
cors.preflight1OPTIONS /cors/smallbaseline.plaintexta CORS preflight answered by the framework's CORS feature, before any handler
cors.request1GET /cors/smalljson.smallthe CORS feature checking the origin and adding its headers to a real response
cors.scopednot measuredGET /json/small
cors.varynot measuredGET /cors/small

cors.disallowed

not measured

A preflight from an origin the policy does not name gets no access-control-allow-origin, which is what stops the browser. The status is not checked, because frameworks differ on it.

Request

OPTIONS /cors/small
originhttps://elsewhere.example.net
access-control-request-methodGET
access-control-request-headersx-rb-tenant

No body.

Expected response

Any status
access-control-allow-originis absent

The body is not checked.

Test source, tests/cors/disallowed.ts
tests/cors/disallowed.tsopen on GitHub →
import { validationTest } from "#kit";
import { CORS } from "#models/configuration";

const path = "/cors/small";
const cors = CORS;

export default validationTest({
  id: { family: "cors", name: "disallowed" },
  path,
  about:
    "A preflight from an origin the policy does not name gets no " +
    "access-control-allow-origin, which is what stops the browser. The status " +
    "is not checked, because frameworks differ on it.",

  request: (c) =>
    c
      .options(path)
      .header("origin", "https://elsewhere.example.net")
      .header("access-control-request-method", cors.method)
      .header("access-control-request-headers", cors.header)
      .noHeader("access-control-allow-origin"),
});

cors.preflight

heft 1

base baseline.plaintext

The question a browser asks before a cross-origin request with a custom header, answered by the CORS feature before any handler runs. The handler on this route writes x-rb-serial, so its absence shows the feature answered alone. 200 and 204 are both accepted, because the Fetch standard takes any 2xx and frameworks split. Read against baseline.plaintext, the difference is the policy being matched.

Request

OPTIONS /cors/small
originhttps://shop.example.com
access-control-request-methodGET
access-control-request-headersx-rb-tenant

No body.

Expected response

HTTP 200 or 204
access-control-allow-originis https://shop.example.com
access-control-allow-headersmatches /(^|,)\s*x-rb-tenant\s*(,|$)/i
access-control-max-ageis 600
x-rb-serialis absent

The body is not checked.

Test source, tests/cors/preflight.ts
tests/cors/preflight.tsopen on GitHub →
import { performanceTest } from "#kit";
import { CORS } from "#models/configuration";

const path = "/cors/small";
const cors = CORS;

/** The header among any others the framework lists, in any case. */
const LISTS = new RegExp(`(^|,)\\s*${cors.header}\\s*(,|$)`, "i");

export default performanceTest({
  id: { family: "cors", name: "preflight" },
  path,
  base: "baseline.plaintext",
  varies: "preflight",
  heft: 1,
  about:
    "The question a browser asks before a cross-origin request with a custom " +
    "header, answered by the CORS feature before any handler runs. The " +
    "handler on this route writes x-rb-serial, so its absence shows the " +
    "feature answered alone. 200 and 204 are both accepted, because the Fetch " +
    "standard takes any 2xx and frameworks split. Read against " +
    "baseline.plaintext, the difference is the policy being matched.",

  request: (c) =>
    c
      .options(path)
      .header("origin", cors.origin)
      .header("access-control-request-method", cors.method)
      .header("access-control-request-headers", cors.header)
      .status(200, 204)
      .hasHeader("access-control-allow-origin", cors.origin)
      .hasHeader("access-control-allow-headers", LISTS)
      .hasHeader("access-control-max-age", String(cors.maxAgeSeconds))
      .noHeader("x-rb-serial"),
});

cors.request

heft 1

base json.small

The cross-origin request itself, with the origin and the custom header the preflight asked about. The feature adds its header and lets the request through to the handler. Read against json.small, the difference is the policy checked on a request that passes it.

Request

GET /cors/small
originhttps://shop.example.com
x-rb-tenant{run.tenant}

No body.

Expected response

HTTP 200
access-control-allow-originis https://shop.example.com
x-rb-serialis new, so the handler ran

payload items.small

{"size":"small","count":1,"items":[{"id":1,"name":"slate-lamp-6647","category":"tools","priceCents":18928,"inStock":true}]}

Compared as parsed JSON, so key order and how a number is written do not matter.

Test source, tests/cors/request.ts
tests/cors/request.tsopen on GitHub →
import { performanceTest } from "#kit";
import { CORS } from "#models/configuration";
import { items } from "#payloads";

const path = "/cors/small";
const cors = CORS;

export default performanceTest({
  id: { family: "cors", name: "request" },
  path,
  base: "json.small",
  varies: "cors",
  heft: 1,
  about:
    "The cross-origin request itself, with the origin and the custom header " +
    "the preflight asked about. The feature adds its header and lets the " +
    "request through to the handler. Read against json.small, the difference " +
    "is the policy checked on a request that passes it.",

  request: (c) =>
    c
      .get(path)
      .header("origin", cors.origin)
      .header(cors.header, c.run.tenant)
      .okWith(items.small)
      .hasHeader("access-control-allow-origin", cors.origin)
      .fresh(),
});

cors.scoped

not measured

The allowed origin asking a route outside /cors gets no access-control-allow-origin, because the policy is attached to /cors and nowhere else. A framework whose CORS feature can only cover the whole application cannot pass this, and says why in the skips of its rb.json.

Request

GET /json/small
originhttps://shop.example.com

No body.

Expected response

HTTP 200
access-control-allow-originis absent

The body is not checked.

Test source, tests/cors/scoped.ts
tests/cors/scoped.tsopen on GitHub →
import { validationTest } from "#kit";
import { CORS } from "#models/configuration";

const path = "/json/small";
const cors = CORS;

export default validationTest({
  id: { family: "cors", name: "scoped" },
  path,
  about:
    "The allowed origin asking a route outside /cors gets no " +
    "access-control-allow-origin, because the policy is attached to /cors and " +
    "nowhere else. A framework whose CORS feature can only cover the whole " +
    "application cannot pass this, and says why in the skips of its rb.json.",

  request: (c) => c.get(path).header("origin", cors.origin).ok().noHeader("access-control-allow-origin"),
});

cors.vary

not measured

The real response carries Vary: Origin. A policy that names its origin answers differently per origin, so a cache in front of the framework has to key on it.

Request

GET /cors/small
originhttps://shop.example.com
x-rb-tenant{run.tenant}

No body.

Expected response

HTTP 200
varymatches /(^|,)\s*origin\s*(,|$)/i

The body is not checked.

Test source, tests/cors/vary.ts
tests/cors/vary.tsopen on GitHub →
import { validationTest } from "#kit";
import { CORS } from "#models/configuration";

const path = "/cors/small";
const cors = CORS;

export default validationTest({
  id: { family: "cors", name: "vary" },
  path,
  about:
    "The real response carries Vary: Origin. A policy that names its origin " +
    "answers differently per origin, so a cache in front of the framework has " +
    "to key on it.",

  request: (c) =>
    c
      .get(path)
      .header("origin", cors.origin)
      .header(cors.header, c.run.tenant)
      .ok()
      .hasHeader("vary", /(^|,)\s*origin\s*(,|$)/i),
});