zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

cowboy_req.erl (36161B)


      1 %% Copyright (c) 2011-2017, Loïc Hoguin <essen@ninenines.eu>
      2 %% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
      3 %%
      4 %% Permission to use, copy, modify, and/or distribute this software for any
      5 %% purpose with or without fee is hereby granted, provided that the above
      6 %% copyright notice and this permission notice appear in all copies.
      7 %%
      8 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15 
     16 -module(cowboy_req).
     17 
     18 %% Request.
     19 -export([method/1]).
     20 -export([version/1]).
     21 -export([peer/1]).
     22 -export([sock/1]).
     23 -export([cert/1]).
     24 -export([scheme/1]).
     25 -export([host/1]).
     26 -export([host_info/1]).
     27 -export([port/1]).
     28 -export([path/1]).
     29 -export([path_info/1]).
     30 -export([qs/1]).
     31 -export([parse_qs/1]).
     32 -export([match_qs/2]).
     33 -export([uri/1]).
     34 -export([uri/2]).
     35 -export([binding/2]).
     36 -export([binding/3]).
     37 -export([bindings/1]).
     38 -export([header/2]).
     39 -export([header/3]).
     40 -export([headers/1]).
     41 -export([parse_header/2]).
     42 -export([parse_header/3]).
     43 -export([filter_cookies/2]).
     44 -export([parse_cookies/1]).
     45 -export([match_cookies/2]).
     46 
     47 %% Request body.
     48 -export([has_body/1]).
     49 -export([body_length/1]).
     50 -export([read_body/1]).
     51 -export([read_body/2]).
     52 -export([read_urlencoded_body/1]).
     53 -export([read_urlencoded_body/2]).
     54 -export([read_and_match_urlencoded_body/2]).
     55 -export([read_and_match_urlencoded_body/3]).
     56 
     57 %% Multipart.
     58 -export([read_part/1]).
     59 -export([read_part/2]).
     60 -export([read_part_body/1]).
     61 -export([read_part_body/2]).
     62 
     63 %% Response.
     64 -export([set_resp_cookie/3]).
     65 -export([set_resp_cookie/4]).
     66 -export([resp_header/2]).
     67 -export([resp_header/3]).
     68 -export([resp_headers/1]).
     69 -export([set_resp_header/3]).
     70 -export([set_resp_headers/2]).
     71 -export([has_resp_header/2]).
     72 -export([delete_resp_header/2]).
     73 -export([set_resp_body/2]).
     74 %% @todo set_resp_body/3 with a ContentType or even Headers argument, to set content headers.
     75 -export([has_resp_body/1]).
     76 -export([inform/2]).
     77 -export([inform/3]).
     78 -export([reply/2]).
     79 -export([reply/3]).
     80 -export([reply/4]).
     81 -export([stream_reply/2]).
     82 -export([stream_reply/3]).
     83 %% @todo stream_body/2 (nofin)
     84 -export([stream_body/3]).
     85 %% @todo stream_events/2 (nofin)
     86 -export([stream_events/3]).
     87 -export([stream_trailers/2]).
     88 -export([push/3]).
     89 -export([push/4]).
     90 
     91 %% Stream handlers.
     92 -export([cast/2]).
     93 
     94 %% Internal.
     95 -export([response_headers/2]).
     96 
     97 -type read_body_opts() :: #{
     98 	length => non_neg_integer() | infinity,
     99 	period => non_neg_integer(),
    100 	timeout => timeout()
    101 }.
    102 -export_type([read_body_opts/0]).
    103 
    104 %% While sendfile allows a Len of 0 that means "everything past Offset",
    105 %% Cowboy expects the real length as it is used as metadata.
    106 -type resp_body() :: iodata()
    107 	| {sendfile, non_neg_integer(), non_neg_integer(), file:name_all()}.
    108 -export_type([resp_body/0]).
    109 
    110 -type push_opts() :: #{
    111 	method => binary(),
    112 	scheme => binary(),
    113 	host => binary(),
    114 	port => inet:port_number(),
    115 	qs => binary()
    116 }.
    117 -export_type([push_opts/0]).
    118 
    119 -type req() :: #{
    120 	%% Public interface.
    121 	method := binary(),
    122 	version := cowboy:http_version() | atom(),
    123 	scheme := binary(),
    124 	host := binary(),
    125 	port := inet:port_number(),
    126 	path := binary(),
    127 	qs := binary(),
    128 	headers := cowboy:http_headers(),
    129 	peer := {inet:ip_address(), inet:port_number()},
    130 	sock := {inet:ip_address(), inet:port_number()},
    131 	cert := binary() | undefined,
    132 
    133 	%% Private interface.
    134 	ref := ranch:ref(),
    135 	pid := pid(),
    136 	streamid := cowboy_stream:streamid(),
    137 
    138 	host_info => cowboy_router:tokens(),
    139 	path_info => cowboy_router:tokens(),
    140 	bindings => cowboy_router:bindings(),
    141 
    142 	has_body := boolean(),
    143 	body_length := non_neg_integer() | undefined,
    144 	has_read_body => true,
    145 	multipart => {binary(), binary()} | done,
    146 
    147 	has_sent_resp => headers | true,
    148 	resp_cookies => #{iodata() => iodata()},
    149 	resp_headers => #{binary() => iodata()},
    150 	resp_body => resp_body(),
    151 
    152 	proxy_header => ranch_proxy_header:proxy_info(),
    153 	media_type => {binary(), binary(), [{binary(), binary()}]},
    154 	language => binary() | undefined,
    155 	charset => binary() | undefined,
    156 	range => {binary(), binary()
    157 		| [{non_neg_integer(), non_neg_integer() | infinity} | neg_integer()]},
    158 	websocket_version => 7 | 8 | 13,
    159 
    160 	%% The user is encouraged to use the Req to store information
    161 	%% when no better solution is available.
    162 	_ => _
    163 }.
    164 -export_type([req/0]).
    165 
    166 %% Request.
    167 
    168 -spec method(req()) -> binary().
    169 method(#{method := Method}) ->
    170 	Method.
    171 
    172 -spec version(req()) -> cowboy:http_version().
    173 version(#{version := Version}) ->
    174 	Version.
    175 
    176 -spec peer(req()) -> {inet:ip_address(), inet:port_number()}.
    177 peer(#{peer := Peer}) ->
    178 	Peer.
    179 
    180 -spec sock(req()) -> {inet:ip_address(), inet:port_number()}.
    181 sock(#{sock := Sock}) ->
    182 	Sock.
    183 
    184 -spec cert(req()) -> binary() | undefined.
    185 cert(#{cert := Cert}) ->
    186 	Cert.
    187 
    188 -spec scheme(req()) -> binary().
    189 scheme(#{scheme := Scheme}) ->
    190 	Scheme.
    191 
    192 -spec host(req()) -> binary().
    193 host(#{host := Host}) ->
    194 	Host.
    195 
    196 %% @todo The host_info is undefined if cowboy_router isn't used. Do we want to crash?
    197 -spec host_info(req()) -> cowboy_router:tokens() | undefined.
    198 host_info(#{host_info := HostInfo}) ->
    199 	HostInfo.
    200 
    201 -spec port(req()) -> inet:port_number().
    202 port(#{port := Port}) ->
    203 	Port.
    204 
    205 -spec path(req()) -> binary().
    206 path(#{path := Path}) ->
    207 	Path.
    208 
    209 %% @todo The path_info is undefined if cowboy_router isn't used. Do we want to crash?
    210 -spec path_info(req()) -> cowboy_router:tokens() | undefined.
    211 path_info(#{path_info := PathInfo}) ->
    212 	PathInfo.
    213 
    214 -spec qs(req()) -> binary().
    215 qs(#{qs := Qs}) ->
    216 	Qs.
    217 
    218 %% @todo Might be useful to limit the number of keys.
    219 -spec parse_qs(req()) -> [{binary(), binary() | true}].
    220 parse_qs(#{qs := Qs}) ->
    221 	try
    222 		cow_qs:parse_qs(Qs)
    223 	catch _:_:Stacktrace ->
    224 		erlang:raise(exit, {request_error, qs,
    225 			'Malformed query string; application/x-www-form-urlencoded expected.'
    226 		}, Stacktrace)
    227 	end.
    228 
    229 -spec match_qs(cowboy:fields(), req()) -> map().
    230 match_qs(Fields, Req) ->
    231 	case filter(Fields, kvlist_to_map(Fields, parse_qs(Req))) of
    232 		{ok, Map} ->
    233 			Map;
    234 		{error, Errors} ->
    235 			exit({request_error, {match_qs, Errors},
    236 				'Query string validation constraints failed for the reasons provided.'})
    237 	end.
    238 
    239 -spec uri(req()) -> iodata().
    240 uri(Req) ->
    241 	uri(Req, #{}).
    242 
    243 -spec uri(req(), map()) -> iodata().
    244 uri(#{scheme := Scheme0, host := Host0, port := Port0,
    245 		path := Path0, qs := Qs0}, Opts) ->
    246 	Scheme = case maps:get(scheme, Opts, Scheme0) of
    247 		S = undefined -> S;
    248 		S -> iolist_to_binary(S)
    249 	end,
    250 	Host = maps:get(host, Opts, Host0),
    251 	Port = maps:get(port, Opts, Port0),
    252 	{Path, Qs} = case maps:get(path, Opts, Path0) of
    253 		<<"*">> -> {<<>>, <<>>};
    254 		P -> {P, maps:get(qs, Opts, Qs0)}
    255 	end,
    256 	Fragment = maps:get(fragment, Opts, undefined),
    257 	[uri_host(Scheme, Scheme0, Port, Host), uri_path(Path), uri_qs(Qs), uri_fragment(Fragment)].
    258 
    259 uri_host(_, _, _, undefined) -> <<>>;
    260 uri_host(Scheme, Scheme0, Port, Host) ->
    261 	case iolist_size(Host) of
    262 		0 -> <<>>;
    263 		_ -> [uri_scheme(Scheme), <<"//">>, Host, uri_port(Scheme, Scheme0, Port)]
    264 	end.
    265 
    266 uri_scheme(undefined) -> <<>>;
    267 uri_scheme(Scheme) ->
    268 	case iolist_size(Scheme) of
    269 		0 -> Scheme;
    270 		_ -> [Scheme, $:]
    271 	end.
    272 
    273 uri_port(_, _, undefined) -> <<>>;
    274 uri_port(undefined, <<"http">>, 80) -> <<>>;
    275 uri_port(undefined, <<"https">>, 443) -> <<>>;
    276 uri_port(<<"http">>, _, 80) -> <<>>;
    277 uri_port(<<"https">>, _, 443) -> <<>>;
    278 uri_port(_, _, Port) ->
    279 	[$:, integer_to_binary(Port)].
    280 
    281 uri_path(undefined) -> <<>>;
    282 uri_path(Path) -> Path.
    283 
    284 uri_qs(undefined) -> <<>>;
    285 uri_qs(Qs) ->
    286 	case iolist_size(Qs) of
    287 		0 -> Qs;
    288 		_ -> [$?, Qs]
    289 	end.
    290 
    291 uri_fragment(undefined) -> <<>>;
    292 uri_fragment(Fragment) ->
    293 	case iolist_size(Fragment) of
    294 		0 -> Fragment;
    295 		_ -> [$#, Fragment]
    296 	end.
    297 
    298 -ifdef(TEST).
    299 uri1_test() ->
    300 	<<"http://localhost/path">> = iolist_to_binary(uri(#{
    301 		scheme => <<"http">>, host => <<"localhost">>, port => 80,
    302 		path => <<"/path">>, qs => <<>>})),
    303 	<<"http://localhost:443/path">> = iolist_to_binary(uri(#{
    304 		scheme => <<"http">>, host => <<"localhost">>, port => 443,
    305 		path => <<"/path">>, qs => <<>>})),
    306 	<<"http://localhost:8080/path">> = iolist_to_binary(uri(#{
    307 		scheme => <<"http">>, host => <<"localhost">>, port => 8080,
    308 		path => <<"/path">>, qs => <<>>})),
    309 	<<"http://localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(#{
    310 		scheme => <<"http">>, host => <<"localhost">>, port => 8080,
    311 		path => <<"/path">>, qs => <<"dummy=2785">>})),
    312 	<<"https://localhost/path">> = iolist_to_binary(uri(#{
    313 		scheme => <<"https">>, host => <<"localhost">>, port => 443,
    314 		path => <<"/path">>, qs => <<>>})),
    315 	<<"https://localhost:8443/path">> = iolist_to_binary(uri(#{
    316 		scheme => <<"https">>, host => <<"localhost">>, port => 8443,
    317 		path => <<"/path">>, qs => <<>>})),
    318 	<<"https://localhost:8443/path?dummy=2785">> = iolist_to_binary(uri(#{
    319 		scheme => <<"https">>, host => <<"localhost">>, port => 8443,
    320 		path => <<"/path">>, qs => <<"dummy=2785">>})),
    321 	ok.
    322 
    323 uri2_test() ->
    324 	Req = #{
    325 		scheme => <<"http">>, host => <<"localhost">>, port => 8080,
    326 		path => <<"/path">>, qs => <<"dummy=2785">>
    327 	},
    328 	<<"http://localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{})),
    329 	%% Disable individual components.
    330 	<<"//localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{scheme => undefined})),
    331 	<<"/path?dummy=2785">> = iolist_to_binary(uri(Req, #{host => undefined})),
    332 	<<"http://localhost/path?dummy=2785">> = iolist_to_binary(uri(Req, #{port => undefined})),
    333 	<<"http://localhost:8080?dummy=2785">> = iolist_to_binary(uri(Req, #{path => undefined})),
    334 	<<"http://localhost:8080/path">> = iolist_to_binary(uri(Req, #{qs => undefined})),
    335 	<<"http://localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{fragment => undefined})),
    336 	<<"http://localhost:8080">> = iolist_to_binary(uri(Req, #{path => undefined, qs => undefined})),
    337 	<<>> = iolist_to_binary(uri(Req, #{host => undefined, path => undefined, qs => undefined})),
    338 	%% Empty values.
    339 	<<"//localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{scheme => <<>>})),
    340 	<<"//localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{scheme => ""})),
    341 	<<"//localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{scheme => [<<>>]})),
    342 	<<"/path?dummy=2785">> = iolist_to_binary(uri(Req, #{host => <<>>})),
    343 	<<"/path?dummy=2785">> = iolist_to_binary(uri(Req, #{host => ""})),
    344 	<<"/path?dummy=2785">> = iolist_to_binary(uri(Req, #{host => [<<>>]})),
    345 	<<"http://localhost:8080?dummy=2785">> = iolist_to_binary(uri(Req, #{path => <<>>})),
    346 	<<"http://localhost:8080?dummy=2785">> = iolist_to_binary(uri(Req, #{path => ""})),
    347 	<<"http://localhost:8080?dummy=2785">> = iolist_to_binary(uri(Req, #{path => [<<>>]})),
    348 	<<"http://localhost:8080/path">> = iolist_to_binary(uri(Req, #{qs => <<>>})),
    349 	<<"http://localhost:8080/path">> = iolist_to_binary(uri(Req, #{qs => ""})),
    350 	<<"http://localhost:8080/path">> = iolist_to_binary(uri(Req, #{qs => [<<>>]})),
    351 	<<"http://localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{fragment => <<>>})),
    352 	<<"http://localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{fragment => ""})),
    353 	<<"http://localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{fragment => [<<>>]})),
    354 	%% Port is integer() | undefined.
    355 	{'EXIT', _} = (catch iolist_to_binary(uri(Req, #{port => <<>>}))),
    356 	{'EXIT', _} = (catch iolist_to_binary(uri(Req, #{port => ""}))),
    357 	{'EXIT', _} = (catch iolist_to_binary(uri(Req, #{port => [<<>>]}))),
    358 	%% Update components.
    359 	<<"https://localhost:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{scheme => "https"})),
    360 	<<"http://example.org:8080/path?dummy=2785">> = iolist_to_binary(uri(Req, #{host => "example.org"})),
    361 	<<"http://localhost:123/path?dummy=2785">> = iolist_to_binary(uri(Req, #{port => 123})),
    362 	<<"http://localhost:8080/custom?dummy=2785">> = iolist_to_binary(uri(Req, #{path => "/custom"})),
    363 	<<"http://localhost:8080/path?smart=42">> = iolist_to_binary(uri(Req, #{qs => "smart=42"})),
    364 	<<"http://localhost:8080/path?dummy=2785#intro">> = iolist_to_binary(uri(Req, #{fragment => "intro"})),
    365 	%% Interesting combinations.
    366 	<<"http://localhost/path?dummy=2785">> = iolist_to_binary(uri(Req, #{port => 80})),
    367 	<<"https://localhost/path?dummy=2785">> = iolist_to_binary(uri(Req, #{scheme => "https", port => 443})),
    368 	ok.
    369 -endif.
    370 
    371 -spec binding(atom(), req()) -> any() | undefined.
    372 binding(Name, Req) ->
    373 	binding(Name, Req, undefined).
    374 
    375 -spec binding(atom(), req(), Default) -> any() | Default when Default::any().
    376 binding(Name, #{bindings := Bindings}, Default) when is_atom(Name) ->
    377 	case Bindings of
    378 		#{Name := Value} -> Value;
    379 		_ -> Default
    380 	end;
    381 binding(Name, _, Default) when is_atom(Name) ->
    382 	Default.
    383 
    384 -spec bindings(req()) -> cowboy_router:bindings().
    385 bindings(#{bindings := Bindings}) ->
    386 	Bindings;
    387 bindings(_) ->
    388 	#{}.
    389 
    390 -spec header(binary(), req()) -> binary() | undefined.
    391 header(Name, Req) ->
    392 	header(Name, Req, undefined).
    393 
    394 -spec header(binary(), req(), Default) -> binary() | Default when Default::any().
    395 header(Name, #{headers := Headers}, Default) ->
    396 	maps:get(Name, Headers, Default).
    397 
    398 -spec headers(req()) -> cowboy:http_headers().
    399 headers(#{headers := Headers}) ->
    400 	Headers.
    401 
    402 -spec parse_header(binary(), Req) -> any() when Req::req().
    403 parse_header(Name = <<"content-length">>, Req) ->
    404 	parse_header(Name, Req, 0);
    405 parse_header(Name = <<"cookie">>, Req) ->
    406 	parse_header(Name, Req, []);
    407 parse_header(Name, Req) ->
    408 	parse_header(Name, Req, undefined).
    409 
    410 -spec parse_header(binary(), Req, any()) -> any() when Req::req().
    411 parse_header(Name, Req, Default) ->
    412 	try
    413 		parse_header(Name, Req, Default, parse_header_fun(Name))
    414 	catch _:_:Stacktrace ->
    415 		erlang:raise(exit, {request_error, {header, Name},
    416 			'Malformed header. Please consult the relevant specification.'
    417 		}, Stacktrace)
    418 	end.
    419 
    420 parse_header_fun(<<"accept">>) -> fun cow_http_hd:parse_accept/1;
    421 parse_header_fun(<<"accept-charset">>) -> fun cow_http_hd:parse_accept_charset/1;
    422 parse_header_fun(<<"accept-encoding">>) -> fun cow_http_hd:parse_accept_encoding/1;
    423 parse_header_fun(<<"accept-language">>) -> fun cow_http_hd:parse_accept_language/1;
    424 parse_header_fun(<<"access-control-request-headers">>) -> fun cow_http_hd:parse_access_control_request_headers/1;
    425 parse_header_fun(<<"access-control-request-method">>) -> fun cow_http_hd:parse_access_control_request_method/1;
    426 parse_header_fun(<<"authorization">>) -> fun cow_http_hd:parse_authorization/1;
    427 parse_header_fun(<<"connection">>) -> fun cow_http_hd:parse_connection/1;
    428 parse_header_fun(<<"content-encoding">>) -> fun cow_http_hd:parse_content_encoding/1;
    429 parse_header_fun(<<"content-language">>) -> fun cow_http_hd:parse_content_language/1;
    430 parse_header_fun(<<"content-length">>) -> fun cow_http_hd:parse_content_length/1;
    431 parse_header_fun(<<"content-type">>) -> fun cow_http_hd:parse_content_type/1;
    432 parse_header_fun(<<"cookie">>) -> fun cow_cookie:parse_cookie/1;
    433 parse_header_fun(<<"expect">>) -> fun cow_http_hd:parse_expect/1;
    434 parse_header_fun(<<"if-match">>) -> fun cow_http_hd:parse_if_match/1;
    435 parse_header_fun(<<"if-modified-since">>) -> fun cow_http_hd:parse_if_modified_since/1;
    436 parse_header_fun(<<"if-none-match">>) -> fun cow_http_hd:parse_if_none_match/1;
    437 parse_header_fun(<<"if-range">>) -> fun cow_http_hd:parse_if_range/1;
    438 parse_header_fun(<<"if-unmodified-since">>) -> fun cow_http_hd:parse_if_unmodified_since/1;
    439 parse_header_fun(<<"max-forwards">>) -> fun cow_http_hd:parse_max_forwards/1;
    440 parse_header_fun(<<"origin">>) -> fun cow_http_hd:parse_origin/1;
    441 parse_header_fun(<<"proxy-authorization">>) -> fun cow_http_hd:parse_proxy_authorization/1;
    442 parse_header_fun(<<"range">>) -> fun cow_http_hd:parse_range/1;
    443 parse_header_fun(<<"sec-websocket-extensions">>) -> fun cow_http_hd:parse_sec_websocket_extensions/1;
    444 parse_header_fun(<<"sec-websocket-protocol">>) -> fun cow_http_hd:parse_sec_websocket_protocol_req/1;
    445 parse_header_fun(<<"sec-websocket-version">>) -> fun cow_http_hd:parse_sec_websocket_version_req/1;
    446 parse_header_fun(<<"trailer">>) -> fun cow_http_hd:parse_trailer/1;
    447 parse_header_fun(<<"upgrade">>) -> fun cow_http_hd:parse_upgrade/1;
    448 parse_header_fun(<<"x-forwarded-for">>) -> fun cow_http_hd:parse_x_forwarded_for/1.
    449 
    450 parse_header(Name, Req, Default, ParseFun) ->
    451 	case header(Name, Req) of
    452 		undefined -> Default;
    453 		Value -> ParseFun(Value)
    454 	end.
    455 
    456 -spec filter_cookies([atom() | binary()], Req) -> Req when Req::req().
    457 filter_cookies(Names0, Req=#{headers := Headers}) ->
    458 	Names = [if
    459 		is_atom(N) -> atom_to_binary(N, utf8);
    460 		true -> N
    461 	end || N <- Names0],
    462 	case header(<<"cookie">>, Req) of
    463 		undefined -> Req;
    464 		Value0 ->
    465 			Cookies0 = binary:split(Value0, <<$;>>),
    466 			Cookies = lists:filter(fun(Cookie) ->
    467 				lists:member(cookie_name(Cookie), Names)
    468 			end, Cookies0),
    469 			Value = iolist_to_binary(lists:join($;, Cookies)),
    470 			Req#{headers => Headers#{<<"cookie">> => Value}}
    471 	end.
    472 
    473 %% This is a specialized function to extract a cookie name
    474 %% regardless of whether the name is valid or not. We skip
    475 %% whitespace at the beginning and take whatever's left to
    476 %% be the cookie name, up to the = sign.
    477 cookie_name(<<$\s, Rest/binary>>) -> cookie_name(Rest);
    478 cookie_name(<<$\t, Rest/binary>>) -> cookie_name(Rest);
    479 cookie_name(Name) -> cookie_name(Name, <<>>).
    480 
    481 cookie_name(<<>>, Name) -> Name;
    482 cookie_name(<<$=, _/bits>>, Name) -> Name;
    483 cookie_name(<<C, Rest/bits>>, Acc) -> cookie_name(Rest, <<Acc/binary, C>>).
    484 
    485 -spec parse_cookies(req()) -> [{binary(), binary()}].
    486 parse_cookies(Req) ->
    487 	parse_header(<<"cookie">>, Req).
    488 
    489 -spec match_cookies(cowboy:fields(), req()) -> map().
    490 match_cookies(Fields, Req) ->
    491 	case filter(Fields, kvlist_to_map(Fields, parse_cookies(Req))) of
    492 		{ok, Map} ->
    493 			Map;
    494 		{error, Errors} ->
    495 			exit({request_error, {match_cookies, Errors},
    496 				'Cookie validation constraints failed for the reasons provided.'})
    497 	end.
    498 
    499 %% Request body.
    500 
    501 -spec has_body(req()) -> boolean().
    502 has_body(#{has_body := HasBody}) ->
    503 	HasBody.
    504 
    505 %% The length may not be known if HTTP/1.1 with a transfer-encoding;
    506 %% or HTTP/2 with no content-length header. The length is always
    507 %% known once the body has been completely read.
    508 -spec body_length(req()) -> undefined | non_neg_integer().
    509 body_length(#{body_length := Length}) ->
    510 	Length.
    511 
    512 -spec read_body(Req) -> {ok, binary(), Req} | {more, binary(), Req} when Req::req().
    513 read_body(Req) ->
    514 	read_body(Req, #{}).
    515 
    516 -spec read_body(Req, read_body_opts()) -> {ok, binary(), Req} | {more, binary(), Req} when Req::req().
    517 read_body(Req=#{has_body := false}, _) ->
    518 	{ok, <<>>, Req};
    519 read_body(Req=#{has_read_body := true}, _) ->
    520 	{ok, <<>>, Req};
    521 read_body(Req, Opts) ->
    522 	Length = maps:get(length, Opts, 8000000),
    523 	Period = maps:get(period, Opts, 15000),
    524 	Timeout = maps:get(timeout, Opts, Period + 1000),
    525 	Ref = make_ref(),
    526 	cast({read_body, self(), Ref, Length, Period}, Req),
    527 	receive
    528 		{request_body, Ref, nofin, Body} ->
    529 			{more, Body, Req};
    530 		{request_body, Ref, fin, BodyLength, Body} ->
    531 			{ok, Body, set_body_length(Req, BodyLength)}
    532 	after Timeout ->
    533 		exit(timeout)
    534 	end.
    535 
    536 set_body_length(Req=#{headers := Headers}, BodyLength) ->
    537 	Req#{
    538 		headers => Headers#{<<"content-length">> => integer_to_binary(BodyLength)},
    539 		body_length => BodyLength,
    540 		has_read_body => true
    541 	}.
    542 
    543 -spec read_urlencoded_body(Req) -> {ok, [{binary(), binary() | true}], Req} when Req::req().
    544 read_urlencoded_body(Req) ->
    545 	read_urlencoded_body(Req, #{length => 64000, period => 5000}).
    546 
    547 -spec read_urlencoded_body(Req, read_body_opts()) -> {ok, [{binary(), binary() | true}], Req} when Req::req().
    548 read_urlencoded_body(Req0, Opts) ->
    549 	case read_body(Req0, Opts) of
    550 		{ok, Body, Req} ->
    551 			try
    552 				{ok, cow_qs:parse_qs(Body), Req}
    553 			catch _:_:Stacktrace ->
    554 				erlang:raise(exit, {request_error, urlencoded_body,
    555 					'Malformed body; application/x-www-form-urlencoded expected.'
    556 				}, Stacktrace)
    557 			end;
    558 		{more, Body, _} ->
    559 			Length = maps:get(length, Opts, 64000),
    560 			if
    561 				byte_size(Body) < Length ->
    562 					exit({request_error, timeout,
    563 						'The request body was not received within the configured time.'});
    564 				true ->
    565 					exit({request_error, payload_too_large,
    566 						'The request body is larger than allowed by configuration.'})
    567 			end
    568 	end.
    569 
    570 -spec read_and_match_urlencoded_body(cowboy:fields(), Req)
    571 	-> {ok, map(), Req} when Req::req().
    572 read_and_match_urlencoded_body(Fields, Req) ->
    573 	read_and_match_urlencoded_body(Fields, Req, #{length => 64000, period => 5000}).
    574 
    575 -spec read_and_match_urlencoded_body(cowboy:fields(), Req, read_body_opts())
    576 	-> {ok, map(), Req} when Req::req().
    577 read_and_match_urlencoded_body(Fields, Req0, Opts) ->
    578 	{ok, Qs, Req} = read_urlencoded_body(Req0, Opts),
    579 	case filter(Fields, kvlist_to_map(Fields, Qs)) of
    580 		{ok, Map} ->
    581 			{ok, Map, Req};
    582 		{error, Errors} ->
    583 			exit({request_error, {read_and_match_urlencoded_body, Errors},
    584 				'Urlencoded request body validation constraints failed for the reasons provided.'})
    585 	end.
    586 
    587 %% Multipart.
    588 
    589 -spec read_part(Req)
    590 	-> {ok, cowboy:http_headers(), Req} | {done, Req}
    591 	when Req::req().
    592 read_part(Req) ->
    593 	read_part(Req, #{length => 64000, period => 5000}).
    594 
    595 -spec read_part(Req, read_body_opts())
    596 	-> {ok, cowboy:http_headers(), Req} | {done, Req}
    597 	when Req::req().
    598 read_part(Req, Opts) ->
    599 	case maps:is_key(multipart, Req) of
    600 		true ->
    601 			{Data, Req2} = stream_multipart(Req, Opts, headers),
    602 			read_part(Data, Opts, Req2);
    603 		false ->
    604 			read_part(init_multipart(Req), Opts)
    605 	end.
    606 
    607 read_part(Buffer, Opts, Req=#{multipart := {Boundary, _}}) ->
    608 	try cow_multipart:parse_headers(Buffer, Boundary) of
    609 		more ->
    610 			{Data, Req2} = stream_multipart(Req, Opts, headers),
    611 			read_part(<< Buffer/binary, Data/binary >>, Opts, Req2);
    612 		{more, Buffer2} ->
    613 			{Data, Req2} = stream_multipart(Req, Opts, headers),
    614 			read_part(<< Buffer2/binary, Data/binary >>, Opts, Req2);
    615 		{ok, Headers0, Rest} ->
    616 			Headers = maps:from_list(Headers0),
    617 			%% Reject multipart content containing duplicate headers.
    618 			true = map_size(Headers) =:= length(Headers0),
    619 			{ok, Headers, Req#{multipart => {Boundary, Rest}}};
    620 		%% Ignore epilogue.
    621 		{done, _} ->
    622 			{done, Req#{multipart => done}}
    623 	catch _:_:Stacktrace ->
    624 		erlang:raise(exit, {request_error, {multipart, headers},
    625 			'Malformed body; multipart expected.'
    626 		}, Stacktrace)
    627 	end.
    628 
    629 -spec read_part_body(Req)
    630 	-> {ok, binary(), Req} | {more, binary(), Req}
    631 	when Req::req().
    632 read_part_body(Req) ->
    633 	read_part_body(Req, #{}).
    634 
    635 -spec read_part_body(Req, read_body_opts())
    636 	-> {ok, binary(), Req} | {more, binary(), Req}
    637 	when Req::req().
    638 read_part_body(Req, Opts) ->
    639 	case maps:is_key(multipart, Req) of
    640 		true ->
    641 			read_part_body(<<>>, Opts, Req, <<>>);
    642 		false ->
    643 			read_part_body(init_multipart(Req), Opts)
    644 	end.
    645 
    646 read_part_body(Buffer, Opts, Req=#{multipart := {Boundary, _}}, Acc) ->
    647 	Length = maps:get(length, Opts, 8000000),
    648 	case byte_size(Acc) > Length of
    649 		true ->
    650 			{more, Acc, Req#{multipart => {Boundary, Buffer}}};
    651 		false ->
    652 			{Data, Req2} = stream_multipart(Req, Opts, body),
    653 			case cow_multipart:parse_body(<< Buffer/binary, Data/binary >>, Boundary) of
    654 				{ok, Body} ->
    655 					read_part_body(<<>>, Opts, Req2, << Acc/binary, Body/binary >>);
    656 				{ok, Body, Rest} ->
    657 					read_part_body(Rest, Opts, Req2, << Acc/binary, Body/binary >>);
    658 				done ->
    659 					{ok, Acc, Req2};
    660 				{done, Body} ->
    661 					{ok, << Acc/binary, Body/binary >>, Req2};
    662 				{done, Body, Rest} ->
    663 					{ok, << Acc/binary, Body/binary >>,
    664 						Req2#{multipart => {Boundary, Rest}}}
    665 			end
    666 	end.
    667 
    668 init_multipart(Req) ->
    669 	{<<"multipart">>, _, Params} = parse_header(<<"content-type">>, Req),
    670 	case lists:keyfind(<<"boundary">>, 1, Params) of
    671 		{_, Boundary} ->
    672 			Req#{multipart => {Boundary, <<>>}};
    673 		false ->
    674 			exit({request_error, {multipart, boundary},
    675 				'Missing boundary parameter for multipart media type.'})
    676 	end.
    677 
    678 stream_multipart(Req=#{multipart := done}, _, _) ->
    679 	{<<>>, Req};
    680 stream_multipart(Req=#{multipart := {_, <<>>}}, Opts, Type) ->
    681 	case read_body(Req, Opts) of
    682 		{more, Data, Req2} ->
    683 			{Data, Req2};
    684 		%% We crash when the data ends unexpectedly.
    685 		{ok, <<>>, _} ->
    686 			exit({request_error, {multipart, Type},
    687 				'Malformed body; multipart expected.'});
    688 		{ok, Data, Req2} ->
    689 			{Data, Req2}
    690 	end;
    691 stream_multipart(Req=#{multipart := {Boundary, Buffer}}, _, _) ->
    692 	{Buffer, Req#{multipart => {Boundary, <<>>}}}.
    693 
    694 %% Response.
    695 
    696 -spec set_resp_cookie(iodata(), iodata(), Req)
    697 	-> Req when Req::req().
    698 set_resp_cookie(Name, Value, Req) ->
    699 	set_resp_cookie(Name, Value, Req, #{}).
    700 
    701 %% The cookie name cannot contain any of the following characters:
    702 %%   =,;\s\t\r\n\013\014
    703 %%
    704 %% The cookie value cannot contain any of the following characters:
    705 %%   ,; \t\r\n\013\014
    706 -spec set_resp_cookie(binary(), iodata(), Req, cow_cookie:cookie_opts())
    707 	-> Req when Req::req().
    708 set_resp_cookie(Name, Value, Req, Opts) ->
    709 	Cookie = cow_cookie:setcookie(Name, Value, Opts),
    710 	RespCookies = maps:get(resp_cookies, Req, #{}),
    711 	Req#{resp_cookies => RespCookies#{Name => Cookie}}.
    712 
    713 %% @todo We could add has_resp_cookie and delete_resp_cookie now.
    714 
    715 -spec set_resp_header(binary(), iodata(), Req)
    716 	-> Req when Req::req().
    717 set_resp_header(Name, Value, Req=#{resp_headers := RespHeaders}) ->
    718 	Req#{resp_headers => RespHeaders#{Name => Value}};
    719 set_resp_header(Name,Value, Req) ->
    720 	Req#{resp_headers => #{Name => Value}}.
    721 
    722 -spec set_resp_headers(cowboy:http_headers(), Req)
    723 	-> Req when Req::req().
    724 set_resp_headers(Headers, Req=#{resp_headers := RespHeaders}) ->
    725 	Req#{resp_headers => maps:merge(RespHeaders, Headers)};
    726 set_resp_headers(Headers, Req) ->
    727 	Req#{resp_headers => Headers}.
    728 
    729 -spec resp_header(binary(), req()) -> binary() | undefined.
    730 resp_header(Name, Req) ->
    731 	resp_header(Name, Req, undefined).
    732 
    733 -spec resp_header(binary(), req(), Default)
    734 	-> binary() | Default when Default::any().
    735 resp_header(Name, #{resp_headers := Headers}, Default) ->
    736 	maps:get(Name, Headers, Default);
    737 resp_header(_, #{}, Default) ->
    738 	Default.
    739 
    740 -spec resp_headers(req()) -> cowboy:http_headers().
    741 resp_headers(#{resp_headers := RespHeaders}) ->
    742 	RespHeaders;
    743 resp_headers(#{}) ->
    744 	#{}.
    745 
    746 -spec set_resp_body(resp_body(), Req) -> Req when Req::req().
    747 set_resp_body(Body, Req) ->
    748 	Req#{resp_body => Body}.
    749 
    750 -spec has_resp_header(binary(), req()) -> boolean().
    751 has_resp_header(Name, #{resp_headers := RespHeaders}) ->
    752 	maps:is_key(Name, RespHeaders);
    753 has_resp_header(_, _) ->
    754 	false.
    755 
    756 -spec has_resp_body(req()) -> boolean().
    757 has_resp_body(#{resp_body := {sendfile, _, _, _}}) ->
    758 	true;
    759 has_resp_body(#{resp_body := RespBody}) ->
    760 	iolist_size(RespBody) > 0;
    761 has_resp_body(_) ->
    762 	false.
    763 
    764 -spec delete_resp_header(binary(), Req)
    765 	-> Req when Req::req().
    766 delete_resp_header(Name, Req=#{resp_headers := RespHeaders}) ->
    767 	Req#{resp_headers => maps:remove(Name, RespHeaders)};
    768 %% There are no resp headers so we have nothing to delete.
    769 delete_resp_header(_, Req) ->
    770 	Req.
    771 
    772 -spec inform(cowboy:http_status(), req()) -> ok.
    773 inform(Status, Req) ->
    774 	inform(Status, #{}, Req).
    775 
    776 -spec inform(cowboy:http_status(), cowboy:http_headers(), req()) -> ok.
    777 inform(_, _, #{has_sent_resp := _}) ->
    778 	error(function_clause); %% @todo Better error message.
    779 inform(Status, Headers, Req) when is_integer(Status); is_binary(Status) ->
    780 	cast({inform, Status, Headers}, Req).
    781 
    782 -spec reply(cowboy:http_status(), Req) -> Req when Req::req().
    783 reply(Status, Req) ->
    784 	reply(Status, #{}, Req).
    785 
    786 -spec reply(cowboy:http_status(), cowboy:http_headers(), Req)
    787 	-> Req when Req::req().
    788 reply(Status, Headers, Req=#{resp_body := Body}) ->
    789 	reply(Status, Headers, Body, Req);
    790 reply(Status, Headers, Req) ->
    791 	reply(Status, Headers, <<>>, Req).
    792 
    793 -spec reply(cowboy:http_status(), cowboy:http_headers(), resp_body(), Req)
    794 	-> Req when Req::req().
    795 reply(_, _, _, #{has_sent_resp := _}) ->
    796 	error(function_clause); %% @todo Better error message.
    797 reply(Status, Headers, {sendfile, _, 0, _}, Req)
    798 		when is_integer(Status); is_binary(Status) ->
    799 	do_reply(Status, Headers#{
    800 		<<"content-length">> => <<"0">>
    801 	}, <<>>, Req);
    802 reply(Status, Headers, SendFile = {sendfile, _, Len, _}, Req)
    803 		when is_integer(Status); is_binary(Status) ->
    804 	do_reply(Status, Headers#{
    805 		<<"content-length">> => integer_to_binary(Len)
    806 	}, SendFile, Req);
    807 %% 204 responses must not include content-length. 304 responses may
    808 %% but only when set explicitly. (RFC7230 3.3.1, RFC7230 3.3.2)
    809 %% Neither status code must include a response body. (RFC7230 3.3)
    810 reply(Status, Headers, Body, Req)
    811 		when Status =:= 204; Status =:= 304 ->
    812 	0 = iolist_size(Body),
    813 	do_reply(Status, Headers, Body, Req);
    814 reply(Status = <<"204",_/bits>>, Headers, Body, Req) ->
    815 	0 = iolist_size(Body),
    816 	do_reply(Status, Headers, Body, Req);
    817 reply(Status = <<"304",_/bits>>, Headers, Body, Req) ->
    818 	0 = iolist_size(Body),
    819 	do_reply(Status, Headers, Body, Req);
    820 reply(Status, Headers, Body, Req)
    821 		when is_integer(Status); is_binary(Status) ->
    822 	do_reply(Status, Headers#{
    823 		<<"content-length">> => integer_to_binary(iolist_size(Body))
    824 	}, Body, Req).
    825 
    826 %% Don't send any body for HEAD responses. While the protocol code is
    827 %% supposed to enforce this rule, we prefer to avoid copying too much
    828 %% data around if we can avoid it.
    829 do_reply(Status, Headers, _, Req=#{method := <<"HEAD">>}) ->
    830 	cast({response, Status, response_headers(Headers, Req), <<>>}, Req),
    831 	done_replying(Req, true);
    832 do_reply(Status, Headers, Body, Req) ->
    833 	cast({response, Status, response_headers(Headers, Req), Body}, Req),
    834 	done_replying(Req, true).
    835 
    836 done_replying(Req, HasSentResp) ->
    837 	maps:without([resp_cookies, resp_headers, resp_body], Req#{has_sent_resp => HasSentResp}).
    838 
    839 -spec stream_reply(cowboy:http_status(), Req) -> Req when Req::req().
    840 stream_reply(Status, Req) ->
    841 	stream_reply(Status, #{}, Req).
    842 
    843 -spec stream_reply(cowboy:http_status(), cowboy:http_headers(), Req)
    844 	-> Req when Req::req().
    845 stream_reply(_, _, #{has_sent_resp := _}) ->
    846 	error(function_clause);
    847 %% 204 and 304 responses must NOT send a body. We therefore
    848 %% transform the call to a full response and expect the user
    849 %% to NOT call stream_body/3 afterwards. (RFC7230 3.3)
    850 stream_reply(Status = 204, Headers=#{}, Req) ->
    851 	reply(Status, Headers, <<>>, Req);
    852 stream_reply(Status = <<"204",_/bits>>, Headers=#{}, Req) ->
    853 	reply(Status, Headers, <<>>, Req);
    854 stream_reply(Status = 304, Headers=#{}, Req) ->
    855 	reply(Status, Headers, <<>>, Req);
    856 stream_reply(Status = <<"304",_/bits>>, Headers=#{}, Req) ->
    857 	reply(Status, Headers, <<>>, Req);
    858 stream_reply(Status, Headers=#{}, Req) when is_integer(Status); is_binary(Status) ->
    859 	cast({headers, Status, response_headers(Headers, Req)}, Req),
    860 	done_replying(Req, headers).
    861 
    862 -spec stream_body(resp_body(), fin | nofin, req()) -> ok.
    863 %% Error out if headers were not sent.
    864 %% Don't send any body for HEAD responses.
    865 stream_body(_, _, #{method := <<"HEAD">>, has_sent_resp := headers}) ->
    866 	ok;
    867 %% Don't send a message if the data is empty, except for the
    868 %% very last message with IsFin=fin. When using sendfile this
    869 %% is converted to a data tuple, however.
    870 stream_body({sendfile, _, 0, _}, nofin, _) ->
    871 	ok;
    872 stream_body({sendfile, _, 0, _}, IsFin=fin, Req=#{has_sent_resp := headers}) ->
    873 	stream_body({data, self(), IsFin, <<>>}, Req);
    874 stream_body({sendfile, O, B, P}, IsFin, Req=#{has_sent_resp := headers})
    875 		when is_integer(O), O >= 0, is_integer(B), B > 0 ->
    876 	stream_body({data, self(), IsFin, {sendfile, O, B, P}}, Req);
    877 stream_body(Data, IsFin=nofin, Req=#{has_sent_resp := headers})
    878 		when not is_tuple(Data) ->
    879 	case iolist_size(Data) of
    880 		0 -> ok;
    881 		_ -> stream_body({data, self(), IsFin, Data}, Req)
    882 	end;
    883 stream_body(Data, IsFin, Req=#{has_sent_resp := headers})
    884 		when not is_tuple(Data) ->
    885 	stream_body({data, self(), IsFin, Data}, Req).
    886 
    887 %% @todo Do we need a timeout?
    888 stream_body(Msg, Req=#{pid := Pid}) ->
    889 	cast(Msg, Req),
    890 	receive {data_ack, Pid} -> ok end.
    891 
    892 -spec stream_events(cow_sse:event() | [cow_sse:event()], fin | nofin, req()) -> ok.
    893 stream_events(Event, IsFin, Req) when is_map(Event) ->
    894 	stream_events([Event], IsFin, Req);
    895 stream_events(Events, IsFin, Req=#{has_sent_resp := headers}) ->
    896 	stream_body({data, self(), IsFin, cow_sse:events(Events)}, Req).
    897 
    898 -spec stream_trailers(cowboy:http_headers(), req()) -> ok.
    899 stream_trailers(Trailers, Req=#{has_sent_resp := headers}) ->
    900 	cast({trailers, Trailers}, Req).
    901 
    902 -spec push(iodata(), cowboy:http_headers(), req()) -> ok.
    903 push(Path, Headers, Req) ->
    904 	push(Path, Headers, Req, #{}).
    905 
    906 %% @todo Optimization: don't send anything at all for HTTP/1.0 and HTTP/1.1.
    907 %% @todo Path, Headers, Opts, everything should be in proper binary,
    908 %% or normalized when creating the Req object.
    909 -spec push(iodata(), cowboy:http_headers(), req(), push_opts()) -> ok.
    910 push(Path, Headers, Req=#{scheme := Scheme0, host := Host0, port := Port0}, Opts) ->
    911 	Method = maps:get(method, Opts, <<"GET">>),
    912 	Scheme = maps:get(scheme, Opts, Scheme0),
    913 	Host = maps:get(host, Opts, Host0),
    914 	Port = maps:get(port, Opts, Port0),
    915 	Qs = maps:get(qs, Opts, <<>>),
    916 	cast({push, Method, Scheme, Host, Port, Path, Qs, Headers}, Req).
    917 
    918 %% Stream handlers.
    919 
    920 -spec cast(any(), req()) -> ok.
    921 cast(Msg, #{pid := Pid, streamid := StreamID}) ->
    922 	Pid ! {{Pid, StreamID}, Msg},
    923 	ok.
    924 
    925 %% Internal.
    926 
    927 %% @todo What about set-cookie headers set through set_resp_header or reply?
    928 -spec response_headers(Headers, req()) -> Headers when Headers::cowboy:http_headers().
    929 response_headers(Headers0, Req) ->
    930 	RespHeaders = maps:get(resp_headers, Req, #{}),
    931 	Headers = maps:merge(#{
    932 		<<"date">> => cowboy_clock:rfc1123(),
    933 		<<"server">> => <<"Cowboy">>
    934 	}, maps:merge(RespHeaders, Headers0)),
    935 	%% The set-cookie header is special; we can only send one cookie per header.
    936 	%% We send the list of values for many cookies in one key of the map,
    937 	%% and let the protocols deal with it directly.
    938 	case maps:get(resp_cookies, Req, undefined) of
    939 		undefined -> Headers;
    940 		RespCookies -> Headers#{<<"set-cookie">> => maps:values(RespCookies)}
    941 	end.
    942 
    943 %% Create map, convert keys to atoms and group duplicate keys into lists.
    944 %% Keys that are not found in the user provided list are entirely skipped.
    945 %% @todo Can probably be done directly while parsing.
    946 kvlist_to_map(Fields, KvList) ->
    947 	Keys = [case K of
    948 		{Key, _} -> Key;
    949 		{Key, _, _} -> Key;
    950 		Key -> Key
    951 	end || K <- Fields],
    952 	kvlist_to_map(Keys, KvList, #{}).
    953 
    954 kvlist_to_map(_, [], Map) ->
    955 	Map;
    956 kvlist_to_map(Keys, [{Key, Value}|Tail], Map) ->
    957 	try binary_to_existing_atom(Key, utf8) of
    958 		Atom ->
    959 			case lists:member(Atom, Keys) of
    960 				true ->
    961 					case maps:find(Atom, Map) of
    962 						{ok, MapValue} when is_list(MapValue) ->
    963 							kvlist_to_map(Keys, Tail,
    964 								Map#{Atom => [Value|MapValue]});
    965 						{ok, MapValue} ->
    966 							kvlist_to_map(Keys, Tail,
    967 								Map#{Atom => [Value, MapValue]});
    968 						error ->
    969 							kvlist_to_map(Keys, Tail,
    970 								Map#{Atom => Value})
    971 					end;
    972 				false ->
    973 					kvlist_to_map(Keys, Tail, Map)
    974 			end
    975 	catch error:badarg ->
    976 		kvlist_to_map(Keys, Tail, Map)
    977 	end.
    978 
    979 filter(Fields, Map0) ->
    980 	filter(Fields, Map0, #{}).
    981 
    982 %% Loop through fields, if value is missing and no default,
    983 %% record the error; else if value is missing and has a
    984 %% default, set default; otherwise apply constraints. If
    985 %% constraint fails, record the error.
    986 %%
    987 %% When there is an error at the end, crash.
    988 filter([], Map, Errors) ->
    989 	case maps:size(Errors) of
    990 		0 -> {ok, Map};
    991 		_ -> {error, Errors}
    992 	end;
    993 filter([{Key, Constraints}|Tail], Map, Errors) ->
    994 	filter_constraints(Tail, Map, Errors, Key, maps:get(Key, Map), Constraints);
    995 filter([{Key, Constraints, Default}|Tail], Map, Errors) ->
    996 	case maps:find(Key, Map) of
    997 		{ok, Value} ->
    998 			filter_constraints(Tail, Map, Errors, Key, Value, Constraints);
    999 		error ->
   1000 			filter(Tail, Map#{Key => Default}, Errors)
   1001 	end;
   1002 filter([Key|Tail], Map, Errors) ->
   1003 	case maps:is_key(Key, Map) of
   1004 		true ->
   1005 			filter(Tail, Map, Errors);
   1006 		false ->
   1007 			filter(Tail, Map, Errors#{Key => required})
   1008 	end.
   1009 
   1010 filter_constraints(Tail, Map, Errors, Key, Value0, Constraints) ->
   1011 	case cowboy_constraints:validate(Value0, Constraints) of
   1012 		{ok, Value} ->
   1013 			filter(Tail, Map#{Key => Value}, Errors);
   1014 		{error, Reason} ->
   1015 			filter(Tail, Map, Errors#{Key => Reason})
   1016 	end.