Skip to content

Commit

Permalink
Merge pull request #39 from inaka/harenson.34.allow-more-than-one-ser…
Browse files Browse the repository at this point in the history
…ver-running-on-the-same-node

[Fix #34] Allow more than one server running on the same node
  • Loading branch information
Brujo Benavides committed Dec 2, 2015
2 parents 089a3f6 + 873b837 commit 2054fc0
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ LOCAL_DEPS := tools compiler syntax_tools common_test inets test_server dialyzer
TEST_ERLC_OPTS += +debug_info
CT_OPTS = -cover test/cowboy_swagger.coverspec -erl_args -config ${CONFIG}

SHELL_OPTS = -s sync
SHELL_OPTS = -s sync -config ${CONFIG}

quicktests: app
@$(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
Expand Down
36 changes: 21 additions & 15 deletions src/cowboy_swagger_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,27 @@

%% Trails
-behaviour(trails_handler).
-export([trails/0]).
-export([trails/0, trails/1]).

-type state() :: #{}.
-type route_match() :: '_' | iodata().
-type options() :: #{server => ranch:ref(), host => route_match()}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Cowboy Callbacks
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% @hidden
-spec init({atom(), atom()}, cowboy_req:req(), state()) ->
-spec init({atom(), atom()}, cowboy_req:req(), options()) ->
{upgrade, protocol, cowboy_rest}.
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.

%% @hidden
-spec rest_init(cowboy_req:req(), state()) ->
{ok, cowboy_req:req(), term()}.
rest_init(Req, _Opts) ->
{ok, Req, #{}}.
-spec rest_init(cowboy_req:req(), options()) ->
{ok, cowboy_req:req(), options()}.
rest_init(Req, Opts) ->
{ok, Req, Opts}.

%% @hidden
-spec content_types_provided(cowboy_req:req(), state()) ->
Expand All @@ -46,18 +48,23 @@ content_types_provided(Req, State) ->

%% @hidden
handle_get(Req, State) ->
Trails = trails:all(),
Server = maps:get(server, State, '_'),
HostMatch = maps:get(host, State, '_'),
Trails = trails:all(Server, HostMatch),
{cowboy_swagger:to_json(Trails), Req, State}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Trails
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% @hidden
%% @doc Implementets `trails_handler:trails/0' callback. This function returns
%% @doc Implements `trails_handler:trails/0' callback. This function returns
%% trails routes for both: static content (Swagger-UI) and this handler
%% that returns the `swagger.json'.
trails() ->
-spec trails() -> trails:trails().
trails() -> trails(#{}).
-spec trails(Options::options()) -> trails:trails().
trails(Options) ->
StaticFiles =
case application:get_env(cowboy_swagger, static_files) of
{ok, Val} -> Val;
Expand All @@ -81,18 +88,17 @@ trails() ->
}
},
Handler = trails:trail(
"/api-docs/swagger.json", cowboy_swagger_handler, [], MD),
"/api-docs/swagger.json", cowboy_swagger_handler, Options, MD),
[Static1, Handler, Static2].

%% @private
-spec cowboy_swagger_priv() -> string().
cowboy_swagger_priv() ->
case code:priv_dir(cowboy_swagger) of
{error, bad_name} ->
filename:join(
[ filename:dirname(code:which(cowboy_swagger_handler))
, ".."
, "priv"
]);
case code:which(cowboy_swagger_handler) of
cover_compiled -> "../../priv"; % required for tests to work
BeamPath -> filename:join([filename:dirname(BeamPath) , ".." , "priv"])
end;
Path -> Path
end.
84 changes: 82 additions & 2 deletions test/cowboy_swagger_handler_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
-export([ all/0
, init_per_suite/1
, end_per_suite/1
, init_per_testcase/2
, end_per_testcase/2
]).

%% Test cases
-export([handler_test/1]).
-export([ handler_test/1
, multiple_hosts_test/1]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Common test
Expand All @@ -22,17 +25,44 @@ all() ->
) -> cowboy_swagger_test_utils:config().
init_per_suite(Config) ->
{ok, _} = shotgun:start(),
{ok, _} = example:start(),
Config.

-spec end_per_suite(
cowboy_swagger_test_utils:config()
) -> cowboy_swagger_test_utils:config().
end_per_suite(Config) ->
_ = shotgun:stop(),
Config.

-spec init_per_testcase(TestCase::atom(),
Config::cowboy_swagger_test_utils:config()) ->
cowboy_swagger_test_utils:config().
init_per_testcase(handler_test, Config) ->
{ok, _} = example:start(),
Config;
init_per_testcase(multiple_hosts_test, Config) ->
{ok, _} = multiple_hosts_servers_example:start(),
Config.

-spec end_per_testcase(TestCase::atom(),
Config::cowboy_swagger_test_utils:config()) ->
cowboy_swagger_test_utils:config().
end_per_testcase(handler_test, Config) ->
_ = example:stop(),
ok = cleanup(),
Config;
end_per_testcase(multiple_hosts_test, Config) ->
_ = multiple_hosts_servers_example:stop(),
ok = cleanup(),
Config.

%% @private
-spec cleanup() -> ok.
cleanup() ->
_ = application:stop(cowboy_swagger),
_ = application:stop(trails),
ok.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test Cases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Expand Down Expand Up @@ -73,5 +103,55 @@ handler_test(_Config) ->
ct:comment("GET /api-docs/unknown-file.ext should return 404 NOT FOUND"),
#{status_code := 404} =
cowboy_swagger_test_utils:api_call(get, "/api-docs/unknown-file.ext"),
{comment, ""}.

-spec multiple_hosts_test(_Config::cowboy_swagger_test_utils:config()) ->
{atom(), string()}.
multiple_hosts_test(_Config) ->
%% api1 - host1
Trails11 = trails:trails(example_echo_handler),
ExpectedPaths11 = get_expected_paths(Trails11),
%% GET swagger.json spec from localhost:8383
ct:comment("GET /api-docs/swagger.json should return 200 OK"),
#{status_code := 200, body := Body11} =
cowboy_swagger_test_utils:api_call(get,
"/api-docs/swagger.json",
"localhost",
8383),
#{<<"swagger">> := <<"2.0">>,
<<"info">> := #{<<"title">> := <<"Example API">>},
<<"paths">> := ExpectedPaths11} = cowboy_swagger:dec_json(Body11),
%% api1 - host2
Trails12 = trails:trails(host1_handler),
ExpectedPaths12 = get_expected_paths(Trails12),
%% GET swagger.json spec from 127.0.0.1:8383
ct:comment("GET /api-docs/swagger.json should return 200 OK"),
#{status_code := 200, body := Body12} =
cowboy_swagger_test_utils:api_call(get,
"/api-docs/swagger.json",
"127.0.0.1",
8383),
#{<<"swagger">> := <<"2.0">>,
<<"info">> := #{<<"title">> := <<"Example API">>},
<<"paths">> := ExpectedPaths12} = cowboy_swagger:dec_json(Body12),
%% api2 - host1
Trails21 = trails:trails([host1_handler, example_echo_handler]),
ExpectedPaths21 = get_expected_paths(Trails21),
%% GET swagger.json spec from localhost:8282
ct:comment("GET /api-docs/swagger.json should return 200 OK"),
#{status_code := 200, body := Body21} =
cowboy_swagger_test_utils:api_call(get,
"/api-docs/swagger.json",
"localhost",
8282),
#{<<"swagger">> := <<"2.0">>,
<<"info">> := #{<<"title">> := <<"Example API">>},
<<"paths">> := ExpectedPaths21} = cowboy_swagger:dec_json(Body21),
{comment, ""}.

%% @private
-spec get_expected_paths(Trails::trails:trails()) -> jiffy:json_value().
get_expected_paths(Trails) ->
SanitizeTrails = cowboy_swagger:filter_cowboy_swagger_handler(Trails),
cowboy_swagger:dec_json(
cowboy_swagger:enc_json(cowboy_swagger:swagger_paths(SanitizeTrails))).
15 changes: 5 additions & 10 deletions test/cowboy_swagger_test_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
, end_per_suite/1
]).
-export([ api_call/2
, api_call/3
, api_call/4
]).

Expand All @@ -28,17 +27,13 @@ end_per_suite(Config) ->

-spec api_call(atom(), string()) -> #{}.
api_call(Method, Uri) ->
api_call(Method, Uri, #{}).
api_call(Method, Uri, "localhost", 8080).

-spec api_call(atom(), string(), #{}) -> #{}.
api_call(Method, Uri, Headers) ->
api_call(Method, Uri, Headers, []).

-spec api_call(atom(), string(), #{}, iodata()) -> #{}.
api_call(Method, Uri, Headers, Body) ->
Port = application:get_env(example, http_port, 8080),
{ok, Pid} = shotgun:open("localhost", Port),
-spec api_call(atom(), string(), string(), integer()) -> #{}.
api_call(Method, Uri, HostMatch, Port) ->
{ok, Pid} = shotgun:open(HostMatch, Port),
try
Headers = #{}, Body = [],
{ok, Response} = shotgun:request(Pid, Method, Uri, Headers, Body, #{}),
Response
after
Expand Down
44 changes: 44 additions & 0 deletions test/host1_handler.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
-module(host1_handler).

-include_lib("mixer/include/mixer.hrl").
-mixin([
{example_default,
[
init/3,
rest_init/2,
content_types_accepted/2,
content_types_provided/2,
resource_exists/2
]}
]).

-export([ allowed_methods/2
, handle_get/2]).

%trails
-behaviour(trails_handler).
-export([trails/0]).

trails() ->
Metadata =
#{get =>
#{tags => ["whoami"],
description => "Get hostname",
produces => ["text/plain"]
}
},
[trails:trail(
"/whoami",
host1_handler,
#{},
Metadata)].

%% cowboy
allowed_methods(Req, State) ->
{[<<"GET">>], Req, State}.

%% internal
handle_get(Req, State) ->
{Host, Req1} = cowboy_req:host(Req),
Body = <<"I am ", Host/binary>>,
{Body, Req1, State}.
18 changes: 18 additions & 0 deletions test/multiple_hosts_servers_example.app
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{application, multiple_hosts_servers_example,
[
{description, "Cowboy Swagger Complex Example."},
{vsn, "0.1"},
{applications,
[ kernel
, stdlib
, sasl

, cowboy
, trails
, cowboy_swagger
]},
{modules, []},
{mod, {multiple_hosts_servers_example, []}},
{start_phases, [{start_multiple_hosts_servers_example_http, []}]}
]
}.
66 changes: 66 additions & 0 deletions test/multiple_hosts_servers_example.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
-module(multiple_hosts_servers_example).

-export([start/0]).
-export([start/2]).
-export([stop/0]).
-export([stop/1]).
-export([start_phase/3]).

%% application
%% @doc Starts the application
start() ->
application:ensure_all_started(multiple_hosts_servers_example).

%% @doc Stops the application
stop() ->
application:stop(multiple_hosts_servers_example).

%% behaviour
%% @private
start(_StartType, _StartArgs) ->
_ = application:stop(lager),
ok = application:stop(sasl),
{ok, _} = application:ensure_all_started(sasl),
{ok, self()}.

%% @private
stop(_State) ->
ok = cowboy:stop_listener(multiple_hosts_servers_http).

-spec start_phase(atom(), application:start_type(), []) -> ok | {error, term()}.
start_phase(start_multiple_hosts_servers_example_http, _StartType, []) ->
%% Host1
{ok, #{hosts := [HostMatch11, HostMatch12], port := Port1}} =
application:get_env(multiple_hosts_servers_example, api1),
{ok, #{hosts := ['_'], port := Port2}} =
application:get_env(multiple_hosts_servers_example, api2),
{ok, ListenerCount} =
application:get_env(multiple_hosts_servers_example, http_listener_count),

Trails11 =
trails:trails(example_echo_handler) ++
cowboy_swagger_handler:trails(#{server => api1, host => HostMatch11}),
Trails12 =
trails:trails(host1_handler) ++
cowboy_swagger_handler:trails(#{server => api1, host => HostMatch12}),
Routes1 = [{HostMatch11, Trails11}, {HostMatch12, Trails12}],

trails:store(api1, Routes1),
Dispatch1 = trails:compile(Routes1),
{ok, _} = start_cowboy(api1, ListenerCount, Dispatch1, Port1),

Trails21 =
trails:trails([host1_handler, example_echo_handler]) ++
cowboy_swagger_handler:trails(#{server => api2}),

trails:store(api2, Trails21),
Dispatch2 = trails:single_host_compile(Trails21),
{ok, _} = start_cowboy(api2, ListenerCount, Dispatch2, Port2),
ok.

%% @private
start_cowboy(Server, ListenerCount, Dispatch, Port) ->
RanchOptions = [{port, Port}],
CowboyOptions =
[{env, [{dispatch, Dispatch}]}, {compress, true}, {timeout, 12000}],
cowboy:start_http(Server, ListenerCount, RanchOptions, CowboyOptions).
11 changes: 9 additions & 2 deletions test/test.config
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@
]
},

{multiple_hosts_servers_example,
[
{http_listener_count, 10},
{api1, #{hosts => ["localhost", "127.0.0.1"], port => 8383}},
{api2, #{hosts => ['_'], port => 8282}}
]
},

{cowboy_swagger,
[
{static_files, "../../priv/swagger"},
{global_spec,
#{swagger => "2.0",
info => #{title => "Example API"},
basePath => "/api-docs"
basePath => ""
}
}
]
Expand Down

0 comments on commit 2054fc0

Please sign in to comment.