aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cowboy_http.erl110
-rw-r--r--src/cowboy_http_req.erl5
2 files changed, 88 insertions, 27 deletions
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl
index 6d14961..6454521 100644
--- a/src/cowboy_http.erl
+++ b/src/cowboy_http.erl
@@ -1,4 +1,5 @@
%% Copyright (c) 2011, Loïc Hoguin <[email protected]>
+%% Copyright (c) 2011, Anthony Ramine <[email protected]>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
@@ -16,7 +17,7 @@
-module(cowboy_http).
%% Parsing.
--export([list/2, nonempty_list/2,
+-export([list/2, nonempty_list/2, content_type/1,
media_range/2, conneg/2, language_range/2, entity_tag_match/1,
http_date/1, rfc1123_date/1, rfc850_date/1, asctime_date/1,
digits/1, token/2, token_ci/2, quoted_string/2]).
@@ -66,16 +67,47 @@ list(Data, Fun, Acc) ->
end)
end).
+%% @doc Parse a content type.
+-spec content_type(binary()) -> any().
+content_type(Data) ->
+ media_type(Data,
+ fun (Rest, Type, SubType) ->
+ content_type_params(Rest,
+ fun (Params) -> {Type, SubType, Params} end, [])
+ end).
+
+-spec content_type_params(binary(), fun(), list({binary(), binary()}))
+ -> any().
+content_type_params(Data, Fun, Acc) ->
+ whitespace(Data,
+ fun (<< $;, Rest/bits >>) -> content_type_param(Rest, Fun, Acc);
+ (<<>>) -> Fun(lists:reverse(Acc));
+ (_Rest) -> {error, badarg}
+ end).
+
+-spec content_type_param(binary(), fun(), list({binary(), binary()}))
+ -> any().
+content_type_param(Data, Fun, Acc) ->
+ whitespace(Data,
+ fun (Rest) ->
+ token_ci(Rest,
+ fun (_Rest2, <<>>) -> {error, badarg};
+ (<< $=, Rest2/bits >>, Attr) ->
+ word(Rest2,
+ fun (Rest3, Value) ->
+ content_type_params(Rest3, Fun,
+ [{Attr, Value}|Acc])
+ end);
+ (_Rest2, _Attr) -> {error, badarg}
+ end)
+ end).
+
%% @doc Parse a media range.
-spec media_range(binary(), fun()) -> any().
media_range(Data, Fun) ->
- token_ci(Data,
- fun (_Rest, <<>>) -> {error, badarg};
- (<< $/, Rest/bits >>, Type) -> token_ci(Rest,
- fun (_Rest2, <<>>) -> {error, badarg};
- (Rest2, SubType) ->
- media_range_params(Rest2, Fun, Type, SubType, [])
- end)
+ media_type(Data,
+ fun (Rest, Type, SubType) ->
+ media_range_params(Rest, Fun, Type, SubType, [])
end).
-spec media_range_params(binary(), fun(), binary(), binary(),
@@ -106,19 +138,24 @@ media_range_param_value(Data, Fun, Type, SubType, Acc, <<"q">>) ->
fun (Rest, Quality) ->
accept_ext(Rest, Fun, Type, SubType, Acc, Quality, [])
end);
-media_range_param_value(Data = << $", _/bits >>, Fun,
- Type, SubType, Acc, Attr) ->
- quoted_string(Data,
+media_range_param_value(Data, Fun, Type, SubType, Acc, Attr) ->
+ word(Data,
fun (Rest, Value) ->
media_range_params(Rest, Fun,
Type, SubType, [{Attr, Value}|Acc])
- end);
-media_range_param_value(Data, Fun, Type, SubType, Acc, Attr) ->
- token(Data,
+ end).
+
+%% @doc Parse a media type.
+-spec media_type(binary(), fun()) -> any().
+media_type(Data, Fun) ->
+ token_ci(Data,
fun (_Rest, <<>>) -> {error, badarg};
- (Rest, Value) ->
- media_range_params(Rest, Fun,
- Type, SubType, [{Attr, Value}|Acc])
+ (<< $/, Rest/bits >>, Type) ->
+ token_ci(Rest,
+ fun (_Rest2, <<>>) -> {error, badarg};
+ (Rest2, SubType) -> Fun(Rest2, Type, SubType)
+ end);
+ (_Rest, _Type) -> {error, badarg}
end).
-spec accept_ext(binary(), fun(), binary(), binary(),
@@ -154,17 +191,9 @@ accept_ext_attr(Data, Fun, Type, SubType, Params, Quality, Acc) ->
-spec accept_ext_value(binary(), fun(), binary(), binary(),
[{binary(), binary()}], 0..1000,
[{binary(), binary()} | binary()], binary()) -> any().
-accept_ext_value(Data = << $", _/bits >>, Fun,
- Type, SubType, Params, Quality, Acc, Attr) ->
- quoted_string(Data,
- fun (Rest, Value) ->
- accept_ext(Rest, Fun,
- Type, SubType, Params, Quality, [{Attr, Value}|Acc])
- end);
accept_ext_value(Data, Fun, Type, SubType, Params, Quality, Acc, Attr) ->
- token(Data,
- fun (_Rest, <<>>) -> {error, badarg};
- (Rest, Value) ->
+ word(Data,
+ fun (Rest, Value) ->
accept_ext(Rest, Fun,
Type, SubType, Params, Quality, [{Attr, Value}|Acc])
end).
@@ -543,6 +572,16 @@ alpha(<< C, Rest/bits >>, 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 = << $", _/bits >>, Fun) ->
+ quoted_string(Data, Fun);
+word(Data, Fun) ->
+ token(Data,
+ fun (_Rest, <<>>) -> {error, badarg};
+ (Rest, Token) -> Fun(Rest, Token)
+ end).
+
%% @doc Parse a case-insensitive token.
%%
%% Changes all characters to lowercase.
@@ -769,6 +808,23 @@ connection_to_atom_test_() ->
[{lists:flatten(io_lib:format("~p", [T])),
fun() -> R = connection_to_atom(T) end} || {T, R} <- Tests].
+content_type_test_() ->
+ %% {ContentType, Result}
+ Tests = [
+ {<<"text/plain; charset=iso-8859-4">>,
+ {<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
+ {<<"multipart/form-data \t;Boundary=\"MultipartIsUgly\"">>,
+ {<<"multipart">>, <<"form-data">>, [
+ {<<"boundary">>, <<"MultipartIsUgly">>}
+ ]}},
+ {<<"foo/bar; one=FirstParam; two=SecondParam">>,
+ {<<"foo">>, <<"bar">>, [
+ {<<"one">>, <<"FirstParam">>},
+ {<<"two">>, <<"SecondParam">>}
+ ]}}
+ ],
+ [{V, fun () -> R = content_type(V) end} || {V, R} <- Tests].
+
digits_test_() ->
%% {Digits, Result}
Tests = [
diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl
index 12d117f..fa4e620 100644
--- a/src/cowboy_http_req.erl
+++ b/src/cowboy_http_req.erl
@@ -244,6 +244,11 @@ parse_header(Name, Req, Default) when Name =:= 'Content-Length' ->
fun (Value) ->
cowboy_http:digits(Value)
end);
+parse_header(Name, Req, Default) when Name =:= 'Content-Type' ->
+ parse_header(Name, Req, Default,
+ fun (Value) ->
+ cowboy_http:content_type(Value)
+ end);
parse_header(Name, Req, Default)
when Name =:= 'If-Match'; Name =:= 'If-None-Match' ->
parse_header(Name, Req, Default,