mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Don't use decode_packet/3 for parsing the headers
Header names are now binaries. Since header names are case insensitive they are all converted to lowercase. For example: <<"content-length">>. The max_line_length option was removed. Three new options have been added instead: * max_request_line_length (defaults to 4096) * max_header_name_length (defaults to 64) * max_header_value_length (defaults to 4096)
This commit is contained in:
parent
8497c8bbcd
commit
34021666cb
9 changed files with 146 additions and 241 deletions
|
@ -62,14 +62,14 @@ stop() ->
|
|||
|
||||
%% @doc Return the current date and time formatted according to RFC-1123.
|
||||
%%
|
||||
%% This format is used in the <em>'Date'</em> header sent with HTTP responses.
|
||||
%% This format is used in the <em>date</em> header sent with HTTP responses.
|
||||
-spec rfc1123() -> binary().
|
||||
rfc1123() ->
|
||||
ets:lookup_element(?TABLE, rfc1123, 2).
|
||||
|
||||
%% @doc Return the current date and time formatted according to RFC-2109.
|
||||
%%
|
||||
%% This format is used in the <em>'Set-Cookie'</em> header sent with
|
||||
%% This format is used in the <em>set-cookie</em> header sent with
|
||||
%% HTTP responses.
|
||||
-spec rfc2109(calendar:datetime()) -> binary().
|
||||
rfc2109(LocalTime) ->
|
||||
|
|
|
@ -56,25 +56,11 @@
|
|||
| {scheme, Scheme::binary(), binary()}
|
||||
| {abs_path, binary()} | binary().
|
||||
-type version() :: {Major::non_neg_integer(), Minor::non_neg_integer()}.
|
||||
-type header() :: 'Cache-Control' | 'Connection' | 'Date' | 'Pragma'
|
||||
| 'Transfer-Encoding' | 'Upgrade' | 'Via' | 'Accept' | 'Accept-Charset'
|
||||
| 'Accept-Encoding' | 'Accept-Language' | 'Authorization' | 'From' | 'Host'
|
||||
| 'If-Modified-Since' | 'If-Match' | 'If-None-Match' | 'If-Range'
|
||||
| 'If-Unmodified-Since' | 'Max-Forwards' | 'Proxy-Authorization' | 'Range'
|
||||
| 'Referer' | 'User-Agent' | 'Age' | 'Location' | 'Proxy-Authenticate'
|
||||
| 'Public' | 'Retry-After' | 'Server' | 'Vary' | 'Warning'
|
||||
| 'Www-Authenticate' | 'Allow' | 'Content-Base' | 'Content-Encoding'
|
||||
| 'Content-Language' | 'Content-Length' | 'Content-Location'
|
||||
| 'Content-Md5' | 'Content-Range' | 'Content-Type' | 'Etag'
|
||||
| 'Expires' | 'Last-Modified' | 'Accept-Ranges' | 'Set-Cookie'
|
||||
| 'Set-Cookie2' | 'X-Forwarded-For' | 'Cookie' | 'Keep-Alive'
|
||||
| 'Proxy-Connection' | binary().
|
||||
-type headers() :: [{header(), iodata()}].
|
||||
-type headers() :: [{binary(), iodata()}].
|
||||
-type status() :: non_neg_integer() | binary().
|
||||
|
||||
-export_type([uri/0]).
|
||||
-export_type([version/0]).
|
||||
-export_type([header/0]).
|
||||
-export_type([headers/0]).
|
||||
-export_type([status/0]).
|
||||
|
||||
|
|
|
@ -42,10 +42,6 @@
|
|||
-export([parse_request/1]).
|
||||
-export([handler_loop/3]).
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-endif.
|
||||
|
||||
-type onrequest_fun() :: fun((Req) -> Req).
|
||||
-type onresponse_fun() ::
|
||||
fun((cowboy_http:status(), cowboy_http:headers(), Req) -> Req).
|
||||
|
@ -66,7 +62,9 @@
|
|||
max_empty_lines :: integer(),
|
||||
req_keepalive = 1 :: integer(),
|
||||
max_keepalive :: integer(),
|
||||
max_line_length :: integer(),
|
||||
max_request_line_length :: integer(),
|
||||
max_header_name_length :: integer(),
|
||||
max_header_value_length :: integer(),
|
||||
timeout :: timeout(),
|
||||
buffer = <<>> :: binary(),
|
||||
host_tokens = undefined :: undefined | cowboy_dispatcher:tokens(),
|
||||
|
@ -100,16 +98,21 @@ init(ListenerPid, Socket, Transport, Opts) ->
|
|||
Dispatch = get_value(dispatch, Opts, []),
|
||||
MaxEmptyLines = get_value(max_empty_lines, Opts, 5),
|
||||
MaxKeepalive = get_value(max_keepalive, Opts, infinity),
|
||||
MaxLineLength = get_value(max_line_length, Opts, 4096),
|
||||
MaxRequestLineLength = get_value(max_request_line_length, Opts, 4096),
|
||||
MaxHeaderNameLength = get_value(max_header_name_length, Opts, 64),
|
||||
MaxHeaderValueLength = get_value(max_header_value_length, Opts, 4096),
|
||||
OnRequest = get_value(onrequest, Opts, undefined),
|
||||
OnResponse = get_value(onresponse, Opts, undefined),
|
||||
Timeout = get_value(timeout, Opts, 5000),
|
||||
URLDecDefault = {fun cowboy_http:urldecode/2, crash},
|
||||
URLDec = get_value(urldecode, Opts, URLDecDefault),
|
||||
ok = ranch:accept_ack(ListenerPid),
|
||||
wait_request(#state{listener=ListenerPid, socket=Socket, transport=Transport,
|
||||
dispatch=Dispatch, max_empty_lines=MaxEmptyLines,
|
||||
max_keepalive=MaxKeepalive, max_line_length=MaxLineLength,
|
||||
wait_request(#state{listener=ListenerPid, socket=Socket,
|
||||
transport=Transport, dispatch=Dispatch,
|
||||
max_empty_lines=MaxEmptyLines, max_keepalive=MaxKeepalive,
|
||||
max_request_line_length=MaxRequestLineLength,
|
||||
max_header_name_length=MaxHeaderNameLength,
|
||||
max_header_value_length=MaxHeaderValueLength,
|
||||
timeout=Timeout, onrequest=OnRequest, onresponse=OnResponse,
|
||||
urldecode=URLDec}).
|
||||
|
||||
|
@ -126,7 +129,7 @@ wait_request(State=#state{socket=Socket, transport=Transport,
|
|||
-spec parse_request(#state{}) -> ok.
|
||||
%% We limit the length of the Request-line to MaxLength to avoid endlessly
|
||||
%% reading from the socket and eventually crashing.
|
||||
parse_request(State=#state{buffer=Buffer, max_line_length=MaxLength,
|
||||
parse_request(State=#state{buffer=Buffer, max_request_line_length=MaxLength,
|
||||
req_empty_lines=ReqEmpty, max_empty_lines=MaxEmpty}) ->
|
||||
case binary:split(Buffer, <<"\r\n">>) of
|
||||
[_] when byte_size(Buffer) > MaxLength ->
|
||||
|
@ -171,29 +174,50 @@ request(State=#state{socket=Socket, transport=Transport,
|
|||
Path, Qs, OnResponse, URLDec)).
|
||||
|
||||
-spec parse_header(#state{}, cowboy_req:req()) -> ok.
|
||||
parse_header(State=#state{buffer=Buffer, max_line_length=MaxLength}, Req) ->
|
||||
case erlang:decode_packet(httph_bin, Buffer, []) of
|
||||
{ok, Header, Rest} -> header(Header, Req, State#state{buffer=Rest});
|
||||
{more, _Length} when byte_size(Buffer) > MaxLength ->
|
||||
parse_header(State=#state{buffer= << "\r\n", Rest/binary >>}, Req) ->
|
||||
header_end(State#state{buffer=Rest}, Req);
|
||||
parse_header(State=#state{buffer=Buffer,
|
||||
max_header_name_length=MaxLength}, Req) ->
|
||||
case binary:split(Buffer, <<":">>) of
|
||||
[_] when byte_size(Buffer) > MaxLength ->
|
||||
error_terminate(413, State);
|
||||
{more, _Length} -> wait_header(Req, State);
|
||||
{error, _Reason} -> error_terminate(400, State)
|
||||
[_] ->
|
||||
wait_header(State, Req, fun parse_header/2);
|
||||
[Name, Rest] ->
|
||||
Name2 = cowboy_bstr:to_lower(Name),
|
||||
Rest2 = cowboy_http:whitespace(Rest, fun(D) -> D end),
|
||||
parse_header_value(State#state{buffer=Rest2}, Req, Name2, <<>>)
|
||||
end.
|
||||
|
||||
-spec wait_header(cowboy_req:req(), #state{}) -> ok.
|
||||
wait_header(Req, State=#state{socket=Socket,
|
||||
transport=Transport, timeout=T, buffer=Buffer}) ->
|
||||
parse_header_value(State=#state{buffer=Buffer,
|
||||
max_header_value_length=MaxLength}, Req, Name, SoFar) ->
|
||||
case binary:split(Buffer, <<"\r\n">>) of
|
||||
[_] when byte_size(Buffer) + byte_size(SoFar) > MaxLength ->
|
||||
error_terminate(413, State);
|
||||
[_] ->
|
||||
wait_header(State, Req,
|
||||
fun(S, R) -> parse_header_value(S, R, Name, SoFar) end);
|
||||
[Value, << C, Rest/binary >>] when C =:= $\s; C =:= $\t ->
|
||||
parse_header_value(State#state{buffer=Rest}, Req, Name,
|
||||
<< SoFar/binary, Value/binary >>);
|
||||
[Value, Rest] ->
|
||||
header(State#state{buffer=Rest}, Req, Name,
|
||||
<< SoFar/binary, Value/binary >>)
|
||||
end.
|
||||
|
||||
-spec wait_header(#state{}, cowboy_req:req(), fun()) -> ok.
|
||||
wait_header(State=#state{socket=Socket, transport=Transport,
|
||||
timeout=T, buffer=Buffer}, Req, Fun) ->
|
||||
case Transport:recv(Socket, 0, T) of
|
||||
{ok, Data} -> parse_header(State#state{
|
||||
{ok, Data} -> Fun(State#state{
|
||||
buffer= << Buffer/binary, Data/binary >>}, Req);
|
||||
{error, timeout} -> error_terminate(408, State);
|
||||
{error, closed} -> terminate(State)
|
||||
end.
|
||||
|
||||
-spec header({http_header, integer(), cowboy_http:header(), any(), binary()}
|
||||
| http_eoh, cowboy_req:req(), #state{}) -> ok.
|
||||
header({http_header, _I, 'Host', _R, RawHost}, Req,
|
||||
State=#state{host_tokens=undefined, transport=Transport}) ->
|
||||
-spec header(#state{}, cowboy_req:req(), binary(), binary()) -> ok.
|
||||
header(State=#state{host_tokens=undefined, transport=Transport},
|
||||
Req, <<"host">>, RawHost) ->
|
||||
RawHost2 = cowboy_bstr:to_lower(RawHost),
|
||||
case catch cowboy_dispatcher:split_host(RawHost2) of
|
||||
{HostTokens, Host, undefined} ->
|
||||
|
@ -207,18 +231,18 @@ header({http_header, _I, 'Host', _R, RawHost}, Req,
|
|||
error_terminate(400, State)
|
||||
end;
|
||||
%% Ignore Host headers if we already have it.
|
||||
header({http_header, _I, 'Host', _R, _V}, Req, State) ->
|
||||
header(State, Req, <<"host">>, _) ->
|
||||
parse_header(State, Req);
|
||||
header({http_header, _I, 'Connection', _R, Connection}, Req,
|
||||
State=#state{req_keepalive=Keepalive, max_keepalive=MaxKeepalive})
|
||||
header(State=#state{req_keepalive=Keepalive, max_keepalive=MaxKeepalive},
|
||||
Req, <<"connection">>, Connection)
|
||||
when Keepalive < MaxKeepalive ->
|
||||
parse_header(State, cowboy_req:set_connection(Connection, Req));
|
||||
header({http_header, _I, Field, _R, Value}, Req, State) ->
|
||||
Field2 = format_header(Field),
|
||||
parse_header(State, cowboy_req:add_header(Field2, Value, Req));
|
||||
header(State, Req, Name, Value) ->
|
||||
parse_header(State, cowboy_req:add_header(Name, Value, Req)).
|
||||
|
||||
%% The Host header is required in HTTP/1.1 and optional in HTTP/1.0.
|
||||
header(http_eoh, Req, State=#state{host_tokens=undefined,
|
||||
buffer=Buffer, transport=Transport}) ->
|
||||
header_end(State=#state{host_tokens=undefined,
|
||||
buffer=Buffer, transport=Transport}, Req) ->
|
||||
case cowboy_req:version(Req) of
|
||||
{{1, 1}, _} ->
|
||||
error_terminate(400, State);
|
||||
|
@ -229,10 +253,8 @@ header(http_eoh, Req, State=#state{host_tokens=undefined,
|
|||
cowboy_req:set_host(<<>>, Port, <<>>, Req2)),
|
||||
State#state{buffer= <<>>, host_tokens=[]})
|
||||
end;
|
||||
header(http_eoh, Req, State=#state{buffer=Buffer}) ->
|
||||
onrequest(cowboy_req:set_buffer(Buffer, Req), State#state{buffer= <<>>});
|
||||
header(_Any, _Req, State) ->
|
||||
error_terminate(400, State).
|
||||
header_end(State=#state{buffer=Buffer}, Req) ->
|
||||
onrequest(cowboy_req:set_buffer(Buffer, Req), State#state{buffer= <<>>}).
|
||||
|
||||
%% Call the global onrequest callback. The callback can send a reply,
|
||||
%% in which case we consider the request handled and move on to the next
|
||||
|
@ -451,45 +473,3 @@ version_to_connection(_, _) ->
|
|||
-spec default_port(atom()) -> 80 | 443.
|
||||
default_port(ssl) -> 443;
|
||||
default_port(_) -> 80.
|
||||
|
||||
%% @todo While 32 should be enough for everybody, we should probably make
|
||||
%% this configurable or something.
|
||||
-spec format_header(atom()) -> atom(); (binary()) -> binary().
|
||||
format_header(Field) when is_atom(Field) ->
|
||||
Field;
|
||||
format_header(Field) when byte_size(Field) =< 20; byte_size(Field) > 32 ->
|
||||
Field;
|
||||
format_header(Field) ->
|
||||
format_header(Field, true, <<>>).
|
||||
|
||||
-spec format_header(binary(), boolean(), binary()) -> binary().
|
||||
format_header(<<>>, _Any, Acc) ->
|
||||
Acc;
|
||||
%% Replicate a bug in OTP for compatibility reasons when there's a - right
|
||||
%% after another. Proper use should always be 'true' instead of 'not Bool'.
|
||||
format_header(<< $-, Rest/bits >>, Bool, Acc) ->
|
||||
format_header(Rest, not Bool, << Acc/binary, $- >>);
|
||||
format_header(<< C, Rest/bits >>, true, Acc) ->
|
||||
format_header(Rest, false, << Acc/binary, (cowboy_bstr:char_to_upper(C)) >>);
|
||||
format_header(<< C, Rest/bits >>, false, Acc) ->
|
||||
format_header(Rest, false, << Acc/binary, (cowboy_bstr:char_to_lower(C)) >>).
|
||||
|
||||
%% Tests.
|
||||
|
||||
-ifdef(TEST).
|
||||
|
||||
format_header_test_() ->
|
||||
%% {Header, Result}
|
||||
Tests = [
|
||||
{<<"Sec-Websocket-Version">>, <<"Sec-Websocket-Version">>},
|
||||
{<<"Sec-WebSocket-Version">>, <<"Sec-Websocket-Version">>},
|
||||
{<<"sec-websocket-version">>, <<"Sec-Websocket-Version">>},
|
||||
{<<"SEC-WEBSOCKET-VERSION">>, <<"Sec-Websocket-Version">>},
|
||||
%% These last tests ensures we're formatting headers exactly like OTP.
|
||||
%% Even though it's dumb, it's better for compatibility reasons.
|
||||
{<<"Sec-WebSocket--Version">>, <<"Sec-Websocket--version">>},
|
||||
{<<"Sec-WebSocket---Version">>, <<"Sec-Websocket---Version">>}
|
||||
],
|
||||
[{H, fun() -> R = format_header(H) end} || {H, R} <- Tests].
|
||||
|
||||
-endif.
|
||||
|
|
|
@ -203,8 +203,8 @@ peer(Req) ->
|
|||
%% @doc Returns the peer address calculated from headers.
|
||||
-spec peer_addr(Req) -> {inet:ip_address(), Req} when Req::req().
|
||||
peer_addr(Req = #http_req{}) ->
|
||||
{RealIp, Req1} = header(<<"X-Real-Ip">>, Req),
|
||||
{ForwardedForRaw, Req2} = header(<<"X-Forwarded-For">>, Req1),
|
||||
{RealIp, Req1} = header(<<"x-real-ip">>, Req),
|
||||
{ForwardedForRaw, Req2} = header(<<"x-forwarded-for">>, Req1),
|
||||
{{PeerIp, _PeerPort}, Req3} = peer(Req2),
|
||||
ForwardedFor = case ForwardedForRaw of
|
||||
undefined ->
|
||||
|
@ -339,15 +339,15 @@ bindings(Req) ->
|
|||
{Req#http_req.bindings, Req}.
|
||||
|
||||
%% @equiv header(Name, Req, undefined)
|
||||
-spec header(atom() | binary(), Req)
|
||||
-spec header(binary(), Req)
|
||||
-> {binary() | undefined, Req} when Req::req().
|
||||
header(Name, Req) when is_atom(Name) orelse is_binary(Name) ->
|
||||
header(Name, Req) ->
|
||||
header(Name, Req, undefined).
|
||||
|
||||
%% @doc Return the header value for the given key, or a default if missing.
|
||||
-spec header(atom() | binary(), Req, Default)
|
||||
-spec header(binary(), Req, Default)
|
||||
-> {binary() | Default, Req} when Req::req(), Default::any().
|
||||
header(Name, Req, Default) when is_atom(Name) orelse is_binary(Name) ->
|
||||
header(Name, Req, Default) ->
|
||||
case lists:keyfind(Name, 1, Req#http_req.headers) of
|
||||
{Name, Value} -> {Value, Req};
|
||||
false -> {Default, Req}
|
||||
|
@ -363,7 +363,7 @@ headers(Req) ->
|
|||
%% When the value isn't found, a proper default value for the type
|
||||
%% returned is used as a return value.
|
||||
%% @see parse_header/3
|
||||
-spec parse_header(cowboy_http:header(), Req)
|
||||
-spec parse_header(binary(), Req)
|
||||
-> {ok, any(), Req} | {undefined, binary(), Req}
|
||||
| {error, badarg} when Req::req().
|
||||
parse_header(Name, Req=#http_req{p_headers=PHeaders}) ->
|
||||
|
@ -373,76 +373,77 @@ parse_header(Name, Req=#http_req{p_headers=PHeaders}) ->
|
|||
end.
|
||||
|
||||
%% @doc Default values for semantic header parsing.
|
||||
-spec parse_header_default(cowboy_http:header()) -> any().
|
||||
parse_header_default('Connection') -> [];
|
||||
parse_header_default('Transfer-Encoding') -> [<<"identity">>];
|
||||
-spec parse_header_default(binary()) -> any().
|
||||
parse_header_default(<<"connection">>) -> [];
|
||||
parse_header_default(<<"transfer-encoding">>) -> [<<"identity">>];
|
||||
parse_header_default(_Name) -> undefined.
|
||||
|
||||
%% @doc Semantically parse headers.
|
||||
%%
|
||||
%% When the header is unknown, the value is returned directly without parsing.
|
||||
-spec parse_header(cowboy_http:header(), Req, any())
|
||||
-spec parse_header(binary(), Req, any())
|
||||
-> {ok, any(), Req} | {undefined, binary(), Req}
|
||||
| {error, badarg} when Req::req().
|
||||
parse_header(Name, Req, Default) when Name =:= 'Accept' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"accept">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:list(Value, fun cowboy_http:media_range/2)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= 'Accept-Charset' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"accept-charset">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:nonempty_list(Value, fun cowboy_http:conneg/2)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= 'Accept-Encoding' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"accept-encoding">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:list(Value, fun cowboy_http:conneg/2)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= 'Accept-Language' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"accept-language">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:nonempty_list(Value, fun cowboy_http:language_range/2)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= 'Connection' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"connection">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= 'Content-Length' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"content-length">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:digits(Value)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= 'Content-Type' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"content-type">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:content_type(Value)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= <<"Expect">> ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"expect">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:nonempty_list(Value, fun cowboy_http:expectation/2)
|
||||
end);
|
||||
parse_header(Name, Req, Default)
|
||||
when Name =:= 'If-Match'; Name =:= 'If-None-Match' ->
|
||||
when Name =:= <<"if-match">>; Name =:= <<"if-none-match">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:entity_tag_match(Value)
|
||||
end);
|
||||
parse_header(Name, Req, Default)
|
||||
when Name =:= 'If-Modified-Since'; Name =:= 'If-Unmodified-Since' ->
|
||||
when Name =:= <<"if-modified-since">>;
|
||||
Name =:= <<"if-unmodified-since">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:http_date(Value)
|
||||
end);
|
||||
%% @todo Extension parameters.
|
||||
parse_header(Name, Req, Default) when Name =:= 'Transfer-Encoding' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"transfer-encoding">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
|
||||
end);
|
||||
parse_header(Name, Req, Default) when Name =:= 'Upgrade' ->
|
||||
parse_header(Name, Req, Default) when Name =:= <<"upgrade">> ->
|
||||
parse_header(Name, Req, Default,
|
||||
fun (Value) ->
|
||||
cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
|
||||
|
@ -475,7 +476,7 @@ cookie(Name, Req) when is_binary(Name) ->
|
|||
-spec cookie(binary(), Req, Default)
|
||||
-> {binary() | true | Default, Req} when Req::req(), Default::any().
|
||||
cookie(Name, Req=#http_req{cookies=undefined}, Default) when is_binary(Name) ->
|
||||
case header('Cookie', Req) of
|
||||
case header(<<"cookie">>, Req) of
|
||||
{undefined, Req2} ->
|
||||
{Default, Req2#http_req{cookies=[]}};
|
||||
{RawCookie, Req2} ->
|
||||
|
@ -491,7 +492,7 @@ cookie(Name, Req, Default) ->
|
|||
%% @doc Return the full list of cookie values.
|
||||
-spec cookies(Req) -> {list({binary(), binary() | true}), Req} when Req::req().
|
||||
cookies(Req=#http_req{cookies=undefined}) ->
|
||||
case header('Cookie', Req) of
|
||||
case header(<<"cookie">>, Req) of
|
||||
{undefined, Req2} ->
|
||||
{[], Req2#http_req{cookies=[]}};
|
||||
{RawCookie, Req2} ->
|
||||
|
@ -532,8 +533,8 @@ set_meta(Name, Value, Req=#http_req{meta=Meta}) ->
|
|||
%% @doc Return whether the request message has a body.
|
||||
-spec has_body(Req) -> {boolean(), Req} when Req::req().
|
||||
has_body(Req) ->
|
||||
Has = lists:keymember('Content-Length', 1, Req#http_req.headers) orelse
|
||||
lists:keymember('Transfer-Encoding', 1, Req#http_req.headers),
|
||||
Has = lists:keymember(<<"content-length">>, 1, Req#http_req.headers) orelse
|
||||
lists:keymember(<<"transfer-encoding">>, 1, Req#http_req.headers),
|
||||
{Has, Req}.
|
||||
|
||||
%% @doc Return the request message body length, if known.
|
||||
|
@ -542,11 +543,11 @@ has_body(Req) ->
|
|||
%% and the body hasn't been read at the time of the call.
|
||||
-spec body_length(Req) -> {undefined | non_neg_integer(), Req} when Req::req().
|
||||
body_length(Req) ->
|
||||
case lists:keymember('Transfer-Encoding', 1, Req#http_req.headers) of
|
||||
case lists:keymember(<<"transfer-encoding">>, 1, Req#http_req.headers) of
|
||||
true ->
|
||||
{undefined, Req};
|
||||
false ->
|
||||
{ok, Length, Req2} = parse_header('Content-Length', Req, 0),
|
||||
{ok, Length, Req2} = parse_header(<<"content-length">>, Req, 0),
|
||||
{Length, Req2}
|
||||
end.
|
||||
|
||||
|
@ -586,7 +587,7 @@ init_stream(TransferDecode, TransferState, ContentDecode, Req) ->
|
|||
| {done, Req} | {error, atom()} when Req::req().
|
||||
stream_body(Req=#http_req{body_state=waiting,
|
||||
version=Version, transport=Transport, socket=Socket}) ->
|
||||
case parse_header(<<"Expect">>, Req) of
|
||||
case parse_header(<<"expect">>, Req) of
|
||||
{ok, [<<"100-continue">>], Req1} ->
|
||||
HTTPVer = cowboy_http:version_to_binary(Version),
|
||||
Transport:send(Socket,
|
||||
|
@ -594,7 +595,7 @@ stream_body(Req=#http_req{body_state=waiting,
|
|||
{ok, undefined, Req1} ->
|
||||
ok
|
||||
end,
|
||||
case parse_header('Transfer-Encoding', Req1) of
|
||||
case parse_header(<<"transfer-encoding">>, Req1) of
|
||||
{ok, [<<"chunked">>], Req2} ->
|
||||
stream_body(Req2#http_req{body_state=
|
||||
{stream, fun cowboy_http:te_chunked/2, {0, 0},
|
||||
|
@ -657,13 +658,13 @@ transfer_decode(Data, Req=#http_req{
|
|||
-> Req when Req::req().
|
||||
transfer_decode_done(Length, Rest, Req=#http_req{
|
||||
headers=Headers, p_headers=PHeaders}) ->
|
||||
Headers2 = lists:keystore('Content-Length', 1, Headers,
|
||||
{'Content-Length', list_to_binary(integer_to_list(Length))}),
|
||||
Headers2 = lists:keystore(<<"content-length">>, 1, Headers,
|
||||
{<<"content-length">>, list_to_binary(integer_to_list(Length))}),
|
||||
%% At this point we just assume TEs were all decoded.
|
||||
Headers3 = lists:keydelete('Transfer-Encoding', 1, Headers2),
|
||||
PHeaders2 = lists:keystore('Content-Length', 1, PHeaders,
|
||||
{'Content-Length', Length}),
|
||||
PHeaders3 = lists:keydelete('Transfer-Encoding', 1, PHeaders2),
|
||||
Headers3 = lists:keydelete(<<"transfer-encoding">>, 1, Headers2),
|
||||
PHeaders2 = lists:keystore(<<"content-length">>, 1, PHeaders,
|
||||
{<<"content-length">>, Length}),
|
||||
PHeaders3 = lists:keydelete(<<"transfer-encoding">>, 1, PHeaders2),
|
||||
Req#http_req{buffer=Rest, body_state=done,
|
||||
headers=Headers3, p_headers=PHeaders3}.
|
||||
|
||||
|
@ -743,9 +744,9 @@ body_qs(Req=#http_req{urldecode={URLDecFun, URLDecArg}}) ->
|
|||
| {end_of_part | eof, Req} when Req::req().
|
||||
multipart_data(Req=#http_req{body_state=waiting}) ->
|
||||
{ok, {<<"multipart">>, _SubType, Params}, Req2} =
|
||||
parse_header('Content-Type', Req),
|
||||
parse_header(<<"content-type">>, Req),
|
||||
{_, Boundary} = lists:keyfind(<<"boundary">>, 1, Params),
|
||||
{ok, Length, Req3} = parse_header('Content-Length', Req2),
|
||||
{ok, Length, Req3} = parse_header(<<"content-length">>, Req2),
|
||||
multipart_data(Req3, Length, {more, cowboy_multipart:parser(Boundary)});
|
||||
multipart_data(Req=#http_req{multipart={Length, Cont}}) ->
|
||||
multipart_data(Req, Length, Cont());
|
||||
|
@ -796,11 +797,10 @@ set_resp_cookie(Name, Value, Options, Req) ->
|
|||
set_resp_header(HeaderName, HeaderValue, Req).
|
||||
|
||||
%% @doc Add a header to the response.
|
||||
-spec set_resp_header(cowboy_http:header(), iodata(), Req)
|
||||
-spec set_resp_header(binary(), iodata(), Req)
|
||||
-> Req when Req::req().
|
||||
set_resp_header(Name, Value, Req=#http_req{resp_headers=RespHeaders}) ->
|
||||
NameBin = header_to_binary(Name),
|
||||
Req#http_req{resp_headers=[{NameBin, Value}|RespHeaders]}.
|
||||
Req#http_req{resp_headers=[{Name, Value}|RespHeaders]}.
|
||||
|
||||
%% @doc Add a body to the response.
|
||||
%%
|
||||
|
@ -831,10 +831,9 @@ set_resp_body_fun(StreamLen, StreamFun, Req) ->
|
|||
Req#http_req{resp_body={StreamLen, StreamFun}}.
|
||||
|
||||
%% @doc Return whether the given header has been set for the response.
|
||||
-spec has_resp_header(cowboy_http:header(), req()) -> boolean().
|
||||
-spec has_resp_header(binary(), req()) -> boolean().
|
||||
has_resp_header(Name, #http_req{resp_headers=RespHeaders}) ->
|
||||
NameBin = header_to_binary(Name),
|
||||
lists:keymember(NameBin, 1, RespHeaders).
|
||||
lists:keymember(Name, 1, RespHeaders).
|
||||
|
||||
%% @doc Return whether a body has been set for the response.
|
||||
-spec has_resp_body(req()) -> boolean().
|
||||
|
@ -844,7 +843,7 @@ has_resp_body(#http_req{resp_body=RespBody}) ->
|
|||
iolist_size(RespBody) > 0.
|
||||
|
||||
%% Remove a header previously set for the response.
|
||||
-spec delete_resp_header(cowboy_http:header(), Req)
|
||||
-spec delete_resp_header(binary(), Req)
|
||||
-> Req when Req::req().
|
||||
delete_resp_header(Name, Req=#http_req{resp_headers=RespHeaders}) ->
|
||||
RespHeaders2 = lists:keydelete(Name, 1, RespHeaders),
|
||||
|
@ -870,13 +869,13 @@ reply(Status, Headers, Body, Req=#http_req{socket=Socket, transport=Transport,
|
|||
RespConn = response_connection(Headers, Connection),
|
||||
ContentLen = case Body of {CL, _} -> CL; _ -> iolist_size(Body) end,
|
||||
HTTP11Headers = case Version of
|
||||
{1, 1} -> [{<<"Connection">>, atom_to_connection(Connection)}];
|
||||
{1, 1} -> [{<<"connection">>, atom_to_connection(Connection)}];
|
||||
_ -> []
|
||||
end,
|
||||
{ReplyType, Req2} = response(Status, Headers, RespHeaders, [
|
||||
{<<"Content-Length">>, integer_to_list(ContentLen)},
|
||||
{<<"Date">>, cowboy_clock:rfc1123()},
|
||||
{<<"Server">>, <<"Cowboy">>}
|
||||
{<<"content-length">>, integer_to_list(ContentLen)},
|
||||
{<<"date">>, cowboy_clock:rfc1123()},
|
||||
{<<"server">>, <<"Cowboy">>}
|
||||
|HTTP11Headers], Req),
|
||||
if Method =:= <<"HEAD">> -> ok;
|
||||
ReplyType =:= hook -> ok; %% Hook replied for us, stop there.
|
||||
|
@ -904,13 +903,13 @@ chunked_reply(Status, Headers, Req=#http_req{
|
|||
RespConn = response_connection(Headers, Connection),
|
||||
HTTP11Headers = case Version of
|
||||
{1, 1} -> [
|
||||
{<<"Connection">>, atom_to_connection(Connection)},
|
||||
{<<"Transfer-Encoding">>, <<"chunked">>}];
|
||||
{<<"connection">>, atom_to_connection(Connection)},
|
||||
{<<"transfer-encoding">>, <<"chunked">>}];
|
||||
_ -> []
|
||||
end,
|
||||
{_, Req2} = response(Status, Headers, RespHeaders, [
|
||||
{<<"Date">>, cowboy_clock:rfc1123()},
|
||||
{<<"Server">>, <<"Cowboy">>}
|
||||
{<<"date">>, cowboy_clock:rfc1123()},
|
||||
{<<"server">>, <<"Cowboy">>}
|
||||
|HTTP11Headers], Req),
|
||||
{ok, Req2#http_req{connection=RespConn, resp_state=chunks,
|
||||
resp_headers=[], resp_body= <<>>}}.
|
||||
|
@ -934,7 +933,7 @@ chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) ->
|
|||
upgrade_reply(Status, Headers, Req=#http_req{
|
||||
resp_state=waiting, resp_headers=RespHeaders}) ->
|
||||
{_, Req2} = response(Status, Headers, RespHeaders, [
|
||||
{<<"Connection">>, <<"Upgrade">>}
|
||||
{<<"connection">>, <<"Upgrade">>}
|
||||
], Req),
|
||||
{ok, Req2#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}.
|
||||
|
||||
|
@ -965,18 +964,18 @@ ensure_response(#http_req{socket=Socket, transport=Transport,
|
|||
-spec set_host(binary(), inet:port_number(), binary(), Req)
|
||||
-> Req when Req::req().
|
||||
set_host(Host, Port, RawHost, Req=#http_req{headers=Headers}) ->
|
||||
Req#http_req{host=Host, port=Port, headers=[{'Host', RawHost}|Headers]}.
|
||||
Req#http_req{host=Host, port=Port, headers=[{<<"host">>, RawHost}|Headers]}.
|
||||
|
||||
%% @private
|
||||
-spec set_connection(binary(), Req) -> Req when Req::req().
|
||||
set_connection(RawConnection, Req=#http_req{headers=Headers}) ->
|
||||
Req2 = Req#http_req{headers=[{'Connection', RawConnection}|Headers]},
|
||||
{ok, ConnTokens, Req3} = parse_header('Connection', Req2),
|
||||
Req2 = Req#http_req{headers=[{<<"connection">>, RawConnection}|Headers]},
|
||||
{ok, ConnTokens, Req3} = parse_header(<<"connection">>, Req2),
|
||||
ConnAtom = cowboy_http:connection_to_atom(ConnTokens),
|
||||
Req3#http_req{connection=ConnAtom}.
|
||||
|
||||
%% @private
|
||||
-spec add_header(cowboy_http:header(), binary(), Req)
|
||||
-spec add_header(binary(), binary(), Req)
|
||||
-> Req when Req::req().
|
||||
add_header(Name, Value, Req=#http_req{headers=Headers}) ->
|
||||
Req#http_req{headers=[{Name, Value}|Headers]}.
|
||||
|
@ -1084,14 +1083,8 @@ response_connection([], Connection) ->
|
|||
Connection;
|
||||
response_connection([{Name, Value}|Tail], Connection) ->
|
||||
case Name of
|
||||
'Connection' -> response_connection_parse(Value);
|
||||
Name when is_atom(Name) -> response_connection(Tail, Connection);
|
||||
Name ->
|
||||
Name2 = cowboy_bstr:to_lower(Name),
|
||||
case Name2 of
|
||||
<<"connection">> -> response_connection_parse(Value);
|
||||
_Any -> response_connection(Tail, Connection)
|
||||
end
|
||||
_ -> response_connection(Tail, Connection)
|
||||
end.
|
||||
|
||||
-spec response_connection_parse(binary()) -> keepalive | close.
|
||||
|
@ -1102,7 +1095,7 @@ response_connection_parse(ReplyConn) ->
|
|||
-spec response_merge_headers(cowboy_http:headers(), cowboy_http:headers(),
|
||||
cowboy_http:headers()) -> cowboy_http:headers().
|
||||
response_merge_headers(Headers, RespHeaders, DefaultHeaders) ->
|
||||
Headers2 = [{header_to_binary(Key), Value} || {Key, Value} <- Headers],
|
||||
Headers2 = [{Key, Value} || {Key, Value} <- Headers],
|
||||
merge_headers(
|
||||
merge_headers(Headers2, RespHeaders),
|
||||
DefaultHeaders).
|
||||
|
@ -1185,61 +1178,6 @@ status(510) -> <<"510 Not Extended">>;
|
|||
status(511) -> <<"511 Network Authentication Required">>;
|
||||
status(B) when is_binary(B) -> B.
|
||||
|
||||
-spec header_to_binary(cowboy_http:header()) -> binary().
|
||||
header_to_binary('Cache-Control') -> <<"Cache-Control">>;
|
||||
header_to_binary('Connection') -> <<"Connection">>;
|
||||
header_to_binary('Date') -> <<"Date">>;
|
||||
header_to_binary('Pragma') -> <<"Pragma">>;
|
||||
header_to_binary('Transfer-Encoding') -> <<"Transfer-Encoding">>;
|
||||
header_to_binary('Upgrade') -> <<"Upgrade">>;
|
||||
header_to_binary('Via') -> <<"Via">>;
|
||||
header_to_binary('Accept') -> <<"Accept">>;
|
||||
header_to_binary('Accept-Charset') -> <<"Accept-Charset">>;
|
||||
header_to_binary('Accept-Encoding') -> <<"Accept-Encoding">>;
|
||||
header_to_binary('Accept-Language') -> <<"Accept-Language">>;
|
||||
header_to_binary('Authorization') -> <<"Authorization">>;
|
||||
header_to_binary('From') -> <<"From">>;
|
||||
header_to_binary('Host') -> <<"Host">>;
|
||||
header_to_binary('If-Modified-Since') -> <<"If-Modified-Since">>;
|
||||
header_to_binary('If-Match') -> <<"If-Match">>;
|
||||
header_to_binary('If-None-Match') -> <<"If-None-Match">>;
|
||||
header_to_binary('If-Range') -> <<"If-Range">>;
|
||||
header_to_binary('If-Unmodified-Since') -> <<"If-Unmodified-Since">>;
|
||||
header_to_binary('Max-Forwards') -> <<"Max-Forwards">>;
|
||||
header_to_binary('Proxy-Authorization') -> <<"Proxy-Authorization">>;
|
||||
header_to_binary('Range') -> <<"Range">>;
|
||||
header_to_binary('Referer') -> <<"Referer">>;
|
||||
header_to_binary('User-Agent') -> <<"User-Agent">>;
|
||||
header_to_binary('Age') -> <<"Age">>;
|
||||
header_to_binary('Location') -> <<"Location">>;
|
||||
header_to_binary('Proxy-Authenticate') -> <<"Proxy-Authenticate">>;
|
||||
header_to_binary('Public') -> <<"Public">>;
|
||||
header_to_binary('Retry-After') -> <<"Retry-After">>;
|
||||
header_to_binary('Server') -> <<"Server">>;
|
||||
header_to_binary('Vary') -> <<"Vary">>;
|
||||
header_to_binary('Warning') -> <<"Warning">>;
|
||||
header_to_binary('Www-Authenticate') -> <<"Www-Authenticate">>;
|
||||
header_to_binary('Allow') -> <<"Allow">>;
|
||||
header_to_binary('Content-Base') -> <<"Content-Base">>;
|
||||
header_to_binary('Content-Encoding') -> <<"Content-Encoding">>;
|
||||
header_to_binary('Content-Language') -> <<"Content-Language">>;
|
||||
header_to_binary('Content-Length') -> <<"Content-Length">>;
|
||||
header_to_binary('Content-Location') -> <<"Content-Location">>;
|
||||
header_to_binary('Content-Md5') -> <<"Content-Md5">>;
|
||||
header_to_binary('Content-Range') -> <<"Content-Range">>;
|
||||
header_to_binary('Content-Type') -> <<"Content-Type">>;
|
||||
header_to_binary('Etag') -> <<"Etag">>;
|
||||
header_to_binary('Expires') -> <<"Expires">>;
|
||||
header_to_binary('Last-Modified') -> <<"Last-Modified">>;
|
||||
header_to_binary('Accept-Ranges') -> <<"Accept-Ranges">>;
|
||||
header_to_binary('Set-Cookie') -> <<"Set-Cookie">>;
|
||||
header_to_binary('Set-Cookie2') -> <<"Set-Cookie2">>;
|
||||
header_to_binary('X-Forwarded-For') -> <<"X-Forwarded-For">>;
|
||||
header_to_binary('Cookie') -> <<"Cookie">>;
|
||||
header_to_binary('Keep-Alive') -> <<"Keep-Alive">>;
|
||||
header_to_binary('Proxy-Connection') -> <<"Proxy-Connection">>;
|
||||
header_to_binary(B) when is_binary(B) -> B.
|
||||
|
||||
%% Tests.
|
||||
|
||||
-ifdef(TEST).
|
||||
|
|
|
@ -210,7 +210,7 @@ content_types_provided(Req, State) ->
|
|||
CTP2 = [normalize_content_types(P) || P <- CTP],
|
||||
State2 = State#state{
|
||||
handler_state=HandlerState, content_types_p=CTP2},
|
||||
{ok, Accept, Req3} = cowboy_req:parse_header('Accept', Req2),
|
||||
{ok, Accept, Req3} = cowboy_req:parse_header(<<"accept">>, Req2),
|
||||
case Accept of
|
||||
undefined ->
|
||||
{PMT, _Fun} = HeadCTP = hd(CTP2),
|
||||
|
@ -305,7 +305,7 @@ languages_provided(Req, State) ->
|
|||
{LP, Req2, HandlerState} ->
|
||||
State2 = State#state{handler_state=HandlerState, languages_p=LP},
|
||||
{ok, AcceptLanguage, Req3} =
|
||||
cowboy_req:parse_header('Accept-Language', Req2),
|
||||
cowboy_req:parse_header(<<"accept-language">>, Req2),
|
||||
case AcceptLanguage of
|
||||
undefined ->
|
||||
set_language(Req3, State2#state{language_a=hd(LP)});
|
||||
|
@ -367,7 +367,7 @@ charsets_provided(Req, State) ->
|
|||
{CP, Req2, HandlerState} ->
|
||||
State2 = State#state{handler_state=HandlerState, charsets_p=CP},
|
||||
{ok, AcceptCharset, Req3} =
|
||||
cowboy_req:parse_header('Accept-Charset', Req2),
|
||||
cowboy_req:parse_header(<<"accept-charset">>, Req2),
|
||||
case AcceptCharset of
|
||||
undefined ->
|
||||
set_content_type(Req3, State2#state{
|
||||
|
@ -479,7 +479,7 @@ resource_exists(Req, State) ->
|
|||
fun if_match_exists/2, fun if_match_musnt_exist/2).
|
||||
|
||||
if_match_exists(Req, State) ->
|
||||
case cowboy_req:parse_header('If-Match', Req) of
|
||||
case cowboy_req:parse_header(<<"if-match">>, Req) of
|
||||
{ok, undefined, Req2} ->
|
||||
if_unmodified_since_exists(Req2, State);
|
||||
{ok, '*', Req2} ->
|
||||
|
@ -497,13 +497,13 @@ if_match(Req, State, EtagsList) ->
|
|||
end.
|
||||
|
||||
if_match_musnt_exist(Req, State) ->
|
||||
case cowboy_req:header('If-Match', Req) of
|
||||
case cowboy_req:header(<<"if-match">>, Req) of
|
||||
{undefined, Req2} -> is_put_to_missing_resource(Req2, State);
|
||||
{_Any, Req2} -> precondition_failed(Req2, State)
|
||||
end.
|
||||
|
||||
if_unmodified_since_exists(Req, State) ->
|
||||
case cowboy_req:parse_header('If-Unmodified-Since', Req) of
|
||||
case cowboy_req:parse_header(<<"if-unmodified-since">>, Req) of
|
||||
{ok, undefined, Req2} ->
|
||||
if_none_match_exists(Req2, State);
|
||||
{ok, IfUnmodifiedSince, Req2} ->
|
||||
|
@ -521,7 +521,7 @@ if_unmodified_since(Req, State, IfUnmodifiedSince) ->
|
|||
end.
|
||||
|
||||
if_none_match_exists(Req, State) ->
|
||||
case cowboy_req:parse_header('If-None-Match', Req) of
|
||||
case cowboy_req:parse_header(<<"if-none-match">>, Req) of
|
||||
{ok, undefined, Req2} ->
|
||||
if_modified_since_exists(Req2, State);
|
||||
{ok, '*', Req2} ->
|
||||
|
@ -549,7 +549,7 @@ precondition_is_head_get(Req, State) ->
|
|||
precondition_failed(Req, State).
|
||||
|
||||
if_modified_since_exists(Req, State) ->
|
||||
case cowboy_req:parse_header('If-Modified-Since', Req) of
|
||||
case cowboy_req:parse_header(<<"if-modified-since">>, Req) of
|
||||
{ok, undefined, Req2} ->
|
||||
method(Req2, State);
|
||||
{ok, IfModifiedSince, Req2} ->
|
||||
|
@ -715,7 +715,7 @@ put_resource(Req, State, OnTrue) ->
|
|||
CTA2 = [normalize_content_types(P) || P <- CTA],
|
||||
State2 = State#state{handler_state=HandlerState},
|
||||
{ok, ContentType, Req3}
|
||||
= cowboy_req:parse_header('Content-Type', Req2),
|
||||
= cowboy_req:parse_header(<<"content-type">>, Req2),
|
||||
choose_content_type(Req3, State2, OnTrue, ContentType, CTA2)
|
||||
end.
|
||||
|
||||
|
|
|
@ -72,11 +72,12 @@ upgrade(ListenerPid, Handler, Opts, Req) ->
|
|||
-> {ok, #state{}, Req} when Req::cowboy_req:req().
|
||||
websocket_upgrade(State, Req) ->
|
||||
{ok, ConnTokens, Req2}
|
||||
= cowboy_req:parse_header('Connection', Req),
|
||||
= cowboy_req:parse_header(<<"connection">>, Req),
|
||||
true = lists:member(<<"upgrade">>, ConnTokens),
|
||||
%% @todo Should probably send a 426 if the Upgrade header is missing.
|
||||
{ok, [<<"websocket">>], Req3} = cowboy_req:parse_header('Upgrade', Req2),
|
||||
{Version, Req4} = cowboy_req:header(<<"Sec-Websocket-Version">>, Req3),
|
||||
{ok, [<<"websocket">>], Req3}
|
||||
= cowboy_req:parse_header(<<"upgrade">>, Req2),
|
||||
{Version, Req4} = cowboy_req:header(<<"sec-websocket-version">>, Req3),
|
||||
websocket_upgrade(Version, State, Req4).
|
||||
|
||||
%% @todo Handle the Sec-Websocket-Protocol header.
|
||||
|
@ -90,9 +91,9 @@ websocket_upgrade(State, Req) ->
|
|||
%% a reply before sending it. Therefore we calculate the challenge
|
||||
%% key only in websocket_handshake/3.
|
||||
websocket_upgrade(undefined, State, Req) ->
|
||||
{Origin, Req2} = cowboy_req:header(<<"Origin">>, Req),
|
||||
{Key1, Req3} = cowboy_req:header(<<"Sec-Websocket-Key1">>, Req2),
|
||||
{Key2, Req4} = cowboy_req:header(<<"Sec-Websocket-Key2">>, Req3),
|
||||
{Origin, Req2} = cowboy_req:header(<<"origin">>, Req),
|
||||
{Key1, Req3} = cowboy_req:header(<<"sec-websocket-key1">>, Req2),
|
||||
{Key2, Req4} = cowboy_req:header(<<"sec-websocket-key2">>, Req3),
|
||||
false = lists:member(undefined, [Origin, Key1, Key2]),
|
||||
EOP = binary:compile_pattern(<< 255 >>),
|
||||
{ok, State#state{version=0, origin=Origin, challenge={Key1, Key2},
|
||||
|
@ -101,7 +102,7 @@ websocket_upgrade(undefined, State, Req) ->
|
|||
websocket_upgrade(Version, State, Req)
|
||||
when Version =:= <<"7">>; Version =:= <<"8">>;
|
||||
Version =:= <<"13">> ->
|
||||
{Key, Req2} = cowboy_req:header(<<"Sec-Websocket-Key">>, Req),
|
||||
{Key, Req2} = cowboy_req:header(<<"sec-websocket-key">>, Req),
|
||||
false = Key =:= undefined,
|
||||
Challenge = hybi_challenge(Key),
|
||||
IntVersion = list_to_integer(binary_to_list(Version)),
|
||||
|
|
|
@ -218,11 +218,11 @@ init_dispatch(Config) ->
|
|||
{[<<"init_shutdown">>], http_handler_init_shutdown, []},
|
||||
{[<<"long_polling">>], http_handler_long_polling, []},
|
||||
{[<<"headers">>, <<"dupe">>], http_handler,
|
||||
[{headers, [{<<"Connection">>, <<"close">>}]}]},
|
||||
[{headers, [{<<"connection">>, <<"close">>}]}]},
|
||||
{[<<"set_resp">>, <<"header">>], http_handler_set_resp,
|
||||
[{headers, [{<<"Vary">>, <<"Accept">>}]}]},
|
||||
[{headers, [{<<"vary">>, <<"Accept">>}]}]},
|
||||
{[<<"set_resp">>, <<"overwrite">>], http_handler_set_resp,
|
||||
[{headers, [{<<"Server">>, <<"DesireDrive/1.0">>}]}]},
|
||||
[{headers, [{<<"server">>, <<"DesireDrive/1.0">>}]}]},
|
||||
{[<<"set_resp">>, <<"body">>], http_handler_set_resp,
|
||||
[{body, <<"A flameless dance does not equal a cycle">>}]},
|
||||
{[<<"stream_body">>, <<"set_resp">>], http_handler_stream_body,
|
||||
|
@ -599,7 +599,7 @@ onrequest_reply(Config) ->
|
|||
onrequest_hook(Req) ->
|
||||
case cowboy_req:qs_val(<<"reply">>, Req) of
|
||||
{undefined, Req2} ->
|
||||
cowboy_req:set_resp_header('Server', <<"Serenity">>, Req2);
|
||||
cowboy_req:set_resp_header(<<"server">>, <<"Serenity">>, Req2);
|
||||
{_, Req2} ->
|
||||
{ok, Req3} = cowboy_req:reply(
|
||||
200, [], <<"replied!">>, Req2),
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
init({_Transport, http}, Req, _Opts) ->
|
||||
{ok, Req2} = cowboy_req:reply(<<"666 Init Shutdown Testing">>,
|
||||
[{'Connection', <<"close">>}], Req),
|
||||
[{<<"connection">>, <<"close">>}], Req),
|
||||
{shutdown, Req2, undefined}.
|
||||
|
||||
handle(Req, State) ->
|
||||
|
|
|
@ -11,12 +11,12 @@ init({_Transport, http}, Req, Opts) ->
|
|||
cowboy_req:set_resp_header(Name, Value, R)
|
||||
end, Req, Headers),
|
||||
Req3 = cowboy_req:set_resp_body(Body, Req2),
|
||||
Req4 = cowboy_req:set_resp_header(<<"X-Cowboy-Test">>, <<"ok">>, Req3),
|
||||
Req4 = cowboy_req:set_resp_header(<<"x-cowboy-test">>, <<"ok">>, Req3),
|
||||
Req5 = cowboy_req:set_resp_cookie(<<"cake">>, <<"lie">>, [], Req4),
|
||||
{ok, Req5, undefined}.
|
||||
|
||||
handle(Req, State) ->
|
||||
case cowboy_req:has_resp_header(<<"X-Cowboy-Test">>, Req) of
|
||||
case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req) of
|
||||
false -> {ok, Req, State};
|
||||
true ->
|
||||
case cowboy_req:has_resp_body(Req) of
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue