0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Add a charset option to cowboy_static

This commit is contained in:
Loïc Hoguin 2018-11-02 15:31:54 +01:00
parent 9569043bdd
commit 571719a164
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
3 changed files with 87 additions and 3 deletions

View file

@ -27,7 +27,10 @@ opts() :: {priv_file, App, Path}
App :: atom()
Path :: binary() | string()
Extra :: [Etag | Mimetypes]
Extra :: [Charset | Etag | Mimetypes]
Charset :: {charset, module(), function()}
| {charset, binary()}
Etag :: {etag, module(), function()}
| {etag, false}
@ -72,6 +75,20 @@ current directory.
The extra options allow you to define how the etag should be
calculated and how the MIME type of files should be detected.
By default the static handler will not send a charset with
the response. You can provide a specific charset that will
be used for all files using the text media type, or provide
a module and function that will be called when needed:
[source,erlang]
----
detect_charset(Path :: binary()) -> Charset :: binary()
----
A charset must always be returned even if it doesn't make
sense considering the media type of the file. A good default
is `<<"utf-8">>`.
By default the static handler will generate an etag based
on the size and modification time of the file. You may disable
the etag entirely with `{etag, false}` or provide a module
@ -112,6 +129,7 @@ when it fails to detect a file's MIME type.
== Changelog
* *2.6*: The `charset` extra option was added.
* *1.0*: Handler introduced.
== Examples

View file

@ -19,11 +19,13 @@
-export([malformed_request/2]).
-export([forbidden/2]).
-export([content_types_provided/2]).
-export([charsets_provided/2]).
-export([resource_exists/2]).
-export([last_modified/2]).
-export([generate_etag/2]).
-export([get_file/2]).
-type extra_charset() :: {charset, module(), function()} | {charset, binary()}.
-type extra_etag() :: {etag, module(), function()} | {etag, false}.
-type extra_mimetypes() :: {mimetypes, module(), function()}
| {mimetypes, binary() | {binary(), binary(), [{binary(), binary()}]}}.
@ -322,6 +324,22 @@ content_types_provided(Req, State={Path, _, Extra}) ->
{[{Type, get_file}], Req, State}
end.
%% Detect the charset of the file.
-spec charsets_provided(Req, State)
-> {[binary()], Req, State}
when State::state().
charsets_provided(Req, State={Path, _, Extra}) ->
case lists:keyfind(charset, 1, Extra) of
%% We simulate the callback not being exported.
false ->
no_call;
{charset, Module, Function} ->
{[Module:Function(Path)], Req, State};
{charset, Charset} ->
{[Charset], Req, State}
end.
%% Assume the resource doesn't exist if it's not a regular file.
-spec resource_exists(Req, State)

View file

