From 2e01b6870b69ca7de8bfa0d7d7c2c8cdc1e4d4cf Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Thu, 4 Jul 2019 11:23:50 +0300 Subject: [PATCH] Add rebar3_fmt format tool --- .tool-versions | 1 + README.md | 8 + apps/epp_proxy/include/epp_proxy.hrl | 24 +-- apps/epp_proxy/src/epp_certs.erl | 29 ++-- apps/epp_proxy/src/epp_http_client.erl | 128 ++++++++-------- .../src/epp_http_client_behaviour.erl | 8 +- apps/epp_proxy/src/epp_pool_supervisor.erl | 3 +- apps/epp_proxy/src/epp_proxy_app.erl | 4 +- apps/epp_proxy/src/epp_proxy_sup.erl | 40 ++--- apps/epp_proxy/src/epp_router.erl | 86 ++++++----- apps/epp_proxy/src/epp_tcp_acceptor.erl | 37 +++-- apps/epp_proxy/src/epp_tcp_worker.erl | 123 +++++++-------- apps/epp_proxy/src/epp_tls_acceptor.erl | 49 +++--- apps/epp_proxy/src/epp_tls_worker.erl | 140 ++++++++---------- apps/epp_proxy/src/epp_util.erl | 43 +++--- apps/epp_proxy/src/epp_xml.erl | 28 ++-- apps/epp_proxy/test/epp_http_client_tests.erl | 10 +- apps/epp_proxy/test/epp_xml_tests.erl | 24 +-- rebar.config | 2 + 19 files changed, 415 insertions(+), 372 deletions(-) diff --git a/.tool-versions b/.tool-versions index c0af933..81a1df3 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1,2 @@ erlang 21.3.8 +rebar 3.9.1 diff --git a/README.md b/README.md index b7e4a78..bb2f9e1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,14 @@ emulate it's behaviour to the biggest possible extent. Aside from the standard library of Erlang/OTP, it uses hackney for making HTTP requests and lager for logging. +Code style +---- +We enforce the style from Erlang's own configuration. +You can use the rebar3_fmt plugin to do it for you: + + $ rebar3 fmt + + Design ---- diff --git a/apps/epp_proxy/include/epp_proxy.hrl b/apps/epp_proxy/include/epp_proxy.hrl index fdc6a2c..b754537 100644 --- a/apps/epp_proxy/include/epp_proxy.hrl +++ b/apps/epp_proxy/include/epp_proxy.hrl @@ -8,22 +8,22 @@ }). -record(epp_error_request, - {method, % get - url, % "https://example.com/some-url" - query_params, % {[{<<"msg">>, <<"Some">>}, {<<"code">>, <<"2001">>}]} - cookies, % [<<"session=SomeSession; Version=1">>] - headers % [{"User-Agent", <<"EPP proxy">>}, {"Other", <<"Header">>}] - }). + {method, % get + url, % "https://example.com/some-url" + query_params, % {[{<<"msg">>, <<"Some">>}, {<<"code">>, <<"2001">>}]} + cookies, % [<<"session=SomeSession; Version=1">>] + headers % [{"User-Agent", <<"EPP proxy">>}, {"Other", <<"Header">>}] + }). %% Unified version of the two records above. Depending on the method, %% it either encodes parameters into multipart form or query parameters and url. -record(epp_unified_request, - {method, % get - url, % "https://example.com/some-url" - params, % {[{<<"msg">>, <<"Some">>}, {<<"code">>, <<"2001">>}]} - cookies, % [<<"session=SomeSession; Version=1">>] - headers % [{"User-Agent", <<"EPP proxy">>}, {"Other", <<"Header">>}] - }). + {method, % get + url, % "https://example.com/some-url" + params, % {[{<<"msg">>, <<"Some">>}, {<<"code">>, <<"2001">>}]} + cookies, % [<<"session=SomeSession; Version=1">>] + headers % [{"User-Agent", <<"EPP proxy">>}, {"Other", <<"Header">>}] + }). -type epp_request() :: #epp_request{}. -type epp_error_request() :: #epp_error_request{}. diff --git a/apps/epp_proxy/src/epp_certs.erl b/apps/epp_proxy/src/epp_certs.erl index 0a2fff6..576c260 100644 --- a/apps/epp_proxy/src/epp_certs.erl +++ b/apps/epp_proxy/src/epp_certs.erl @@ -2,11 +2,12 @@ -include_lib("public_key/include/public_key.hrl"). --export([pem_certificate/1, subject_from_otp_certificate/1, - common_name_from_subject/1, certificate_to_pem/1, - der_certificate/1, headers_from_cert/1]). +-export([certificate_to_pem/1, + common_name_from_subject/1, der_certificate/1, + headers_from_cert/1, pem_certificate/1, + subject_from_otp_certificate/1]). -% Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT} + % Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT} headers_from_cert(Der) -> OTPCertificate = der_certificate(Der), Subject = subject_from_otp_certificate(OTPCertificate), @@ -24,26 +25,31 @@ der_certificate(Der) -> certificate_to_pem(Certificate) -> PemEntry = {'Certificate', Certificate, not_encrypted}, PemString = public_key:pem_encode([PemEntry]), - CleanBinary = binary:replace(PemString, <<"\n">>, <<" ">>, [global]), + CleanBinary = binary:replace(PemString, <<"\n">>, + <<" ">>, [global]), CleanBinary. %% Read only a specific type of certificate, otherwise fail. -subject_from_otp_certificate(Certificate) when is_record(Certificate, 'OTPCertificate') -> - Subject = Certificate#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject, +subject_from_otp_certificate(Certificate) + when is_record(Certificate, 'OTPCertificate') -> + Subject = + (Certificate#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subject, Subject. %% Take a subject rdnSequence that can be set into %% HTTP header SSL_CLIENT_S_DN_CN. common_name_from_subject(Subject) -> - CommonName = ?'id-at-commonName', - {_Type, Field} = field_from_subject(Subject, CommonName), + CommonName = (?'id-at-commonName'), + {_Type, Field} = field_from_subject(Subject, + CommonName), Field. %% Only used for local development test, is not required for the application. pem_certificate(PathToCert) -> {ok, PemBin} = file:file(PathToCert), PemEntries = public_key:pem_decode(PemBin), - {value, CertEntry} = lists:keysearch('Certificate', 1, PemEntries), + {value, CertEntry} = lists:keysearch('Certificate', 1, + PemEntries), {_, DerCert, _} = CertEntry, Decoded = public_key:pkix_decode_cert(DerCert, otp), Decoded. @@ -52,6 +58,7 @@ field_from_subject({rdnSequence, Attributes}, Field) -> FlatList = lists:flatten(Attributes), ValidAttrs = lists:filter(fun (X) -> X#'AttributeTypeAndValue'.type =:= Field - end, FlatList), + end, + FlatList), Attribute = lists:last(ValidAttrs), Attribute#'AttributeTypeAndValue'.value. diff --git a/apps/epp_proxy/src/epp_http_client.erl b/apps/epp_proxy/src/epp_http_client.erl index 3f89350..d20d3d9 100644 --- a/apps/epp_proxy/src/epp_http_client.erl +++ b/apps/epp_proxy/src/epp_http_client.erl @@ -4,126 +4,130 @@ -behaviour(epp_http_client_behaviour). --export([request/1, error_request/1, request_builder/1]). +-export([error_request/1, request/1, + request_builder/1]). %% Callback API request(#epp_request{} = Request) -> HackneyArgs = handle_args(Request), case apply(hackney, request, HackneyArgs) of - {error, Error} -> - log_and_return_canned(Error, Request); + {error, Error} -> log_and_return_canned(Error, Request); {Status, _StatusCode, _Headers, ClientRef} -> - {ok, Body} = hackney:body(ClientRef), - {Status, Body} - end. + {ok, Body} = hackney:body(ClientRef), {Status, Body} + end. error_request(#epp_error_request{} = Request) -> HackneyArgs = handle_error_args(Request), case apply(hackney, request, HackneyArgs) of - {error, Error} -> - log_and_return_canned(Error, Request); + {error, Error} -> log_and_return_canned(Error, Request); {Status, _StatusCode, _Headers, ClientRef} -> - {ok, Body} = hackney:body(ClientRef), - {Status, Body} - end. + {ok, Body} = hackney:body(ClientRef), {Status, Body} + end. -request_builder(Map) -> - request_from_map(Map). +request_builder(Map) -> request_from_map(Map). %% Private API -spec handle_args(epp_request()) -> list(). -handle_args(#epp_request{method=get, url=URL, headers=Headers, body="", - cookies=Cookies}) -> + +handle_args(#epp_request{method = get, url = URL, + headers = Headers, body = "", cookies = Cookies}) -> [get, URL, Headers, "", [{cookie, Cookies}, insecure]]; -handle_args(#epp_request{method=post, url=URL, headers=Headers, body=Body, - cookies=Cookies}) -> - [post, URL, Headers, Body, [{cookie, Cookies}, insecure]]. +handle_args(#epp_request{method = post, url = URL, + headers = Headers, body = Body, cookies = Cookies}) -> + [post, URL, Headers, Body, + [{cookie, Cookies}, insecure]]. -spec handle_error_args(epp_error_request()) -> list(). -handle_error_args(#epp_error_request{method=get, url=URL, headers=Headers, - query_params=Params, cookies=Cookies}) -> + +handle_error_args(#epp_error_request{method = get, + url = URL, headers = Headers, + query_params = Params, + cookies = Cookies}) -> QueryString = hackney_url:qs(Params), CompleteURL = [URL, <<"?">>, QueryString], - [get, CompleteURL, Headers, "", [{cookie, Cookies}, insecure]]. - + [get, CompleteURL, Headers, "", + [{cookie, Cookies}, insecure]]. %% Map request and return values. -request_from_map(#{command := "error", session_id := SessionId, - code := Code, message := Message, headers:=Headers, +request_from_map(#{command := "error", + session_id := SessionId, code := Code, + message := Message, headers := Headers, cl_trid := ClTRID}) -> URL = epp_router:route_request("error"), RequestMethod = epp_router:request_method("error"), - Cookie = hackney_cookie:setcookie("session", SessionId, []), + Cookie = hackney_cookie:setcookie("session", SessionId, + []), QueryParams = query_params(Code, Message, ClTRID), - Headers=Headers, - Request = #epp_error_request{url=URL, - method=RequestMethod, - query_params=QueryParams, - cookies=[Cookie], - headers=Headers}, + Headers = Headers, + Request = #epp_error_request{url = URL, + method = RequestMethod, + query_params = QueryParams, cookies = [Cookie], + headers = Headers}, lager:info("Error Request from map: [~p]~n", [Request]), Request; -request_from_map(#{command := Command, session_id := SessionId, - raw_frame := RawFrame, headers:=Headers, cl_trid := ClTRID}) -> +request_from_map(#{command := Command, + session_id := SessionId, raw_frame := RawFrame, + headers := Headers, cl_trid := ClTRID}) -> URL = epp_router:route_request(Command), RequestMethod = epp_router:request_method(Command), - Cookie = hackney_cookie:setcookie("session", SessionId, []), + Cookie = hackney_cookie:setcookie("session", SessionId, + []), Body = request_body(Command, RawFrame, ClTRID), - Headers=Headers, - Request = #epp_request{url=URL, - method=RequestMethod, - body=Body, - cookies=[Cookie], - headers=Headers}, + Headers = Headers, + Request = #epp_request{url = URL, + method = RequestMethod, body = Body, + cookies = [Cookie], headers = Headers}, lager:info("Request from map: [~p]~n", [Request]), Request; -request_from_map(#{command := Command, session_id := SessionId, - raw_frame := RawFrame, common_name := CommonName, - client_cert := ClientCert, peer_ip := PeerIp, cl_trid := ClTRID}) -> +request_from_map(#{command := Command, + session_id := SessionId, raw_frame := RawFrame, + common_name := CommonName, client_cert := ClientCert, + peer_ip := PeerIp, cl_trid := ClTRID}) -> URL = epp_router:route_request(Command), RequestMethod = epp_router:request_method(Command), - Cookie = hackney_cookie:setcookie("session", SessionId, []), + Cookie = hackney_cookie:setcookie("session", SessionId, + []), Body = request_body(Command, RawFrame, ClTRID), Headers = [{"SSL_CLIENT_CERT", ClientCert}, {"SSL_CLIENT_S_DN_CN", CommonName}, {"User-Agent", <<"EPP proxy">>}, {"X-Forwarded-for", epp_util:readable_ip(PeerIp)}], - Request = #epp_request{url=URL, - method=RequestMethod, - body=Body, - cookies=[Cookie], - headers=Headers}, + Request = #epp_request{url = URL, + method = RequestMethod, body = Body, + cookies = [Cookie], headers = Headers}, lager:info("Request from map: [~p]~n", [Request]), Request. %% Return form data or an empty list. -request_body("hello", _, _) -> - ""; +request_body("hello", _, _) -> ""; request_body(_Command, RawFrame, nomatch) -> {multipart, [{<<"raw_frame">>, RawFrame}]}; request_body(_Command, RawFrame, ClTRID) -> - {multipart, [{<<"raw_frame">>, RawFrame}, {<<"clTRID">>, ClTRID}]}. + {multipart, + [{<<"raw_frame">>, RawFrame}, {<<"clTRID">>, ClTRID}]}. query_params(Code, Message, nomatch) -> [{<<"code">>, Code}, {<<"msg">>, Message}]; query_params(Code, Message, ClTRID) -> - [{<<"code">>, Code}, {<<"msg">>, Message}, {<<"clTRID">>, ClTRID}]. + [{<<"code">>, Code}, {<<"msg">>, Message}, + {<<"clTRID">>, ClTRID}]. %% Log critical information about a request that failed, and then %% return a canned response with internal error status. log_and_return_canned(Error, Request) -> lager:alert("Registry cannot be reached!"), - lager:alert("Error contacting registry: [~p, ~p]~n", [Error, Request]), + lager:alert("Error contacting registry: [~p, ~p]~n", + [Error, Request]), {2400, canned_response()}. %% In case registry is not accessible, return this response. %% In the future, this should be configurable. canned_response() -> - <<" - - - - Internal server error. - - -">>. + <<"\n<" + "epp xmlns=\"https://epp.tld.ee/schema/epp-ee-" + "1.0.xsd\" xmlns:xsi=\"http://www.w3.org/2001/" + "XMLSchema-instance\" xsi:schemaLocation=\"lib" + "/schemas/epp-ee-1.0.xsd\">\n \n " + " \n Internal server error.\n " + " \n \n">>. diff --git a/apps/epp_proxy/src/epp_http_client_behaviour.erl b/apps/epp_proxy/src/epp_http_client_behaviour.erl index b8980e1..b659bf1 100644 --- a/apps/epp_proxy/src/epp_http_client_behaviour.erl +++ b/apps/epp_proxy/src/epp_http_client_behaviour.erl @@ -7,5 +7,9 @@ %% Abstract module for http client behaviour. It should call EPP HTTP server %% and return a response back to the caller. -callback request(epp_request()) -> http_response(). --callback error_request(epp_error_request()) -> http_response(). --callback request_builder(map()) -> epp_request() | epp_error_request(). + +-callback +error_request(epp_error_request()) -> http_response(). + +-callback request_builder(map()) -> epp_request() | + epp_error_request(). diff --git a/apps/epp_proxy/src/epp_pool_supervisor.erl b/apps/epp_proxy/src/epp_pool_supervisor.erl index 0b6100a..c2d1077 100644 --- a/apps/epp_proxy/src/epp_pool_supervisor.erl +++ b/apps/epp_proxy/src/epp_pool_supervisor.erl @@ -12,5 +12,6 @@ start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). init([]) -> - SupFlags = #{strategy => one_for_one, intensity => 3, period => 60}, + SupFlags = #{strategy => one_for_one, intensity => 3, + period => 60}, {ok, {SupFlags, []}}. diff --git a/apps/epp_proxy/src/epp_proxy_app.erl b/apps/epp_proxy/src/epp_proxy_app.erl index 6c07e47..8d25864 100644 --- a/apps/epp_proxy/src/epp_proxy_app.erl +++ b/apps/epp_proxy/src/epp_proxy_app.erl @@ -18,9 +18,9 @@ start(_StartType, _StartArgs) -> epp_proxy_sup:start_link(). %%-------------------------------------------------------------------- -stop(_State) -> - ok. +stop(_State) -> ok. %%==================================================================== %% Internal functions %%==================================================================== + diff --git a/apps/epp_proxy/src/epp_proxy_sup.erl b/apps/epp_proxy/src/epp_proxy_sup.erl index a8c7bfe..74b0618 100644 --- a/apps/epp_proxy/src/epp_proxy_sup.erl +++ b/apps/epp_proxy/src/epp_proxy_sup.erl @@ -14,19 +14,21 @@ -export([init/1]). -define(SERVER, ?MODULE). --define(DevMode, application:get_env(epp_proxy, dev_mode)). + +-define(DevMode, + application:get_env(epp_proxy, dev_mode)). + -define(TCPPort, case application:get_env(epp_proxy, tcp_port) of undefined -> undefined; {ok, Value} -> Value - end). + end). -define(TLSPort, case application:get_env(epp_proxy, tls_port) of undefined -> undefined; {ok, Val} -> Val - end). - + end). %%==================================================================== %% API functions @@ -44,25 +46,25 @@ start_link() -> %% Before OTP 18 tuples must be used to specify a child. e.g. %% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} init([]) -> - SupFlags = #{strategy => one_for_one, intensity => 3, period => 60}, - TCPAcceptor = #{id => epp_tcp_acceptor, - type => worker, - modules => [epp_tcp_acceptor], - start => {epp_tcp_acceptor, start_link, [?TCPPort]}}, - TLSAcceptor = #{id => epp_tls_acceptor, - type => worker, - modules => [epp_tls_acceptor], - start => {epp_tls_acceptor, start_link, [?TLSPort]}}, + SupFlags = #{strategy => one_for_one, intensity => 3, + period => 60}, + TCPAcceptor = #{id => epp_tcp_acceptor, type => worker, + modules => [epp_tcp_acceptor], + start => {epp_tcp_acceptor, start_link, [?TCPPort]}}, + TLSAcceptor = #{id => epp_tls_acceptor, type => worker, + modules => [epp_tls_acceptor], + start => {epp_tls_acceptor, start_link, [?TLSPort]}}, PoolSupervisor = #{id => epp_pool_supervisor, - type => supervisor, - modules => [epp_pool_supervisor], - start => {epp_pool_supervisor, start_link, []}}, + type => supervisor, modules => [epp_pool_supervisor], + start => {epp_pool_supervisor, start_link, []}}, ChildrenSpec = case ?DevMode of - {ok, true} -> [TCPAcceptor, TLSAcceptor, PoolSupervisor]; - _ -> [TLSAcceptor, PoolSupervisor] - end, + {ok, true} -> + [TCPAcceptor, TLSAcceptor, PoolSupervisor]; + _ -> [TLSAcceptor, PoolSupervisor] + end, {ok, {SupFlags, ChildrenSpec}}. %%==================================================================== %% Internal functions %%==================================================================== + diff --git a/apps/epp_proxy/src/epp_router.erl b/apps/epp_proxy/src/epp_router.erl index 85de405..34e4032 100644 --- a/apps/epp_proxy/src/epp_router.erl +++ b/apps/epp_proxy/src/epp_router.erl @@ -1,53 +1,71 @@ -module(epp_router). --export([route_request/1, request_method/1]). +-export([request_method/1, route_request/1]). --define(validCommands, ["hello", "login", "logout", "check", "info", "poll", - "create", "delete", "renew", "update", "transfer"]). +-define(validCommands, + ["hello", "login", "logout", "check", "info", "poll", + "create", "delete", "renew", "update", "transfer"]). %% 47 is the character code -define(forwardSlash, 47). %% request method: GET for greeting, POST for everything else. -request_method("hello") -> - get; -request_method(<<"hello">>) -> - get; -request_method("error") -> - get; -request_method(<<"error">>) -> - get; -request_method(_) -> - post. +request_method("hello") -> get; +request_method(<<"hello">>) -> get; +request_method("error") -> get; +request_method(<<"error">>) -> get; +request_method(_) -> post. %% Base router route_request(Command) when is_binary(Command) -> - List = binary_to_list(Command), - url_map(List); -route_request(Command) when is_list(Command) -> url_map(Command). + List = binary_to_list(Command), url_map(List); +route_request(Command) when is_list(Command) -> + url_map(Command). %% Actually route to places url_map(Command) when is_list(Command) -> case Command of %% Session commands - "hello" -> unicode:characters_to_list([base_session_url(), Command]); - "login" -> unicode:characters_to_list([base_session_url(), Command]); - "logout" -> unicode:characters_to_list([base_session_url(), Command]); + "hello" -> + unicode:characters_to_list([base_session_url(), + Command]); + "login" -> + unicode:characters_to_list([base_session_url(), + Command]); + "logout" -> + unicode:characters_to_list([base_session_url(), + Command]); %% Poll commands - "check" -> unicode:characters_to_list([base_command_url(), Command]); - "info" -> unicode:characters_to_list([base_command_url(), Command]); - "poll" -> unicode:characters_to_list([base_command_url(), Command]); + "check" -> + unicode:characters_to_list([base_command_url(), + Command]); + "info" -> + unicode:characters_to_list([base_command_url(), + Command]); + "poll" -> + unicode:characters_to_list([base_command_url(), + Command]); %% Transform commands - "create" -> unicode:characters_to_list([base_command_url(), Command]); - "delete" -> unicode:characters_to_list([base_command_url(), Command]); - "renew" -> unicode:characters_to_list([base_command_url(), Command]); - "update" -> unicode:characters_to_list([base_command_url(), Command]); - % Transfer is both poll and query - "transfer" -> unicode:characters_to_list([base_command_url(), Command]); - % Error route - "error" -> base_error_url() - % Anything else should fail. - end. + "create" -> + unicode:characters_to_list([base_command_url(), + Command]); + "delete" -> + unicode:characters_to_list([base_command_url(), + Command]); + "renew" -> + unicode:characters_to_list([base_command_url(), + Command]); + "update" -> + unicode:characters_to_list([base_command_url(), + Command]); + % Transfer is both poll and query + "transfer" -> + unicode:characters_to_list([base_command_url(), + Command]); + % Error route + "error" -> + base_error_url() % Anything else should fail. + end. %% This allows the person who configures proxy to not care about trailing %% slashes in HTTP. @@ -62,8 +80,8 @@ appendable_route(Route) -> parametrizable_route(Route) -> case lists:last(Route) of ?forwardSlash -> lists:droplast(Route); - _ -> Route - end. + _ -> Route + end. %% Every time a request is made, this will go to ETS to check what's the route, %% But that is fast enough to not be noticed by anyone. diff --git a/apps/epp_proxy/src/epp_tcp_acceptor.erl b/apps/epp_proxy/src/epp_tcp_acceptor.erl index cbe0016..996c490 100644 --- a/apps/epp_proxy/src/epp_tcp_acceptor.erl +++ b/apps/epp_proxy/src/epp_tcp_acceptor.erl @@ -6,45 +6,50 @@ -module(epp_tcp_acceptor). -behaviour(gen_server). + -define(SERVER, ?MODULE). + -define(POOL_SUPERVISOR, epp_pool_supervisor). + -define(WORKER, epp_tcp_worker). %% gen_server callbacks --export([init/1, handle_cast/2, handle_call/3, start_link/1]). +-export([handle_call/3, handle_cast/2, init/1, + start_link/1]). -record(state, {socket, port, options}). start_link(Port) -> - gen_server:start_link({local, ?SERVER}, ?MODULE, Port, []). + gen_server:start_link({local, ?SERVER}, ?MODULE, Port, + []). init(Port) -> - Options = [binary, - {packet, raw}, - {active, false}, + Options = [binary, {packet, raw}, {active, false}, {reuseaddr, true}], - {ok, ListenSocket} = gen_tcp:listen(Port, Options), gen_server:cast(self(), accept), - - {ok, #state{socket=ListenSocket, port=Port, options=Options}}. + {ok, + #state{socket = ListenSocket, port = Port, + options = Options}}. handle_cast(accept, - State = #state{socket=ListenSocket, port=Port, options=Options}) -> + State = #state{socket = ListenSocket, port = Port, + options = Options}) -> {ok, AcceptSocket} = gen_tcp:accept(ListenSocket), {ok, NewOwner} = create_worker(AcceptSocket), - ok = gen_tcp:controlling_process(AcceptSocket, NewOwner), + ok = gen_tcp:controlling_process(AcceptSocket, + NewOwner), gen_server:cast(NewOwner, serve), gen_server:cast(NewOwner, greeting), gen_server:cast(self(), accept), - {noreply, State#state{socket=ListenSocket, port=Port, options=Options}}. + {noreply, + State#state{socket = ListenSocket, port = Port, + options = Options}}. handle_call(_E, _From, State) -> {noreply, State}. create_worker(Socket) -> - ChildSpec = #{id => rand:uniform(), - type => worker, - modules => [?WORKER], - restart => temporary, - start => {?WORKER, start_link, [Socket]}}, + ChildSpec = #{id => rand:uniform(), type => worker, + modules => [?WORKER], restart => temporary, + start => {?WORKER, start_link, [Socket]}}, supervisor:start_child(?POOL_SUPERVISOR, ChildSpec). diff --git a/apps/epp_proxy/src/epp_tcp_worker.erl b/apps/epp_proxy/src/epp_tcp_worker.erl index 2fde5dc..7ee0267 100644 --- a/apps/epp_proxy/src/epp_tcp_worker.erl +++ b/apps/epp_proxy/src/epp_tcp_worker.erl @@ -1,25 +1,25 @@ -module(epp_tcp_worker). -behaviour(gen_server). + -define(SERVER, ?MODULE). -include("epp_proxy.hrl"). %% gen_server callbacks --export([init/1, handle_cast/2, handle_call/3, start_link/1]). +-export([handle_call/3, handle_cast/2, init/1, + start_link/1]). + -export([code_change/3]). --record(valid_frame, {command, - cl_trid, - raw_frame}). --record(invalid_frame, {code, - cl_trid, - message}). --record(state, {socket, - session_id, - headers }). +-record(valid_frame, {command, cl_trid, raw_frame}). + +-record(invalid_frame, {code, cl_trid, message}). + +-record(state, {socket, session_id, headers}). -define(XMLErrorCode, <<"2001">>). + -define(XMLErrorMessage, <<"Command syntax error.">>). %% Initialize process @@ -27,7 +27,7 @@ init(Socket) -> lager:info("Created a worker process: [~p]", [self()]), SessionId = epp_util:session_id(self()), - {ok, #state{socket=Socket, session_id=SessionId}}. + {ok, #state{socket = Socket, session_id = SessionId}}. start_link(Socket) -> gen_server:start_link(?MODULE, Socket, []). @@ -36,32 +36,28 @@ start_link(Socket) -> %% Gather data that we use a headers for %% http request: %% Client IP address -handle_cast(serve, State = #state{socket=Socket}) -> +handle_cast(serve, State = #state{socket = Socket}) -> NewState = state_from_socket(Socket, State), {noreply, NewState}; - %% Step two: %% Using the state of the connection, get the hello route from http server. %% Send the response from HTTP server back to EPP client. %% When this succeeds, send "process_command" to self and await further %% commands. -handle_cast(greeting, State = #state{socket=Socket, - session_id=SessionId, - headers=Headers}) -> - - - Request = epp_http_client:request_builder(#{command => "hello", +handle_cast(greeting, + State = #state{socket = Socket, session_id = SessionId, + headers = Headers}) -> + Request = epp_http_client:request_builder(#{command => + "hello", session_id => SessionId, raw_frame => "", headers => Headers, cl_trid => nomatch}), - {_Status, Body} = epp_http_client:request(Request), - frame_to_socket(Body, Socket), gen_server:cast(self(), process_command), - {noreply, State#state{socket=Socket, session_id=SessionId}}; - + {noreply, + State#state{socket = Socket, session_id = SessionId}}; %% Step three to N: %% Await input from client. Parse it, and perform an appropriate http request. %% Commands go to commands, invalid XML goes to error. @@ -72,44 +68,44 @@ handle_cast(greeting, State = #state{socket=Socket, %% %% Otherwise send "process_command" again to self to repeat the process. handle_cast(process_command, - State = #state{socket=Socket,session_id=SessionId, - headers=Headers}) -> + State = #state{socket = Socket, session_id = SessionId, + headers = Headers}) -> RawFrame = frame_from_socket(Socket, State), - case parse_frame(RawFrame) of - #valid_frame{command=Command, cl_trid=ClTRID} -> - Request = epp_http_client:request_builder(#{command => Command, + #valid_frame{command = Command, cl_trid = ClTRID} -> + Request = epp_http_client:request_builder(#{command => + Command, session_id => SessionId, raw_frame => RawFrame, headers => Headers, cl_trid => ClTRID}), - {_Status, Body} = epp_http_client:request(Request); - #invalid_frame{message=Message, code=Code, cl_trid=ClTRID} -> + #invalid_frame{message = Message, code = Code, + cl_trid = ClTRID} -> Command = "error", - Request = epp_http_client:request_builder(#{command => Command, + Request = epp_http_client:request_builder(#{command => + Command, session_id => SessionId, headers => Headers, code => Code, message => Message, cl_trid => ClTRID}), {_Status, Body} = epp_http_client:error_request(Request) - end, - + end, frame_to_socket(Body, Socket), - %% On logout, close the socket. %% Else, go back to the beginning of the loop. - if - Command =:= "logout" -> + if Command =:= "logout" -> ok = gen_tcp:shutdown(Socket, read_write), {stop, normal, State}; - true -> + true -> gen_server:cast(self(), process_command), - {noreply, State#state{socket=Socket, session_id=SessionId}} + {noreply, + State#state{socket = Socket, session_id = SessionId}} end. handle_call(_E, _From, State) -> {noreply, State}. + code_change(_OldVersion, State, _Extra) -> {ok, State}. %% Private function @@ -120,54 +116,49 @@ read_length(Socket) -> case gen_tcp:recv(Socket, 4) of {ok, Data} -> Length = binary:decode_unsigned(Data, big), - LengthToReceive = epp_util:frame_length_to_receive(Length), + LengthToReceive = + epp_util:frame_length_to_receive(Length), {ok, LengthToReceive}; {error, Reason} -> - io:format("Error: ~p~n", [Reason]), - {error, Reason} + io:format("Error: ~p~n", [Reason]), {error, Reason} end. read_frame(Socket, FrameLength) -> case gen_tcp:recv(Socket, FrameLength) of - {ok, Data} -> - {ok, Data}; + {ok, Data} -> {ok, Data}; {error, Reason} -> - io:format("Error: ~p~n", [Reason]), - {error, Reason} + io:format("Error: ~p~n", [Reason]), {error, Reason} end. %% Wrap a message in EPP frame, and then send it to socket. frame_to_socket(Message, Socket) -> Length = epp_util:frame_length_to_send(Message), - ByteSize = << Length:32/big >>, + ByteSize = <>, write_line(Socket, ByteSize), write_line(Socket, Message). %% Extract state info from socket. Fail if you must. state_from_socket(Socket, State) -> - {ok, {PeerIp, _PeerPort}} = inet:peername(Socket), + {ok, {PeerIp, _PeerPort}} = inet:peername(Socket), Headers = [{"User-Agent", <<"EPP proxy">>}, {"X-Forwarded-for", epp_util:readable_ip(PeerIp)}], - NewState = State#state{socket=Socket, headers=Headers}, - lager:info("Established connection with: [~p]~n", [NewState]), + NewState = State#state{socket = Socket, + headers = Headers}, + lager:info("Established connection with: [~p]~n", + [NewState]), NewState. %% First, listen for 4 bytes, then listen until the declared length. %% Return the frame binary at the very end. frame_from_socket(Socket, State) -> Length = case read_length(Socket) of - {ok, Data} -> - Data; - {error, _Details} -> - {stop, normal, State} - end, - + {ok, Data} -> Data; + {error, _Details} -> {stop, normal, State} + end, Frame = case read_frame(Socket, Length) of - {ok, FrameData} -> - FrameData; - {error, _FrameDetails} -> - {stop, normal, State} - end, + {ok, FrameData} -> FrameData; + {error, _FrameDetails} -> {stop, normal, State} + end, Frame. %% Get status, XML record, command and clTRID if defined. @@ -177,11 +168,9 @@ parse_frame(Frame) -> case epp_xml:parse(Frame) of {ok, XMLRecord} -> Command = epp_xml:get_command(XMLRecord), - #valid_frame{command=Command, - cl_trid=ClTRID, - raw_frame=Frame}; + #valid_frame{command = Command, cl_trid = ClTRID, + raw_frame = Frame}; {error, _} -> - #invalid_frame{code=?XMLErrorCode, - message=?XMLErrorMessage, - cl_trid=ClTRID} - end. + #invalid_frame{code = ?XMLErrorCode, + message = ?XMLErrorMessage, cl_trid = ClTRID} + end. diff --git a/apps/epp_proxy/src/epp_tls_acceptor.erl b/apps/epp_proxy/src/epp_tls_acceptor.erl index f2b3be8..1d9bffd 100644 --- a/apps/epp_proxy/src/epp_tls_acceptor.erl +++ b/apps/epp_proxy/src/epp_tls_acceptor.erl @@ -3,39 +3,36 @@ -behaviour(gen_server). -define(SERVER, ?MODULE). + -define(POOL_SUPERVISOR, epp_pool_supervisor). + -define(WORKER, epp_tls_worker). %% gen_server callbacks --export([init/1, handle_cast/2, handle_call/3, start_link/1]). +-export([handle_call/3, handle_cast/2, init/1, + start_link/1]). -export([crl_file/0]). -record(state, {socket, port, options}). start_link(Port) -> - gen_server:start_link({local, ?SERVER}, ?MODULE, Port, []). + gen_server:start_link({local, ?SERVER}, ?MODULE, Port, + []). init(Port) -> - Options = [binary, - {packet, raw}, - {active, false}, - {reuseaddr, true}, - {verify, verify_peer}, - {depth, 1}, - {cacertfile, ca_cert_file()}, - {certfile, cert_file()}, - {keyfile, key_file()}, - {crl_check, peer}, - {crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}}], - + Options = [binary, {packet, raw}, {active, false}, + {reuseaddr, true}, {verify, verify_peer}, {depth, 1}, + {cacertfile, ca_cert_file()}, {certfile, cert_file()}, + {keyfile, key_file()}, {crl_check, peer}, + {crl_cache, + {ssl_crl_cache, {internal, [{http, 5000}]}}}], ssl_crl_cache:insert({file, crl_file()}), - {ok, ListenSocket} = ssl:listen(Port, Options), gen_server:cast(self(), accept), - - {ok, #state{socket=ListenSocket, port=Port, options=Options}}. - + {ok, + #state{socket = ListenSocket, port = Port, + options = Options}}. %% Acceptor has only one state that goes in a loop: %% 1. Listen for a connection from anyone. @@ -43,25 +40,27 @@ init(Port) -> %% 3. Pass the connection to the worker and make it a controlling process for %% the socket. %% 4. Go back to listening. -handle_cast(accept, State = #state{socket=ListenSocket, port=Port, options=Options}) -> +handle_cast(accept, + State = #state{socket = ListenSocket, port = Port, + options = Options}) -> {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), {ok, NewOwner} = create_worker(AcceptSocket), ok = ssl:controlling_process(AcceptSocket, NewOwner), gen_server:cast(NewOwner, serve), gen_server:cast(NewOwner, greeting), gen_server:cast(self(), accept), - {noreply, State#state{socket=ListenSocket, port=Port, options=Options}}. + {noreply, + State#state{socket = ListenSocket, port = Port, + options = Options}}. handle_call(_E, _From, State) -> {noreply, State}. %% Create a worker process. These are short lived and should not be restarted, %% but for the purpose of order we should put them in a supervision tree. create_worker(Socket) -> - ChildSpec = #{id => rand:uniform(), - type => worker, - modules => [?WORKER], - restart => temporary, - start => {?WORKER, start_link, [Socket]}}, + ChildSpec = #{id => rand:uniform(), type => worker, + modules => [?WORKER], restart => temporary, + start => {?WORKER, start_link, [Socket]}}, supervisor:start_child(?POOL_SUPERVISOR, ChildSpec). %% Private functions for returning paths to files. It costs almost nothing diff --git a/apps/epp_proxy/src/epp_tls_worker.erl b/apps/epp_proxy/src/epp_tls_worker.erl index 49bb43b..4cbdd1b 100644 --- a/apps/epp_proxy/src/epp_tls_worker.erl +++ b/apps/epp_proxy/src/epp_tls_worker.erl @@ -1,25 +1,25 @@ -module(epp_tls_worker). -behaviour(gen_server). + -define(SERVER, ?MODULE). -include("epp_proxy.hrl"). %% gen_server callbacks --export([init/1, handle_cast/2, handle_call/3, start_link/1]). +-export([handle_call/3, handle_cast/2, init/1, + start_link/1]). + -export([code_change/3]). --record(valid_frame, {command, - cl_trid, - raw_frame}). --record(invalid_frame, {code, - cl_trid, - message}). --record(state, {socket, - session_id, - headers }). +-record(valid_frame, {command, cl_trid, raw_frame}). + +-record(invalid_frame, {code, cl_trid, message}). + +-record(state, {socket, session_id, headers}). -define(XMLErrorCode, <<"2001">>). + -define(XMLErrorMessage, <<"Command syntax error.">>). %% Initialize process @@ -27,7 +27,7 @@ init(Socket) -> lager:info("Created a worker process: [~p]", [self()]), SessionId = epp_util:session_id(self()), - {ok, #state{socket=Socket, session_id=SessionId}}. + {ok, #state{socket = Socket, session_id = SessionId}}. start_link(Socket) -> gen_server:start_link(?MODULE, Socket, []). @@ -35,37 +35,33 @@ start_link(Socket) -> %% First step after spinning off the process: %% Perform an TLS handshake and gather data that we use a headers for %% http request: -%% Common name -%% Client certificate -%% Client IP address +%% Common name, +%% Client certificate, +%% Client IP address, %% If certificate is revoked, this will fail right away here. %% mod_epp does exactly the same thing. -handle_cast(serve, State = #state{socket=Socket}) -> +handle_cast(serve, State = #state{socket = Socket}) -> {ok, SecureSocket} = ssl:handshake(Socket), NewState = state_from_socket(SecureSocket, State), {noreply, NewState}; - -%% Step two: -%% Using the state of the connection, get the hello route from http server. -%% Send the response from HTTP server back to EPP client. -%% When this succeeds, send "process_command" to self and await further -%% commands. -handle_cast(greeting, State = #state{socket=Socket, - session_id=SessionId, - headers=Headers}) -> - - Request = epp_http_client:request_builder(#{command => "hello", +%% Step two: Using the state of the connection, get the hello route +%% from http server. Send the response from HTTP server back to EPP +%% client. When this succeeds, send "process_command" to self and +%% await further commands. +handle_cast(greeting, + State = #state{socket = Socket, session_id = SessionId, + headers = Headers}) -> + Request = epp_http_client:request_builder(#{command => + "hello", session_id => SessionId, raw_frame => "", headers => Headers, cl_trid => nomatch}), - {_Status, Body} = epp_http_client:request(Request), - frame_to_socket(Body, Socket), gen_server:cast(self(), process_command), - {noreply, State#state{socket=Socket, session_id=SessionId}}; - + {noreply, + State#state{socket = Socket, session_id = SessionId}}; %% Step three to N: %% Await input from client. Parse it, and perform an appropriate http request. %% Commands go to commands, invalid XML goes to error. @@ -76,44 +72,44 @@ handle_cast(greeting, State = #state{socket=Socket, %% %% Otherwise send "process_command" again to self to repeat the process. handle_cast(process_command, - State = #state{socket=Socket,session_id=SessionId, - headers=Headers}) -> + State = #state{socket = Socket, session_id = SessionId, + headers = Headers}) -> RawFrame = frame_from_socket(Socket, State), - case parse_frame(RawFrame) of - #valid_frame{command=Command, cl_trid=ClTRID} -> - Request = epp_http_client:request_builder(#{command => Command, + #valid_frame{command = Command, cl_trid = ClTRID} -> + Request = epp_http_client:request_builder(#{command => + Command, session_id => SessionId, raw_frame => RawFrame, headers => Headers, cl_trid => ClTRID}), - {_Status, Body} = epp_http_client:request(Request); - #invalid_frame{message=Message, code=Code, cl_trid=ClTRID} -> + #invalid_frame{message = Message, code = Code, + cl_trid = ClTRID} -> Command = "error", - Request = epp_http_client:request_builder(#{command => Command, + Request = epp_http_client:request_builder(#{command => + Command, session_id => SessionId, headers => Headers, code => Code, message => Message, cl_trid => ClTRID}), {_Status, Body} = epp_http_client:error_request(Request) - end, - + end, frame_to_socket(Body, Socket), - %% On logout, close the socket. %% Else, go back to the beginning of the loop. - if - Command =:= "logout" -> + if Command =:= "logout" -> ok = ssl:shutdown(Socket, read_write), {stop, normal, State}; - true -> + true -> gen_server:cast(self(), process_command), - {noreply, State#state{socket=Socket, session_id=SessionId}} + {noreply, + State#state{socket = Socket, session_id = SessionId}} end. handle_call(_E, _From, State) -> {noreply, State}. + code_change(_OldVersion, State, _Extra) -> {ok, State}. %% Private functions @@ -121,47 +117,39 @@ read_length(Socket) -> case ssl:recv(Socket, 4) of {ok, Data} -> Length = binary:decode_unsigned(Data, big), - LengthToReceive = epp_util:frame_length_to_receive(Length), + LengthToReceive = + epp_util:frame_length_to_receive(Length), {ok, LengthToReceive}; - {error, Reason} -> - {error, Reason} + {error, Reason} -> {error, Reason} end. read_frame(Socket, FrameLength) -> case ssl:recv(Socket, FrameLength) of - {ok, Data} -> - {ok, Data}; - {error, Reason} -> - {error, Reason} + {ok, Data} -> {ok, Data}; + {error, Reason} -> {error, Reason} end. %% Wrap a message in EPP frame, and then send it to socket. frame_to_socket(Message, Socket) -> Length = epp_util:frame_length_to_send(Message), - ByteSize = << Length:32/big >>, + ByteSize = <>, write_line(Socket, ByteSize), write_line(Socket, Message). -write_line(Socket, Line) -> - ok = ssl:send(Socket, Line). +write_line(Socket, Line) -> ok = ssl:send(Socket, Line). %% First, listen for 4 bytes, then listen until the declared length. %% Return the frame binary at the very end. %% If the client closes connection abruptly, then kill the process frame_from_socket(Socket, State) -> Length = case read_length(Socket) of - {ok, Data} -> - Data; - {error, closed} -> - log_and_exit(State) - end, - + {ok, Data} -> Data; + {error, closed} -> log_and_exit(State) + end, Frame = case read_frame(Socket, Length) of - {ok, FrameData} -> - FrameData; - {error, closed} -> - log_and_exit(State) - end, + {ok, FrameData} -> FrameData; + {error, closed} -> log_and_exit(State) + end, Frame. log_and_exit(State) -> @@ -171,15 +159,17 @@ log_and_exit(State) -> %% Extract state info from socket. Fail if you must. state_from_socket(Socket, State) -> {ok, PeerCert} = ssl:peercert(Socket), - {ok, {PeerIp, _PeerPort}} = ssl:peername(Socket), + {ok, {PeerIp, _PeerPort}} = ssl:peername(Socket), {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT} = epp_certs:headers_from_cert(PeerCert), Headers = [{"SSL-CLIENT-CERT", SSL_CLIENT_CERT}, {"SSL-CLIENT-S-DN-CN", SSL_CLIENT_S_DN_CN}, {"User-Agent", <<"EPP proxy">>}, {"X-Forwarded-for", epp_util:readable_ip(PeerIp)}], - NewState = State#state{socket=Socket, headers=Headers}, - lager:info("Established connection with: [~p]~n", [NewState]), + NewState = State#state{socket = Socket, + headers = Headers}, + lager:info("Established connection with: [~p]~n", + [NewState]), NewState. %% Get status, XML record, command and clTRID if defined. @@ -189,11 +179,9 @@ parse_frame(Frame) -> case epp_xml:parse(Frame) of {ok, XMLRecord} -> Command = epp_xml:get_command(XMLRecord), - #valid_frame{command=Command, - cl_trid=ClTRID, - raw_frame=Frame}; + #valid_frame{command = Command, cl_trid = ClTRID, + raw_frame = Frame}; {error, _} -> - #invalid_frame{code=?XMLErrorCode, - message=?XMLErrorMessage, - cl_trid=ClTRID} - end. + #invalid_frame{code = ?XMLErrorCode, + message = ?XMLErrorMessage, cl_trid = ClTRID} + end. diff --git a/apps/epp_proxy/src/epp_util.erl b/apps/epp_proxy/src/epp_util.erl index 1442f44..443ad7e 100644 --- a/apps/epp_proxy/src/epp_util.erl +++ b/apps/epp_proxy/src/epp_util.erl @@ -1,21 +1,23 @@ -module(epp_util). --export([create_map/1, create_session_id/1, frame_length/1, - frame_length_to_receive/1, frame_length_to_send/1, - session_id/1, readable_ip/1]). +-export([create_map/1, create_session_id/1, + frame_length/1, frame_length_to_receive/1, + frame_length_to_send/1, readable_ip/1, session_id/1]). -define(OFFSET, 4). %% Given a pid, return a sha512 hash of unique attributes. --spec session_id(pid()) -> list(char()). +-spec session_id(pid()) -> [char()]. + session_id(Pid) -> UniqueMap = create_map(Pid), BinaryHash = create_session_id(UniqueMap), BinaryHash. %% Give me a process id, I'll create a random map for you. --spec create_map(pid()) -> #{string() => pid(), string() => float(), - string() => string()}. +-spec create_map(pid()) -> #{string() => pid(), + string() => float(), string() => string()}. + create_map(Pid) when is_pid(Pid) -> Now = erlang:system_time(second), #{"pid" => Pid, "random" => rand:uniform(), @@ -23,25 +25,29 @@ create_map(Pid) when is_pid(Pid) -> %% Given the special data structure, return back a binary hash to pass to the %% application server. --spec create_session_id(#{string() => pid(), string() => float(), - string() => string()}) -> list(char()). -create_session_id(#{"pid" := Pid, "random" := Random, "timestamp" := Timestamp}) -> - Map = #{"pid" => pid_to_list(Pid), "random" => float_to_list(Random), +-spec create_session_id(#{string() => pid(), + string() => float(), + string() => string()}) -> [char()]. + +create_session_id(#{"pid" := Pid, "random" := Random, + "timestamp" := Timestamp}) -> + Map = #{"pid" => pid_to_list(Pid), + "random" => float_to_list(Random), "timestamp" => Timestamp}, ListOfTuples = maps:to_list(Map), ListOfLists = [[X, ",", Y] || {X, Y} <- ListOfTuples], - NestedList = lists:join(",",ListOfLists), + NestedList = lists:join(",", ListOfLists), ListOfGlyphs = lists:flatten(NestedList), BinaryHash = crypto:hash(sha512, ListOfGlyphs), - String = lists:flatten([integer_to_list(X,16) || <> <= BinaryHash]), + String = lists:flatten([integer_to_list(X, 16) + || <> <= BinaryHash]), String. frame_length_to_receive(Size) when Size >= 0 -> - Size - ?OFFSET. + Size - (?OFFSET). frame_length_to_send(Frame) -> - Length = frame_length(Frame), - Length + ?OFFSET. + Length = frame_length(Frame), Length + (?OFFSET). frame_length(Frame) when is_binary(Frame) -> byte_size(Frame); @@ -50,8 +56,11 @@ frame_length(Frame) when is_list(Frame) -> byte_size(Bin). %% Pass a tuple of IP address, return a binary for sending over the wire. --spec readable_ip({integer(), integer(), integer(), integer()}) -> binary(). -readable_ip({FirstOctet, SecondOctet, ThirdOctet, FourthOctet}) -> +-spec readable_ip({integer(), integer(), integer(), + integer()}) -> binary(). + +readable_ip({FirstOctet, SecondOctet, ThirdOctet, + FourthOctet}) -> List = [integer_to_list(FirstOctet), ".", integer_to_list(SecondOctet), ".", integer_to_list(ThirdOctet), ".", diff --git a/apps/epp_proxy/src/epp_xml.erl b/apps/epp_proxy/src/epp_xml.erl index 1728390..514a443 100644 --- a/apps/epp_proxy/src/epp_xml.erl +++ b/apps/epp_proxy/src/epp_xml.erl @@ -5,25 +5,28 @@ -include_lib("xmerl/include/xmerl.hrl"). %% Get command from an xmlElement. Otherwise return undefined. -get_command(Record) when is_record(Record, xmlElement) -> - case xmerl_xpath:string("name(/epp/command/*[1])", Record) of +get_command(Record) + when is_record(Record, xmlElement) -> + case xmerl_xpath:string("name(/epp/command/*[1])", + Record) + of {xmlObj, string, []} -> undefined; {xmlObj, string, Command} -> Command - end; + end; get_command(_) -> undefined. %% xml_erl expects a list of characters, so let's give it to it. %% Otherwise return an error tuple. parse(Text) when is_list(Text) -> parse_list(Text); parse(Text) when is_binary(Text) -> - List = binary_to_list(Text), - parse_list(List); + List = binary_to_list(Text), parse_list(List); parse(_) -> {error, {fatal, {expected_binary_or_list}}}. %% Parse a record that came from the wire and return a xmlElement record. parse_list(List) when is_list(List) -> - try xmerl_scan:string(List, [{quiet, 'true'}]) of - {Record, []} when is_record(Record, xmlElement) -> {ok, Record} + try xmerl_scan:string(List, [{quiet, true}]) of + {Record, []} when is_record(Record, xmlElement) -> + {ok, Record} catch exit:X -> {error, X} end. @@ -31,13 +34,16 @@ parse_list(List) when is_list(List) -> %% The idea is that even when XML command is invalid, %% we should recover cltrid from it, hence the regex use. -spec find_cltrid(any()) -> binary() | nomatch. + find_cltrid(Text) when is_list(Text) -> run_regex(Text); -find_cltrid(Text) when is_binary(Text) -> run_regex(Text); +find_cltrid(Text) when is_binary(Text) -> + run_regex(Text); find_cltrid(_) -> nomatch. run_regex(Text) -> - {ok, MP} = re:compile("(?.+)<\/clTRID>"), - case re:run(Text, MP, [{capture, ["cltrid"], binary}]) of + {ok, MP} = re:compile("(?.+)"), + case re:run(Text, MP, [{capture, ["cltrid"], binary}]) + of {match, [Binary]} -> Binary; nomatch -> nomatch - end. + end. diff --git a/apps/epp_proxy/test/epp_http_client_tests.erl b/apps/epp_proxy/test/epp_http_client_tests.erl index 48e8c32..b1304ce 100644 --- a/apps/epp_proxy/test/epp_http_client_tests.erl +++ b/apps/epp_proxy/test/epp_http_client_tests.erl @@ -9,9 +9,9 @@ hello_request_builder_test() -> headers => [{"User-Agent", <<"EPP proxy">>}]}, Request = epp_http_client:request_builder(RequestMap), ExpectedTuple = {epp_request,get,"https://registry.test/epp/session/hello", - [], - [<<"session=Random; Version=1">>], - [{"User-Agent",<<"EPP proxy">>}]}, + [], + [<<"session=Random; Version=1">>], + [{"User-Agent",<<"EPP proxy">>}]}, ?assert(is_record(Request, epp_request)), ?assertEqual(ExpectedTuple, Request). @@ -38,8 +38,8 @@ command_request_builder_test() -> ExpectedTuple = {epp_request,post, "https://registry.test/epp/command/create", {multipart, - [{<<"raw_frame">>,"Some XML here"}, - {<<"clTRID">>,"EE-123456789"}]}, + [{<<"raw_frame">>,"Some XML here"}, + {<<"clTRID">>,"EE-123456789"}]}, [<<"session=Random; Version=1">>], [{"User-Agent",<<"EPP proxy">>}]}, ?assert(is_record(Request, epp_request)), diff --git a/apps/epp_proxy/test/epp_xml_tests.erl b/apps/epp_proxy/test/epp_xml_tests.erl index 17bd465..260625d 100644 --- a/apps/epp_proxy/test/epp_xml_tests.erl +++ b/apps/epp_proxy/test/epp_xml_tests.erl @@ -7,25 +7,25 @@ -define(sampleCommandList, " - - test - test - - sample1trid - - "). + + test + test + + sample1trid + + "). -define(validXMLNotEPPList, " test - test@test.com - "). + test@test.com + "). %% parse parse_not_a_list_or_binary_test() -> - Input = 1234, - ExpectedOutput = {error, {fatal, {expected_binary_or_list}}}, - ?assertEqual(ExpectedOutput, epp_xml:parse(Input)). + Input = 1234, + ExpectedOutput = {error, {fatal, {expected_binary_or_list}}}, + ?assertEqual(ExpectedOutput, epp_xml:parse(Input)). parse_sample_valid_xml_list_test() -> Input = ?sampleCommandList, diff --git a/rebar.config b/rebar.config index 3550c0f..502d63f 100644 --- a/rebar.config +++ b/rebar.config @@ -47,3 +47,5 @@ }, {test, [{deps, [proper]}]}] }. + +{plugins, [rebar3_fmt]}.