aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--manual/cowboy_req.md9
-rw-r--r--manual/cowboy_rest.md11
-rw-r--r--src/cowboy_http.erl20
-rw-r--r--src/cowboy_req.erl10
-rw-r--r--src/cowboy_rest.erl6
-rw-r--r--src/cowboy_spdy.erl46
-rw-r--r--test/http_SUITE.erl97
-rw-r--r--test/http_SUITE_data/rest_post_charset_resource.erl15
-rw-r--r--test/spdy_SUITE.erl14
9 files changed, 152 insertions, 76 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 4d5862a..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.
@@ -299,7 +303,8 @@ REST callbacks description
> This value will be sent as the value of the etag header.
>
> If a binary is returned, then the value will be parsed
-> to the tuple form automatically.
+> to the tuple form automatically. The value must be in
+> the same format as the etag header, including quotes.
### is_authorized
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/src/cowboy_req.erl b/src/cowboy_req.erl
index 093663c..0e1c8a7 100644
--- a/src/cowboy_req.erl
+++ b/src/cowboy_req.erl
@@ -492,7 +492,10 @@ cookie(Name, Req=#http_req{cookies=undefined}, Default) when is_binary(Name) ->
{ok, undefined, Req2} ->
{Default, Req2#http_req{cookies=[]}};
{ok, Cookies, Req2} ->
- cookie(Name, Req2#http_req{cookies=Cookies}, Default)
+ cookie(Name, Req2#http_req{cookies=Cookies}, Default);
+ %% Flash player incorrectly sends an empty Cookie header.
+ {error, badarg} ->
+ {Default, Req#http_req{cookies=[]}}
end;
cookie(Name, Req, Default) ->
case lists:keyfind(Name, 1, Req#http_req.cookies) of
@@ -507,7 +510,10 @@ cookies(Req=#http_req{cookies=undefined}) ->
{ok, undefined, Req2} ->
{[], Req2#http_req{cookies=[]}};
{ok, Cookies, Req2} ->
- cookies(Req2#http_req{cookies=Cookies})
+ cookies(Req2#http_req{cookies=Cookies});
+ %% Flash player incorrectly sends an empty Cookie header.
+ {error, badarg} ->
+ {[], Req#http_req{cookies=[]}}
end;
cookies(Req=#http_req{cookies=Cookies}) ->
{Cookies, Req}.
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index b87c7df..ecbe7bc 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -145,6 +145,9 @@ allowed_methods(Req, State=#state{method=Method}) ->
end
end.
+method_not_allowed(Req, State, []) ->
+ Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
+ respond(Req2, State, 405);
method_not_allowed(Req, State, Methods) ->
<< ", ", Allow/binary >> = << << ", ", M/binary >> || M <- Methods >>,
Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
@@ -186,6 +189,9 @@ valid_entity_length(Req, State) ->
%% you should do it directly in the options/2 call using set_resp_headers.
options(Req, State=#state{allowed_methods=Methods, method= <<"OPTIONS">>}) ->
case call(Req, State, options) of
+ no_call when Methods =:= [] ->
+ Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
+ respond(Req2, State, 200);
no_call ->
<< ", ", Allow/binary >>
= << << ", ", M/binary >> || M <- Methods >>,
diff --git a/src/cowboy_spdy.erl b/src/cowboy_spdy.erl
index 3fe477b..182e6da 100644
--- a/src/cowboy_spdy.erl
+++ b/src/cowboy_spdy.erl
@@ -42,6 +42,7 @@
%% Internal transport functions.
-export([name/0]).
-export([send/2]).
+-export([sendfile/2]).
-record(child, {
streamid :: non_neg_integer(),
@@ -174,6 +175,14 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
Children2 = lists:keyreplace(StreamID,
#child.streamid, Children, Child#child{output=fin}),
loop(State#state{children=Children2});
+ {sendfile, {Pid, StreamID}, Filepath}
+ when Pid =:= self() ->
+ Child = #child{output=nofin} = lists:keyfind(StreamID,
+ #child.streamid, Children),
+ data_from_file(State, StreamID, Filepath),
+ Children2 = lists:keyreplace(StreamID,
+ #child.streamid, Children, Child#child{output=fin}),
+ loop(State#state{children=Children2});
{'EXIT', Parent, Reason} ->
exit(Reason);
{'EXIT', Pid, _} ->
@@ -212,12 +221,14 @@ system_code_change(Misc, _, _, _) ->
{ok, Misc}.
%% We do not support SYN_STREAM with FLAG_UNIDIRECTIONAL set.
-control_frame(State, << _:38, 1:1, _:26, StreamID:31, _/bits >>) ->
+control_frame(State, << 1:1, 3:15, 1:16, _:6, 1:1, _:26,
+ StreamID:31, _/bits >>) ->
rst_stream(State, StreamID, internal_error),
loop(State);
%% We do not support Associated-To-Stream-ID and CREDENTIAL Slot.
-control_frame(State, << _:65, StreamID:31, _:1, AssocToStreamID:31,
- _:8, Slot:8, _/bits >>) when AssocToStreamID =/= 0; Slot =/= 0 ->
+control_frame(State, << 1:1, 3:15, 1:16, _:33, StreamID:31, _:1,
+ AssocToStreamID:31, _:8, Slot:8, _/bits >>)
+ when AssocToStreamID =/= 0; Slot =/= 0 ->
rst_stream(State, StreamID, internal_error),
loop(State);
%% SYN_STREAM
@@ -435,6 +446,27 @@ data(#state{socket=Socket, transport=Transport}, IsFin, StreamID, Data) ->
<< 0:1, StreamID:31, Flags:8, Len:24 >>,
Data]).
+data_from_file(#state{socket=Socket, transport=Transport},
+ StreamID, Filepath) ->
+ {ok, IoDevice} = file:open(Filepath, [read, binary, raw]),
+ data_from_file(Socket, Transport, StreamID, IoDevice).
+
+data_from_file(Socket, Transport, StreamID, IoDevice) ->
+ case file:read(IoDevice, 16#1fff) of
+ eof ->
+ _ = Transport:send(Socket, << 0:1, StreamID:31, 1:8, 0:24 >>),
+ ok;
+ {ok, Data} ->
+ Len = byte_size(Data),
+ Data2 = [<< 0:1, StreamID:31, 0:8, Len:24 >>, Data],
+ case Transport:send(Socket, Data2) of
+ ok ->
+ data_from_file(Socket, Transport, StreamID, IoDevice);
+ {error, _} ->
+ ok
+ end
+ end.
+
%% Request process.
request_init(Parent, StreamID, Peer,
@@ -540,10 +572,16 @@ stream_close(Socket = {Pid, _}) ->
ok.
%% Internal transport functions.
-%% @todo recv, sendfile
+%% @todo recv
name() ->
spdy.
send(Socket, Data) ->
stream_data(Socket, Data).
+
+%% We don't wait for the result of the actual sendfile call,
+%% therefore we can't know how much was actually sent.
+sendfile(Socket = {Pid, _}, Filepath) ->
+ _ = Pid ! {sendfile, Socket, Filepath},
+ {ok, undefined}.
diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl
index 21cdd4b..2d7f420 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,
@@ -187,9 +189,13 @@ init_per_suite(Config) ->
application:start(crypto),
application:start(ranch),
application:start(cowboy),
- Config.
+ Dir = ?config(priv_dir, Config) ++ "/static",
+ ct_helper:create_static_dir(Dir),
+ [{static_dir, Dir}|Config].
-end_per_suite(_Config) ->
+end_per_suite(Config) ->
+ Dir = ?config(static_dir, Config),
+ ct_helper:delete_static_dir(Dir),
application:stop(cowboy),
application:stop(ranch),
application:stop(crypto),
@@ -197,62 +203,58 @@ end_per_suite(_Config) ->
init_per_group(http, Config) ->
Transport = ranch_tcp,
- Config1 = init_static_dir(Config),
{ok, _} = cowboy:start_http(http, 100, [{port, 0}], [
- {env, [{dispatch, init_dispatch(Config1)}]},
+ {env, [{dispatch, init_dispatch(Config)}]},
{max_keepalive, 50},
{timeout, 500}
]),
Port = ranch:get_port(http),
{ok, Client} = cowboy_client:init([]),
[{scheme, <<"http">>}, {port, Port}, {opts, []},
- {transport, Transport}, {client, Client}|Config1];
+ {transport, Transport}, {client, Client}|Config];
init_per_group(https, Config) ->
Transport = ranch_ssl,
{_, Cert, Key} = ct_helper:make_certs(),
Opts = [{cert, Cert}, {key, Key}],
- Config1 = init_static_dir(Config),
application:start(public_key),
application:start(ssl),
{ok, _} = cowboy:start_https(https, 100, Opts ++ [{port, 0}], [
- {env, [{dispatch, init_dispatch(Config1)}]},
+ {env, [{dispatch, init_dispatch(Config)}]},
{max_keepalive, 50},
{timeout, 500}
]),
Port = ranch:get_port(https),
{ok, Client} = cowboy_client:init(Opts),
[{scheme, <<"https">>}, {port, Port}, {opts, Opts},
- {transport, Transport}, {client, Client}|Config1];
+ {transport, Transport}, {client, Client}|Config];
init_per_group(http_compress, Config) ->
Transport = ranch_tcp,
- Config1 = init_static_dir(Config),
{ok, _} = cowboy:start_http(http_compress, 100, [{port, 0}], [
{compress, true},
- {env, [{dispatch, init_dispatch(Config1)}]},
+ {env, [{dispatch, init_dispatch(Config)}]},
{max_keepalive, 50},
{timeout, 500}
]),
Port = ranch:get_port(http_compress),
{ok, Client} = cowboy_client:init([]),
[{scheme, <<"http">>}, {port, Port}, {opts, []},
- {transport, Transport}, {client, Client}|Config1];
+ {transport, Transport}, {client, Client}|Config];
init_per_group(https_compress, Config) ->
Transport = ranch_ssl,
{_, Cert, Key} = ct_helper:make_certs(),
Opts = [{cert, Cert}, {key, Key}],
- Config1 = init_static_dir(Config),
application:start(public_key),
application:start(ssl),
{ok, _} = cowboy:start_https(https_compress, 100, Opts ++ [{port, 0}], [
{compress, true},
- {env, [{dispatch, init_dispatch(Config1)}]},
+ {env, [{dispatch, init_dispatch(Config)}]},
{max_keepalive, 50},
{timeout, 500}
]),
Port = ranch:get_port(https_compress),
{ok, Client} = cowboy_client:init(Opts),
[{scheme, <<"https">>}, {port, Port}, {opts, Opts},
- {transport, Transport}, {client, Client}|Config1];
+ {transport, Transport}, {client, Client}|Config];
init_per_group(onrequest, Config) ->
Transport = ranch_tcp,
{ok, _} = cowboy:start_http(onrequest, 100, [{port, 0}], [
@@ -301,15 +303,11 @@ init_per_group(set_env, Config) ->
[{scheme, <<"http">>}, {port, Port}, {opts, []},
{transport, Transport}, {client, Client}|Config].
-end_per_group(Group, Config) when Group =:= https; Group =:= https_compress ->
- cowboy:stop_listener(https),
+end_per_group(Name, _) when Name =:= https; Name =:= https_compress ->
+ cowboy:stop_listener(Name),
application:stop(ssl),
application:stop(public_key),
- end_static_dir(Config),
ok;
-end_per_group(Group, Config) when Group =:= http; Group =:= http_compress ->
- cowboy:stop_listener(http),
- end_static_dir(Config);
end_per_group(Name, _) ->
cowboy:stop_listener(Name),
ok.
@@ -357,7 +355,7 @@ init_dispatch(Config) ->
{"/static_specify_file/[...]", cowboy_static,
[{directory, ?config(static_dir, Config)},
{mimetypes, [{<<".css">>, [<<"text/css">>]}]},
- {file, <<"test_file.css">>}]},
+ {file, <<"style.css">>}]},
{"/multipart", http_multipart, []},
{"/echo/body", http_echo_body, []},
{"/echo/body_qs", http_body_qs, []},
@@ -370,6 +368,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, []},
@@ -381,29 +380,6 @@ init_dispatch(Config) ->
]}
]).
-init_static_dir(Config) ->
- Dir = filename:join(?config(priv_dir, Config), "static"),
- Level1 = fun(Name) -> filename:join(Dir, Name) end,
- ok = file:make_dir(Dir),
- ok = file:write_file(Level1("test_file"), "test_file\n"),
- ok = file:write_file(Level1("test_file.css"), "test_file.css\n"),
- ok = file:write_file(Level1("test_noread"), "test_noread\n"),
- ok = file:change_mode(Level1("test_noread"), 8#0333),
- ok = file:write_file(Level1("test.html"), "test.html\n"),
- ok = file:make_dir(Level1("test_dir")),
- [{static_dir, Dir}|Config].
-
-end_static_dir(Config) ->
- Dir = ?config(static_dir, Config),
- Level1 = fun(Name) -> filename:join(Dir, Name) end,
- ok = file:delete(Level1("test_file")),
- ok = file:delete(Level1("test_file.css")),
- ok = file:delete(Level1("test_noread")),
- ok = file:delete(Level1("test.html")),
- ok = file:del_dir(Level1("test_dir")),
- ok = file:del_dir(Dir),
- Config.
-
%% Convenience functions.
quick_raw(Data, Config) ->
@@ -513,9 +489,9 @@ check_status(Config) ->
{400, "/static/%2f"},
{400, "/static/%2e"},
{400, "/static/%2e%2e"},
- {403, "/static/test_dir"},
- {403, "/static/test_dir/"},
- {403, "/static/test_noread"},
+ {403, "/static/directory"},
+ {403, "/static/directory/"},
+ {403, "/static/unreadable"},
{404, "/not/found"},
{404, "/static/not_found"},
{500, "/handler_errors?case=handler_before_reply"},
@@ -999,6 +975,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 = [
@@ -1114,9 +1099,9 @@ slowloris2(Config) ->
static_attribute_etag(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
- build_url("/static_attribute_etag/test.html", Config), Client),
+ build_url("/static_attribute_etag/index.html", Config), Client),
{ok, Client3} = cowboy_client:request(<<"GET">>,
- build_url("/static_attribute_etag/test.html", Config), Client2),
+ build_url("/static_attribute_etag/index.html", Config), Client2),
{ok, 200, Headers1, Client4} = cowboy_client:response(Client3),
{ok, 200, Headers2, _} = cowboy_client:response(Client4),
{<<"etag">>, ETag1} = lists:keyfind(<<"etag">>, 1, Headers1),
@@ -1127,9 +1112,9 @@ static_attribute_etag(Config) ->
static_function_etag(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
- build_url("/static_function_etag/test.html", Config), Client),
+ build_url("/static_function_etag/index.html", Config), Client),
{ok, Client3} = cowboy_client:request(<<"GET">>,
- build_url("/static_function_etag/test.html", Config), Client2),
+ build_url("/static_function_etag/index.html", Config), Client2),
{ok, 200, Headers1, Client4} = cowboy_client:response(Client3),
{ok, 200, Headers2, _} = cowboy_client:response(Client4),
{<<"etag">>, ETag1} = lists:keyfind(<<"etag">>, 1, Headers1),
@@ -1150,7 +1135,7 @@ static_function_etag(Arguments, etag_data) ->
static_mimetypes_function(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
- build_url("/static_mimetypes_function/test.html", Config), Client),
+ build_url("/static_mimetypes_function/index.html", Config), Client),
{ok, 200, Headers, _} = cowboy_client:response(Client2),
{<<"content-type">>, <<"text/html">>}
= lists:keyfind(<<"content-type">>, 1, Headers).
@@ -1162,7 +1147,7 @@ static_specify_file(Config) ->
{ok, 200, Headers, Client3} = cowboy_client:response(Client2),
{<<"content-type">>, <<"text/css">>}
= lists:keyfind(<<"content-type">>, 1, Headers),
- {ok, <<"test_file.css\n">>, _} = cowboy_client:response_body(Client3).
+ {ok, <<"body{color:red}\n">>, _} = cowboy_client:response_body(Client3).
static_specify_file_catchall(Config) ->
Client = ?config(client, Config),
@@ -1171,12 +1156,12 @@ static_specify_file_catchall(Config) ->
{ok, 200, Headers, Client3} = cowboy_client:response(Client2),
{<<"content-type">>, <<"text/css">>}
= lists:keyfind(<<"content-type">>, 1, Headers),
- {ok, <<"test_file.css\n">>, _} = cowboy_client:response_body(Client3).
+ {ok, <<"body{color:red}\n">>, _} = cowboy_client:response_body(Client3).
static_test_file(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
- build_url("/static/test_file", Config), Client),
+ build_url("/static/unknown", Config), Client),
{ok, 200, Headers, _} = cowboy_client:response(Client2),
{<<"content-type">>, <<"application/octet-stream">>}
= lists:keyfind(<<"content-type">>, 1, Headers).
@@ -1184,7 +1169,7 @@ static_test_file(Config) ->
static_test_file_css(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
- build_url("/static/test_file.css", Config), Client),
+ build_url("/static/style.css", Config), Client),
{ok, 200, Headers, _} = cowboy_client:response(Client2),
{<<"content-type">>, <<"text/css">>}
= lists:keyfind(<<"content-type">>, 1, 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}.
diff --git a/test/spdy_SUITE.erl b/test/spdy_SUITE.erl
index df29281..1089991 100644
--- a/test/spdy_SUITE.erl
+++ b/test/spdy_SUITE.erl
@@ -44,9 +44,13 @@ init_per_suite(Config) ->
application:start(cowboy),
application:start(public_key),
application:start(ssl),
- Config.
+ Dir = ?config(priv_dir, Config) ++ "/static",
+ ct_helper:create_static_dir(Dir),
+ [{static_dir, Dir}|Config].
-end_per_suite(_Config) ->
+end_per_suite(Config) ->
+ Dir = ?config(static_dir, Config),
+ ct_helper:delete_static_dir(Dir),
application:stop(ssl),
application:stop(public_key),
application:stop(cowboy),
@@ -69,9 +73,12 @@ end_per_group(Name, _) ->
%% Dispatch configuration.
-init_dispatch(_) ->
+init_dispatch(Config) ->
cowboy_router:compile([
{"localhost", [
+ {"/static/[...]", cowboy_static,
+ [{directory, ?config(static_dir, Config)},
+ {mimetypes, [{<<".css">>, [<<"text/css">>]}]}]},
{"/chunked", http_chunked, []},
{"/", http_handler, []}
]}
@@ -152,6 +159,7 @@ check_status(Config) ->
Tests = [
{200, nofin, "localhost", "/"},
{200, nofin, "localhost", "/chunked"},
+ {200, nofin, "localhost", "/static/style.css"},
{400, fin, "bad-host", "/"},
{400, fin, "localhost", "bad-path"},
{404, fin, "localhost", "/this/path/does/not/exist"}