diff options
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | examples/README.md | 28 | ||||
-rw-r--r-- | src/cowboy_http.erl | 63 | ||||
-rw-r--r-- | src/cowboy_req.erl | 6 |
4 files changed, 71 insertions, 44 deletions
@@ -18,13 +18,6 @@ Because it uses Ranch for managing connections, Cowboy can easily be No parameterized module. No process dictionary. **Clean** Erlang code. -Quick start ------------ - - * Add Cowboy as a rebar dependency to your application - * Start Cowboy and add one or more listeners - * Write handlers for your application - Getting Started --------------- @@ -32,10 +25,17 @@ Getting Started * Look at the examples in the `examples/` directory * Build API documentation with `make docs`; open `doc/index.html` +Support +------- + * Official IRC Channel: #ninenines on irc.freenode.net + * [Mailing Lists](http://lists.ninenines.eu) + * [Commercial Support](http://ninenines.eu/support) -Old README ----------- + + +Old README (deprecated) +----------------------- This and all following sections will be removed as soon as their equivalent appear in the Cowboy guide. diff --git a/examples/README.md b/examples/README.md index 0289754..598420c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,20 +1,22 @@ -Cowboy examples +Cowboy Examples =============== -* [hello_world](./examples/hello_world): -simplest example application + * [chunked_hello_world](./examples/chunked_hello_world): + demonstrates chunked data transfer with two one-second delays -* [echo_get](./examples/echo_get): -parse and echo a GET query string -* [echo_post](./examples/echo_post): -parse and echo a POST parameter + * [echo_get](./examples/echo_get): + parse and echo a GET query string -* [rest_hello_world](./examples/rest_hello_world): -return the data type that matches the request type (ex: html, text, json...) + * [echo_post](./examples/echo_post): + parse and echo a POST parameter -* [chunked_hello_world](./examples/chunked_hello_world): -demonstrates chunked data transfer with two one-second delays -* [static](./examples/static): -an example file server + * [hello_world](./examples/hello_world): + simplest example application + + * [rest_hello_world](./examples/rest_hello_world): + return the data type that matches the request type (ex: html, text, json) + + * [static](./examples/static): + an example file server diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index 44984e0..4754649 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -815,9 +815,20 @@ ce_identity(Data) -> -spec cookie_to_iodata(iodata(), iodata(), cowboy_req:cookie_opts()) -> iodata(). cookie_to_iodata(Name, Value, Opts) -> + case binary:match(Name, [<<$=>>, <<$,>>, <<$;>>, + <<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of + nomatch -> ok + end, + case binary:match(Value, [<<$,>>, <<$;>>, + <<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of + nomatch -> ok + end, MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of false -> <<>>; - {_, MaxAge} when is_integer(MaxAge), MaxAge >= 0 -> + {_, 0} -> + %% MSIE requires an Expires date in the past to delete a cookie. + <<"; Expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0">>; + {_, MaxAge} when is_integer(MaxAge), MaxAge > 0 -> UTC = calendar:universal_time(), Secs = calendar:datetime_to_gregorian_seconds(UTC), Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge), @@ -826,11 +837,11 @@ cookie_to_iodata(Name, Value, Opts) -> end, DomainBin = case lists:keyfind(domain, 1, Opts) of false -> <<>>; - {_, Domain} -> [<<"; Domain=">>, quote(Domain)] + {_, Domain} -> [<<"; Domain=">>, Domain] end, PathBin = case lists:keyfind(path, 1, Opts) of false -> <<>>; - {_, Path} -> [<<"; Path=">>, quote(Path)] + {_, Path} -> [<<"; Path=">>, Path] end, SecureBin = case lists:keyfind(secure, 1, Opts) of false -> <<>>; @@ -840,21 +851,9 @@ cookie_to_iodata(Name, Value, Opts) -> false -> <<>>; {_, true} -> <<"; HttpOnly">> end, - [Name, <<"=">>, quote(Value), <<"; Version=1">>, + [Name, <<"=">>, Value, <<"; Version=1">>, MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin]. --spec quote(binary()) -> binary(). -quote(Bin) -> - quote(Bin, << $" >>). - --spec quote(binary(), binary()) -> binary(). -quote(<<>>, Acc) -> - << Acc/binary, $" >>; -quote(<< $", Rest/bits >>, Acc) -> - quote(Rest, << Acc/binary, $\\, $" >>); -quote(<< C, Rest/bits >>, Acc) -> - quote(Rest, << Acc/binary, C >>). - %% @doc Convert an HTTP version tuple to its binary form. -spec version_to_binary(version()) -> binary(). version_to_binary({1, 1}) -> <<"HTTP/1.1">>; @@ -1160,14 +1159,14 @@ cookie_to_iodata_test_() -> Tests = [ {<<"Customer">>, <<"WILE_E_COYOTE">>, [{http_only, true}, {domain, <<"acme.com">>}], - <<"Customer=\"WILE_E_COYOTE\"; Version=1; " - "Domain=\"acme.com\"; HttpOnly">>}, + <<"Customer=WILE_E_COYOTE; Version=1; " + "Domain=acme.com; HttpOnly">>}, {<<"Customer">>, <<"WILE_E_COYOTE">>, [{path, <<"/acme">>}], - <<"Customer=\"WILE_E_COYOTE\"; Version=1; Path=\"/acme\"">>}, + <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>}, {<<"Customer">>, <<"WILE_E_COYOTE">>, [{path, <<"/acme">>}, {badoption, <<"negatory">>}], - <<"Customer=\"WILE_E_COYOTE\"; Version=1; Path=\"/acme\"">>} + <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>} ], [{R, fun() -> R = iolist_to_binary(cookie_to_iodata(N, V, O)) end} || {N, V, O, R} <- Tests]. @@ -1177,7 +1176,7 @@ cookie_to_iodata_max_age_test() -> binary:split(iolist_to_binary( cookie_to_iodata(N, V, O)), <<";">>, [global]) end, - [<<"Customer=\"WILE_E_COYOTE\"">>, + [<<"Customer=WILE_E_COYOTE">>, <<" Version=1">>, <<" Expires=", _/binary>>, <<" Max-Age=111">>, @@ -1186,13 +1185,33 @@ cookie_to_iodata_max_age_test() -> case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of {'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok end, - [<<"Customer=\"WILE_E_COYOTE\"">>, + [<<"Customer=WILE_E_COYOTE">>, <<" Version=1">>, <<" Expires=", _/binary>>, <<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, 86417}]), ok. +cookie_to_iodata_failures_test_() -> + F = fun(N, V) -> + try cookie_to_iodata(N, V, []) of + _ -> + false + catch _:_ -> + true + end + end, + Tests = [ + {<<"Na=me">>, <<"Value">>}, + {<<"Name;">>, <<"Value">>}, + {<<"\r\name">>, <<"Value">>}, + {<<"Name">>, <<"Value;">>}, + {<<"Name">>, <<"\value">>} + ], + [{iolist_to_binary(io_lib:format("{~p, ~p} failure", [N, V])), + fun() -> true = F(N, V) end} + || {N, V} <- Tests]. + x_www_form_urlencoded_test_() -> %% {Qs, Result} Tests = [ diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl index dc98e30..b29d694 100644 --- a/src/cowboy_req.erl +++ b/src/cowboy_req.erl @@ -800,6 +800,12 @@ multipart_skip(Req) -> %% Response API. %% @doc Add a cookie header to the response. +%% +%% The cookie name cannot contain any of the following characters: +%% =,;\s\t\r\n\013\014 +%% +%% The cookie value cannot contain any of the following characters: +%% ,; \t\r\n\013\014 -spec set_resp_cookie(iodata(), iodata(), cookie_opts(), Req) -> Req when Req::req(). set_resp_cookie(Name, Value, Opts, Req) -> |