1.0.0 • Published 1 year ago

@httpland/cors-middleware v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

cors-middleware

deno land deno doc GitHub release (latest by date) codecov GitHub

test NPM

HTTP cross-origin resource sharing(CORS) middleware.

Compliant with Fetch living standard, 3.2. CORS protocol.

Terms

Middleware

For a definition of Universal HTTP middleware, see the http-middleware project.

CORS request

Add a CORS header to the response in the downstream.

No special action is taken in response to CORS preflight requests. Use preflight for that.

import {
  cors,
  type Handler,
} from "https://deno.land/x/cors_middleware@$VERSION/mod.ts";
import { assert } from "https://deno.land/std/testing/asserts.ts";

const middleware = cors();
const corsRequest = new Request("test:", {
  headers: { origin: "<origin>" },
});
declare const handler: Handler;

const response = await middleware(corsRequest, handler);

assert(response.headers.has("access-control-allow-origin"));

yield:

Access-Control-Allow-Origin: *
Vary: Origin

CORS request options

cors accept following options:

NameTypeDescription
allowOrigins* | (string | RegExp )[]Allowed origin list.
allowCredentialstrue | "true"Access-Control-Allow-Credentials
exposeHeadersstring[]Access-Control-Expose-Headers

AllowOrigins

allowOrigins is * or a list of allowed origins.

The default is *.

Asterisk

If *, Access-Control-Allow-Origin(*) will add to the response.

List

The list may consist of strings or regular expression objects.

The middleware compares Origin header and each element of the allowOrigins.

If matched, Access-Control-Allow-Origin(Origin header) will add to the response.

If no match, Access-Control-Allow-Origin(null) will add to the response.

import {
  cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
import { assert } from "https://deno.land/std/testing/asserts.ts";

const middleware = cors({
  allowOrigins: ["https://test.example", /^https:\/\/cdn\..*/],
});

yield:

Access-Control-Allow-Origin: <Origin>
Vary: Origin

AllowCredentials

The allowCredentials value will serialize and added to the response as Access-Control-Allow-Credentials header.

import {
  cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";

const middleware = cors({ allowCredentials: true });

yield:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Vary: Origin

ExposeHeaders

The value of exposeHeaders will serialize and added to the response as an Access-Control-Expose-Headers header.

However, if the request is a CORS preflight request, it is not added.

import {
  cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";

const middleware = cors({ exposeHeaders: ["x-test"] });

yield:

Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: x-test
Vary: Origin

CORS options serialization error

Each option will serialize.

If serialization fails, it throws an error as follows:

import {
  cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
import { assertThrows } from "https://deno.land/std/testing/asserts.ts";

assertThrows(() => cors({ exposeHeaders: ["<invalid:field-name>"] }));

Effects

Middleware will make changes to the following elements of the HTTP message.

  • HTTP Headers
    • Access-Control-Allow-Origin
    • Access-Control-Allow-Credentials
    • Access-Control-Expose-Headers
    • Vary

CORS preflight request

Create CORS preflight request handler.

import {
  type Handler,
  preflight,
} from "https://deno.land/x/cors_middleware@$VERSION/mod.ts";
import { assert } from "https://deno.land/std/testing/asserts.ts";
import { assertSpyCalls, spy } from "https://deno.land/std/testing/mock.ts";

const corsPreflightRequest = new Request("test:", {
  method: "OPTIONS",
  headers: {
    origin: "<origin>",
    "access-control-request-method": "POST",
    "access-control-request-headers": "content-type",
  },
});

declare const handler: Handler;
const next = spy(handler);
const handlePreflight = preflight();
const response = await handlePreflight(corsPreflightRequest, next);

assertSpyCalls(next, 0);
assert(response.status === 204);
assert(response.headers.has("access-control-allow-origin"));
assert(response.headers.has("access-control-allow-methods"));
assert(response.headers.has("access-control-allow-headers"));
assert(response.headers.has("vary"));

yield:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: content-type
Vary: origin, access-control-request-method, access-control-request-headers

If the request is not a CORS preflight request, next will execute.

CORS preflight options

preflight accept following options:

NameTypeDescription
allowMethodsstring[]Access-Control-Allow-Methods
allowHeadersstring[]Access-Control-Allow-Headers
maxAgenumberAccess-Control-Max-Age
status200 | 204Preflight response status code.

and CORS request options without exposeHeaders.

AllowMethods

The value of allowMethods will serialize and added to the response as an Access-Control-Allow-Methods header.

If not specified, Access-Control-Request-Method header will add as Access-Control-Allow-Methods header to the response.

import { preflight } from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";

const handler = preflight({ allowMethods: ["POST", "PUT"] });

yield:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, PUT
Access-Control-Allow-Headers: <Access-Control-Request-Headers>
Vary: origin, access-control-request-method, access-control-request-headers

AllowHeaders

The value of allowHeaders will serialize and added to the response as an Access-Control-Allow-Headers header.

If not specified, Access-Control-Request-Headers will add as Access-Control-Allow-Headers header to the response.

import { preflight } from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";

const handler = preflight({ allowHeaders: ["x-test1", "x-test2"] });

yield:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: <Access-Control-Request-Method>
Access-Control-Allow-Headers: x-test1, x-test2
Vary: origin, access-control-request-method, access-control-request-headers

MaxAge

The value of maxAge will serialize and added to the response as an Access-Control-Max-Age header.

import { preflight } from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";

const handler = preflight({ maxAge: 86400 });

yield:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: <Access-Control-Request-Method>
Access-Control-Allow-Headers: <Access-Control-Request-Headers>
Access-Control-Max-Age: 86400
Vary: origin, access-control-request-method, access-control-request-headers

Status

The default is 204 No Content.

You can change to 200 OK.

Serialization error

Throws an error if option has an invalid value.

This is following case:

import {
  preflight,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
import { assertThrows } from "https://deno.land/std/testing/asserts.ts";

assertThrows(() => preflight({ allowMethods: ["<invalid:method>"] }));
assertThrows(() => preflight({ allowHeaders: ["<invalid:field-name>"] }));
assertThrows(() => preflight({ maxAge: NaN }));

API

All APIs can be found in the deno doc.

FAQ

Because the two offer different functions. cors creates middleware to provide CORS headers.

On the other hand, preflight creates a handler for CORS preflight requests. (Although it is actually a middleware signature, since it transfers processing to subsequent requests other than CORS preflight requests.)

Mixing middleware with handler characteristics and middleware characteristics will create expressive constraints.

License

Copyright © 2023-present httpland.

Released under the MIT license