aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_http.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/cowboy_http.erl')
-rw-r--r--src/cowboy_http.erl250
1 files changed, 3 insertions, 247 deletions
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl
index 30866de..754e74a 100644
--- a/src/cowboy_http.erl
+++ b/src/cowboy_http.erl
@@ -13,7 +13,7 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-%% @doc Core HTTP parsing API.
+%% Deprecated HTTP parsing API.
-module(cowboy_http).
%% Parsing.
@@ -40,19 +40,10 @@
-export([parameterized_tokens/1]).
%% Decoding.
--export([te_chunked/2]).
--export([te_identity/2]).
-export([ce_identity/1]).
-%% Interpretation.
--export([urldecode/1]).
--export([urldecode/2]).
--export([urlencode/1]).
--export([urlencode/2]).
-
%% Parsing.
-%% @doc Parse a non-empty list of the given type.
-spec nonempty_list(binary(), fun()) -> [any(), ...] | {error, badarg}.
nonempty_list(Data, Fun) ->
case list(Data, Fun, []) of
@@ -61,7 +52,6 @@ nonempty_list(Data, Fun) ->
L -> lists:reverse(L)
end.
-%% @doc Parse a list of the given type.
-spec list(binary(), fun()) -> list() | {error, badarg}.
list(Data, Fun) ->
case list(Data, Fun, []) of
@@ -89,8 +79,6 @@ list(Data, Fun, Acc) ->
end)
end).
-%% @doc Parse a content type.
-%%
%% We lowercase the charset header as we know it's case insensitive.
-spec content_type(binary()) -> any().
content_type(Data) ->
@@ -112,7 +100,6 @@ content_type(Data) ->
end)
end).
-%% @doc Parse a media range.
-spec media_range(binary(), fun()) -> any().
media_range(Data, Fun) ->
media_type(Data,
@@ -155,7 +142,6 @@ media_range_param_value(Data, Fun, Type, SubType, Acc, Attr) ->
Type, SubType, [{Attr, Value}|Acc])
end).
-%% @doc Parse a media type.
-spec media_type(binary(), fun()) -> any().
media_type(Data, Fun) ->
token_ci(Data,
@@ -215,8 +201,6 @@ accept_ext_value(Data, Fun, Type, SubType, Params, Quality, Acc, Attr) ->
Type, SubType, Params, Quality, [{Attr, Value}|Acc])
end).
-%% @doc Parse a conneg header (Accept-Charset, Accept-Encoding),
-%% followed by an optional quality value.
-spec conneg(binary(), fun()) -> any().
conneg(Data, Fun) ->
token_ci(Data,
@@ -228,7 +212,6 @@ conneg(Data, Fun) ->
end)
end).
-%% @doc Parse a language range, followed by an optional quality value.
-spec language_range(binary(), fun()) -> any().
language_range(<< $*, Rest/binary >>, Fun) ->
language_range_ret(Rest, Fun, '*');
@@ -289,12 +272,10 @@ maybe_qparam(Data, Fun) ->
Fun(Rest, 1000)
end).
-%% @doc Parse a quality parameter string (for example q=0.500).
-spec qparam(binary(), fun()) -> any().
qparam(<< Q, $=, Data/binary >>, Fun) when Q =:= $q; Q =:= $Q ->
qvalue(Data, Fun).
-%% @doc Parse either a list of entity tags or a "*".
-spec entity_tag_match(binary()) -> any().
entity_tag_match(<< $*, Rest/binary >>) ->
whitespace(Rest,
@@ -304,7 +285,6 @@ entity_tag_match(<< $*, Rest/binary >>) ->
entity_tag_match(Data) ->
nonempty_list(Data, fun entity_tag/2).
-%% @doc Parse an entity-tag.
-spec entity_tag(binary(), fun()) -> any().
entity_tag(<< "W/", Rest/binary >>, Fun) ->
opaque_tag(Rest, Fun, weak);
@@ -318,7 +298,6 @@ opaque_tag(Data, Fun, Strength) ->
(Rest, OpaqueTag) -> Fun(Rest, {Strength, OpaqueTag})
end).
-%% @doc Parse an expectation.
-spec expectation(binary(), fun()) -> any().
expectation(Data, Fun) ->
token_ci(Data,
@@ -334,7 +313,6 @@ expectation(Data, Fun) ->
Fun(Rest, Expectation)
end).
-%% @doc Parse a list of parameters (a=b;c=d).
-spec params(binary(), fun()) -> any().
params(Data, Fun) ->
params(Data, Fun, []).
@@ -366,9 +344,6 @@ param(Data, Fun) ->
end)
end).
-%% @doc Parse an HTTP date (RFC1123, RFC850 or asctime date).
-%% @end
-%%
%% While this may not be the most efficient date parsing we can do,
%% it should work fine for our purposes because all HTTP dates should
%% be sent as RFC1123 dates in HTTP/1.1.
@@ -391,7 +366,6 @@ http_date(Data) ->
HTTPDate
end.
-%% @doc Parse an RFC1123 date.
-spec rfc1123_date(binary()) -> any().
rfc1123_date(Data) ->
wkday(Data,
@@ -411,7 +385,6 @@ rfc1123_date(Data) ->
{error, badarg}
end).
-%% @doc Parse an RFC850 date.
-spec rfc850_date(binary()) -> any().
%% From the RFC:
%% HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
@@ -435,7 +408,6 @@ rfc850_date(Data) ->
{error, badarg}
end).
-%% @doc Parse an asctime date.
-spec asctime_date(binary()) -> any().
asctime_date(Data) ->
wkday(Data,
@@ -594,7 +566,6 @@ time(<< H1, H2, ":", M1, M2, ":", S1, S2, Rest/binary >>, Fun)
{error, badarg}
end.
-%% @doc Skip whitespace.
-spec whitespace(binary(), fun()) -> any().
whitespace(<< C, Rest/binary >>, Fun)
when C =:= $\s; C =:= $\t ->
@@ -602,7 +573,6 @@ whitespace(<< C, Rest/binary >>, Fun)
whitespace(Data, Fun) ->
Fun(Data).
-%% @doc Parse a list of digits as a non negative integer.
-spec digits(binary()) -> non_neg_integer() | {error, badarg}.
digits(Data) ->
digits(Data,
@@ -629,8 +599,6 @@ digits(<< C, Rest/binary >>, Fun, Acc)
digits(Data, Fun, Acc) ->
Fun(Data, Acc).
-%% @doc Parse a list of case-insensitive alpha characters.
-%%
%% Changes all characters to lowercase.
-spec alpha(binary(), fun()) -> any().
alpha(Data, Fun) ->
@@ -647,7 +615,6 @@ alpha(<< C, Rest/binary >>, Fun, Acc)
alpha(Data, Fun, Acc) ->
Fun(Data, Acc).
-%% @doc Parse either a token or a quoted string.
-spec word(binary(), fun()) -> any().
word(Data = << $", _/binary >>, Fun) ->
quoted_string(Data, Fun);
@@ -657,14 +624,11 @@ word(Data, Fun) ->
(Rest, Token) -> Fun(Rest, Token)
end).
-%% @doc Parse a case-insensitive token.
-%%
%% Changes all characters to lowercase.
-spec token_ci(binary(), fun()) -> any().
token_ci(Data, Fun) ->
token(Data, Fun, ci, <<>>).
-%% @doc Parse a token.
-spec token(binary(), fun()) -> any().
token(Data, Fun) ->
token(Data, Fun, cs, <<>>).
@@ -685,7 +649,6 @@ token(<< C, Rest/binary >>, Fun, Case = ci, Acc) ->
token(<< C, Rest/binary >>, Fun, Case, Acc) ->
token(Rest, Fun, Case, << Acc/binary, C >>).
-%% @doc Parse a quoted string.
-spec quoted_string(binary(), fun()) -> any().
quoted_string(<< $", Rest/binary >>, Fun) ->
quoted_string(Rest, Fun, <<>>).
@@ -700,7 +663,6 @@ quoted_string(<< $\\, C, Rest/binary >>, Fun, Acc) ->
quoted_string(<< C, Rest/binary >>, Fun, Acc) ->
quoted_string(Rest, Fun, << Acc/binary, C >>).
-%% @doc Parse a quality value.
-spec qvalue(binary(), fun()) -> any().
qvalue(<< $0, $., Rest/binary >>, Fun) ->
qvalue(Rest, Fun, 0, 100);
@@ -729,8 +691,7 @@ qvalue(<< C, Rest/binary >>, Fun, Q, M)
qvalue(Data, Fun, Q, _M) ->
Fun(Data, Q).
-%% @doc Parse authorization value according rfc 2617.
-%% Only Basic authorization is supported so far.
+%% Only RFC2617 Basic authorization is supported so far.
-spec authorization(binary(), binary()) -> {binary(), any()} | {error, badarg}.
authorization(UserPass, Type = <<"basic">>) ->
whitespace(UserPass,
@@ -746,7 +707,6 @@ authorization(UserPass, Type = <<"basic">>) ->
authorization(String, Type) ->
whitespace(String, fun(Rest) -> {Type, Rest} end).
-%% @doc Parse user credentials.
-spec authorization_basic_userid(binary(), fun()) -> any().
authorization_basic_userid(Data, Fun) ->
authorization_basic_userid(Data, Fun, <<>>).
@@ -773,7 +733,6 @@ authorization_basic_password(<<>>, Fun, Acc) ->
authorization_basic_password(<<C, Rest/binary>>, Fun, Acc) ->
authorization_basic_password(Rest, Fun, <<Acc/binary, C>>).
-%% @doc Parse range header according rfc 2616.
-spec range(binary()) -> {Unit, [Range]} | {error, badarg} when
Unit :: binary(),
Range :: {non_neg_integer(), non_neg_integer() | infinity} | neg_integer().
@@ -831,7 +790,6 @@ range_digits(Data, Default, Fun) ->
Fun(Data, Default)
end).
-%% @doc Parse a non empty list of tokens followed with optional parameters.
-spec parameterized_tokens(binary()) -> any().
parameterized_tokens(Data) ->
nonempty_list(Data,
@@ -876,170 +834,15 @@ parameterized_tokens_param(Data, Fun) ->
%% Decoding.
-%% @doc Decode a stream of chunks.
--spec te_chunked(Bin, TransferState)
- -> more | {more, non_neg_integer(), Bin, TransferState}
- | {ok, Bin, Bin, TransferState}
- | {done, non_neg_integer(), Bin} | {error, badarg}
- when Bin::binary(), TransferState::{non_neg_integer(), non_neg_integer()}.
-te_chunked(<< "0\r\n\r\n", Rest/binary >>, {0, Streamed}) ->
- {done, Streamed, Rest};
-te_chunked(Data, {0, Streamed}) ->
- %% @todo We are expecting an hex size, not a general token.
- token(Data,
- fun (<< "\r\n", Rest/binary >>, BinLen) ->
- case list_to_integer(binary_to_list(BinLen), 16) of
- %% Final chunk is parsed in one go above. Rest would be
- %% <<\r\n">> if complete.
- 0 when byte_size(Rest) < 2 ->
- more;
- %% Normal chunk. Add 2 to Len for trailing <<"\r\n">>. Note
- %% that repeated <<"-2\r\n">> would be streamed, and
- %% accumulated, until out of memory if Len could be -2.
- Len when Len > 0 ->
- te_chunked(Rest, {Len + 2, Streamed})
- end;
- %% Chunk size shouldn't take too many bytes,
- %% don't try to stream forever.
- (Rest, _) when byte_size(Rest) < 16 ->
- more;
- (_, _) ->
- {error, badarg}
- end);
-%% <<"\n">> from trailing <<"\r\n">>.
-te_chunked(<< "\n", Rest/binary>>, {1, Streamed}) ->
- {ok, <<>>, Rest, {0, Streamed}};
-te_chunked(<<>>, State={1, _Streamed}) ->
- {more, 1, <<>>, State};
-%% Remainder of chunk (if any) and as much of trailing <<"\r\n">> as possible.
-te_chunked(Data, {ChunkRem, Streamed}) when byte_size(Data) >= ChunkRem - 2 ->
- ChunkSize = ChunkRem - 2,
- Streamed2 = Streamed + ChunkSize,
- case Data of
- << Chunk:ChunkSize/binary, "\r\n", Rest/binary >> ->
- {ok, Chunk, Rest, {0, Streamed2}};
- << Chunk:ChunkSize/binary, "\r" >> ->
- {more, 1, Chunk, {1, Streamed2}};
- << Chunk:ChunkSize/binary >> ->
- {more, 2, Chunk, {2, Streamed2}}
- end;
-%% Incomplete chunk.
-te_chunked(Data, {ChunkRem, Streamed}) ->
- ChunkRem2 = ChunkRem - byte_size(Data),
- Streamed2 = Streamed + byte_size(Data),
- {more, ChunkRem2, Data, {ChunkRem2, Streamed2}}.
-
-%% @doc Decode an identity stream.
--spec te_identity(Bin, TransferState)
- -> {more, non_neg_integer(), Bin, TransferState}
- | {done, Bin, non_neg_integer(), Bin}
- when Bin::binary(), TransferState::{non_neg_integer(), non_neg_integer()}.
-te_identity(Data, {Streamed, Total})
- when Streamed + byte_size(Data) < Total ->
- Streamed2 = Streamed + byte_size(Data),
- {more, Total - Streamed2, Data, {Streamed2, Total}};
-te_identity(Data, {Streamed, Total}) ->
- Size = Total - Streamed,
- << Data2:Size/binary, Rest/binary >> = Data,
- {done, Data2, Total, Rest}.
-
-%% @doc Decode an identity content.
+%% @todo Move this to cowlib too I suppose. :-)
-spec ce_identity(binary()) -> {ok, binary()}.
ce_identity(Data) ->
{ok, Data}.
-%% Interpretation.
-
-%% @doc Decode a URL encoded binary.
-%% @equiv urldecode(Bin, crash)
--spec urldecode(binary()) -> binary().
-urldecode(Bin) when is_binary(Bin) ->
- urldecode(Bin, <<>>, crash).
-
-%% @doc Decode a URL encoded binary.
-%% The second argument specifies how to handle percent characters that are not
-%% followed by two valid hex characters. Use `skip' to ignore such errors,
-%% if `crash' is used the function will fail with the reason `badarg'.
--spec urldecode(binary(), crash | skip) -> binary().
-urldecode(Bin, OnError) when is_binary(Bin) ->
- urldecode(Bin, <<>>, OnError).
-
--spec urldecode(binary(), binary(), crash | skip) -> binary().
-urldecode(<<$%, H, L, Rest/binary>>, Acc, OnError) ->
- G = unhex(H),
- M = unhex(L),
- if G =:= error; M =:= error ->
- case OnError of skip -> ok; crash -> erlang:error(badarg) end,
- urldecode(<<H, L, Rest/binary>>, <<Acc/binary, $%>>, OnError);
- true ->
- urldecode(Rest, <<Acc/binary, (G bsl 4 bor M)>>, OnError)
- end;
-urldecode(<<$%, Rest/binary>>, Acc, OnError) ->
- case OnError of skip -> ok; crash -> erlang:error(badarg) end,
- urldecode(Rest, <<Acc/binary, $%>>, OnError);
-urldecode(<<$+, Rest/binary>>, Acc, OnError) ->
- urldecode(Rest, <<Acc/binary, $ >>, OnError);
-urldecode(<<C, Rest/binary>>, Acc, OnError) ->
- urldecode(Rest, <<Acc/binary, C>>, OnError);
-urldecode(<<>>, Acc, _OnError) ->
- Acc.
-
--spec unhex(byte()) -> byte() | error.
-unhex(C) when C >= $0, C =< $9 -> C - $0;
-unhex(C) when C >= $A, C =< $F -> C - $A + 10;
-unhex(C) when C >= $a, C =< $f -> C - $a + 10;
-unhex(_) -> error.
-
-
-%% @doc URL encode a string binary.
-%% @equiv urlencode(Bin, [])
--spec urlencode(binary()) -> binary().
-urlencode(Bin) ->
- urlencode(Bin, []).
-
-%% @doc URL encode a string binary.
-%% The `noplus' option disables the default behaviour of quoting space
-%% characters, `\s', as `+'. The `upper' option overrides the default behaviour
-%% of writing hex numbers using lowecase letters to using uppercase letters
-%% instead.
--spec urlencode(binary(), [noplus|upper]) -> binary().
-urlencode(Bin, Opts) ->
- Plus = not lists:member(noplus, Opts),
- Upper = lists:member(upper, Opts),
- urlencode(Bin, <<>>, Plus, Upper).
-
--spec urlencode(binary(), binary(), boolean(), boolean()) -> binary().
-urlencode(<<C, Rest/binary>>, Acc, P=Plus, U=Upper) ->
- if C >= $0, C =< $9 -> urlencode(Rest, <<Acc/binary, C>>, P, U);
- C >= $A, C =< $Z -> urlencode(Rest, <<Acc/binary, C>>, P, U);
- C >= $a, C =< $z -> urlencode(Rest, <<Acc/binary, C>>, P, U);
- C =:= $.; C =:= $-; C =:= $~; C =:= $_ ->
- urlencode(Rest, <<Acc/binary, C>>, P, U);
- C =:= $ , Plus ->
- urlencode(Rest, <<Acc/binary, $+>>, P, U);
- true ->
- H = C band 16#F0 bsr 4, L = C band 16#0F,
- H1 = if Upper -> tohexu(H); true -> tohexl(H) end,
- L1 = if Upper -> tohexu(L); true -> tohexl(L) end,
- urlencode(Rest, <<Acc/binary, $%, H1, L1>>, P, U)
- end;
-urlencode(<<>>, Acc, _Plus, _Upper) ->
- Acc.
-
--spec tohexu(byte()) -> byte().
-tohexu(C) when C < 10 -> $0 + C;
-tohexu(C) when C < 16 -> $A + C - 10.
-
--spec tohexl(byte()) -> byte().
-tohexl(C) when C < 10 -> $0 + C;
-tohexl(C) when C < 16 -> $a + C - 10.
-
%% Tests.
-ifdef(TEST).
-
nonempty_charset_list_test_() ->
- %% {Value, Result}
Tests = [
{<<>>, {error, badarg}},
{<<"iso-8859-5, unicode-1-1;q=0.8">>, [
@@ -1056,7 +859,6 @@ nonempty_charset_list_test_() ->
[{V, fun() -> R = nonempty_list(V, fun conneg/2) end} || {V, R} <- Tests].
nonempty_language_range_list_test_() ->
- %% {Value, Result}
Tests = [
{<<"da, en-gb;q=0.8, en;q=0.7">>, [
{<<"da">>, 1000},
@@ -1075,7 +877,6 @@ nonempty_language_range_list_test_() ->
|| {V, R} <- Tests].
nonempty_token_list_test_() ->
- %% {Value, Result}
Tests = [
{<<>>, {error, badarg}},
{<<" ">>, {error, badarg}},
@@ -1091,7 +892,6 @@ nonempty_token_list_test_() ->
[{V, fun() -> R = nonempty_list(V, fun token/2) end} || {V, R} <- Tests].
media_range_list_test_() ->
- %% {Tokens, Result}
Tests = [
{<<"audio/*; q=0.2, audio/basic">>, [
{{<<"audio">>, <<"*">>, []}, 200, []},
@@ -1136,7 +936,6 @@ media_range_list_test_() ->
[{V, fun() -> R = list(V, fun media_range/2) end} || {V, R} <- Tests].
entity_tag_match_test_() ->
- %% {Tokens, Result}
Tests = [
{<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
{<<"\"xyzzy\", W/\"r2d2xxxx\", \"c3piozzzz\"">>,
@@ -1148,7 +947,6 @@ entity_tag_match_test_() ->
[{V, fun() -> R = entity_tag_match(V) end} || {V, R} <- Tests].
http_date_test_() ->
- %% {Tokens, Result}
Tests = [
{<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
{<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
@@ -1157,28 +955,24 @@ http_date_test_() ->
[{V, fun() -> R = http_date(V) end} || {V, R} <- Tests].
rfc1123_date_test_() ->
- %% {Tokens, Result}
Tests = [
{<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
],
[{V, fun() -> R = rfc1123_date(V) end} || {V, R} <- Tests].
rfc850_date_test_() ->
- %% {Tokens, Result}
Tests = [
{<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
],
[{V, fun() -> R = rfc850_date(V) end} || {V, R} <- Tests].
asctime_date_test_() ->
- %% {Tokens, Result}
Tests = [
{<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
],
[{V, fun() -> R = asctime_date(V) end} || {V, R} <- Tests].
content_type_test_() ->
- %% {ContentType, Result}
Tests = [
{<<"text/plain; charset=iso-8859-4">>,
{<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
@@ -1195,7 +989,6 @@ content_type_test_() ->
[{V, fun () -> R = content_type(V) end} || {V, R} <- Tests].
parameterized_tokens_test_() ->
- %% {ParameterizedTokens, Result}
Tests = [
{<<"foo">>, [{<<"foo">>, []}]},
{<<"bar; baz=2">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}]}]},
@@ -1206,7 +999,6 @@ parameterized_tokens_test_() ->
[{V, fun () -> R = parameterized_tokens(V) end} || {V, R} <- Tests].
digits_test_() ->
- %% {Digits, Result}
Tests = [
{<<"42 ">>, 42},
{<<"69\t">>, 69},
@@ -1214,41 +1006,6 @@ digits_test_() ->
],
[{V, fun() -> R = digits(V) end} || {V, R} <- Tests].
-urldecode_test_() ->
- F = fun(Qs, O) ->
- try urldecode(Qs, O) of
- R ->
- {ok, R}
- catch _:E ->
- {error, E}
- end
- end,
- Tests = [
- {<<"%20">>, crash, {ok, <<" ">>}},
- {<<"+">>, crash, {ok, <<" ">>}},
- {<<"%00">>, crash, {ok, <<0>>}},
- {<<"%fF">>, crash, {ok, <<255>>}},
- {<<"123">>, crash, {ok, <<"123">>}},
- {<<"%i5">>, skip, {ok, <<"%i5">>}},
- {<<"%5">>, skip, {ok, <<"%5">>}},
- {<<"%i5">>, crash, {error, badarg}},
- {<<"%5">>, crash, {error, badarg}}
- ],
- [{Qs, fun() -> R = F(Qs,O) end} || {Qs, O, R} <- Tests].
-
-urlencode_test_() ->
- Tests = [
- {<<255,0>>, [], <<"%ff%00">>},
- {<<255,0>>, [upper], <<"%FF%00">>},
- {<<" ">>, [], <<"+">>},
- {<<" ">>, [noplus], <<"%20">>},
- {<<"aBc">>, [], <<"aBc">>},
- {<<".-~_">>, [], <<".-~_">>}
- ],
- Tests2 = [{<<255, " ">>,<<"%ff+">>}],
- [{V, fun() -> R = urlencode(V, O) end} || {V, O, R} <- Tests] ++
- [{V, fun() -> R = urlencode(V) end} || {V, R} <- Tests2].
-
http_authorization_test_() ->
Tests = [
{<<"basic">>, <<"QWxsYWRpbjpvcGVuIHNlc2FtZQ==">>,
@@ -1288,5 +1045,4 @@ http_range_test_() ->
{error, badarg}}
],
[fun() -> R = range(V) end ||{V, R} <- Tests].
-
-endif.