|
| 1 | +-module(charreada_config). |
| 2 | +-behaviour(gen_server). |
| 3 | + |
| 4 | +%% API |
| 5 | +-export([start_link/1]). |
| 6 | +-export([add_proxy/1, remove_proxy/1, redirect_req/6]). |
| 7 | + |
| 8 | +%% gen_server callbacks |
| 9 | +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). |
| 10 | + |
| 11 | +%% Internal functions |
| 12 | +-export([wait_for_connect/6]). |
| 13 | + |
| 14 | +-define(SERVER, ?MODULE). |
| 15 | + |
| 16 | +%%% API |
| 17 | +start_link(Tid) -> |
| 18 | + gen_server:start_link({local, ?SERVER}, ?MODULE, Tid, []). |
| 19 | + |
| 20 | +-spec add_proxy ({atom(), term()}) -> ok; ({binary(), term()}) -> ok. |
| 21 | +add_proxy(Proxy) -> |
| 22 | + gen_server:cast(?SERVER, {add, Proxy}). |
| 23 | + |
| 24 | +-spec remove_proxy (atom()) -> ok; (binary()) -> ok. |
| 25 | +remove_proxy(Name) -> |
| 26 | + gen_server:cast(?SERVER, {remove, Name}). |
| 27 | + |
| 28 | +-spec redirect_req( |
| 29 | + binary(), binary(), binary(), [{binary(), binary()}], function(), timeout()) -> |
| 30 | + ok | {error, cowboy_http:status()}. |
| 31 | +redirect_req(Method, Host, Path, Headers, BodyFun, Timeout) -> |
| 32 | + Msg = {redirect, self(), Method, Host, Path, Headers, BodyFun, Timeout}, |
| 33 | + gen_server:call(?SERVER, Msg). |
| 34 | + |
| 35 | +%%% gen_server callbacks |
| 36 | + |
| 37 | +init(Tid) -> |
| 38 | + {ok, Tid}. |
| 39 | + |
| 40 | +handle_call({redirect, Pid, Method, Host, Path, Headers, BodyFun, Timeout}, _From, Tid) -> |
| 41 | + Proxy = match_host(Tid, binary:split(Host, <<".">>)), |
| 42 | + Reply = start_redirect(Proxy, Pid, Method, Path, Headers, BodyFun, Timeout), |
| 43 | + {reply, Reply, Tid}. |
| 44 | + |
| 45 | +handle_cast({add, Proxy}, Tid) -> |
| 46 | + ets:insert(Tid, normalize(Proxy)), |
| 47 | + {noreply, Tid}; |
| 48 | +handle_cast({remove, Name}, Tid) -> |
| 49 | + ets:delete(Tid, normalize_key(Name)), |
| 50 | + {noreply, Tid}. |
| 51 | + |
| 52 | +handle_info(_, State) -> |
| 53 | + {noreply, State}. |
| 54 | + |
| 55 | +terminate(_Reason, _State) -> |
| 56 | + ok. |
| 57 | + |
| 58 | +code_change(_OldVsn, State, _Extra) -> |
| 59 | + {ok, State}. |
| 60 | + |
| 61 | +%%% Internal methods |
| 62 | + |
| 63 | +normalize({Name, Transport, Host, Port, User, Password}) -> |
| 64 | + {to_lower_binary(Name), Transport, Host, Port, User, Password}. |
| 65 | + |
| 66 | +normalize_key(Name) -> |
| 67 | + to_lower_binary(Name). |
| 68 | + |
| 69 | +match_host(Tid, [Subdomain | _T]) -> |
| 70 | + ets:lookup(Tid, to_lower_binary(Subdomain)); |
| 71 | +match_host(_Tid, []) -> |
| 72 | + []. |
| 73 | + |
| 74 | +start_redirect([], _Pid, _Method, _Path, _Headers, _BodyFun, _Timeout) -> |
| 75 | + {error, 404}; |
| 76 | +start_redirect([{_, Transport, Host, Port, User, Password}], |
| 77 | + Pid, OrgMethod, Path, OrgHeaders, BodyFun, Timeout) -> |
| 78 | + Url = url(Transport, Host, Port, Path), |
| 79 | + Headers = to_strings(OrgHeaders, []), |
| 80 | + Method = to_atom(OrgMethod), |
| 81 | + Options = to_ibrowse_options(Pid, User, Password), |
| 82 | + Params = [Url, Headers, Method, BodyFun, Options, Timeout], |
| 83 | + proc_lib:spawn(?MODULE, wait_for_connect, Params), |
| 84 | + ok. |
| 85 | + |
| 86 | +wait_for_connect(Url, Headers, Method, BodyFun, Options, Timeout) -> |
| 87 | + ibrowse:send_req(Url, Headers, Method, BodyFun, Options, Timeout). |
| 88 | + |
| 89 | +url(Transport, Host, Port, Path) -> |
| 90 | + to_string(Transport) ++ "://" ++ to_string(Host) ++ ":" ++ to_string(Port) ++ to_string(Path). |
| 91 | + |
| 92 | +to_lower_binary(Value) when is_binary(Value) -> |
| 93 | + list_to_binary(string:to_lower(binary_to_list(Value))); |
| 94 | +to_lower_binary(Value) when is_atom(Value) -> |
| 95 | + list_to_binary(string:to_lower(atom_to_list(Value))); |
| 96 | +to_lower_binary(Value) when is_list(Value) -> |
| 97 | + list_to_binary(string:to_lower(Value)). |
| 98 | + |
| 99 | +to_string(Value) when is_binary(Value) -> |
| 100 | + binary_to_list(Value); |
| 101 | +to_string(Value) when is_atom(Value) -> |
| 102 | + atom_to_list(Value); |
| 103 | +to_string(Value) when is_integer(Value) -> |
| 104 | + integer_to_list(Value); |
| 105 | +to_string({IP1, IP2, IP3, IP4}) -> |
| 106 | + to_string(IP1) ++ "." ++ to_string(IP2) ++ "." ++ to_string(IP3) ++ "." ++ to_string(IP4). |
| 107 | + |
| 108 | +to_atom(<<"GET">>) -> get; |
| 109 | +to_atom(<<"POST">>) -> post; |
| 110 | +to_atom(<<"HEAD">>) -> head; |
| 111 | +to_atom(<<"OPTIONS">>) -> options; |
| 112 | +to_atom(<<"PUT">>) -> put; |
| 113 | +to_atom(<<"DELETE">>) -> delete; |
| 114 | +to_atom(<<"TRACE">>) -> trace; |
| 115 | +to_atom(<<"MKCOL">>) -> mkcol; |
| 116 | +to_atom(<<"PROPFIND">>) -> propfind; |
| 117 | +to_atom(<<"PROPPATCH">>) -> proppatch; |
| 118 | +to_atom(<<"LOCK">>) -> lock; |
| 119 | +to_atom(<<"UNLOCK">>) -> unlock; |
| 120 | +to_atom(<<"MOVE">>) -> move; |
| 121 | +to_atom(<<"COPY">>) -> copy. |
| 122 | + |
| 123 | +to_strings([{Key, Value}|T], Acc) -> |
| 124 | + to_strings(T, [{to_string(Key), to_string(Value)}|Acc]); |
| 125 | +to_strings([], Acc) -> |
| 126 | + lists:reverse(Acc). |
| 127 | + |
| 128 | +to_ibrowse_options(Pid, undefined, _Password) -> |
| 129 | + to_ibrowse_options([], Pid); |
| 130 | +to_ibrowse_options(Pid, _User, undefined) -> |
| 131 | + to_ibrowse_options([], Pid); |
| 132 | +to_ibrowse_options(Pid, User, Password) -> |
| 133 | + to_ibrowse_options([{basic_auth, {to_string(User), to_string(Password)}}], Pid). |
| 134 | + |
| 135 | +to_ibrowse_options(Acc, Pid) -> |
| 136 | + [{stream_to, {Pid, once}}|Acc]. |
0 commit comments