diff --git a/doc/configuration/Modules.md b/doc/configuration/Modules.md index bd3a93a489..d84e64d404 100644 --- a/doc/configuration/Modules.md +++ b/doc/configuration/Modules.md @@ -162,13 +162,6 @@ Handles push notifications generated by [mod_pubsub](../modules/mod_pubsub.md)'s Implements [XEP-0077: In-Band Registration](http://xmpp.org/extensions/xep-0077.html), that enables creating an account and changing the password once connected. This does not provide a solution to the forgotten password use case via SMS or email. -### mod_revproxy -With this extension, MongooseIM may serve as a reverse proxy. - -!!! Warning - This module is deprecated and can only be configured with the older, `.cfg` configuration file. - Please refer to the older versions of the documentation to see how to do this. - ### [mod_roster](../modules/mod_roster.md) Roster support, specified in [RFC 6121](http://xmpp.org/rfcs/rfc6121.html). Includes support for [XEP-0237: Roster Versioning](http://xmpp.org/extensions/xep-0237.html). diff --git a/doc/migrations/5.0.0_5.1.0.md b/doc/migrations/5.0.0_5.1.0.md index 944da98660..d9c157b8bf 100644 --- a/doc/migrations/5.0.0_5.1.0.md +++ b/doc/migrations/5.0.0_5.1.0.md @@ -55,3 +55,7 @@ There's an experimental and undocumented module called `mod_smart_markers`, that ## Inbox The archive functionality recently introduced has been extended to support many more boxes. IQ queries can remain as they were, but, a new flag called `box` is now introduced, and if provided, it takes preference over the old `archive` flag. The database requires a migration, as the archive is now a column storing the proper name of the box, see the migrations for Postgres, MySQL and MSSQL in the [`priv/migrations`](https://github.com/esl/MongooseIM/tree/master/priv/migrations) directory. + +## Removal of deprecated modules +* `mod_revproxy` - removed from the code base as it was unsupported since 4.2.0. +* `mod_aws_sns` - its functionality is fully covered by [`mod_event_pusher`](../modules/mod_event_pusher.md). diff --git a/include/mod_revproxy.hrl b/include/mod_revproxy.hrl deleted file mode 100644 index bfe69fb430..0000000000 --- a/include/mod_revproxy.hrl +++ /dev/null @@ -1,13 +0,0 @@ --record(upstream, {type :: uri | host, - protocol :: binary(), - host = [] :: [binary() | atom()], - path = [] :: [binary() | atom()]}). - --type upstream() :: #upstream{}. - --record(match, {upstream :: upstream(), - remainder = [] :: [binary() | atom()], - path = [] :: '_' | [binary() | atom()], - bindings = [] :: [{atom(), binary()}]}). - --type match() :: #match{}. diff --git a/rebar.config b/rebar.config index 9a60d62da9..8ea3f95f3b 100644 --- a/rebar.config +++ b/rebar.config @@ -21,7 +21,7 @@ %% *_backend mod_bosh_backend, mod_global_distrib_mapping_backend, - mod_pubsub_db_backend, mod_revproxy_dynamic, + mod_pubsub_db_backend, mod_shared_roster, %% Deprecated functions {crypto, rand_uniform, 2}, diff --git a/src/mod_revproxy.erl b/src/mod_revproxy.erl deleted file mode 100644 index 17be2d03dd..0000000000 --- a/src/mod_revproxy.erl +++ /dev/null @@ -1,394 +0,0 @@ -%%%=================================================================== -%%% @copyright (C) 2014, Erlang Solutions Ltd. -%%% @doc HTTP(S) reverse proxy for MongooseIM's Cowboy listener -%%% @end -%%%=================================================================== --module(mod_revproxy). --behaviour(gen_mod). --behaviour(cowboy_handler). --behaviour(mongoose_module_metrics). - -%% API --export([compile/1]). - -%% gen_mod callbacks --export([start/2, - stop/1]). - -%% cowboy_http_handler callbacks --export([init/2, - terminate/3]). - -%% to be used by tests only --export([compile_routes/1, - match/4, - upstream_uri/1, - split/4]). - --ignore_xref([{mod_revproxy_dynamic, rules, 0}, - compile/1, compile_routes/1, match/4, split/4, upstream_uri/1]). - --include("mod_revproxy.hrl"). - --record(state, {timeout, length, custom_headers}). - --type option() :: {atom(), any()}. --type state() :: #state{}. - --type host() :: '_' | string() | binary(). --type path() :: '_' | string() | binary(). --type method() :: '_' | atom() | string() | binary(). --type route() :: {host(), method(), upstream()} | - {host(), path(), method(), upstream()}. - -%%-------------------------------------------------------------------- -%% API -%%-------------------------------------------------------------------- --spec compile([route()]) -> ok. -compile(Routes) -> - Source = mod_revproxy_dynamic_src(Routes), - {Module, Code} = dynamic_compile:from_string(Source), - code:load_binary(Module, "mod_revproxy_dynamic.erl", Code), - ok. - -%%-------------------------------------------------------------------- -%% gen_mod callbacks -%%-------------------------------------------------------------------- --spec start(jid:server(), [option()]) -> ok. -start(_Host, Opts) -> - Routes = gen_mod:get_opt(routes, Opts, []), - compile(Routes). - --spec stop(jid:server()) -> ok. -stop(_Host) -> - ok. - -%%-------------------------------------------------------------------- -%% cowboy_http_handler callbacks -%%-------------------------------------------------------------------- --spec init(cowboy_req:req(), [option()]) - -> {ok, cowboy_req:req(), state()}. -init(Req, Opts) -> - Timeout = gen_mod:get_opt(timeout, Opts, 5000), - Length = gen_mod:get_opt(body_length, Opts, 8000000), - Headers = gen_mod:get_opt(custom_headers, Opts, []), - State = #state{timeout=Timeout, - length=Length, - custom_headers=Headers}, - handle(Req, State). - --spec handle(cowboy_req:req(), state()) -> {ok, cowboy_req:req(), state()}. -handle(Req, State) -> - Host = cowboy_req:header(<<"host">>, Req), - Path = cowboy_req:path(Req), - QS = cowboy_req:qs(Req), - PathQS = case QS of - <<>> -> - Path; - _ -> - <> - end, - Method = cowboy_req:method(Req), - Match = match(mod_revproxy_dynamic:rules(), Host, PathQS, Method), - handle_match(Match, Method, Req, State). - --spec terminate(any(), cowboy_req:req(), state()) -> ok. -terminate(_Reason, _Req, _State) -> - ok. - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -%% Passing and receiving request via fusco -handle_match(#match{}=Match, Method, Req, State) -> - {Host, Path} = upstream_uri(Match), - pass_request(Host, Path, Method, Req, State); -handle_match(false, _, Req, State) -> - Req1 = cowboy_req:reply(404, Req), - {ok, Req1, State}. - -pass_request(Host, Path, Method, Req, - #state{timeout=Timeout, custom_headers=CustomHeaders}=State) -> - {ok, Pid} = fusco:start_link(Host, [{connect_timeout, Timeout}]), - Headers = maps:to_list(cowboy_req:headers(Req)), - {Body, Req1} = request_body(Req, State), - Headers1 = Headers ++ CustomHeaders, - Response = fusco:request(Pid, Path, Method, Headers1, Body, Timeout), - fusco:disconnect(Pid), - return_response(Response, Req1, State). - -return_response({ok, {{Status, _}, Headers, Body, _, _}}, Req, State) -> - StatusI = binary_to_integer(Status), - Headers1 = remove_confusing_headers(Headers), - Req1 = cowboy_req:reply(StatusI, maps:from_list(Headers1), Body, Req), - {ok, Req1, State}; -return_response({error, connect_timeout}, Req, State) -> - Req1 = cowboy_req:reply(504, Req), - {ok, Req1, State}; -return_response({error, timeout}, Req, State) -> - Req1 = cowboy_req:reply(504, Req), - {ok, Req1, State}; -return_response({error, _Other}, Req, State) -> - Req1 = cowboy_req:reply(502, Req), - {ok, Req1, State}. - -request_body(Req, #state{length=Length}) -> - case cowboy_req:has_body(Req) of - false -> - {<<>>, Req}; - true -> - {ok, Data, Req1} = cowboy_req:read_body(Req, #{length => Length}), - {Data, Req1} - end. - -remove_confusing_headers(List) -> - [Header || {Field, _}=Header <- List, - not is_header_confusing(cowboy_bstr:to_lower(Field))]. - -is_header_confusing(<<"transfer-encoding">>) -> true; -is_header_confusing(_) -> false. - -%% Cowboy-like routing functions -upstream_uri(#match{upstream=Upstream, remainder=Remainder, - bindings=Bindings, path=Path}) -> - #upstream{type=Type, - protocol=Protocol, - host=UpHost, - path=UpPath} = Upstream, - BoundHost = upstream_bindings(UpHost, $., Bindings, <<>>), - PathSegments = case {Type, Path} of - {uri, _} -> UpPath ++ Remainder; - {_, '_'} -> UpPath ++ Remainder; - _ -> UpPath ++ Path ++ Remainder - end, - BoundPath = upstream_bindings(PathSegments, $/, Bindings, <<>>), - FullHost = <>, - FullPath = <<"/", BoundPath/binary>>, - {binary_to_list(FullHost), FullPath}. - -upstream_bindings([], _, _, Acc) -> - Acc; -upstream_bindings([<<>>|Tail], S, Bindings, Acc) when Tail =/= [] -> - upstream_bindings(Tail, S, Bindings, Acc); -upstream_bindings([Binding|Tail], S, Bindings, Acc) when is_atom(Binding) -> - {Binding, Value} = lists:keyfind(Binding, 1, Bindings), - upstream_bindings(Tail, S, Bindings, upstream_append(Value, S, Acc)); -upstream_bindings([Head|Tail], S, Bindings, Acc) -> - upstream_bindings(Tail, S, Bindings, upstream_append(Head, S, Acc)). - -upstream_append(Value, _, <<>>) -> - Value; -upstream_append(Value, S, Acc) -> - <>. - -%% Matching request to the upstream -match(Rules, Host, Path, Method) when is_list(Host), is_list(Path) -> - match_rules(Rules, Host, Path, Method); -match(Rules, Host, Path, Method) -> - match(Rules, split_host(Host), split_path(Path), Method). - -match_rules([], _, _, _) -> - false; -match_rules([Rule|Tail], Host, Path, Method) -> - case match_method(Rule, Host, Path, Method) of - false -> - match_rules(Tail, Host, Path, Method); - Result -> - Result - end. - -match_method({_, _, '_', _}=Rule, Host, Path, _Method) -> - match_path(Rule, Host, Path); -match_method({_, _, Method, _}=Rule, Host, Path, Method) -> - match_path(Rule, Host, Path); -match_method(_, _, _, _) -> - false. - -match_path({_, '_', _, _}=Rule, Host, Path) -> - match_host(Rule, Host, Path, []); -match_path({_, RulePath, _, _}=Rule, Host, Path) -> - match_path_segments(RulePath, Path, Rule, Host, []). - -match_path_segments([], Remainder, Rule, Host, Bindings) -> - match_host(Rule, Host, Remainder, Bindings); -match_path_segments([<<>>|T], Remainder, Rule, Host, Bindings) -> - match_path_segments(T, Remainder, Rule, Host, Bindings); -match_path_segments([H|T1], [H|T2], Rule, Host, Bindings) -> - match_path_segments(T1, T2, Rule, Host, Bindings); -match_path_segments([Binding|T1], [H|T2], Rule, Host, Bindings) - when is_atom(Binding) -> - case match_bindings(Binding, H, Bindings) of - false -> - false; - Bindings1 -> - match_path_segments(T1, T2, Rule, Host, Bindings1) - end; -match_path_segments(_, _, _, _, _) -> - false. - -match_host({'_', RulePath, _, Upstream}, _Host, Remainder, Bindings) -> - #match{upstream = Upstream, - path = RulePath, - remainder = Remainder, - bindings = Bindings}; -match_host({RuleHost, Path, _, Upstream}, Host, Remainder, Bindings) -> - match_host_segments(RuleHost, Host, Upstream, Remainder, Path, Bindings). - -match_host_segments([], [], Upstream, Remainder, Path, Bindings) -> - #match{upstream = Upstream, - path = Path, - remainder = Remainder, - bindings = Bindings}; -match_host_segments([H|T1], [H|T2], Upstream, Remainder, Path, Bindings) -> - match_host_segments(T1, T2, Upstream, Remainder, Path, Bindings); -match_host_segments([Binding|T1], [H|T2], Upstream, Remainder, Path, Bindings) - when is_atom(Binding) -> - case match_bindings(Binding, H, Bindings) of - false -> - false; - Bindings1 -> - match_host_segments(T1, T2, Upstream, Remainder, Path, Bindings1) - end; -match_host_segments(_, _, _, _, _, _) -> - false. - -match_bindings(Binding, Value, Bindings) -> - case lists:keyfind(Binding, 1, Bindings) of - {Binding, Value} -> - Bindings; - {Binding, _} -> - false; - false -> - lists:keystore(Binding, 1, Bindings, {Binding, Value}) - end. - -%% Rules compilation -compile_routes(Routes) -> - compile_routes(Routes, []). - -compile_routes([], Acc) -> - lists:reverse(Acc); -compile_routes([{Host, Method, Upstream}|Tail], Acc) -> - compile_routes([{Host, '_', Method, Upstream}|Tail], Acc); -compile_routes([{HostMatch, PathMatch, MethodMatch, UpstreamMatch}|Tail], Acc) -> - HostRule = compile_host(HostMatch), - Method = compile_method(MethodMatch), - Upstream = compile_upstream(UpstreamMatch), - PathRule = compile_path(PathMatch), - Host = {HostRule, PathRule, Method, Upstream}, - compile_routes(Tail, [Host|Acc]). - -compile_host('_') -> - '_'; -compile_host("_") -> - '_'; -compile_host(HostMatch) when is_list(HostMatch) -> - compile_host(list_to_binary(HostMatch)); -compile_host(HostMatch) when is_binary(HostMatch) -> - split_host(HostMatch). - -compile_path('_') -> - '_'; -compile_path("_") -> - '_'; -compile_path(PathMatch) when is_list(PathMatch) -> - compile_path(iolist_to_binary(PathMatch)); -compile_path(PathMatch) when is_binary(PathMatch) -> - split_path(PathMatch). - -compile_method('_') -> - '_'; -compile_method("_") -> - '_'; -compile_method(Bin) when is_binary(Bin) -> - cowboy_bstr:to_upper(Bin); -compile_method(List) when is_list(List) -> - compile_method(list_to_binary(List)); -compile_method(Atom) when is_atom(Atom) -> - compile_method(atom_to_binary(Atom, utf8)). - -compile_upstream(Bin) when is_binary(Bin) -> - split_upstream(Bin); -compile_upstream(List) when is_list(List) -> - compile_upstream(list_to_binary(List)); -compile_upstream(Atom) when is_atom(Atom) -> - Atom; -compile_upstream(_) -> - erlang:error(badarg). - -split_host(Host) -> - split(Host, $., [], <<>>). - -split_path(Path) -> - Split = split(Path, $/, [], <<>>), - Trailing = include_trailing(Path, $/, Split), - lists:reverse(Trailing). - -split_upstream(<<"http://", Rest/binary>>) -> - split_upstream(Rest, <<"http://">>); -split_upstream(<<"https://", Rest/binary>>) -> - split_upstream(Rest, <<"https://">>). - -split_upstream(URI, Protocol) -> - {Host, Path, Type} = case binary:split(URI, <<"/">>) of - [HostSeg] -> - {HostSeg, <<>>, host}; - [HostSeg, PathSeg] -> - {HostSeg, PathSeg, uri} - end, - HostSegments = split_host(Host), - PathSegments = split_path(Path), - #upstream{type = Type, - protocol = Protocol, - host = lists:reverse(HostSegments), - path = PathSegments}. - -include_trailing(<<>>, _, Segments) -> - Segments; -include_trailing(<>, Separator, Segments) -> - Segments; -include_trailing(Bin, Separator, Segments) -> - case binary:at(Bin, byte_size(Bin)-1) of - Separator -> [<<>>|Segments]; - _ -> Segments - end. - -split(<<>>, _S, Segments, <<>>) -> - Segments; -split(<<>>, _S, Segments, Acc) -> - [Acc|Segments]; -split(<>, S, Segments, <<>>) -> - split(Rest, S, Segments, <<>>); -split(<>, S, Segments, Acc) -> - split(Rest, S, [Acc|Segments], <<>>); -split(<<$:, Rest/binary>>, S, Segments, <<>>) -> - {BindingBin, Rest1} = compile_binding(Rest, S, <<>>), - Binding = binary_to_atom(BindingBin, utf8), - split(Rest1, S, [Binding|Segments], <<>>); -split(<<$:, D, Rest/binary>>, S, Segments, Acc) - when D >= $0, D =< $9 -> - split(Rest, S, Segments, <>); -split(<<$:, _Rest/binary>>, _S, _Segments, _Acc) -> - erlang:error(badarg); -split(<>, S, Segments, Acc) -> - split(Rest, S, Segments, <>). - -compile_binding(<<>>, _S, <<>>) -> - erlang:error(badarg); -compile_binding(<<>>, _S, Acc) -> - {Acc, <<>>}; -compile_binding(<>, S, Acc) -> - {Acc, Rest}; -compile_binding(<>, S, Acc) -> - compile_binding(Rest, S, <>). - -%% Dynamically compiled configuration module -mod_revproxy_dynamic_src(Routes) -> - Rules = compile_routes(Routes), - lists:flatten( - ["-module(mod_revproxy_dynamic). - -export([rules/0]). - - rules() -> - ", io_lib:format("~p", [Rules]), ".\n"]). diff --git a/src/system_metrics/mongoose_system_metrics_collector.erl b/src/system_metrics/mongoose_system_metrics_collector.erl index 6be45293d1..0516234f3e 100644 --- a/src/system_metrics/mongoose_system_metrics_collector.erl +++ b/src/system_metrics/mongoose_system_metrics_collector.erl @@ -128,9 +128,9 @@ get_api() -> filter_unknown_api(ApiList) -> AllowedToReport = [ mongoose_api, mongoose_client_api_rooms_messages, mongoose_client_api_rooms_users, mongoose_client_api_rooms_config, - mongoose_client_api_rooms ,mongoose_client_api_contacts, + mongoose_client_api_rooms, mongoose_client_api_contacts, mongoose_client_api_messages, lasse_handler, mongoose_api_admin, - mod_bosh, mod_websockets, mod_revproxy], + mod_bosh, mod_websockets], [Api || Api <- ApiList, lists:member(Api, AllowedToReport)]. get_transport_mechanisms() -> diff --git a/test/pubsub_backend_SUITE.erl b/test/pubsub_backend_SUITE.erl index bbf0608e47..89e755b666 100644 --- a/test/pubsub_backend_SUITE.erl +++ b/test/pubsub_backend_SUITE.erl @@ -58,6 +58,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> mod_pubsub_db_mnesia:stop(), mnesia:stop(), + mnesia:delete_schema([node()]), Config. init_per_group(_GroupName, Config) -> diff --git a/test/revproxy_SUITE.erl b/test/revproxy_SUITE.erl deleted file mode 100644 index b863339b81..0000000000 --- a/test/revproxy_SUITE.erl +++ /dev/null @@ -1,704 +0,0 @@ -%%============================================================================== -%% Copyright 2014 Erlang Solutions Ltd. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%============================================================================== - --module(revproxy_SUITE). --compile([export_all, nowarn_export_all]). - --include_lib("common_test/include/ct.hrl"). - --include("mod_revproxy.hrl"). - --import(ejabberd_helper, [start_ejabberd/1, - stop_ejabberd/0, - use_config_file/2, - start_ejabberd_with_config/2]). - -%%-------------------------------------------------------------------- -%% Suite configuration -%%-------------------------------------------------------------------- - -all() -> - [{group, compile_routes}, - {group, match_routes}, - {group, generate_upstream}, - {group, requests_http}]. - -groups() -> - [{compile_routes, [sequence], [compile_example_routes, - example_dynamic_compile]}, - {match_routes, [sequence], [exact_path_match, - remainder_match, - capture_subdomain_match, - method_match, - qs_match, - slash_ending_match]}, - {generate_upstream, [sequence], [upstream_uri, - upstream_host, - upstream_bindings, - upstream_qs, - upstream_slash_path, - upstream_slash_remainder]}, - {requests_http, [sequence], [no_upstreams, - http_upstream, - nomatch_upstream, - https_upstream]}]. - -suite() -> - []. - -%%-------------------------------------------------------------------- -%% Init & teardown -%%-------------------------------------------------------------------- - --define(APPS, [crypto, ssl, fusco, ranch, cowlib, cowboy]). - -init_per_suite(Config) -> - [application:start(App) || App <- ?APPS], - {ok, Pid} = create_handler(), - [{meck_pid, Pid}|Config]. - -end_per_suite(Config) -> - remove_handler(Config), - mnesia:stop(), - mnesia:delete_schema([node()]), - Config. - -init_per_group(requests_http, Config) -> - start_revproxy(), - Config; -init_per_group(match_routes, Config) -> - Rules = mod_revproxy:compile_routes(example_routes()), - [{rules, Rules}|Config]; -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(requests_http, Config) -> - stop_revproxy(), - Config; -end_per_group(_GroupName, Config) -> - Config. - -init_per_testcase(http_upstream, Config) -> - meck:new(inet, [unstick, passthrough]), - meck:expect(inet, getaddr, fun mock_getaddr/2), - meck:expect(inet, getaddrs_tm, fun mock_getaddrs_tm/3), - - start_http_upstream(), - - Config; -init_per_testcase(https_upstream, Config) -> - start_https_upstream(Config), - Config; -init_per_testcase(_CaseName, Config) -> - Config. - -end_per_testcase(http_upstream, Config) -> - stop_upstream(http_listener), - meck:unload(inet), - Config; -end_per_testcase(https_upstream, Config) -> - stop_upstream(https_listener), - Config; -end_per_testcase(_CaseName, Config) -> - Config. - -%%-------------------------------------------------------------------- -%% Routes compile tests -%%-------------------------------------------------------------------- -compile_example_routes(_Config) -> - %% Given - Expected = compiled_example_routes(), - - %% When - Compiled = mod_revproxy:compile_routes(example_routes()), - - %% Then - Expected = Compiled. - -example_dynamic_compile(_Config) -> - %% Given - Expected = compiled_example_routes(), - - %% When - ok = mod_revproxy:compile(example_routes()), - - %% Then - Expected = mod_revproxy_dynamic:rules(). - -%%-------------------------------------------------------------------- -%% HTTP requests tests -%%-------------------------------------------------------------------- -no_upstreams(_Config) -> - %% Given - Host = "http://localhost:8080", - Path = <<"/abc/index.html">>, - Method = "GET", - Headers = [{<<"host">>, <<"qwerty.com">>}], - Body = [], - - %% When - Response = execute_request(Host, Path, Method, Headers, Body), - - %% Then - true = is_status_code(Response, 502). - -http_upstream(_Config) -> - %% Given - Host = "http://localhost:8080", - QS = <<"ts=1231231232222&st=223232&page=32">>, - Path = <<"/abc/login.htm?", QS/binary>>, - Method = "GET", - Headers = [{<<"host">>, <<"qwerty.com">>}], - Body = "some example body :)", - - %% When - Response = execute_request(Host, Path, Method, Headers, Body), - - %% Then - true = is_status_code(Response, 200), - true = does_response_match(Response, - <<"qwerty.com">>, - <<"domain/qwerty/path/abc/login.htm">>, - Method, - Body, - QS), - - assert_contain_header(Response, <<"custom-header-1">>, <<"value">>), - assert_contain_header(Response, <<"custom-header-2">>, <<"some other value">>). - -nomatch_upstream(_Config) -> - %% Given - Host = "http://localhost:8080", - Path = <<"/abc/def">>, - Method = "GET", - Headers = [{<<"host">>, <<"domain.net">>}], - Body = [], - - %% When - Response = execute_request(Host, Path, Method, Headers, Body), - - %% Then - true = is_status_code(Response, 404). - -https_upstream(_Config) -> - %% Given - Host = "http://localhost:8080", - Path = <<"/admin/index.html">>, - Method = "POST", - Headers = [{<<"host">>, <<"otherdomain.com">>}], - Body = [], - - %% When - Response = execute_request(Host, Path, Method, Headers, Body), - - %% Then - assert_status_code(Response, 200), - true = does_response_match(Response, - <<"otherdomain.com">>, - <<"secret_admin/otherdomain/index.html">>, - Method, - Body, - <<>>). - -%%-------------------------------------------------------------------- -%% Routes matching tests -%%-------------------------------------------------------------------- -exact_path_match(Config) -> - %% Given - Rules = ?config(rules, Config), - Host = <<"domain.com">>, - Path = <<"/abc">>, - Method1 = <<"GET">>, - Method2 = <<"POST">>, - - %% When - Match1 = mod_revproxy:match(Rules, Host, Path, Method1), - Match2 = mod_revproxy:match(Rules, Host, Path, Method2), - - %% Then - Upstream = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:8080">>]}, - #match{upstream = Upstream} = Match1 - = Match2. - -remainder_match(Config) -> - %% Given - Rules = ?config(rules, Config), - Host = <<"domain.com">>, - Path1 = <<"/abc/def/ghi/index.html">>, - Path2 = <<"/def/ghi/index.html">>, - Method = <<"GET">>, - - %% When - Match1 = mod_revproxy:match(Rules, Host, Path1, Method), - Match2 = mod_revproxy:match(Rules, Host, Path2, Method), - - %% Then - Upstream1 = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:8080">>]}, - #match{upstream = Upstream1, - remainder = [<<"def">>, <<"ghi">>, <<"index.html">>], - path = [<<"abc">>]} = Match1, - - Upstream2 = #upstream{type = host, - protocol = <<"http://">>, - host = [<<"localhost:1234">>]}, - #match{upstream = Upstream2, - remainder = [<<"def">>, <<"ghi">>, <<"index.html">>], - path = '_'} = Match2. - -capture_subdomain_match(Config) -> - %% Given - Rules = ?config(rules, Config), - Host1 = <<"static.domain.com">>, - Host2 = <<"nonstatic.domain.com">>, - Path = <<"/a/b/c">>, - Method = <<"GET">>, - - %% When - Match1 = mod_revproxy:match(Rules, Host1, Path, Method), - Match2 = mod_revproxy:match(Rules, Host2, Path, Method), - - %% Then - Upstream1 = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:9999">>]}, - #match{upstream = Upstream1, - remainder = [<<"a">>, <<"b">>, <<"c">>], - path = '_'} = Match1, - - Upstream2 = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:8888">>], - path = [whatever, <<>>]}, - #match{upstream = Upstream2, - remainder = [<<"a">>, <<"b">>, <<"c">>], - bindings = [{whatever, <<"nonstatic">>}], - path = []} = Match2. - -method_match(Config) -> - %% Given - Rules = ?config(rules, Config), - Host = <<"domain.com">>, - Path = <<"/path/a/b/c">>, - Method1 = <<"GET">>, - Method2 = <<"POST">>, - - %% When - Match1 = mod_revproxy:match(Rules, Host, Path, Method1), - Match2 = mod_revproxy:match(Rules, Host, Path, Method2), - - %% Then - Upstream1 = #upstream{type = host, - protocol = <<"http://">>, - host = [<<"localhost:1234">>]}, - #match{upstream = Upstream1, - remainder = [<<"path">>, <<"a">>, <<"b">>, <<"c">>], - path = '_'} = Match1, - - Upstream2 = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:6543">>], - path = [<<"detailed_path">>, host, path]}, - #match{upstream = Upstream2, - remainder = [<<"b">>, <<"c">>], - bindings = Bindings, - path = [<<"path">>, path, <<>>]} = Match2, - <<"domain">> = proplists:get_value(host, Bindings), - <<"a">> = proplists:get_value(path, Bindings). - -qs_match(Config) -> - %% Given - Rules = ?config(rules, Config), - Host = <<"dummydomain.com">>, - QS = <<"login.htm?ts=1231231232222&st=223232&page=32&ap=123442" - "&whatever=somewordshere">>, - Path = <<"/a/b/c/", QS/binary>>, - Method = <<"GET">>, - - %% When - Match = mod_revproxy:match(Rules, Host, Path, Method), - - %% Then - Upstream = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:5678">>], - path = [placeholder]}, - #match{upstream = Upstream, - remainder = [<<"a">>, <<"b">>, <<"c">>, QS], - bindings = [{placeholder, <<"dummydomain">>}], - path = '_'} = Match. - - -slash_ending_match(Config) -> - %% Given - Rules = ?config(rules, Config), - Host = <<"dummydomain.com">>, - Path = <<"/a/b/c/">>, - Method = <<"GET">>, - - %% When - Match = mod_revproxy:match(Rules, Host, Path, Method), - - %% Then - Upstream = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:5678">>], - path = [placeholder]}, - #match{upstream = Upstream, - remainder = [<<"a">>, <<"b">>, <<"c">>, <<>>], - bindings = [{placeholder, <<"dummydomain">>}], - path = '_'} = Match. - -%%-------------------------------------------------------------------- -%% Upstream URI generation -%%-------------------------------------------------------------------- -upstream_uri(_Config) -> - %% Given - Upstream = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:8080">>]}, - Remainder = [<<"def">>, <<"index.html">>], - Bindings = [{host, <<"domain">>}], - Path = [<<"host">>, host], - Match = #match{upstream = Upstream, - remainder = Remainder, - bindings = Bindings, - path = Path}, - - %% When - URI = mod_revproxy:upstream_uri(Match), - - %% Then - {"http://localhost:8080", <<"/def/index.html">>} = URI. - -upstream_host(_Config) -> - %% Given - Upstream = #upstream{type = host, - protocol = <<"http://">>, - host = [<<"localhost:8080">>]}, - Remainder = [<<"def">>, <<"index.html">>], - Bindings = [], - Path = [<<"host">>], - Match = #match{upstream = Upstream, - remainder = Remainder, - bindings = Bindings, - path = Path}, - - %% When - URI = mod_revproxy:upstream_uri(Match), - - %% Then - {"http://localhost:8080", <<"/host/def/index.html">>} = URI. - -upstream_bindings(_Config) -> - %% Given - Upstream = #upstream{type = uri, - protocol = <<"https://">>, - host = [domain, host, <<"localhost:8080">>], - path = [<<"host">>, host, <<"domain">>, domain]}, - Remainder = [<<"dir">>, <<"index.html">>], - Bindings = [{host, <<"test_host">>}, {domain, <<"test_domain">>}], - Path = '_', - Match = #match{upstream = Upstream, - remainder = Remainder, - bindings = Bindings, - path = Path}, - - %% When - URI = mod_revproxy:upstream_uri(Match), - - %% Then - {"https://test_domain.test_host.localhost:8080", - <<"/host/test_host/domain/test_domain/dir/index.html">>} = URI. - -upstream_qs(_Config) -> - %% Given - Upstream = #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:1234">>], - path = [<<"ghi">>]}, - Path = [<<"abc">>], - QS = <<"login.htm?ts=1231231232222&st=223232&page=32&ap=123442" - "&whatever=somewordshere">>, - Remainder = [<<"def">>, QS], - Match = #match{upstream = Upstream, - remainder = Remainder, - bindings = [], - path = Path}, - - %% When - URI = mod_revproxy:upstream_uri(Match), - - %% Then - Resource = <<"/ghi/def/", QS/binary>>, - {"http://localhost:1234", Resource} = URI. - -upstream_slash_path(_Config) -> - %% Given - Upstream = #upstream{type = host, - protocol = <<"http://">>, - host = [<<"localhost:1234">>], - path = [<<"abc">>]}, - Path = [<<"abc">>, <<"def">>, <<>>], - Match = #match{upstream = Upstream, - remainder = [], - bindings = [], - path = Path}, - - %% When - URI = mod_revproxy:upstream_uri(Match), - - %% Then - {"http://localhost:1234", <<"/abc/abc/def/">>} = URI. - -upstream_slash_remainder(_Config) -> - %% Given - Upstream = #upstream{type = host, - protocol = <<"http://">>, - host = [<<"localhost:1234">>], - path = [<<"abc">>]}, - Path = [<<"abc">>, <<"def">>, <<>>], - Remainder = [<<"ghi">>, <<"jkl">>, <<>>], - Match = #match{upstream = Upstream, - remainder = Remainder, - bindings = [], - path = Path}, - - %% When - URI = mod_revproxy:upstream_uri(Match), - - %% Then - {"http://localhost:1234", <<"/abc/abc/def/ghi/jkl/">>} = URI. - -%%-------------------------------------------------------------------- -%% Helpers -%%-------------------------------------------------------------------- -copy(Src, Dst) -> - {ok, _} = file:copy(Src, Dst). - -data(File, Config) -> - filename:join([?config(data_dir, Config), File]). - -example_routes() -> - [{"domain.com", "/abc", "_", "http://localhost:8080/"}, - {"domain.com", get, "http://localhost:1234"}, - {"static.domain.com", get, "http://localhost:9999/"}, - {":host.com", "/path/:path/", "_", - "http://localhost:6543/detailed_path/:host/:path"}, - {":placeholder.com", get, "http://localhost:5678/:placeholder"}, - {":whatever.domain.com", "/", "_", "http://localhost:8888/:whatever/"}]. - -compiled_example_routes() -> - [{[<<"com">>, <<"domain">>], [<<"abc">>], '_', - #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:8080">>]}}, - {[<<"com">>, <<"domain">>], '_', <<"GET">>, - #upstream{type = host, - protocol = <<"http://">>, - host = [<<"localhost:1234">>]}}, - {[<<"com">>,<<"domain">>,<<"static">>], '_', <<"GET">>, - #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:9999">>]}}, - {[<<"com">>, host], [<<"path">>, path, <<>>], '_', - #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:6543">>], - path = [<<"detailed_path">>, host, path]}}, - {[<<"com">>, placeholder], '_', <<"GET">>, - #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:5678">>], - path = [placeholder]}}, - {[<<"com">>, <<"domain">>, whatever], [], '_', - #upstream{type = uri, - protocol = <<"http://">>, - host = [<<"localhost:8888">>], - path = [whatever, <<>>]}}]. - -start_revproxy() -> - Routes = {routes, [{":domain.com", "/admin", "_", - "https://localhost:5678/secret_admin/:domain/"}, - {":domain.com", "/:path/", get, - "http://:domain.localhost:1234/domain/:domain/path/:path/"}]}, - CustomHeaders = [{<<"custom-header-1">>, <<"value">>}, - {<<"custom-header-2">>, <<"some other value">>}], - Dispatch = cowboy_router:compile([ - {'_', - [{"/[...]", mod_revproxy, [{custom_headers, CustomHeaders}]}]} - ]), - mod_revproxy:start(nvm, [Routes]), - cowboy:start_clear(revproxy_listener, - [{port, 8080}, {num_acceptors, 20}], - #{env => #{dispatch => Dispatch}}). - -stop_revproxy() -> - ok = cowboy:stop_listener(revproxy_listener). - -start_http_upstream() -> - Dispatch = cowboy_router:compile([ - {'_', [{"/[...]", revproxy_handler, []}]} - ]), - cowboy:start_clear(http_listener, - [{port, 1234}, {num_acceptors, 20}], - #{env => #{dispatch => Dispatch}}). - -start_https_upstream(Config) -> - Dispatch = cowboy_router:compile([ - {'_', [{"/[...]", revproxy_handler, []}]} - ]), - Opts = [{port, 5678}, - {keyfile, data("server.key", Config)}, - {certfile, data("server.crt", Config)}, - {num_acceptors, 20}], - cowboy:start_tls(https_listener, - Opts, - #{env => #{dispatch => Dispatch}}). - -stop_upstream(Upstream) -> - case cowboy:stop_listener(Upstream) of - ok -> - ok; - Other -> - ct:fail(#{issue => stop_listener_failed, - ref => Upstream, - reason => Other}) - end. - -execute_request(Host, Path, Method, Headers, Body) -> - {ok, Pid} = fusco:start_link(Host, []), - Response = fusco:request(Pid, Path, Method, Headers, Body, 5000), - fusco:disconnect(Pid), - Response. - -assert_status_code(Result, Code) -> - case is_status_code(Result, Code) of - true -> - ok; - false -> - ct:fail(#{issue => assert_status_code, - result => Result, - expected_code => Code}) - end. - -is_status_code({ok, {{CodeBin, _}, _, _, _, _}}, Code) -> - case binary_to_integer(CodeBin) of - Code -> true; - _ -> false - end. - -does_response_match({ok, {{_, _}, _, Response, _, _}}, - Host, Path, Method, Body, QS) -> - ResponseEls = binary:split(Response, <<"\n">>, [global]), - [RHost,RMethod,RPath,RBody,RQS|_] = ResponseEls, - PathSegments = binary:split(Path, <<"/">>, [global, trim]), - RPath = to_formatted_binary(PathSegments), - RHost = to_formatted_binary(Host), - RMethod = to_formatted_binary(list_to_binary(Method)), - RBody = to_formatted_binary(list_to_binary(Body)), - RQS = to_formatted_binary(QS), - true. - -to_formatted_binary(Subject) -> - iolist_to_binary(io_lib:format("~p", [Subject])). - -assert_contain_header(Result, Header, Value) -> - case does_contain_header(Result, Header, Value) of - true -> - ok; - false -> - ct:fail(#{reason => assert_contain_header, - req => Result, - header => Header, - expected_value => Value}) - end. - -does_contain_header({ok, {{_, _}, _, Response, _, _}}, Header, Value) -> - HeaderL = cowboy_bstr:to_lower(Header), - ValueL = cowboy_bstr:to_lower(Value), - Match = iolist_to_binary(io_lib:format("~p", [{HeaderL, ValueL}])), - case binary:match(Response, Match) of - nomatch -> false; - _ -> true - end. - -%%-------------------------------------------------------------------- -%% revproxy handler mock -%%-------------------------------------------------------------------- -create_handler() -> - Owner = self(), - F = fun() -> - ok = meck:new(revproxy_handler, [non_strict]), - ok = meck:expect(revproxy_handler, init, fun handler_init/2), - ok = meck:expect(revproxy_handler, terminate, fun handler_terminate/3), - Owner ! ok, - timer:sleep(infinity) - end, - Pid = spawn(F), - receive - ok -> - {ok, Pid} - after 5000 -> - {error, timeout} - end. - -remove_handler(Config) -> - meck:unload(revproxy_handler), - exit(?config(meck_pid, Config), kill). - -handler_init(Req, _Opts) -> - handler_handle(Req, no_state). - -handler_handle(Req, State) -> - Host = cowboy_req:host(Req), - PathInfo = cowboy_req:path_info(Req), - Method = cowboy_req:method(Req), - Headers = maps:to_list(cowboy_req:headers(Req)), - ContentType = [{<<"content-type">>, <<"text/plain">>}], - QS = cowboy_req:qs(Req), - {Body, Req2} = case cowboy_req:has_body(Req) of - false -> - {<<>>, Req}; - true -> - {ok, Body1, Req1} = cowboy_req:read_body(Req), - {Body1, Req1} - end, - Response = io_lib:format("~p~n~p~n~p~n~p~n~p~n~p", - [Host, Method, PathInfo, Body, QS, Headers]), - Req3 = cowboy_req:reply(200, maps:from_list(ContentType), Response, Req2), - {ok, Req3, State}. - -handler_terminate(_Reason, _Req, _State) -> - ok. - -mock_getaddr("qwerty.localhost",inet) -> - {ok, {127,0,0,1}}; -mock_getaddr("qwerty.localhost",inet6) -> - {ok,{0,0,0,0,0,0,0,1}}; -mock_getaddr(Host, Opt) -> - meck:passthrough([Host, Opt]). - - -mock_getaddrs_tm("qwerty.localhost",inet,_) -> - {ok, [{127,0,0,1}]}; -mock_getaddrs_tm("qwerty.localhost",inet6,_) -> - {ok,[{0,0,0,0,0,0,0,1}]}; -mock_getaddrs_tm(Host, Opt, Timer) -> - meck:passthrough([Host, Opt, Timer]). diff --git a/test/revproxy_SUITE_data/server.crt b/test/revproxy_SUITE_data/server.crt deleted file mode 100644 index 05ce5c68da..0000000000 --- a/test/revproxy_SUITE_data/server.crt +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICWDCCAcGgAwIBAgIJAOH4GpuAwqZeMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTQxMDIyMTEyNDA0WhcNMjQxMDE5MTEyNDA0WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQCz2CgND+abN0gQ8HOILyRUXko4kV/eFw5UXnAlQUejBzcqhY2OEpU+38Lg+++I -B9LeyNHbRRmX2TTFKGNPN/j+8aHgJOzAMM+eN/zZVzf0+DOwTONx5tJIhgsw/qQK -AWSOFg/U5U9tkFM1UGh0O+fyqiEXk5ynCh8qphy21iVRlQIDAQABo1AwTjAdBgNV -HQ4EFgQUY5jgLtj7XSVkOnJAveI29CCBU6kwHwYDVR0jBBgwFoAUY5jgLtj7XSVk -OnJAveI29CCBU6kwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBCWlZ1 -1hgq/tJHWBBuknWDCqKhgdL0tif1ovPaOIARFZL0hUfHx/gOHRGg/ZZwheb2am94 -9uA41kyJi7EL/4FUHzRarHqmHo54YlsZTJI0U1fLmS50GNQIXG/5vjlMLi0EE4Al -lu6f89jEP41xPmpOAPzXQ/NOvxEw+tk2b21n/w== ------END CERTIFICATE----- diff --git a/test/revproxy_SUITE_data/server.key b/test/revproxy_SUITE_data/server.key deleted file mode 100644 index 4c9609cc70..0000000000 --- a/test/revproxy_SUITE_data/server.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQCz2CgND+abN0gQ8HOILyRUXko4kV/eFw5UXnAlQUejBzcqhY2O -EpU+38Lg+++IB9LeyNHbRRmX2TTFKGNPN/j+8aHgJOzAMM+eN/zZVzf0+DOwTONx -5tJIhgsw/qQKAWSOFg/U5U9tkFM1UGh0O+fyqiEXk5ynCh8qphy21iVRlQIDAQAB -AoGBAIN5E95or2btBOwNPAhkniRnQc8Ux4auSDV5THaPwHT4A2Y8d/1SrHRoVCjx -RGyne4qOOIwZedo+WYuAOQlim81am2Stt2raInzWwG+4+HWBKmhAcqb1zTg8r38j -40YMX9M1xR5wnjiShRibEBdzLAR44Lls/RHrc2phEaFVHd9BAkEA3rQaZZ218eSz -7lA48cUBalhWhNSEg6RWbjsIVAcZ1oHXWH0OgCZbEu32XJgnsinDcuV/ddSb4mfn -DDLaKFxiKQJBAM67ol5OFftvQ8sCOXnOZRzxDD6vw+5np1cMOtDWUL7CGaioXyb+ -CouvdYSScQw6/FThys4OUz6vZkmlSIZrWY0CQELEPCW64pFiv47y/h99sK7xTP8F -t7S/0Cm8CQMoHdbxll4xH+fLmAJU1UdtVnlTjAUmSNBKrXFzNRGzLTRsfgECQQCF -DSfj7/ef2i1Ug7sAFQDojo7l7XN5hYaitIFKFRTkoMGo1axfyRJ6clN9Hk7Zo0Dx -VFyVH40eA/8eGN1DfeWlAkAfBdS53QhoF87N0LcjWdu/D9fgDn709dleECHecACd -nLmai5o67gnvx1thC/Nqo3MAhzqPlf1fB5QGGd/9gyAQ ------END RSA PRIVATE KEY-----