@ -132,6 +132,12 @@ init_dispatch(Config) ->
[{mimetypes, <<"application/vnd.ninenines.cowboy+xml;v=1">>}]}},
{"/mime/hardcode/tuple-form", cowboy_static, {priv_file, ct_helper, "static/file.cowboy",
[{mimetypes, {<<"application">>, <<"vnd.ninenines.cowboy+xml">>, [{<<"v">>, <<"1">>}]}}]}},
{"/charset/custom/[...]", cowboy_static, {priv_dir, ct_helper, "static",
[{charset, ?MODULE, do_charset_custom}]}},
{"/charset/crash/[...]", cowboy_static, {priv_dir, ct_helper, "static",
[{charset, ?MODULE, do_charset_crash}]}},
{"/charset/hardcode/[...]", cowboy_static, {priv_file, ct_helper, "static/index.html",
[{charset, <<"utf-8">>}]}},
{"/etag/custom", cowboy_static, {file, config(static_dir, Config) ++ "/style.css",
[{etag, ?MODULE, do_etag_custom}]}},
{"/etag/crash", cowboy_static, {file, config(static_dir, Config) ++ "/style.css",
@ -151,6 +157,7 @@ init_dispatch(Config) ->
{"/bad/file/path", cowboy_static, {file, "/bad/path/style.css"}},
{"/bad/options", cowboy_static, {priv_file, ct_helper, "static/style.css", bad}},
{"/bad/options/mime", cowboy_static, {priv_file, ct_helper, "static/style.css", [{mimetypes, bad}]}},
{"/bad/options/charset", cowboy_static, {priv_file, ct_helper, "static/style.css", [{charset, bad}]}},
{"/bad/options/etag", cowboy_static, {priv_file, ct_helper, "static/style.css", [{etag, true}]}},
{"/unknown/option", cowboy_static, {priv_file, ct_helper, "static/style.css", [{bad, option}]}},
{"/char/[...]", cowboy_static, {dir, config(char_dir, Config)}},
@ -162,6 +169,18 @@ init_dispatch(Config) ->
%% Internal functions.
-spec do_charset_crash(_) -> no_return().
do_charset_crash(_) ->
ct_helper_error_h:ignore(?MODULE, do_charset_crash, 1),
exit(crash).
do_charset_custom(Path) ->
case filename:extension(Path) of
<<".cowboy">> -> <<"utf-32">>;
<<".html">> -> <<"utf-16">>;
_ -> <<"utf-8">>
end.
-spec do_etag_crash(_, _, _) -> no_return().
do_etag_crash(_, _, _) ->
ct_helper_error_h:ignore(?MODULE, do_etag_crash, 3),
@ -746,17 +765,46 @@ mime_custom_txt(Config) ->
ok.
mime_hardcode_binary(Config) ->
doc("Get a .cowboy file with hardcoded route."),
doc("Get a .cowboy file with hardcoded route and media type in binary form."),
{200, Headers, _} = do_get("/mime/hardcode/binary-form", Config),
{_, <<"application/vnd.ninenines.cowboy+xml;v=1">>} = lists:keyfind(<<"content-type">>, 1, Headers),
ok.
mime_hardcode_tuple(Config) ->
doc("Get a .cowboy file with hardcoded route."),
doc("Get a .cowboy file with hardcoded route and media type in tuple form."),
{200, Headers, _} = do_get("/mime/hardcode/tuple-form", Config),
{_, <<"application/vnd.ninenines.cowboy+xml;v=1">>} = lists:keyfind(<<"content-type">>, 1, Headers),
ok.
charset_crash(Config) ->
doc("Get a file with a crashing charset function."),
{500, _, _} = do_get("/charset/crash/style.css", Config),
ok.
charset_custom_cowboy(Config) ->
doc("Get a .cowboy file."),
{200, Headers, _} = do_get("/charset/custom/file.cowboy", Config),
{_, <<"application/octet-stream">>} = lists:keyfind(<<"content-type">>, 1, Headers),
ok.
charset_custom_css(Config) ->
doc("Get a .css file."),
{200, Headers, _} = do_get("/charset/custom/style.css", Config),
{_, <<"text/css; charset=utf-8">>} = lists:keyfind(<<"content-type">>, 1, Headers),
ok.
charset_custom_html(Config) ->
doc("Get a .html file."),
{200, Headers, _} = do_get("/charset/custom/index.html", Config),
{_, <<"text/html; charset=utf-16">>} = lists:keyfind(<<"content-type">>, 1, Headers),
ok.
charset_hardcode_binary(Config) ->
doc("Get a .html file with hardcoded route and charset."),
{200, Headers, _} = do_get("/charset/hardcode", Config),
{_, <<"text/html; charset=utf-8">>} = lists:keyfind(<<"content-type">>, 1, Headers),
ok.
priv_dir_in_ez_archive(Config) ->
doc("Get a file from a priv_dir stored in Erlang application .ez archive."),
{200, Headers, <<"<h1>It works!</h1>\n">>} = do_get("/ez_priv_dir/index.html", Config),