mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Add cowboy_req:filter_cookies/2
This commit is contained in:
parent
5ffb4f98e0
commit
03dac1486d
5 changed files with 126 additions and 0 deletions
|
@ -53,6 +53,7 @@ Processed request:
|
||||||
* link:man:cowboy_req:parse_qs(3)[cowboy_req:parse_qs(3)] - Parse the query string
|
* link:man:cowboy_req:parse_qs(3)[cowboy_req:parse_qs(3)] - Parse the query string
|
||||||
* link:man:cowboy_req:match_qs(3)[cowboy_req:match_qs(3)] - Match the query string against constraints
|
* link:man:cowboy_req:match_qs(3)[cowboy_req:match_qs(3)] - Match the query string against constraints
|
||||||
* link:man:cowboy_req:parse_header(3)[cowboy_req:parse_header(3)] - Parse the given HTTP header
|
* link:man:cowboy_req:parse_header(3)[cowboy_req:parse_header(3)] - Parse the given HTTP header
|
||||||
|
* link:man:cowboy_req:filter_cookies(3)[cowboy_req:filter_cookies(3)] - Filter cookie headers
|
||||||
* link:man:cowboy_req:parse_cookies(3)[cowboy_req:parse_cookies(3)] - Parse cookie headers
|
* link:man:cowboy_req:parse_cookies(3)[cowboy_req:parse_cookies(3)] - Parse cookie headers
|
||||||
* link:man:cowboy_req:match_cookies(3)[cowboy_req:match_cookies(3)] - Match cookies against constraints
|
* link:man:cowboy_req:match_cookies(3)[cowboy_req:match_cookies(3)] - Match cookies against constraints
|
||||||
* link:man:cowboy_req:binding(3)[cowboy_req:binding(3)] - Access a value bound from the route
|
* link:man:cowboy_req:binding(3)[cowboy_req:binding(3)] - Access a value bound from the route
|
||||||
|
|
70
doc/src/manual/cowboy_req.filter_cookies.asciidoc
Normal file
70
doc/src/manual/cowboy_req.filter_cookies.asciidoc
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
= cowboy_req:filter_cookies(3)
|
||||||
|
|
||||||
|
== Name
|
||||||
|
|
||||||
|
cowboy_req:filter_cookies - Filter cookie headers
|
||||||
|
|
||||||
|
== Description
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
filter_cookies(Names, Req) -> Req
|
||||||
|
|
||||||
|
Names :: [atom() | binary()]
|
||||||
|
----
|
||||||
|
|
||||||
|
Filter cookie headers.
|
||||||
|
|
||||||
|
This function is meant to be used before attempting to parse
|
||||||
|
or match cookies in order to remove cookies that are not
|
||||||
|
relevant and are potentially malformed. Because Cowboy by
|
||||||
|
default crashes on malformed cookies, this function allows
|
||||||
|
processing requests that would otherwise result in a 400
|
||||||
|
error.
|
||||||
|
|
||||||
|
Malformed cookies are unfortunately fairly common due to
|
||||||
|
the string-based interface provided by browsers and this
|
||||||
|
function provides a middle ground between Cowboy's strict
|
||||||
|
behavior and chaotic real world use cases.
|
||||||
|
|
||||||
|
Note that there may still be crashes even after filtering
|
||||||
|
cookies because this function does not correct malformed
|
||||||
|
values. Cookies that have malformed values should probably
|
||||||
|
be unset in an error response or in a redirect.
|
||||||
|
|
||||||
|
This function can be called even if there are no cookies
|
||||||
|
in the request.
|
||||||
|
|
||||||
|
== Arguments
|
||||||
|
|
||||||
|
Names::
|
||||||
|
|
||||||
|
The cookies that should be kept.
|
||||||
|
|
||||||
|
Req::
|
||||||
|
|
||||||
|
The Req object.
|
||||||
|
|
||||||
|
== Return value
|
||||||
|
|
||||||
|
The Req object is returned with its cookie header value
|
||||||
|
filtered.
|
||||||
|
|
||||||
|
== Changelog
|
||||||
|
|
||||||
|
* *2.7*: Function introduced.
|
||||||
|
|
||||||
|
== Examples
|
||||||
|
|
||||||
|
.Filter then parse cookies
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
Req = cowboy_req:filter_cookies([session_id, token], Req0),
|
||||||
|
Cookies = cowboy_req:parse_cookies(Req).
|
||||||
|
----
|
||||||
|
|
||||||
|
== See also
|
||||||
|
|
||||||
|
link:man:cowboy_req(3)[cowboy_req(3)],
|
||||||
|
link:man:cowboy_req:parse_cookies(3)[cowboy_req:parse_cookies(3)],
|
||||||
|
link:man:cowboy_req:match_cookies(3)[cowboy_req:match_cookies(3)]
|
|
@ -44,6 +44,7 @@
|
||||||
-export([headers/1]).
|
-export([headers/1]).
|
||||||
-export([parse_header/2]).
|
-export([parse_header/2]).
|
||||||
-export([parse_header/3]).
|
-export([parse_header/3]).
|
||||||
|
-export([filter_cookies/2]).
|
||||||
-export([parse_cookies/1]).
|
-export([parse_cookies/1]).
|
||||||
-export([match_cookies/2]).
|
-export([match_cookies/2]).
|
||||||
|
|
||||||
|
@ -450,6 +451,35 @@ parse_header(Name, Req, Default, ParseFun) ->
|
||||||
Value -> ParseFun(Value)
|
Value -> ParseFun(Value)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec filter_cookies([atom() | binary()], Req) -> Req when Req::req().
|
||||||
|
filter_cookies(Names0, Req=#{headers := Headers}) ->
|
||||||
|
Names = [if
|
||||||
|
is_atom(N) -> atom_to_binary(N, utf8);
|
||||||
|
true -> N
|
||||||
|
end || N <- Names0],
|
||||||
|
case header(<<"cookie">>, Req) of
|
||||||
|
undefined -> Req;
|
||||||
|
Value0 ->
|
||||||
|
Cookies0 = binary:split(Value0, <<$;>>),
|
||||||
|
Cookies = lists:filter(fun(Cookie) ->
|
||||||
|
lists:member(cookie_name(Cookie), Names)
|
||||||
|
end, Cookies0),
|
||||||
|
Value = iolist_to_binary(lists:join($;, Cookies)),
|
||||||
|
Req#{headers => Headers#{<<"cookie">> => Value}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% This is a specialized function to extract a cookie name
|
||||||
|
%% regardless of whether the name is valid or not. We skip
|
||||||
|
%% whitespace at the beginning and take whatever's left to
|
||||||
|
%% be the cookie name, up to the = sign.
|
||||||
|
cookie_name(<<$\s, Rest/binary>>) -> cookie_name(Rest);
|
||||||
|
cookie_name(<<$\t, Rest/binary>>) -> cookie_name(Rest);
|
||||||
|
cookie_name(Name) -> cookie_name(Name, <<>>).
|
||||||
|
|
||||||
|
cookie_name(<<>>, Name) -> Name;
|
||||||
|
cookie_name(<<$=, _/bits>>, Name) -> Name;
|
||||||
|
cookie_name(<<C, Rest/bits>>, Acc) -> cookie_name(Rest, <<Acc/binary, C>>).
|
||||||
|
|
||||||
-spec parse_cookies(req()) -> [{binary(), binary()}].
|
-spec parse_cookies(req()) -> [{binary(), binary()}].
|
||||||
parse_cookies(Req) ->
|
parse_cookies(Req) ->
|
||||||
parse_header(<<"cookie">>, Req).
|
parse_header(<<"cookie">>, Req).
|
||||||
|
|
|
@ -92,6 +92,10 @@ echo(<<"match">>, Req, Opts) ->
|
||||||
Match
|
Match
|
||||||
end,
|
end,
|
||||||
{ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts};
|
{ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts};
|
||||||
|
echo(<<"filter_then_parse_cookies">>, Req0, Opts) ->
|
||||||
|
Req = cowboy_req:filter_cookies([cake, color], Req0),
|
||||||
|
Value = cowboy_req:parse_cookies(Req),
|
||||||
|
{ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts};
|
||||||
echo(What, Req, Opts) ->
|
echo(What, Req, Opts) ->
|
||||||
Key = binary_to_atom(What, latin1),
|
Key = binary_to_atom(What, latin1),
|
||||||
Value = case cowboy_req:path(Req) of
|
Value = case cowboy_req:path(Req) of
|
||||||
|
|
|
@ -286,6 +286,27 @@ parse_cookies(Config) ->
|
||||||
[{<<"cookie">>, "goodname=strawberry\tmilkshake"}], Config),
|
[{<<"cookie">>, "goodname=strawberry\tmilkshake"}], Config),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
filter_then_parse_cookies(Config) ->
|
||||||
|
doc("Filter cookies then parse them."),
|
||||||
|
<<"[]">> = do_get_body("/filter_then_parse_cookies", Config),
|
||||||
|
<<"[{<<\"cake\">>,<<\"strawberry\">>}]">>
|
||||||
|
= do_get_body("/filter_then_parse_cookies", [{<<"cookie">>, "cake=strawberry"}], Config),
|
||||||
|
<<"[{<<\"cake\">>,<<\"strawberry\">>},{<<\"color\">>,<<\"blue\">>}]">>
|
||||||
|
= do_get_body("/filter_then_parse_cookies", [{<<"cookie">>, "cake=strawberry; color=blue"}], Config),
|
||||||
|
<<"[{<<\"cake\">>,<<\"strawberry\">>},{<<\"color\">>,<<\"blue\">>}]">>
|
||||||
|
= do_get_body("/filter_then_parse_cookies",
|
||||||
|
[{<<"cookie">>, "cake=strawberry"}, {<<"cookie">>, "color=blue"}], Config),
|
||||||
|
<<"[]">>
|
||||||
|
= do_get_body("/filter_then_parse_cookies",
|
||||||
|
[{<<"cookie">>, "bad name=strawberry"}], Config),
|
||||||
|
<<"[{<<\"cake\">>,<<\"strawberry\">>}]">>
|
||||||
|
= do_get_body("/filter_then_parse_cookies",
|
||||||
|
[{<<"cookie">>, "bad name=strawberry; cake=strawberry"}], Config),
|
||||||
|
<<"[]">>
|
||||||
|
= do_get_body("/filter_then_parse_cookies",
|
||||||
|
[{<<"cookie">>, "Blocked by http://www.example.com/upgrade-to-remove"}], Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
parse_header(Config) ->
|
parse_header(Config) ->
|
||||||
doc("Parsed request header with/without default."),
|
doc("Parsed request header with/without default."),
|
||||||
<<"[{{<<\"text\">>,<<\"html\">>,[]},1000,[]}]">>
|
<<"[{{<<\"text\">>,<<\"html\">>,[]},1000,[]}]">>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue