mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-15 12:40:25 +00:00
Update the cookies guide chapter
This commit is contained in:
parent
88dbc30391
commit
5495b57174
1 changed files with 73 additions and 94 deletions
|
@ -4,112 +4,94 @@
|
||||||
Cookies are a mechanism allowing applications to maintain
|
Cookies are a mechanism allowing applications to maintain
|
||||||
state on top of the stateless HTTP protocol.
|
state on top of the stateless HTTP protocol.
|
||||||
|
|
||||||
Cowboy provides facilities for handling cookies. It is highly
|
Cookies are a name/value store where the names and values are
|
||||||
recommended to use them instead of writing your own, as the
|
stored in plain text. They expire either after a delay
|
||||||
implementation of cookies can vary greatly between clients.
|
or when the browser closes. They can be configured on a
|
||||||
|
specific domain name or path, and restricted to secure
|
||||||
|
resources (sent or downloaded over HTTPS), or restricted
|
||||||
|
to the server (disallowing access from client-side scripts).
|
||||||
|
|
||||||
|
Cookie names are de facto case sensitive.
|
||||||
|
|
||||||
Cookies are stored client-side and sent with every subsequent
|
Cookies are stored client-side and sent with every subsequent
|
||||||
request that matches the domain and path for which they were
|
request that matches the domain and path for which they were
|
||||||
stored, including requests for static files. For this reason
|
stored, until they expire. This can create a non-negligible
|
||||||
they can incur a cost which must be taken in consideration.
|
cost.
|
||||||
|
|
||||||
Also consider that, regardless of the options used, cookies
|
Cookies should not be considered secure. They are stored on
|
||||||
are not to be trusted. They may be read and modified by any
|
the user's computer in plain text, and can be read by any
|
||||||
program on the user's computer, but also by proxies. You
|
program. They can also be read by proxies when using clear
|
||||||
should always validate cookie values before using them. Do
|
connections. Always validate the value before using it,
|
||||||
not store any sensitive information in cookies either.
|
and never store any sensitive information inside it.
|
||||||
|
|
||||||
When explicitly setting the domain, the cookie will be sent
|
Cookies set by the server are only available in requests
|
||||||
for the domain and all subdomains from that domain. Otherwise
|
following the client reception of the response containing
|
||||||
the current domain will be used. The same is true for the
|
them.
|
||||||
path.
|
|
||||||
|
|
||||||
When the server sets cookies, they will only be available
|
Cookies may be sent repeatedly. This is often useful to
|
||||||
for requests that are sent after the client receives the
|
update the expiration time and avoid losing a cookie.
|
||||||
response.
|
|
||||||
|
|
||||||
Cookies are sent in HTTP headers, therefore they must have
|
|
||||||
text values. It is your responsibility to encode any other
|
|
||||||
data type. Also note that cookie names are de facto case
|
|
||||||
sensitive.
|
|
||||||
|
|
||||||
Cookies can be set for the client session (which generally
|
|
||||||
means until the browser is closed), or it can be set for
|
|
||||||
a number of seconds. Once it expires, or when the server
|
|
||||||
says the cookie must exist for up to 0 seconds, the cookie
|
|
||||||
is deleted by the client. To avoid this while the user
|
|
||||||
is browsing your site, you should set the cookie for
|
|
||||||
every request, essentially resetting the expiration time.
|
|
||||||
|
|
||||||
Cookies can be restricted to secure channels. This typically
|
|
||||||
means that such a cookie will only be sent over HTTPS,
|
|
||||||
and that it will only be available by client-side scripts
|
|
||||||
that run from HTTPS webpages.
|
|
||||||
|
|
||||||
Finally, cookies can be restricted to HTTP and HTTPS requests,
|
|
||||||
essentially disabling their access from client-side scripts.
|
|
||||||
|
|
||||||
=== Setting cookies
|
=== Setting cookies
|
||||||
|
|
||||||
By default, cookies you set are defined for the session.
|
// @todo So I am not particularly happy about set_resp_cookie/4
|
||||||
|
// having Opts as a *third* argument, instead of the *last* like
|
||||||
|
// all other functions that come with an Opts argument. We will
|
||||||
|
// probably need to change this before 2.0.
|
||||||
|
|
||||||
|
By default cookies are defined for the duration of the session:
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
SessionID = generate_session_id(),
|
SessionID = generate_session_id(),
|
||||||
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [], Req).
|
Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, Req0).
|
||||||
|
|
||||||
You can also make them expire at a specific point in the
|
They can also be set for a duration in seconds:
|
||||||
future.
|
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
----
|
----
|
||||||
SessionID = generate_session_id(),
|
SessionID = generate_session_id(),
|
||||||
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [
|
Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
|
||||||
{max_age, 3600}
|
#{max_age => 3600}, Req0).
|
||||||
], Req).
|
|
||||||
----
|
----
|
||||||
|
|
||||||
You can delete cookies that have already been set. The value
|
To delete cookies, set `max_age` to 0:
|
||||||
is ignored.
|
|
||||||
|
|
||||||
[source,erlang]
|
|
||||||
----
|
|
||||||
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, <<>>, [
|
|
||||||
{max_age, 0}
|
|
||||||
], Req).
|
|
||||||
----
|
|
||||||
|
|
||||||
You can restrict them to a specific domain and path.
|
|
||||||
For example, the following cookie will be set for the domain
|
|
||||||
`my.example.org` and all its subdomains, but only on the path
|
|
||||||
`/account` and all its subdirectories.
|
|
||||||
|
|
||||||
[source,erlang]
|
|
||||||
----
|
|
||||||
Req2 = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>, [
|
|
||||||
{domain, "my.example.org"},
|
|
||||||
{path, "/account"}
|
|
||||||
], Req).
|
|
||||||
----
|
|
||||||
|
|
||||||
You can restrict the cookie to secure channels, typically HTTPS.
|
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
----
|
----
|
||||||
SessionID = generate_session_id(),
|
SessionID = generate_session_id(),
|
||||||
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [
|
Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
|
||||||
{secure, true}
|
#{max_age => 0}, Req0).
|
||||||
], Req).
|
|
||||||
----
|
----
|
||||||
|
|
||||||
You can restrict the cookie to client-server communication
|
To restrict cookies to a specific domain and path, the options
|
||||||
only. Such a cookie will not be available to client-side scripts.
|
of the same name can be used:
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
Req = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>,
|
||||||
|
#{domain => "my.example.org", path => "/account"}, Req0).
|
||||||
|
----
|
||||||
|
|
||||||
|
Cookies will be sent with requests to this domain and all
|
||||||
|
its subdomains, and to resources on this path or deeper
|
||||||
|
in the path hierarchy.
|
||||||
|
|
||||||
|
To restrict cookies to secure channels (typically resources
|
||||||
|
available over HTTPS):
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
----
|
----
|
||||||
SessionID = generate_session_id(),
|
SessionID = generate_session_id(),
|
||||||
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [
|
Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
|
||||||
{http_only, true}
|
#{secure => true}, Req0).
|
||||||
], Req).
|
----
|
||||||
|
|
||||||
|
To prevent client-side scripts from accessing a cookie:
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
SessionID = generate_session_id(),
|
||||||
|
Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
|
||||||
|
#{http_only => true}, Req0).
|
||||||
----
|
----
|
||||||
|
|
||||||
Cookies may also be set client-side, for example using
|
Cookies may also be set client-side, for example using
|
||||||
|
@ -117,23 +99,23 @@ Javascript.
|
||||||
|
|
||||||
=== Reading cookies
|
=== Reading cookies
|
||||||
|
|
||||||
As we said, the client sends cookies with every request.
|
The client only ever sends back the cookie name and value.
|
||||||
But unlike the server, the client only sends the cookie
|
All other options that can be set are never sent back.
|
||||||
name and value.
|
|
||||||
|
|
||||||
Cowboy provides two different ways to read cookies. You
|
Cowboy provides two functions for reading cookies. Both
|
||||||
can either parse them as a list of key/value pairs, or
|
involve parsing the cookie header(s) and so should not
|
||||||
match them into a map, optionally applying constraints
|
be called repeatedly.
|
||||||
to the values or providing a default if they are missing.
|
|
||||||
|
|
||||||
You can parse the cookies and then use standard library
|
You can get all cookies as a key/value list:
|
||||||
functions to access individual values.
|
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
Cookies = cowboy_req:parse_cookies(Req),
|
Cookies = cowboy_req:parse_cookies(Req),
|
||||||
{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies).
|
{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies).
|
||||||
|
|
||||||
You can match the cookies into a map.
|
Or you can perform a match against cookies and retrieve
|
||||||
|
only the ones you need, while at the same time doing
|
||||||
|
any required post processing using xref:constraints[constraints].
|
||||||
|
This function returns a map:
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
#{id := ID, lang := Lang} = cowboy_req:match_cookies([id, lang], Req).
|
#{id := ID, lang := Lang} = cowboy_req:match_cookies([id, lang], Req).
|
||||||
|
@ -141,8 +123,7 @@ You can match the cookies into a map.
|
||||||
You can use constraints to validate the values while matching
|
You can use constraints to validate the values while matching
|
||||||
them. The following snippet will crash if the `id` cookie is
|
them. The following snippet will crash if the `id` cookie is
|
||||||
not an integer number or if the `lang` cookie is empty. Additionally
|
not an integer number or if the `lang` cookie is empty. Additionally
|
||||||
the `id` cookie value will be converted to an integer term, saving
|
the `id` cookie value will be converted to an integer term:
|
||||||
you a conversion step.
|
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req).
|
CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req).
|
||||||
|
@ -150,14 +131,12 @@ CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req).
|
||||||
Note that if two cookies share the same name, then the map value
|
Note that if two cookies share the same name, then the map value
|
||||||
will be a list of the two cookie values.
|
will be a list of the two cookie values.
|
||||||
|
|
||||||
Read more about xref:constraints[constraints].
|
|
||||||
|
|
||||||
A default value can be provided. The default will be used
|
A default value can be provided. The default will be used
|
||||||
if the `lang` cookie is not found. It will not be used if
|
if the `lang` cookie is not found. It will not be used if
|
||||||
the cookie is found but has an empty value.
|
the cookie is found but has an empty value:
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
#{lang := Lang} = cowboy_req:match_cookies([{lang, [], <<"en-US">>}], Req).
|
#{lang := Lang} = cowboy_req:match_cookies([{lang, [], <<"en-US">>}], Req).
|
||||||
|
|
||||||
If no default is provided and the value is missing, the
|
If no default is provided and the value is missing, an
|
||||||
query string is deemed invalid and the process will crash.
|
exception is thrown.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue