diff options
Diffstat (limited to 'src/cowboy_http_req.erl')
-rw-r--r-- | src/cowboy_http_req.erl | 201 |
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]. |