Skip to content

Commit

Permalink
In content-types, the charset parameter is converted to lowercase
Browse files Browse the repository at this point in the history
We know this specific parameter is case insensitive so we
automatically lowercase it to make things simpler to the
developer.
  • Loading branch information
essen committed May 31, 2013
1 parent 8fac4ee commit 4fde6cb
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 10 deletions.
9 changes: 5 additions & 4 deletions manual/cowboy_req.md
Original file line number Diff line number Diff line change
Expand Up @@ -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}]` |
Expand All @@ -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
Expand All @@ -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
Expand Down
8 changes: 6 additions & 2 deletions manual/cowboy_rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
20 changes: 16 additions & 4 deletions src/cowboy_http.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions test/http_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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]).
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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, []},
Expand Down Expand Up @@ -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 = [
Expand Down
15 changes: 15 additions & 0 deletions test/http_SUITE_data/rest_post_charset_resource.erl
Original file line number Diff line number Diff line change
@@ -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}.

0 comments on commit 4fde6cb

Please sign in to comment.