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 request-line
First step in making all methods and header names binaries to get rid of many inconsistencies caused by decode_packet/3. Methods are all binary now. Note that since they are case sensitive, the usual methods become <<"GET">>, <<"POST">> and so on.
This commit is contained in:
parent
f6791b008a
commit
8497c8bbcd
10 changed files with 147 additions and 107 deletions
|
@ -16,9 +16,9 @@ handle(Req, State) ->
|
||||||
{ok, Req4} = echo(Method, Echo, Req3),
|
{ok, Req4} = echo(Method, Echo, Req3),
|
||||||
{ok, Req4, State}.
|
{ok, Req4, State}.
|
||||||
|
|
||||||
echo('GET', undefined, Req) ->
|
echo(<<"GET">>, undefined, Req) ->
|
||||||
cowboy_req:reply(400, [], <<"Missing echo parameter.">>, Req);
|
cowboy_req:reply(400, [], <<"Missing echo parameter.">>, Req);
|
||||||
echo('GET', Echo, Req) ->
|
echo(<<"GET">>, Echo, Req) ->
|
||||||
cowboy_req:reply(200,
|
cowboy_req:reply(200,
|
||||||
[{<<"Content-Encoding">>, <<"utf-8">>}], Echo, Req);
|
[{<<"Content-Encoding">>, <<"utf-8">>}], Echo, Req);
|
||||||
echo(_, _, Req) ->
|
echo(_, _, Req) ->
|
||||||
|
|
|
@ -16,11 +16,11 @@ handle(Req, State) ->
|
||||||
{ok, Req4} = maybe_echo(Method, HasBody, Req3),
|
{ok, Req4} = maybe_echo(Method, HasBody, Req3),
|
||||||
{ok, Req4, State}.
|
{ok, Req4, State}.
|
||||||
|
|
||||||
maybe_echo('POST', true, Req) ->
|
maybe_echo(<<"POST">>, true, Req) ->
|
||||||
{ok, PostVals, Req2} = cowboy_req:body_qs(Req),
|
{ok, PostVals, Req2} = cowboy_req:body_qs(Req),
|
||||||
Echo = proplists:get_value(<<"echo">>, PostVals),
|
Echo = proplists:get_value(<<"echo">>, PostVals),
|
||||||
echo(Echo, Req2);
|
echo(Echo, Req2);
|
||||||
maybe_echo('POST', false, Req) ->
|
maybe_echo(<<"POST">>, false, Req) ->
|
||||||
cowboy_req:reply(400, [], <<"Missing body.">>, Req);
|
cowboy_req:reply(400, [], <<"Missing body.">>, Req);
|
||||||
maybe_echo(_, _, Req) ->
|
maybe_echo(_, _, Req) ->
|
||||||
%% Method not allowed.
|
%% Method not allowed.
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
-module(cowboy_http).
|
-module(cowboy_http).
|
||||||
|
|
||||||
%% Parsing.
|
%% Parsing.
|
||||||
|
-export([request_line/1]).
|
||||||
-export([list/2]).
|
-export([list/2]).
|
||||||
-export([nonempty_list/2]).
|
-export([nonempty_list/2]).
|
||||||
-export([content_type/1]).
|
-export([content_type/1]).
|
||||||
|
@ -50,8 +51,6 @@
|
||||||
-export([urlencode/2]).
|
-export([urlencode/2]).
|
||||||
-export([x_www_form_urlencoded/2]).
|
-export([x_www_form_urlencoded/2]).
|
||||||
|
|
||||||
-type method() :: 'OPTIONS' | 'GET' | 'HEAD'
|
|
||||||
| 'POST' | 'PUT' | 'DELETE' | 'TRACE' | binary().
|
|
||||||
-type uri() :: '*' | {absoluteURI, http | https, Host::binary(),
|
-type uri() :: '*' | {absoluteURI, http | https, Host::binary(),
|
||||||
Port::integer() | undefined, Path::binary()}
|
Port::integer() | undefined, Path::binary()}
|
||||||
| {scheme, Scheme::binary(), binary()}
|
| {scheme, Scheme::binary(), binary()}
|
||||||
|
@ -73,7 +72,6 @@
|
||||||
-type headers() :: [{header(), iodata()}].
|
-type headers() :: [{header(), iodata()}].
|
||||||
-type status() :: non_neg_integer() | binary().
|
-type status() :: non_neg_integer() | binary().
|
||||||
|
|
||||||
-export_type([method/0]).
|
|
||||||
-export_type([uri/0]).
|
-export_type([uri/0]).
|
||||||
-export_type([version/0]).
|
-export_type([version/0]).
|
||||||
-export_type([header/0]).
|
-export_type([header/0]).
|
||||||
|
@ -86,6 +84,46 @@
|
||||||
|
|
||||||
%% Parsing.
|
%% Parsing.
|
||||||
|
|
||||||
|
%% @doc Parse a request-line.
|
||||||
|
-spec request_line(binary())
|
||||||
|
-> {binary(), binary(), version()} | {error, badarg}.
|
||||||
|
request_line(Data) ->
|
||||||
|
token(Data,
|
||||||
|
fun (Rest, Method) ->
|
||||||
|
whitespace(Rest,
|
||||||
|
fun (Rest2) ->
|
||||||
|
uri_to_abspath(Rest2,
|
||||||
|
fun (Rest3, AbsPath) ->
|
||||||
|
whitespace(Rest3,
|
||||||
|
fun (<< "HTTP/", Maj, ".", Min, _/binary >>)
|
||||||
|
when Maj >= $0, Maj =< $9,
|
||||||
|
Min >= $0, Min =< $9 ->
|
||||||
|
{Method, AbsPath, {Maj - $0, Min - $0}};
|
||||||
|
(_) ->
|
||||||
|
{error, badarg}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end).
|
||||||
|
|
||||||
|
%% We just want to extract the path/qs and skip everything else.
|
||||||
|
%% We do not really parse the URI, nor do we need to.
|
||||||
|
uri_to_abspath(Data, Fun) ->
|
||||||
|
case binary:split(Data, <<" ">>) of
|
||||||
|
[_] -> %% We require the HTTP version.
|
||||||
|
{error, badarg};
|
||||||
|
[URI, Rest] ->
|
||||||
|
case binary:split(URI, <<"://">>) of
|
||||||
|
[_] -> %% Already is a path or "*".
|
||||||
|
Fun(Rest, URI);
|
||||||
|
[_, NoScheme] ->
|
||||||
|
case binary:split(NoScheme, <<"/">>) of
|
||||||
|
[_] -> <<"/">>;
|
||||||
|
[_, NoHost] -> Fun(Rest, << "/", NoHost/binary >>)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
%% @doc Parse a non-empty list of the given type.
|
%% @doc Parse a non-empty list of the given type.
|
||||||
-spec nonempty_list(binary(), fun()) -> [any(), ...] | {error, badarg}.
|
-spec nonempty_list(binary(), fun()) -> [any(), ...] | {error, badarg}.
|
||||||
nonempty_list(Data, Fun) ->
|
nonempty_list(Data, Fun) ->
|
||||||
|
|
|
@ -113,19 +113,6 @@ init(ListenerPid, Socket, Transport, Opts) ->
|
||||||
timeout=Timeout, onrequest=OnRequest, onresponse=OnResponse,
|
timeout=Timeout, onrequest=OnRequest, onresponse=OnResponse,
|
||||||
urldecode=URLDec}).
|
urldecode=URLDec}).
|
||||||
|
|
||||||
%% @private
|
|
||||||
-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}) ->
|
|
||||||
case erlang:decode_packet(http_bin, Buffer, []) of
|
|
||||||
{ok, Request, Rest} -> request(Request, State#state{buffer=Rest});
|
|
||||||
{more, _Length} when byte_size(Buffer) > MaxLength ->
|
|
||||||
error_terminate(413, State);
|
|
||||||
{more, _Length} -> wait_request(State);
|
|
||||||
{error, _Reason} -> error_terminate(400, State)
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec wait_request(#state{}) -> ok.
|
-spec wait_request(#state{}) -> ok.
|
||||||
wait_request(State=#state{socket=Socket, transport=Transport,
|
wait_request(State=#state{socket=Socket, transport=Transport,
|
||||||
timeout=T, buffer=Buffer}) ->
|
timeout=T, buffer=Buffer}) ->
|
||||||
|
@ -135,48 +122,56 @@ wait_request(State=#state{socket=Socket, transport=Transport,
|
||||||
{error, _Reason} -> terminate(State)
|
{error, _Reason} -> terminate(State)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec request({http_request, cowboy_http:method(), cowboy_http:uri(),
|
%% @private
|
||||||
cowboy_http:version()}, #state{}) -> ok.
|
-spec parse_request(#state{}) -> ok.
|
||||||
request({http_request, _Method, _URI, Version}, State)
|
%% 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,
|
||||||
|
req_empty_lines=ReqEmpty, max_empty_lines=MaxEmpty}) ->
|
||||||
|
case binary:split(Buffer, <<"\r\n">>) of
|
||||||
|
[_] when byte_size(Buffer) > MaxLength ->
|
||||||
|
error_terminate(413, State);
|
||||||
|
[<< "\n", _/binary >>] ->
|
||||||
|
error_terminate(400, State);
|
||||||
|
[_] ->
|
||||||
|
wait_request(State);
|
||||||
|
[<<>>, _] when ReqEmpty =:= MaxEmpty ->
|
||||||
|
error_terminate(400, State);
|
||||||
|
[<<>>, Rest] ->
|
||||||
|
parse_request(State#state{
|
||||||
|
buffer=Rest, req_empty_lines=ReqEmpty + 1});
|
||||||
|
[RequestLine, Rest] ->
|
||||||
|
case cowboy_http:request_line(RequestLine) of
|
||||||
|
{Method, AbsPath, Version} ->
|
||||||
|
request(State#state{buffer=Rest}, Method, AbsPath, Version);
|
||||||
|
{error, _} ->
|
||||||
|
error_terminate(400, State)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec request(#state{}, binary(), binary(), cowboy_http:version()) -> ok.
|
||||||
|
request(State, _, _, Version)
|
||||||
when Version =/= {1, 0}, Version =/= {1, 1} ->
|
when Version =/= {1, 0}, Version =/= {1, 1} ->
|
||||||
error_terminate(505, State);
|
error_terminate(505, State);
|
||||||
%% We still receive the original Host header.
|
request(State=#state{socket=Socket, transport=Transport,
|
||||||
request({http_request, Method, {absoluteURI, _Scheme, _Host, _Port, Path},
|
onresponse=OnResponse, urldecode=URLDec},
|
||||||
Version}, State) ->
|
Method, <<"*">>, Version) ->
|
||||||
request({http_request, Method, {abs_path, Path}, Version}, State);
|
Connection = version_to_connection(State, Version),
|
||||||
request({http_request, Method, {abs_path, AbsPath}, Version},
|
parse_header(State#state{path_tokens= '*'},
|
||||||
State=#state{socket=Socket, transport=Transport,
|
cowboy_req:new(Socket, Transport, Connection, Method, Version,
|
||||||
req_keepalive=Keepalive, max_keepalive=MaxKeepalive,
|
<<"*">>, <<>>, OnResponse, URLDec));
|
||||||
onresponse=OnResponse, urldecode={URLDecFun, URLDecArg}=URLDec}) ->
|
request(State=#state{socket=Socket, transport=Transport,
|
||||||
URLDecode = fun(Bin) -> URLDecFun(Bin, URLDecArg) end,
|
onresponse=OnResponse, urldecode=URLDec={URLDecFun, URLDecArg}},
|
||||||
{PathTokens, RawPath, Qs}
|
Method, AbsPath, Version) ->
|
||||||
= cowboy_dispatcher:split_path(AbsPath, URLDecode),
|
Connection = version_to_connection(State, Version),
|
||||||
ConnAtom = if Keepalive < MaxKeepalive -> version_to_connection(Version);
|
{PathTokens, Path, Qs} = cowboy_dispatcher:split_path(AbsPath,
|
||||||
true -> close
|
fun(Bin) -> URLDecFun(Bin, URLDecArg) end),
|
||||||
end,
|
parse_header(State#state{path_tokens=PathTokens},
|
||||||
parse_header(cowboy_req:new(Socket, Transport, ConnAtom, Method, Version,
|
cowboy_req:new(Socket, Transport, Connection, Method, Version,
|
||||||
RawPath, Qs, OnResponse, URLDec), State#state{path_tokens=PathTokens});
|
Path, Qs, OnResponse, URLDec)).
|
||||||
request({http_request, Method, '*', Version},
|
|
||||||
State=#state{socket=Socket, transport=Transport,
|
|
||||||
req_keepalive=Keepalive, max_keepalive=MaxKeepalive,
|
|
||||||
onresponse=OnResponse, urldecode=URLDec}) ->
|
|
||||||
ConnAtom = if Keepalive < MaxKeepalive -> version_to_connection(Version);
|
|
||||||
true -> close
|
|
||||||
end,
|
|
||||||
parse_header(cowboy_req:new(Socket, Transport, ConnAtom, Method, Version,
|
|
||||||
<<"*">>, <<>>, OnResponse, URLDec), State#state{path_tokens='*'});
|
|
||||||
request({http_request, _Method, _URI, _Version}, State) ->
|
|
||||||
error_terminate(501, State);
|
|
||||||
request({http_error, <<"\r\n">>},
|
|
||||||
State=#state{req_empty_lines=N, max_empty_lines=N}) ->
|
|
||||||
error_terminate(400, State);
|
|
||||||
request({http_error, <<"\r\n">>}, State=#state{req_empty_lines=N}) ->
|
|
||||||
parse_request(State#state{req_empty_lines=N + 1});
|
|
||||||
request(_Any, State) ->
|
|
||||||
error_terminate(400, State).
|
|
||||||
|
|
||||||
-spec parse_header(cowboy_req:req(), #state{}) -> ok.
|
-spec parse_header(#state{}, cowboy_req:req()) -> ok.
|
||||||
parse_header(Req, State=#state{buffer=Buffer, max_line_length=MaxLength}) ->
|
parse_header(State=#state{buffer=Buffer, max_line_length=MaxLength}, Req) ->
|
||||||
case erlang:decode_packet(httph_bin, Buffer, []) of
|
case erlang:decode_packet(httph_bin, Buffer, []) of
|
||||||
{ok, Header, Rest} -> header(Header, Req, State#state{buffer=Rest});
|
{ok, Header, Rest} -> header(Header, Req, State#state{buffer=Rest});
|
||||||
{more, _Length} when byte_size(Buffer) > MaxLength ->
|
{more, _Length} when byte_size(Buffer) > MaxLength ->
|
||||||
|
@ -189,8 +184,8 @@ parse_header(Req, State=#state{buffer=Buffer, max_line_length=MaxLength}) ->
|
||||||
wait_header(Req, State=#state{socket=Socket,
|
wait_header(Req, State=#state{socket=Socket,
|
||||||
transport=Transport, timeout=T, buffer=Buffer}) ->
|
transport=Transport, timeout=T, buffer=Buffer}) ->
|
||||||
case Transport:recv(Socket, 0, T) of
|
case Transport:recv(Socket, 0, T) of
|
||||||
{ok, Data} -> parse_header(Req, State#state{
|
{ok, Data} -> parse_header(State#state{
|
||||||
buffer= << Buffer/binary, Data/binary >>});
|
buffer= << Buffer/binary, Data/binary >>}, Req);
|
||||||
{error, timeout} -> error_terminate(408, State);
|
{error, timeout} -> error_terminate(408, State);
|
||||||
{error, closed} -> terminate(State)
|
{error, closed} -> terminate(State)
|
||||||
end.
|
end.
|
||||||
|
@ -203,24 +198,24 @@ header({http_header, _I, 'Host', _R, RawHost}, Req,
|
||||||
case catch cowboy_dispatcher:split_host(RawHost2) of
|
case catch cowboy_dispatcher:split_host(RawHost2) of
|
||||||
{HostTokens, Host, undefined} ->
|
{HostTokens, Host, undefined} ->
|
||||||
Port = default_port(Transport:name()),
|
Port = default_port(Transport:name()),
|
||||||
parse_header(cowboy_req:set_host(Host, Port, RawHost, Req),
|
parse_header(State#state{host_tokens=HostTokens},
|
||||||
State#state{host_tokens=HostTokens});
|
cowboy_req:set_host(Host, Port, RawHost, Req));
|
||||||
{HostTokens, Host, Port} ->
|
{HostTokens, Host, Port} ->
|
||||||
parse_header(cowboy_req:set_host(Host, Port, RawHost, Req),
|
parse_header(State#state{host_tokens=HostTokens},
|
||||||
State#state{host_tokens=HostTokens});
|
cowboy_req:set_host(Host, Port, RawHost, Req));
|
||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
error_terminate(400, State)
|
error_terminate(400, State)
|
||||||
end;
|
end;
|
||||||
%% Ignore Host headers if we already have it.
|
%% Ignore Host headers if we already have it.
|
||||||
header({http_header, _I, 'Host', _R, _V}, Req, State) ->
|
header({http_header, _I, 'Host', _R, _V}, Req, State) ->
|
||||||
parse_header(Req, State);
|
parse_header(State, Req);
|
||||||
header({http_header, _I, 'Connection', _R, Connection}, Req,
|
header({http_header, _I, 'Connection', _R, Connection}, Req,
|
||||||
State=#state{req_keepalive=Keepalive, max_keepalive=MaxKeepalive})
|
State=#state{req_keepalive=Keepalive, max_keepalive=MaxKeepalive})
|
||||||
when Keepalive < MaxKeepalive ->
|
when Keepalive < MaxKeepalive ->
|
||||||
parse_header(cowboy_req:set_connection(Connection, Req), State);
|
parse_header(State, cowboy_req:set_connection(Connection, Req));
|
||||||
header({http_header, _I, Field, _R, Value}, Req, State) ->
|
header({http_header, _I, Field, _R, Value}, Req, State) ->
|
||||||
Field2 = format_header(Field),
|
Field2 = format_header(Field),
|
||||||
parse_header(cowboy_req:add_header(Field2, Value, Req), State);
|
parse_header(State, cowboy_req:add_header(Field2, Value, Req));
|
||||||
%% The Host header is required in HTTP/1.1 and optional in HTTP/1.0.
|
%% The Host header is required in HTTP/1.1 and optional in HTTP/1.0.
|
||||||
header(http_eoh, Req, State=#state{host_tokens=undefined,
|
header(http_eoh, Req, State=#state{host_tokens=undefined,
|
||||||
buffer=Buffer, transport=Transport}) ->
|
buffer=Buffer, transport=Transport}) ->
|
||||||
|
@ -431,7 +426,7 @@ error_terminate(Code, State=#state{socket=Socket, transport=Transport,
|
||||||
{cowboy_req, resp_sent} -> ok
|
{cowboy_req, resp_sent} -> ok
|
||||||
after 0 ->
|
after 0 ->
|
||||||
_ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
|
_ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
|
||||||
close, 'GET', {1, 1}, <<>>, <<>>, OnResponse, undefined)),
|
close, <<"GET">>, {1, 1}, <<>>, <<>>, OnResponse, undefined)),
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
terminate(State).
|
terminate(State).
|
||||||
|
@ -443,9 +438,15 @@ terminate(#state{socket=Socket, transport=Transport}) ->
|
||||||
|
|
||||||
%% Internal.
|
%% Internal.
|
||||||
|
|
||||||
-spec version_to_connection(cowboy_http:version()) -> keepalive | close.
|
-spec version_to_connection(#state{}, cowboy_http:version())
|
||||||
version_to_connection({1, 1}) -> keepalive;
|
-> keepalive | close.
|
||||||
version_to_connection(_Any) -> close.
|
version_to_connection(#state{req_keepalive=Keepalive,
|
||||||
|
max_keepalive=MaxKeepalive}, _) when Keepalive >= MaxKeepalive ->
|
||||||
|
close;
|
||||||
|
version_to_connection(_, {1, 1}) ->
|
||||||
|
keepalive;
|
||||||
|
version_to_connection(_, _) ->
|
||||||
|
close.
|
||||||
|
|
||||||
-spec default_port(atom()) -> 80 | 443.
|
-spec default_port(atom()) -> 80 | 443.
|
||||||
default_port(ssl) -> 443;
|
default_port(ssl) -> 443;
|
||||||
|
|
|
@ -130,7 +130,7 @@
|
||||||
|
|
||||||
%% Request.
|
%% Request.
|
||||||
pid = undefined :: pid(),
|
pid = undefined :: pid(),
|
||||||
method = 'GET' :: cowboy_http:method(),
|
method = <<"GET">> :: binary(),
|
||||||
version = {1, 1} :: cowboy_http:version(),
|
version = {1, 1} :: cowboy_http:version(),
|
||||||
peer = undefined :: undefined | {inet:ip_address(), inet:port_number()},
|
peer = undefined :: undefined | {inet:ip_address(), inet:port_number()},
|
||||||
host = undefined :: undefined | binary(),
|
host = undefined :: undefined | binary(),
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
%% This function takes care of setting the owner's pid to self().
|
%% This function takes care of setting the owner's pid to self().
|
||||||
%% @private
|
%% @private
|
||||||
-spec new(inet:socket(), module(), keepalive | close,
|
-spec new(inet:socket(), module(), keepalive | close,
|
||||||
cowboy_http:method(), cowboy_http:version(), binary(), binary(),
|
binary(), cowboy_http:version(), binary(), binary(),
|
||||||
undefined | fun(), undefined | {fun(), atom()})
|
undefined | fun(), undefined | {fun(), atom()})
|
||||||
-> req().
|
-> req().
|
||||||
new(Socket, Transport, Connection, Method, Version, Path, Qs,
|
new(Socket, Transport, Connection, Method, Version, Path, Qs,
|
||||||
|
@ -182,7 +182,7 @@ new(Socket, Transport, Connection, Method, Version, Path, Qs,
|
||||||
onresponse=OnResponse, urldecode=URLDecode}.
|
onresponse=OnResponse, urldecode=URLDecode}.
|
||||||
|
|
||||||
%% @doc Return the HTTP method of the request.
|
%% @doc Return the HTTP method of the request.
|
||||||
-spec method(Req) -> {cowboy_http:method(), Req} when Req::req().
|
-spec method(Req) -> {binary(), Req} when Req::req().
|
||||||
method(Req) ->
|
method(Req) ->
|
||||||
{Req#http_req.method, Req}.
|
{Req#http_req.method, Req}.
|
||||||
|
|
||||||
|
@ -878,7 +878,7 @@ reply(Status, Headers, Body, Req=#http_req{socket=Socket, transport=Transport,
|
||||||
{<<"Date">>, cowboy_clock:rfc1123()},
|
{<<"Date">>, cowboy_clock:rfc1123()},
|
||||||
{<<"Server">>, <<"Cowboy">>}
|
{<<"Server">>, <<"Cowboy">>}
|
||||||
|HTTP11Headers], Req),
|
|HTTP11Headers], Req),
|
||||||
if Method =:= 'HEAD' -> ok;
|
if Method =:= <<"HEAD">> -> ok;
|
||||||
ReplyType =:= hook -> ok; %% Hook replied for us, stop there.
|
ReplyType =:= hook -> ok; %% Hook replied for us, stop there.
|
||||||
true ->
|
true ->
|
||||||
case Body of
|
case Body of
|
||||||
|
@ -919,7 +919,7 @@ chunked_reply(Status, Headers, Req=#http_req{
|
||||||
%%
|
%%
|
||||||
%% A chunked reply must have been initiated before calling this function.
|
%% A chunked reply must have been initiated before calling this function.
|
||||||
-spec chunk(iodata(), req()) -> ok | {error, atom()}.
|
-spec chunk(iodata(), req()) -> ok | {error, atom()}.
|
||||||
chunk(_Data, #http_req{socket=_Socket, transport=_Transport, method='HEAD'}) ->
|
chunk(_Data, #http_req{method= <<"HEAD">>}) ->
|
||||||
ok;
|
ok;
|
||||||
chunk(Data, #http_req{socket=Socket, transport=Transport, version={1, 0}}) ->
|
chunk(Data, #http_req{socket=Socket, transport=Transport, version={1, 0}}) ->
|
||||||
Transport:send(Socket, Data);
|
Transport:send(Socket, Data);
|
||||||
|
@ -950,7 +950,7 @@ ensure_response(Req=#http_req{resp_state=waiting}, Status) ->
|
||||||
_ = reply(Status, [], [], Req),
|
_ = reply(Status, [], [], Req),
|
||||||
ok;
|
ok;
|
||||||
%% Terminate the chunked body for HTTP/1.1 only.
|
%% Terminate the chunked body for HTTP/1.1 only.
|
||||||
ensure_response(#http_req{method='HEAD', resp_state=chunks}, _) ->
|
ensure_response(#http_req{method= <<"HEAD">>, resp_state=chunks}, _) ->
|
||||||
ok;
|
ok;
|
||||||
ensure_response(#http_req{version={1, 0}, resp_state=chunks}, _) ->
|
ensure_response(#http_req{version={1, 0}, resp_state=chunks}, _) ->
|
||||||
ok;
|
ok;
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
-export([upgrade/4]).
|
-export([upgrade/4]).
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
method = undefined :: cowboy_http:method(),
|
method = undefined :: binary(),
|
||||||
|
|
||||||
%% Handler.
|
%% Handler.
|
||||||
handler :: atom(),
|
handler :: atom(),
|
||||||
|
@ -87,9 +87,10 @@ service_available(Req, State) ->
|
||||||
%% known_methods/2 should return a list of atoms or binary methods.
|
%% known_methods/2 should return a list of atoms or binary methods.
|
||||||
known_methods(Req, State=#state{method=Method}) ->
|
known_methods(Req, State=#state{method=Method}) ->
|
||||||
case call(Req, State, known_methods) of
|
case call(Req, State, known_methods) of
|
||||||
no_call when Method =:= 'HEAD'; Method =:= 'GET'; Method =:= 'POST';
|
no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>;
|
||||||
Method =:= 'PUT'; Method =:= 'DELETE'; Method =:= 'TRACE';
|
Method =:= <<"POST">>; Method =:= <<"PUT">>;
|
||||||
Method =:= 'CONNECT'; Method =:= 'OPTIONS' ->
|
Method =:= <<"DELETE">>; Method =:= <<"TRACE">>;
|
||||||
|
Method =:= <<"CONNECT">>; Method =:= <<"OPTIONS">> ->
|
||||||
next(Req, State, fun uri_too_long/2);
|
next(Req, State, fun uri_too_long/2);
|
||||||
no_call ->
|
no_call ->
|
||||||
next(Req, State, 501);
|
next(Req, State, 501);
|
||||||
|
@ -109,10 +110,10 @@ uri_too_long(Req, State) ->
|
||||||
%% allowed_methods/2 should return a list of atoms or binary methods.
|
%% allowed_methods/2 should return a list of atoms or binary methods.
|
||||||
allowed_methods(Req, State=#state{method=Method}) ->
|
allowed_methods(Req, State=#state{method=Method}) ->
|
||||||
case call(Req, State, allowed_methods) of
|
case call(Req, State, allowed_methods) of
|
||||||
no_call when Method =:= 'HEAD'; Method =:= 'GET' ->
|
no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
|
||||||
next(Req, State, fun malformed_request/2);
|
next(Req, State, fun malformed_request/2);
|
||||||
no_call ->
|
no_call ->
|
||||||
method_not_allowed(Req, State, ['GET', 'HEAD']);
|
method_not_allowed(Req, State, [<<"GET">>, <<"HEAD">>]);
|
||||||
{halt, Req2, HandlerState} ->
|
{halt, Req2, HandlerState} ->
|
||||||
terminate(Req2, State#state{handler_state=HandlerState});
|
terminate(Req2, State#state{handler_state=HandlerState});
|
||||||
{List, Req2, HandlerState} ->
|
{List, Req2, HandlerState} ->
|
||||||
|
@ -172,7 +173,7 @@ valid_entity_length(Req, State) ->
|
||||||
|
|
||||||
%% If you need to add additional headers to the response at this point,
|
%% If you need to add additional headers to the response at this point,
|
||||||
%% you should do it directly in the options/2 call using set_resp_headers.
|
%% you should do it directly in the options/2 call using set_resp_headers.
|
||||||
options(Req, State=#state{method='OPTIONS'}) ->
|
options(Req, State=#state{method= <<"OPTIONS">>}) ->
|
||||||
case call(Req, State, options) of
|
case call(Req, State, options) of
|
||||||
{halt, Req2, HandlerState} ->
|
{halt, Req2, HandlerState} ->
|
||||||
terminate(Req2, State#state{handler_state=HandlerState});
|
terminate(Req2, State#state{handler_state=HandlerState});
|
||||||
|
@ -542,7 +543,7 @@ if_none_match(Req, State, EtagsList) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
precondition_is_head_get(Req, State=#state{method=Method})
|
precondition_is_head_get(Req, State=#state{method=Method})
|
||||||
when Method =:= 'HEAD'; Method =:= 'GET' ->
|
when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
|
||||||
not_modified(Req, State);
|
not_modified(Req, State);
|
||||||
precondition_is_head_get(Req, State) ->
|
precondition_is_head_get(Req, State) ->
|
||||||
precondition_failed(Req, State).
|
precondition_failed(Req, State).
|
||||||
|
@ -584,7 +585,7 @@ not_modified(Req, State) ->
|
||||||
precondition_failed(Req, State) ->
|
precondition_failed(Req, State) ->
|
||||||
respond(Req, State, 412).
|
respond(Req, State, 412).
|
||||||
|
|
||||||
is_put_to_missing_resource(Req, State=#state{method='PUT'}) ->
|
is_put_to_missing_resource(Req, State=#state{method= <<"PUT">>}) ->
|
||||||
moved_permanently(Req, State, fun is_conflict/2);
|
moved_permanently(Req, State, fun is_conflict/2);
|
||||||
is_put_to_missing_resource(Req, State) ->
|
is_put_to_missing_resource(Req, State) ->
|
||||||
previously_existed(Req, State).
|
previously_existed(Req, State).
|
||||||
|
@ -626,7 +627,7 @@ moved_temporarily(Req, State) ->
|
||||||
is_post_to_missing_resource(Req, State, 410)
|
is_post_to_missing_resource(Req, State, 410)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
is_post_to_missing_resource(Req, State=#state{method='POST'}, OnFalse) ->
|
is_post_to_missing_resource(Req, State=#state{method= <<"POST">>}, OnFalse) ->
|
||||||
allow_missing_post(Req, State, OnFalse);
|
allow_missing_post(Req, State, OnFalse);
|
||||||
is_post_to_missing_resource(Req, State, OnFalse) ->
|
is_post_to_missing_resource(Req, State, OnFalse) ->
|
||||||
respond(Req, State, OnFalse).
|
respond(Req, State, OnFalse).
|
||||||
|
@ -634,14 +635,14 @@ is_post_to_missing_resource(Req, State, OnFalse) ->
|
||||||
allow_missing_post(Req, State, OnFalse) ->
|
allow_missing_post(Req, State, OnFalse) ->
|
||||||
expect(Req, State, allow_missing_post, true, fun post_is_create/2, OnFalse).
|
expect(Req, State, allow_missing_post, true, fun post_is_create/2, OnFalse).
|
||||||
|
|
||||||
method(Req, State=#state{method='DELETE'}) ->
|
method(Req, State=#state{method= <<"DELETE">>}) ->
|
||||||
delete_resource(Req, State);
|
delete_resource(Req, State);
|
||||||
method(Req, State=#state{method='POST'}) ->
|
method(Req, State=#state{method= <<"POST">>}) ->
|
||||||
post_is_create(Req, State);
|
post_is_create(Req, State);
|
||||||
method(Req, State=#state{method='PUT'}) ->
|
method(Req, State=#state{method= <<"PUT">>}) ->
|
||||||
is_conflict(Req, State);
|
is_conflict(Req, State);
|
||||||
method(Req, State=#state{method=Method})
|
method(Req, State=#state{method=Method})
|
||||||
when Method =:= 'GET'; Method =:= 'HEAD' ->
|
when Method =:= <<"GET">>; Method =:= <<"HEAD">> ->
|
||||||
set_resp_body(Req, State);
|
set_resp_body(Req, State);
|
||||||
method(Req, State) ->
|
method(Req, State) ->
|
||||||
multiple_choices(Req, State).
|
multiple_choices(Req, State).
|
||||||
|
|
|
@ -247,9 +247,9 @@ rest_init(Req, Opts) ->
|
||||||
|
|
||||||
%% @private Only allow GET and HEAD requests on files.
|
%% @private Only allow GET and HEAD requests on files.
|
||||||
-spec allowed_methods(Req, #state{})
|
-spec allowed_methods(Req, #state{})
|
||||||
-> {[atom()], Req, #state{}} when Req::cowboy_req:req().
|
-> {[binary()], Req, #state{}} when Req::cowboy_req:req().
|
||||||
allowed_methods(Req, State) ->
|
allowed_methods(Req, State) ->
|
||||||
{['GET', 'HEAD'], Req, State}.
|
{[<<"GET">>, <<"HEAD">>], Req, State}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
-spec malformed_request(Req, #state{})
|
-spec malformed_request(Req, #state{})
|
||||||
|
|
|
@ -327,17 +327,17 @@ check_raw_status(Config) ->
|
||||||
HugeCookie = lists:flatten(["whatever_man_biiiiiiiiiiiig_cookie_me_want_77="
|
HugeCookie = lists:flatten(["whatever_man_biiiiiiiiiiiig_cookie_me_want_77="
|
||||||
"Wed Apr 06 2011 10:38:52 GMT-0500 (CDT)" || _ <- lists:seq(1, 40)]),
|
"Wed Apr 06 2011 10:38:52 GMT-0500 (CDT)" || _ <- lists:seq(1, 40)]),
|
||||||
ResponsePacket =
|
ResponsePacket =
|
||||||
"HTTP/1.0 302 Found
|
"HTTP/1.0 302 Found\r
|
||||||
Location: http://www.google.co.il/
|
Location: http://www.google.co.il/\r
|
||||||
Cache-Control: private
|
Cache-Control: private\r
|
||||||
Content-Type: text/html; charset=UTF-8
|
Content-Type: text/html; charset=UTF-8\r
|
||||||
Set-Cookie: PREF=ID=568f67013d4a7afa:FF=0:TM=1323014101:LM=1323014101:S=XqctDWC65MzKT0zC; expires=Tue, 03-Dec-2013 15:55:01 GMT; path=/; domain=.google.com
|
Set-Cookie: PREF=ID=568f67013d4a7afa:FF=0:TM=1323014101:LM=1323014101:S=XqctDWC65MzKT0zC; expires=Tue, 03-Dec-2013 15:55:01 GMT; path=/; domain=.google.com\r
|
||||||
Date: Sun, 04 Dec 2011 15:55:01 GMT
|
Date: Sun, 04 Dec 2011 15:55:01 GMT\r
|
||||||
Server: gws
|
Server: gws\r
|
||||||
Content-Length: 221
|
Content-Length: 221\r
|
||||||
X-XSS-Protection: 1; mode=block
|
X-XSS-Protection: 1; mode=block\r
|
||||||
X-Frame-Options: SAMEORIGIN
|
X-Frame-Options: SAMEORIGIN\r
|
||||||
|
\r
|
||||||
<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">
|
<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">
|
||||||
<TITLE>302 Moved</TITLE></HEAD><BODY>
|
<TITLE>302 Moved</TITLE></HEAD><BODY>
|
||||||
<H1>302 Moved</H1>
|
<H1>302 Moved</H1>
|
||||||
|
|
|
@ -10,7 +10,7 @@ rest_init(Req, [Forbidden]) ->
|
||||||
{ok, Req, Forbidden}.
|
{ok, Req, Forbidden}.
|
||||||
|
|
||||||
allowed_methods(Req, State) ->
|
allowed_methods(Req, State) ->
|
||||||
{['GET', 'HEAD', 'POST'], Req, State}.
|
{[<<"GET">>, <<"HEAD">>, <<"POST">>], Req, State}.
|
||||||
|
|
||||||
forbidden(Req, State=true) ->
|
forbidden(Req, State=true) ->
|
||||||
{true, Req, State};
|
{true, Req, State};
|
||||||
|
|
|
@ -6,7 +6,7 @@ init(_Transport, _Req, _Opts) ->
|
||||||
{upgrade, protocol, cowboy_rest}.
|
{upgrade, protocol, cowboy_rest}.
|
||||||
|
|
||||||
allowed_methods(Req, State) ->
|
allowed_methods(Req, State) ->
|
||||||
{['GET', 'HEAD', 'DELETE'], Req, State}.
|
{[<<"GET">>, <<"HEAD">>, <<"DELETE">>], Req, State}.
|
||||||
|
|
||||||
|
|
||||||
content_types_provided(Req, State) ->
|
content_types_provided(Req, State) ->
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue