aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_protocol.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2012-09-20 06:22:51 +0200
committerLoïc Hoguin <[email protected]>2012-09-21 08:54:57 +0200
commit8497c8bbcdcfd8754c500e65557ee09d9bd1bed0 (patch)
tree76abb26e328b916addb95db4778f0666a1a7d4fb /src/cowboy_protocol.erl
parentf6791b008ac8ccaa331bfbae9afb69af5bf36a7a (diff)
downloadcowboy-8497c8bbcdcfd8754c500e65557ee09d9bd1bed0.tar.gz
cowboy-8497c8bbcdcfd8754c500e65557ee09d9bd1bed0.tar.bz2
cowboy-8497c8bbcdcfd8754c500e65557ee09d9bd1bed0.zip
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.
Diffstat (limited to 'src/cowboy_protocol.erl')
-rw-r--r--src/cowboy_protocol.erl133
1 files changed, 67 insertions, 66 deletions
diff --git a/src/cowboy_protocol.erl b/src/cowboy_protocol.erl
index 2e734c5..4caa00b 100644
--- a/src/cowboy_protocol.erl
+++ b/src/cowboy_protocol.erl
@@ -113,19 +113,6 @@ init(ListenerPid, Socket, Transport, Opts) ->
timeout=Timeout, onrequest=OnRequest, onresponse=OnResponse,
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.
wait_request(State=#state{socket=Socket, transport=Transport,
timeout=T, buffer=Buffer}) ->
@@ -135,48 +122,56 @@ wait_request(State=#state{socket=Socket, transport=Transport,
{error, _Reason} -> terminate(State)
end.
--spec request({http_request, cowboy_http:method(), cowboy_http:uri(),
- cowboy_http:version()}, #state{}) -> ok.
-request({http_request, _Method, _URI, Version}, State)
+%% @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,
+ 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} ->
error_terminate(505, State);
-%% We still receive the original Host header.
-request({http_request, Method, {absoluteURI, _Scheme, _Host, _Port, Path},
- Version}, State) ->
- request({http_request, Method, {abs_path, Path}, Version}, State);
-request({http_request, Method, {abs_path, AbsPath}, Version},
- State=#state{socket=Socket, transport=Transport,
- req_keepalive=Keepalive, max_keepalive=MaxKeepalive,
- onresponse=OnResponse, urldecode={URLDecFun, URLDecArg}=URLDec}) ->
- URLDecode = fun(Bin) -> URLDecFun(Bin, URLDecArg) end,
- {PathTokens, RawPath, Qs}
- = cowboy_dispatcher:split_path(AbsPath, URLDecode),
- ConnAtom = if Keepalive < MaxKeepalive -> version_to_connection(Version);
- true -> close
- end,
- parse_header(cowboy_req:new(Socket, Transport, ConnAtom, Method, Version,
- RawPath, Qs, OnResponse, URLDec), State#state{path_tokens=PathTokens});
-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.
-parse_header(Req, State=#state{buffer=Buffer, max_line_length=MaxLength}) ->
+request(State=#state{socket=Socket, transport=Transport,
+ onresponse=OnResponse, urldecode=URLDec},
+ Method, <<"*">>, Version) ->
+ Connection = version_to_connection(State, Version),
+ parse_header(State#state{path_tokens= '*'},
+ cowboy_req:new(Socket, Transport, Connection, Method, Version,
+ <<"*">>, <<>>, OnResponse, URLDec));
+request(State=#state{socket=Socket, transport=Transport,
+ onresponse=OnResponse, urldecode=URLDec={URLDecFun, URLDecArg}},
+ Method, AbsPath, Version) ->
+ Connection = version_to_connection(State, Version),
+ {PathTokens, Path, Qs} = cowboy_dispatcher:split_path(AbsPath,
+ fun(Bin) -> URLDecFun(Bin, URLDecArg) end),
+ parse_header(State#state{path_tokens=PathTokens},
+ cowboy_req:new(Socket, Transport, Connection, Method, Version,
+ 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 ->
@@ -189,8 +184,8 @@ parse_header(Req, State=#state{buffer=Buffer, max_line_length=MaxLength}) ->
wait_header(Req, State=#state{socket=Socket,
transport=Transport, timeout=T, buffer=Buffer}) ->
case Transport:recv(Socket, 0, T) of
- {ok, Data} -> parse_header(Req, State#state{
- buffer= << Buffer/binary, Data/binary >>});
+ {ok, Data} -> parse_header(State#state{
+ buffer= << Buffer/binary, Data/binary >>}, Req);
{error, timeout} -> error_terminate(408, State);
{error, closed} -> terminate(State)
end.
@@ -203,24 +198,24 @@ header({http_header, _I, 'Host', _R, RawHost}, Req,
case catch cowboy_dispatcher:split_host(RawHost2) of
{HostTokens, Host, undefined} ->
Port = default_port(Transport:name()),
- parse_header(cowboy_req:set_host(Host, Port, RawHost, Req),
- State#state{host_tokens=HostTokens});
+ parse_header(State#state{host_tokens=HostTokens},
+ cowboy_req:set_host(Host, Port, RawHost, Req));
{HostTokens, Host, Port} ->
- parse_header(cowboy_req:set_host(Host, Port, RawHost, Req),
- State#state{host_tokens=HostTokens});
+ parse_header(State#state{host_tokens=HostTokens},
+ cowboy_req:set_host(Host, Port, RawHost, Req));
{'EXIT', _Reason} ->
error_terminate(400, State)
end;
%% Ignore Host headers if we already have it.
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,
State=#state{req_keepalive=Keepalive, max_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) ->
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.
header(http_eoh, Req, State=#state{host_tokens=undefined,
buffer=Buffer, transport=Transport}) ->
@@ -431,7 +426,7 @@ error_terminate(Code, State=#state{socket=Socket, transport=Transport,
{cowboy_req, resp_sent} -> ok
after 0 ->
_ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
- close, 'GET', {1, 1}, <<>>, <<>>, OnResponse, undefined)),
+ close, <<"GET">>, {1, 1}, <<>>, <<>>, OnResponse, undefined)),
ok
end,
terminate(State).
@@ -443,9 +438,15 @@ terminate(#state{socket=Socket, transport=Transport}) ->
%% Internal.
--spec version_to_connection(cowboy_http:version()) -> keepalive | close.
-version_to_connection({1, 1}) -> keepalive;
-version_to_connection(_Any) -> close.
+-spec version_to_connection(#state{}, cowboy_http:version())
+ -> keepalive | 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.
default_port(ssl) -> 443;