diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cowboy.app.src | 3 | ||||
-rw-r--r-- | src/cowboy_dispatcher.erl | 11 | ||||
-rw-r--r-- | src/cowboy_req.erl | 32 | ||||
-rw-r--r-- | src/cowboy_rest.erl | 40 | ||||
-rw-r--r-- | src/cowboy_static.erl | 4 |
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. |