diff options
-rw-r--r-- | manual/cowboy_req.md | 9 | ||||
-rw-r--r-- | manual/cowboy_rest.md | 8 | ||||
-rw-r--r-- | src/cowboy_http.erl | 20 | ||||
-rw-r--r-- | test/http_SUITE.erl | 12 | ||||
-rw-r--r-- | test/http_SUITE_data/rest_post_charset_resource.erl | 15 |
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}. |