aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_http_req.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/cowboy_http_req.erl')
-rw-r--r--src/cowboy_http_req.erl201
1 files changed, 101 insertions, 100 deletions
diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl
index f0fc15c..e2239eb 100644
--- a/src/cowboy_http_req.erl
+++ b/src/cowboy_http_req.erl
@@ -59,7 +59,7 @@ peer(Req) ->
host(Req) ->
{Req#http_req.host, Req}.
--spec raw_host(Req::#http_req{}) -> {RawHost::string(), Req::#http_req{}}.
+-spec raw_host(Req::#http_req{}) -> {RawHost::binary(), Req::#http_req{}}.
raw_host(Req) ->
{Req#http_req.raw_host, Req}.
@@ -72,18 +72,18 @@ port(Req) ->
path(Req) ->
{Req#http_req.path, Req}.
--spec raw_path(Req::#http_req{}) -> {RawPath::string(), Req::#http_req{}}.
+-spec raw_path(Req::#http_req{}) -> {RawPath::binary(), Req::#http_req{}}.
raw_path(Req) ->
{Req#http_req.raw_path, Req}.
--spec qs_val(Name::string(), Req::#http_req{})
- -> {Value::string() | true | undefined, Req::#http_req{}}.
+-spec qs_val(Name::binary(), Req::#http_req{})
+ -> {Value::binary() | true | undefined, Req::#http_req{}}.
%% @equiv qs_val(Name, Req) -> qs_val(Name, Req, undefined)
qs_val(Name, Req) ->
qs_val(Name, Req, undefined).
--spec qs_val(Name::string(), Req::#http_req{}, Default)
- -> {Value::string() | true | Default, Req::#http_req{}}
+-spec qs_val(Name::binary(), Req::#http_req{}, Default)
+ -> {Value::binary() | true | Default, Req::#http_req{}}
when Default::term().
qs_val(Name, Req=#http_req{raw_qs=RawQs, qs_vals=undefined}, Default) ->
QsVals = parse_qs(RawQs),
@@ -95,25 +95,25 @@ qs_val(Name, Req, Default) ->
end.
-spec qs_vals(Req::#http_req{})
- -> {list({Name::string(), Value::string() | true}), Req::#http_req{}}.
+ -> {list({Name::binary(), Value::binary() | true}), Req::#http_req{}}.
qs_vals(Req=#http_req{raw_qs=RawQs, qs_vals=undefined}) ->
QsVals = parse_qs(RawQs),
qs_vals(Req#http_req{qs_vals=QsVals});
qs_vals(Req=#http_req{qs_vals=QsVals}) ->
{QsVals, Req}.
--spec raw_qs(Req::#http_req{}) -> {RawQs::string(), Req::#http_req{}}.
+-spec raw_qs(Req::#http_req{}) -> {RawQs::binary(), Req::#http_req{}}.
raw_qs(Req) ->
{Req#http_req.raw_qs, Req}.
-spec binding(Name::atom(), Req::#http_req{})
- -> {Value::string() | undefined, Req::#http_req{}}.
+ -> {Value::binary() | undefined, Req::#http_req{}}.
%% @equiv binding(Name, Req) -> binding(Name, Req, undefined)
binding(Name, Req) ->
binding(Name, Req, undefined).
-spec binding(Name::atom(), Req::#http_req{}, Default)
- -> {Value::string() | Default, Req::#http_req{}} when Default::term().
+ -> {Value::binary() | Default, Req::#http_req{}} when Default::term().
binding(Name, Req, Default) ->
case lists:keyfind(Name, 1, Req#http_req.bindings) of
{Name, Value} -> {Value, Req};
@@ -121,18 +121,18 @@ binding(Name, Req, Default) ->
end.
-spec bindings(Req::#http_req{})
- -> {list({Name::atom(), Value::string()}), Req::#http_req{}}.
+ -> {list({Name::atom(), Value::binary()}), Req::#http_req{}}.
bindings(Req) ->
{Req#http_req.bindings, Req}.
--spec header(Name::atom() | string(), Req::#http_req{})
- -> {Value::string() | undefined, Req::#http_req{}}.
+-spec header(Name::atom() | binary(), Req::#http_req{})
+ -> {Value::binary() | undefined, Req::#http_req{}}.
%% @equiv header(Name, Req) -> header(Name, Req, undefined)
header(Name, Req) ->
header(Name, Req, undefined).
--spec header(Name::atom() | string(), Req::#http_req{}, Default)
- -> {Value::string() | Default, Req::#http_req{}} when Default::term().
+-spec header(Name::atom() | binary(), Req::#http_req{}, Default)
+ -> {Value::binary() | Default, Req::#http_req{}} when Default::term().
header(Name, Req, Default) ->
case lists:keyfind(Name, 1, Req#http_req.headers) of
{Name, Value} -> {Value, Req};
@@ -154,26 +154,28 @@ body(Req) ->
case Length of
undefined -> {error, badarg};
_Any ->
- Length2 = list_to_integer(Length),
+ Length2 = list_to_integer(binary_to_list(Length)),
body(Length2, Req2)
end.
%% @todo We probably want to configure the timeout.
-spec body(Length::non_neg_integer(), Req::#http_req{})
-> {ok, Body::binary(), Req::#http_req{}} | {error, Reason::atom()}.
+body(Length, Req=#http_req{body_state=waiting, buffer=Buffer})
+ when Length =:= byte_size(Buffer) ->
+ {ok, Buffer, Req#http_req{body_state=done, buffer= <<>>}};
body(Length, Req=#http_req{socket=Socket, transport=Transport,
- body_state=waiting}) ->
- Transport:setopts(Socket, [{packet, raw}]),
- case Transport:recv(Socket, Length, 5000) of
- {ok, Body} -> {ok, Body, Req#http_req{body_state=done}};
+ body_state=waiting, buffer=Buffer}) when Length > byte_size(Buffer) ->
+ case Transport:recv(Socket, Length - byte_size(Buffer), 5000) of
+ {ok, Body} -> {ok, << Buffer/binary, Body/binary >>, Req#http_req{body_state=done, buffer= <<>>}};
{error, Reason} -> {error, Reason}
end.
-spec body_qs(Req::#http_req{})
- -> {list({Name::string(), Value::string() | true}), Req::#http_req{}}.
+ -> {list({Name::binary(), Value::binary() | true}), Req::#http_req{}}.
body_qs(Req) ->
{ok, Body, Req2} = body(Req),
- {parse_qs(binary_to_list(Body)), Req2}.
+ {parse_qs(Body), Req2}.
%% Response API.
@@ -182,91 +184,90 @@ body_qs(Req) ->
reply(Code, Headers, Body, Req=#http_req{socket=Socket,
transport=Transport, connection=Connection,
resp_state=waiting}) ->
- StatusLine = ["HTTP/1.1 ", status(Code), "\r\n"],
+ StatusLine = <<"HTTP/1.1 ", (status(Code))/binary, "\r\n">>,
DefaultHeaders = [
- {"Connection", atom_to_connection(Connection)},
- {"Content-Length", integer_to_list(iolist_size(Body))}
+ {<<"Connection">>, atom_to_connection(Connection)},
+ {<<"Content-Length">>, list_to_binary(integer_to_list(iolist_size(Body)))}
],
Headers2 = lists:keysort(1, Headers),
Headers3 = lists:ukeymerge(1, Headers2, DefaultHeaders),
- Headers4 = [[Key, ": ", Value, "\r\n"] || {Key, Value} <- Headers3],
- Transport:send(Socket, [StatusLine, Headers4, "\r\n", Body]),
+ Headers4 = [<< Key/binary, ": ", Value/binary, "\r\n">> || {Key, Value} <- Headers3],
+ Transport:send(Socket, [StatusLine, Headers4, <<"\r\n">>, Body]),
{ok, Req#http_req{resp_state=done}}.
%% Internal.
--spec parse_qs(Qs::string()) -> list({Name::string(), Value::string() | true}).
+-spec parse_qs(Qs::binary()) -> list({Name::binary(), Value::binary() | true}).
+parse_qs(<<>>) ->
+ [];
parse_qs(Qs) ->
- Tokens = string:tokens(Qs, "&"),
- [case string:chr(Token, $=) of
- 0 ->
- {Token, true};
- N ->
- {Name, [$=|Value]} = lists:split(N - 1, Token),
- {Name, Value}
+ Tokens = binary:split(Qs, <<"&">>, [global, trim]),
+ [case binary:split(Token, <<"=">>) of
+ [Token] -> {Token, true};
+ [Name, Value] -> {Name, Value}
end || Token <- Tokens].
--spec atom_to_connection(Atom::keepalive | close) -> string().
+-spec atom_to_connection(Atom::keepalive | close) -> binary().
atom_to_connection(keepalive) ->
- "keep-alive";
+ <<"keep-alive">>;
atom_to_connection(close) ->
- "close".
-
--spec status(Code::http_status()) -> string().
-status(100) -> "100 Continue";
-status(101) -> "101 Switching Protocols";
-status(102) -> "102 Processing";
-status(200) -> "200 OK";
-status(201) -> "201 Created";
-status(202) -> "202 Accepted";
-status(203) -> "203 Non-Authoritative Information";
-status(204) -> "204 No Content";
-status(205) -> "205 Reset Content";
-status(206) -> "206 Partial Content";
-status(207) -> "207 Multi-Status";
-status(226) -> "226 IM Used";
-status(300) -> "300 Multiple Choices";
-status(301) -> "301 Moved Permanently";
-status(302) -> "302 Found";
-status(303) -> "303 See Other";
-status(304) -> "304 Not Modified";
-status(305) -> "305 Use Proxy";
-status(306) -> "306 Switch Proxy";
-status(307) -> "307 Temporary Redirect";
-status(400) -> "400 Bad Request";
-status(401) -> "401 Unauthorized";
-status(402) -> "402 Payment Required";
-status(403) -> "403 Forbidden";
-status(404) -> "404 Not Found";
-status(405) -> "405 Method Not Allowed";
-status(406) -> "406 Not Acceptable";
-status(407) -> "407 Proxy Authentication Required";
-status(408) -> "408 Request Timeout";
-status(409) -> "409 Conflict";
-status(410) -> "410 Gone";
-status(411) -> "411 Length Required";
-status(412) -> "412 Precondition Failed";
-status(413) -> "413 Request Entity Too Large";
-status(414) -> "414 Request-URI Too Long";
-status(415) -> "415 Unsupported Media Type";
-status(416) -> "416 Requested Range Not Satisfiable";
-status(417) -> "417 Expectation Failed";
-status(418) -> "418 I'm a teapot";
-status(422) -> "422 Unprocessable Entity";
-status(423) -> "423 Locked";
-status(424) -> "424 Failed Dependency";
-status(425) -> "425 Unordered Collection";
-status(426) -> "426 Upgrade Required";
-status(500) -> "500 Internal Server Error";
-status(501) -> "501 Not Implemented";
-status(502) -> "502 Bad Gateway";
-status(503) -> "503 Service Unavailable";
-status(504) -> "504 Gateway Timeout";
-status(505) -> "505 HTTP Version Not Supported";
-status(506) -> "506 Variant Also Negotiates";
-status(507) -> "507 Insufficient Storage";
-status(510) -> "510 Not Extended";
-status(L) when is_list(L) -> L.
+ <<"close">>.
+
+-spec status(Code::http_status()) -> binary().
+status(100) -> <<"100 Continue">>;
+status(101) -> <<"101 Switching Protocols">>;
+status(102) -> <<"102 Processing">>;
+status(200) -> <<"200 OK">>;
+status(201) -> <<"201 Created">>;
+status(202) -> <<"202 Accepted">>;
+status(203) -> <<"203 Non-Authoritative Information">>;
+status(204) -> <<"204 No Content">>;
+status(205) -> <<"205 Reset Content">>;
+status(206) -> <<"206 Partial Content">>;
+status(207) -> <<"207 Multi-Status">>;
+status(226) -> <<"226 IM Used">>;
+status(300) -> <<"300 Multiple Choices">>;
+status(301) -> <<"301 Moved Permanently">>;
+status(302) -> <<"302 Found">>;
+status(303) -> <<"303 See Other">>;
+status(304) -> <<"304 Not Modified">>;
+status(305) -> <<"305 Use Proxy">>;
+status(306) -> <<"306 Switch Proxy">>;
+status(307) -> <<"307 Temporary Redirect">>;
+status(400) -> <<"400 Bad Request">>;
+status(401) -> <<"401 Unauthorized">>;
+status(402) -> <<"402 Payment Required">>;
+status(403) -> <<"403 Forbidden">>;
+status(404) -> <<"404 Not Found">>;
+status(405) -> <<"405 Method Not Allowed">>;
+status(406) -> <<"406 Not Acceptable">>;
+status(407) -> <<"407 Proxy Authentication Required">>;
+status(408) -> <<"408 Request Timeout">>;
+status(409) -> <<"409 Conflict">>;
+status(410) -> <<"410 Gone">>;
+status(411) -> <<"411 Length Required">>;
+status(412) -> <<"412 Precondition Failed">>;
+status(413) -> <<"413 Request Entity Too Large">>;
+status(414) -> <<"414 Request-URI Too Long">>;
+status(415) -> <<"415 Unsupported Media Type">>;
+status(416) -> <<"416 Requested Range Not Satisfiable">>;
+status(417) -> <<"417 Expectation Failed">>;
+status(418) -> <<"418 I'm a teapot">>;
+status(422) -> <<"422 Unprocessable Entity">>;
+status(423) -> <<"423 Locked">>;
+status(424) -> <<"424 Failed Dependency">>;
+status(425) -> <<"425 Unordered Collection">>;
+status(426) -> <<"426 Upgrade Required">>;
+status(500) -> <<"500 Internal Server Error">>;
+status(501) -> <<"501 Not Implemented">>;
+status(502) -> <<"502 Bad Gateway">>;
+status(503) -> <<"503 Service Unavailable">>;
+status(504) -> <<"504 Gateway Timeout">>;
+status(505) -> <<"505 HTTP Version Not Supported">>;
+status(506) -> <<"506 Variant Also Negotiates">>;
+status(507) -> <<"507 Insufficient Storage">>;
+status(510) -> <<"510 Not Extended">>;
+status(B) when is_binary(B) -> B.
%% Tests.
@@ -275,12 +276,12 @@ status(L) when is_list(L) -> L.
parse_qs_test_() ->
%% {Qs, Result}
Tests = [
- {"", []},
- {"a=b", [{"a", "b"}]},
- {"aaa=bbb", [{"aaa", "bbb"}]},
- {"a&b", [{"a", true}, {"b", true}]},
- {"a=b&c&d=e", [{"a", "b"}, {"c", true}, {"d", "e"}]},
- {"a=b=c=d=e&f=g", [{"a", "b=c=d=e"}, {"f", "g"}]}
+ {<<"">>, []},
+ {<<"a=b">>, [{<<"a">>, <<"b">>}]},
+ {<<"aaa=bbb">>, [{<<"aaa">>, <<"bbb">>}]},
+ {<<"a&b">>, [{<<"a">>, true}, {<<"b">>, true}]},
+ {<<"a=b&c&d=e">>, [{<<"a">>, <<"b">>}, {<<"c">>, true}, {<<"d">>, <<"e">>}]},
+ {<<"a=b=c=d=e&f=g">>, [{<<"a">>, <<"b=c=d=e">>}, {<<"f">>, <<"g">>}]}
],
[{Qs, fun() -> R = parse_qs(Qs) end} || {Qs, R} <- Tests].