aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--manual/cowboy_req.md9
-rw-r--r--manual/cowboy_rest.md8
-rw-r--r--src/cowboy_http.erl20
-rw-r--r--test/http_SUITE.erl12
-rw-r--r--test/http_SUITE_data/rest_post_charset_resource.erl15
5 files changed, 54 insertions, 10 deletions
diff --git a/manual/cowboy_req.md b/manual/cowboy_req.md
index f10120a..8a765dc 100644
--- a/manual/cowboy_req.md
+++ b/manual/cowboy_req.md
@@ -176,7 +176,7 @@ Request related exports
> | accept-language | `[{LanguageTag, Quality}]` |
> | authorization | `{AuthType, Credentials}` |
> | content-length | `non_neg_integer()` |
-> | content-type | `{Type, SubType, Params}` |
+> | content-type | `{Type, SubType, ContentTypeParams}` |
> | cookie | `[{binary(), binary()}]` |
> | expect | `[Expect | {Expect, ExpectValue, Params}]` |
> | if-match | `'*' | [{weak | strong, OpaqueTag}]` |
@@ -192,7 +192,7 @@ Request related exports
> Types for the above table:
> * Type = SubType = Charset = Encoding = LanguageTag = binary()
> * AuthType = Expect = OpaqueTag = Unit = binary()
-> * Params = [{binary(), binary()}]
+> * Params = ContentTypeParams = [{binary(), binary()}]
> * Quality = 0..1000
> * AcceptExt = [{binary(), binary()} | binary()]
> * Credentials - see below
@@ -201,8 +201,9 @@ Request related exports
> The cookie names and values, the values of the sec-websocket-protocol
> and x-forwarded-for headers, the values in `AcceptExt` and `Params`,
> the authorization `Credentials`, the `ExpectValue` and `OpaqueTag`
-> are case sensitive. All other values are case insensitive and
-> will be returned as lowercase.
+> are case sensitive. All values in `ContentTypeParams` are case sensitive
+> except the value of the charset parameter, which is case insensitive.
+> All other values are case insensitive and will be returned as lowercase.
>
> The headers accept, accept-encoding and cookie headers can return
> an empty list. Others will return `{error, badarg}` if the header
diff --git a/manual/cowboy_rest.md b/manual/cowboy_rest.md
index 4891d9d..110e224 100644
--- a/manual/cowboy_rest.md
+++ b/manual/cowboy_rest.md
@@ -168,7 +168,9 @@ REST callbacks description
> Cowboy will select the most appropriate content-type from the list.
> If any parameter is acceptable, then the tuple form should be used
> with parameters set to `'*'`. If the parameters value is set to `[]`
-> only content-type values with no parameters will be accepted.
+> only content-type values with no parameters will be accepted. All
+> parameter values are treated in a case sensitive manner except the
+> `charset` parameter, if present, which is case insensitive.
>
> This function will be called for POST, PUT and PATCH requests.
> It is entirely possible to define different callbacks for different
@@ -219,7 +221,9 @@ REST callbacks description
> Cowboy will select the most appropriate content-type from the list.
> If any parameter is acceptable, then the tuple form should be used
> with parameters set to `'*'`. If the parameters value is set to `[]`
-> only content-type values with no parameters will be accepted.
+> only content-type values with no parameters will be accepted. All
+> parameter values are treated in a case sensitive manner except the
+> `charset` parameter, if present, which is case insensitive.
>
> The `ProvideResource` value is the name of the callback that will
> be called if the content-type matches. It is defined as follow.
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl
index af60dd9..d2bdf3b 100644
--- a/src/cowboy_http.erl
+++ b/src/cowboy_http.erl
@@ -162,14 +162,26 @@ cookie_value(<< C, Rest/binary >>, Fun, Acc) ->
cookie_value(Rest, Fun, << Acc/binary, C >>).
%% @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) ->
media_type(Data,
fun (Rest, Type, SubType) ->
- params(Rest,
- fun (<<>>, Params) -> {Type, SubType, Params};
- (_Rest2, _) -> {error, badarg}
- end)
+ params(Rest,
+ fun (<<>>, Params) ->
+ case lists:keyfind(<<"charset">>, 1, Params) of
+ false ->
+ {Type, SubType, Params};
+ {_, Charset} ->
+ Charset2 = cowboy_bstr:to_lower(Charset),
+ Params2 = lists:keyreplace(<<"charset">>,
+ 1, Params, {<<"charset">>, Charset2}),
+ {Type, SubType, Params2}
+ end;
+ (_Rest2, _) ->
+ {error, badarg}
+ end)
end).
%% @doc Parse a media range.
diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl
index 21cdd4b..54bc92a 100644
--- a/test/http_SUITE.erl
+++ b/test/http_SUITE.erl
@@ -64,6 +64,7 @@
-export([rest_options_default/1]).
-export([rest_param_all/1]).
-export([rest_patch/1]).
+-export([rest_post_charset/1]).
-export([rest_postonly/1]).
-export([rest_resource_etags/1]).
-export([rest_resource_etags_if_none_match/1]).
@@ -138,6 +139,7 @@ groups() ->
rest_options_default,
rest_param_all,
rest_patch,
+ rest_post_charset,
rest_postonly,
rest_resource_etags,
rest_resource_etags_if_none_match,
@@ -370,6 +372,7 @@ init_dispatch(Config) ->
{"/missing_get_callbacks", rest_missing_callbacks, []},
{"/missing_put_callbacks", rest_missing_callbacks, []},
{"/nodelete", rest_nodelete_resource, []},
+ {"/post_charset", rest_post_charset_resource, []},
{"/postonly", rest_postonly_resource, []},
{"/patch", rest_patch_resource, []},
{"/resetags", rest_resource_etags, []},
@@ -999,6 +1002,15 @@ rest_patch(Config) ->
ok
end || {Status, Headers, Body} <- Tests].
+rest_post_charset(Config) ->
+ Client = ?config(client, Config),
+ Headers = [
+ {<<"content-type">>, <<"text/plain;charset=UTF-8">>}
+ ],
+ {ok, Client2} = cowboy_client:request(<<"POST">>,
+ build_url("/post_charset", Config), Headers, "12345", Client),
+ {ok, 204, _, _} = cowboy_client:response(Client2).
+
rest_postonly(Config) ->
Client = ?config(client, Config),
Headers = [
diff --git a/test/http_SUITE_data/rest_post_charset_resource.erl b/test/http_SUITE_data/rest_post_charset_resource.erl
new file mode 100644
index 0000000..9ccfa61
--- /dev/null
+++ b/test/http_SUITE_data/rest_post_charset_resource.erl
@@ -0,0 +1,15 @@
+-module(rest_post_charset_resource).
+-export([init/3, allowed_methods/2, content_types_accepted/2, from_text/2]).
+
+init(_Transport, _Req, _Opts) ->
+ {upgrade, protocol, cowboy_rest}.
+
+allowed_methods(Req, State) ->
+ {[<<"POST">>], Req, State}.
+
+content_types_accepted(Req, State) ->
+ {[{{<<"text">>, <<"plain">>, [{<<"charset">>, <<"utf-8">>}]},
+ from_text}], Req, State}.
+
+from_text(Req, State) ->
+ {true, Req, State}.