Corsica
Plug and DSL for handling CORS requests.
Corsica provides facilites for working with CORS in Plug-based applications. It is (well, tries to be!) compliant with the CORS specification defined by the W3C.
Corsica can be used in two ways:
- as a generator for fine-tuned plugs
- as a stand-alone plug
Generator
The Corsica
module can be used in any module in order to turn that module
into a plug that handles CORS request. This is useful if there are a lot of
different cases to handle (multiple origins, headers based on resources) since
it gives fine control over the behaviour of the generated plug.
A quick example may make this easier to understand:
defmodule MyApp.CORS do
use Corsica,
origins: "*",
max_age: 600,
allow_headers: ~w(X-My-Header)
resources ["/foo", "/bar"], allow_methods: ~w(PUT PATCH)
resources ["/users"],
allow_credentials: true,
allow_methods: ~w(HEAD GET POST PUT PATCH DELETE)
end
In the example above, the MyApp.CORS
module becomes a valid plug since the
Corsica
module is used. The resources/2
macro is used in order to declare
resources (routes) for which the server should respond with CORS headers.
Options passed to resources/2
are used to set the values of the CORS
headers (for more information on options, read the “Options” section); they
override the global options that can be passed to the use
macro, which apply
to all declared resources.
Given the example above, the MyApp.CORS
plug can be used as follows:
defmodule MyApp.Router do
use Plug.Router
plug MyApp.CORS
plug :match
plug :dispatch
match _ do
send_resp(conn, 200, "OK")
end
end
For more information on declaring single resources, have a look at the
Corsica.DSL.resources/2
macro (which is imported when Corsica
is used).
Plug
In its simplest form, Corsica can be used as a stand-alone plug too. This prevents from fine-tuning the CORS header on a per-resource basis, but it supports all other options supported by the plug generator and can be useful for simple CORS needs.
defmodule MyApp.Endpoint do
plug Corsica,
origins: ["http://foo.com"],
max_age: 600,
allow_headers: ~w(X-Header),
allow_methods: ~w(GET POST),
expose_headers: ~w(Content-Type)
plug Plug.Session
plug :router
end
Origins
Allowed origins can be specified by passing the :origins
options either when
Corsica
is used or when the Corsica
plug is plugged to a pipeline.
:origins
can be a single value or a list of values. "*"
can only appear as
a single value. The default value is "*"
. Origins can be specified either
as:
- strings - the allowed origin and the actual origin have to be identical
- regexes - the actual origin has to match the allowed regex functions with
- a type
(binary -> boolean)
- the function applied to the actual origin has to returntrue
If :origins
is a list with more than one value and the request origin
matches, then a Vary: Origin
header is added to the response.
Options
Besides :origins
, the options that can be passed to the use
macro, to
Corsica.DSL.resource/2
and to the Corsica
plug (along with their default
values) are:
:allow_headers
- is a list of headers (as binaries). Sets the value of theaccess-control-allow-headers
header used with preflight requests. Defaults to[]
(no headers are allowed).:allow_methods
- is a list of HTTP methods (as binaries). Sets the value of theaccess-control-allow-methods
header used with preflight requests. Defaults to["HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"]
.:allow_credentials
- is a boolean. Iftrue
, sends theaccess-control-allow-credentials
with valuetrue
. Iffalse
, prevents that header from being sent at all. If:origins
is set to"*"
and:allow_credentials
is set totrue
, than the value of theaccess-control-allow-origin
header will always be the value of theorigin
request header (as per the W3C CORS specification) and not*
.:expose_headers
- is a list of headers (as binaries). Sets the value of theaccess-control-expose-headers
response header. This option doesn’t have a default value; if it’s not provided, theaccess-control-expose-headers
header is not sent at all.:max_age
- is an integer or a binary. Sets the value of theaccess-control-max-age
header used with preflight requests. This option doesn’t have a default value; if it’s not provided, theaccess-control-max-age
header is not sent at all.
When used as a plug, Corsica supports the additional :resources
options,
which is a list of resources exactly like the ones passed to the
Corsica.DSL.resources/2
macro. By default, this option is not present,
meaning that all resources are CORS-enabled.
Responding to preflight requests
When the request is a preflight request and a valid one (valid origin, valid
request method and valid request headers), Corsica directly sends a response
to that request instead of just adding headers to the connection (so that a
possible plug pipeline can continue). To do this, Corsica halts the
connection (through Plug.Conn.halt/1
) and sends a 200 OK response with
a body of ""
.
Summary↑
call(conn, opts) | Callback implementation of |
cors_request?(conn) | Checks whether a given connection holds a CORS request |
init(opts) | Callback implementation of |
preflight_request?(conn) | Checks whether a given connection holds a CORS preflight request |
Functions
Callback implementation of Plug.call/2
.
Specs:
- cors_request?(Plug.Conn.t) :: boolean
Checks whether a given connection holds a CORS request.
It doesn’t check if the CORS request is valid: it just checks that it’s a CORS
request. A request is a CORS request if and only if it has an Origin
request
header.
Callback implementation of Plug.init/1
.
Specs:
- preflight_request?(Plug.Conn.t) :: boolean
Checks whether a given connection holds a CORS preflight request.
Like cors_request?/1
, it doesn’t check that the preflight request is valid:
it just checks that it’s a preflight request. A CORS request is considered to
be a preflight request if and only if it is an OPTIONS
request and it has an
Access-Control-Request-Method
request header.
Note that this function does not check if the given request is a CORS one. If
you want to check for that too, use cors_request?/1
.