aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md18
-rw-r--r--examples/README.md28
-rw-r--r--src/cowboy_http.erl63
-rw-r--r--src/cowboy_req.erl6
4 files changed, 71 insertions, 44 deletions
diff --git a/README.md b/README.md
index 826f271..b850083 100644
--- a/README.md
+++ b/README.md
@@ -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) ->