aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cowboy.app.src3
-rw-r--r--src/cowboy_dispatcher.erl11
-rw-r--r--src/cowboy_req.erl32
-rw-r--r--src/cowboy_rest.erl40
-rw-r--r--src/cowboy_static.erl4
5 files changed, 63 insertions, 27 deletions
diff --git a/src/cowboy.app.src b/src/cowboy.app.src
index b68ef14..d32262e 100644
--- a/src/cowboy.app.src
+++ b/src/cowboy.app.src
@@ -13,7 +13,10 @@
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
{application, cowboy, [
+ {id, "Cowboy"},
{description, "Small, fast, modular HTTP server."},
+ {sub_description, "Cowboy is also a socket acceptor pool, "
+ "able to accept connections for any kind of TCP protocol."},
{vsn, "0.7.0"},
{modules, []},
{registered, [cowboy_clock, cowboy_sup]},
diff --git a/src/cowboy_dispatcher.erl b/src/cowboy_dispatcher.erl
index d21f1b6..fae18fc 100644
--- a/src/cowboy_dispatcher.erl
+++ b/src/cowboy_dispatcher.erl
@@ -21,7 +21,7 @@
-type bindings() :: [{atom(), binary()}].
-type tokens() :: [binary()].
--type match_rule() :: '_' | '*' | [binary() | '_' | '...' | atom()].
+-type match_rule() :: '_' | <<_:8>> | [binary() | '_' | '...' | atom()].
-type dispatch_path() :: [{match_rule(), module(), any()}].
-type dispatch_rule() :: {Host::match_rule(), Path::dispatch_path()}.
-type dispatch_rules() :: [dispatch_rule()].
@@ -45,9 +45,10 @@
%% <em>PathRules</em> being a list of <em>{Path, HandlerMod, HandlerOpts}</em>.
%%
%% <em>Hostname</em> and <em>Path</em> are match rules and can be either the
-%% atom <em>'_'</em>, which matches everything for a single token, the atom
-%% <em>'*'</em>, which matches everything for the rest of the tokens, or a
-%% list of tokens. Each token can be either a binary, the atom <em>'_'</em>,
+%% atom <em>'_'</em>, which matches everything, <<"*">>, which match the
+%% wildcard path, or a list of tokens.
+%%
+%% Each token can be either a binary, the atom <em>'_'</em>,
%% the atom '...' or a named atom. A binary token must match exactly,
%% <em>'_'</em> matches everything for a single token, <em>'...'</em> matches
%% everything for the rest of the tokens and a named atom will bind the
@@ -97,7 +98,7 @@ match_path([], _, _, _) ->
{error, notfound, path};
match_path([{'_', Handler, Opts}|_Tail], HostInfo, _, Bindings) ->
{ok, Handler, Opts, Bindings, HostInfo, undefined};
-match_path([{'*', Handler, Opts}|_Tail], HostInfo, '*', Bindings) ->
+match_path([{<<"*">>, Handler, Opts}|_Tail], HostInfo, <<"*">>, Bindings) ->
{ok, Handler, Opts, Bindings, HostInfo, undefined};
match_path([{PathMatch, Handler, Opts}|Tail], HostInfo, Tokens,
Bindings) when is_list(Tokens) ->
diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl
index 67d2a0f..3426dbc 100644
--- a/src/cowboy_req.erl
+++ b/src/cowboy_req.erl
@@ -1138,8 +1138,18 @@ response_merge_headers(Headers, RespHeaders, DefaultHeaders) ->
-spec merge_headers(cowboy_http:headers(), cowboy_http:headers())
-> cowboy_http:headers().
+
+%% Merge headers by prepending the tuples in the second list to the
+%% first list. It also handles Set-Cookie properly, which supports
+%% duplicated entries. Notice that, while the RFC2109 does allow more
+%% than one cookie to be set per Set-Cookie header, we are following
+%% the implementation of common web servers and applications which
+%% return many distinct headers per each Set-Cookie entry to avoid
+%% issues with clients/browser which may not support it.
merge_headers(Headers, []) ->
Headers;
+merge_headers(Headers, [{<<"set-cookie">>, Value}|Tail]) ->
+ merge_headers([{<<"set-cookie">>, Value}|Headers], Tail);
merge_headers(Headers, [{Name, Value}|Tail]) ->
Headers2 = case lists:keymember(Name, 1, Headers) of
true -> Headers;
@@ -1333,4 +1343,26 @@ connection_to_atom_test_() ->
[{lists:flatten(io_lib:format("~p", [T])),
fun() -> R = connection_to_atom(T) end} || {T, R} <- Tests].
+merge_headers_test() ->
+ Left0 = [{<<"content-length">>,<<"13">>},{<<"server">>,<<"Cowboy">>}],
+ Right0 = [{<<"set-cookie">>,<<"foo=bar">>},{<<"content-length">>,<<"11">>}],
+
+ ?assertMatch(
+ [{<<"set-cookie">>,<<"foo=bar">>},
+ {<<"content-length">>,<<"13">>},
+ {<<"server">>,<<"Cowboy">>}],
+ merge_headers(Left0, Right0)),
+
+ Left1 = [{<<"content-length">>,<<"13">>},{<<"server">>,<<"Cowboy">>}],
+ Right1 = [{<<"set-cookie">>,<<"foo=bar">>},{<<"set-cookie">>,<<"bar=baz">>}],
+
+ ?assertMatch(
+ [{<<"set-cookie">>,<<"bar=baz">>},
+ {<<"set-cookie">>,<<"foo=bar">>},
+ {<<"content-length">>,<<"13">>},
+ {<<"server">>,<<"Cowboy">>}],
+ merge_headers(Left1, Right1)),
+
+ ok.
+
-endif.
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index 61062ab..1c0554a 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -73,7 +73,7 @@ upgrade(_ListenerPid, Handler, Opts, Req) ->
catch Class:Reason ->
PLReq = cowboy_req:to_list(Req),
error_logger:error_msg(
- "** Handler ~p terminating in rest_init/3~n"
+ "** Handler ~p terminating in rest_init/2~n"
" for the reason ~p:~p~n** Options were ~p~n"
"** Request was ~p~n** Stacktrace: ~p~n~n",
[Handler, Class, Reason, Opts, PLReq, erlang:get_stacktrace()]),
@@ -126,7 +126,7 @@ allowed_methods(Req, State=#state{method=Method}) ->
method_not_allowed(Req, State, Methods) ->
Req2 = cowboy_req:set_resp_header(
- <<"Allow">>, method_not_allowed_build(Methods, []), Req),
+ <<"allow">>, method_not_allowed_build(Methods, []), Req),
respond(Req2, State, 405).
method_not_allowed_build([], []) ->
@@ -153,7 +153,7 @@ is_authorized(Req, State) ->
forbidden(Req2, State#state{handler_state=HandlerState});
{{false, AuthHead}, Req2, HandlerState} ->
Req3 = cowboy_req:set_resp_header(
- <<"Www-Authenticate">>, AuthHead, Req2),
+ <<"www-authenticate">>, AuthHead, Req2),
respond(Req3, State#state{handler_state=HandlerState}, 401)
end.
@@ -166,7 +166,7 @@ valid_content_headers(Req, State) ->
known_content_type(Req, State) ->
expect(Req, State, known_content_type, true,
- fun valid_entity_length/2, 413).
+ fun valid_entity_length/2, 415).
valid_entity_length(Req, State) ->
expect(Req, State, valid_entity_length, true, fun options/2, 413).
@@ -351,7 +351,7 @@ match_language(Req, State, Accept, [Provided|Tail],
end.
set_language(Req, State=#state{language_a=Language}) ->
- Req2 = cowboy_req:set_resp_header(<<"Content-Language">>, Language, Req),
+ Req2 = cowboy_req:set_resp_header(<<"content-language">>, Language, Req),
charsets_provided(cowboy_req:set_meta(language, Language, Req2), State).
%% charsets_provided should return a list of binary values indicating
@@ -415,7 +415,7 @@ set_content_type(Req, State=#state{
undefined -> ContentType;
Charset -> [ContentType, <<"; charset=">>, Charset]
end,
- Req2 = cowboy_req:set_resp_header(<<"Content-Type">>, ContentType2, Req),
+ Req2 = cowboy_req:set_resp_header(<<"content-type">>, ContentType2, Req),
encodings_provided(cowboy_req:set_meta(charset, Charset, Req2), State).
set_content_type_build_params([], []) ->
@@ -446,17 +446,17 @@ variances(Req, State=#state{content_types_p=CTP,
Variances = case CTP of
[] -> [];
[_] -> [];
- [_|_] -> [<<"Accept">>]
+ [_|_] -> [<<"accept">>]
end,
Variances2 = case LP of
[] -> Variances;
[_] -> Variances;
- [_|_] -> [<<"Accept-Language">>|Variances]
+ [_|_] -> [<<"accept-language">>|Variances]
end,
Variances3 = case CP of
[] -> Variances2;
[_] -> Variances2;
- [_|_] -> [<<"Accept-Charset">>|Variances2]
+ [_|_] -> [<<"accept-charset">>|Variances2]
end,
{Variances4, Req3, State2} = case call(Req, State, variances) of
no_call ->
@@ -470,13 +470,13 @@ variances(Req, State=#state{content_types_p=CTP,
resource_exists(Req3, State2);
[[<<", ">>, H]|Variances5] ->
Req4 = cowboy_req:set_resp_header(
- <<"Vary">>, [H|Variances5], Req3),
+ <<"vary">>, [H|Variances5], Req3),
resource_exists(Req4, State2)
end.
resource_exists(Req, State) ->
expect(Req, State, resource_exists, true,
- fun if_match_exists/2, fun if_match_musnt_exist/2).
+ fun if_match_exists/2, fun if_match_must_not_exist/2).
if_match_exists(Req, State) ->
case cowboy_req:parse_header(<<"if-match">>, Req) of
@@ -496,7 +496,7 @@ if_match(Req, State, EtagsList) ->
false -> precondition_failed(Req2, State2)
end.
-if_match_musnt_exist(Req, State) ->
+if_match_must_not_exist(Req, State) ->
case cowboy_req:header(<<"if-match">>, Req) of
{undefined, Req2} -> is_put_to_missing_resource(Req2, State);
{_Any, Req2} -> precondition_failed(Req2, State)
@@ -577,7 +577,7 @@ if_modified_since(Req, State, IfModifiedSince) ->
end.
not_modified(Req, State) ->
- Req2 = cowboy_req:delete_resp_header(<<"Content-Type">>, Req),
+ Req2 = cowboy_req:delete_resp_header(<<"content-type">>, Req),
{Req3, State2} = set_resp_etag(Req2, State),
{Req4, State3} = set_resp_expires(Req3, State2),
respond(Req4, State3, 304).
@@ -596,7 +596,7 @@ moved_permanently(Req, State, OnFalse) ->
case call(Req, State, moved_permanently) of
{{true, Location}, Req2, HandlerState} ->
Req3 = cowboy_req:set_resp_header(
- <<"Location">>, Location, Req2),
+ <<"location">>, Location, Req2),
respond(Req3, State#state{handler_state=HandlerState}, 301);
{false, Req2, HandlerState} ->
OnFalse(Req2, State#state{handler_state=HandlerState});
@@ -617,7 +617,7 @@ moved_temporarily(Req, State) ->
case call(Req, State, moved_temporarily) of
{{true, Location}, Req2, HandlerState} ->
Req3 = cowboy_req:set_resp_header(
- <<"Location">>, Location, Req2),
+ <<"location">>, Location, Req2),
respond(Req3, State#state{handler_state=HandlerState}, 307);
{false, Req2, HandlerState} ->
is_post_to_missing_resource(Req2, State#state{handler_state=HandlerState}, 410);
@@ -670,7 +670,7 @@ create_path(Req, State) ->
{HostURL, Req3} = cowboy_req:host_url(Req2),
State2 = State#state{handler_state=HandlerState},
Req4 = cowboy_req:set_resp_header(
- <<"Location">>, << HostURL/binary, Path/binary >>, Req3),
+ <<"location">>, << HostURL/binary, Path/binary >>, Req3),
put_resource(cowboy_req:set_meta(put_path, Path, Req4),
State2, 303)
end.
@@ -744,7 +744,7 @@ choose_content_type(Req, State, OnTrue, ContentType, [_Any|Tail]) ->
%% This is easily testable because we would have set the Location
%% header by this point if we did so.
is_new_resource(Req, State) ->
- case cowboy_req:has_resp_header(<<"Location">>, Req) of
+ case cowboy_req:has_resp_header(<<"location">>, Req) of
true -> respond(Req, State, 201);
false -> has_resp_body(Req, State)
end.
@@ -767,7 +767,7 @@ set_resp_body(Req, State=#state{content_type_a={_Type, Fun}}) ->
LastModified ->
LastModifiedStr = httpd_util:rfc1123_date(LastModified),
Req4 = cowboy_req:set_resp_header(
- <<"Last-Modified">>, LastModifiedStr, Req3)
+ <<"last-modified">>, LastModifiedStr, Req3)
end,
{Req5, State4} = set_resp_expires(Req4, State3),
case call(Req5, State4, Fun) of
@@ -796,7 +796,7 @@ set_resp_etag(Req, State) ->
{Req2, State2};
Etag ->
Req3 = cowboy_req:set_resp_header(
- <<"ETag">>, encode_etag(Etag), Req2),
+ <<"etag">>, encode_etag(Etag), Req2),
{Req3, State2}
end.
@@ -812,7 +812,7 @@ set_resp_expires(Req, State) ->
Expires ->
ExpiresStr = httpd_util:rfc1123_date(Expires),
Req3 = cowboy_req:set_resp_header(
- <<"Expires">>, ExpiresStr, Req2),
+ <<"expires">>, ExpiresStr, Req2),
{Req3, State2}
end.
diff --git a/src/cowboy_static.erl b/src/cowboy_static.erl
index 3b63afe..1b4ff89 100644
--- a/src/cowboy_static.erl
+++ b/src/cowboy_static.erl
@@ -158,7 +158,7 @@
%% {file, <<"index.html">>}]}
%%
%% %% Serve cowboy/priv/www/page.html under http://example.com/*/page
-%% {['*', <<"page">>], cowboy_static,
+%% {['_', <<"page">>], cowboy_static,
%% [{directory, {priv_dir, cowboy, [<<"www">>]}}
%% {file, <<"page.html">>}]}.
%%
@@ -451,7 +451,7 @@ path_to_mimetypes(Filepath, Extensions) when is_binary(Filepath) ->
-spec path_to_mimetypes_(binary(), [{binary(), [mimedef()]}]) -> [mimedef()].
path_to_mimetypes_(Ext, Extensions) ->
- case lists:keyfind(Ext, 1, Extensions) of
+ case lists:keyfind(cowboy_bstr:to_lower(Ext), 1, Extensions) of
{_, MTs} -> MTs;
_Unknown -> default_mimetype()
end.