├── .gitignore ├── LICENSE ├── Makefile ├── README ├── doc ├── .gitignore ├── overview.edoc ├── rfc2068.txt └── xmlrpc_spec.txt ├── ebin └── .gitignore ├── examples ├── BankClient.java ├── DateClient.java ├── EchoClient.java ├── FibClient.java ├── Makefile ├── README ├── date_server.erl ├── echo_server.erl ├── fib_server.erl ├── reply.hrl ├── robust_bank_client.erl ├── robust_bank_server.erl ├── robust_banking.txt └── validator.erl ├── rebar ├── rebar.config ├── src ├── log.hrl ├── xmlrpc.app.src ├── xmlrpc.erl ├── xmlrpc_decode.erl ├── xmlrpc_encode.erl ├── xmlrpc_http.erl ├── xmlrpc_tcp_serv.erl └── xmlrpc_util.erl └── test ├── Makefile ├── T50.java ├── T60.java ├── test.erl └── xmlrpc-1.1.jar /.gitignore: -------------------------------------------------------------------------------- 1 | /.eunit 2 | /ebin/* 3 | *.beam 4 | *~ 5 | erl_crash.dump 6 | doc/*.css 7 | doc/*.html 8 | doc/*.png 9 | doc/edoc-info 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/LICENSE -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all check test clean 2 | 3 | all: 4 | ./rebar compile 5 | 6 | docs: 7 | ./rebar doc 8 | 9 | check: 10 | ./rebar check-plt 11 | ./rebar dialyze 12 | 13 | test: 14 | ./rebar eunit 15 | 16 | 17 | conf_clean: 18 | @: 19 | 20 | clean: 21 | ./rebar clean 22 | 23 | release: all 24 | rm -fr releases/xmlrpc-$(VSN) 25 | mkdir releases/xmlrpc-$(VSN) 26 | install -m 644 LICENSE README releases/xmlrpc-$(VSN) 27 | mkdir releases/xmlrpc-$(VSN)/doc 28 | install -m 644 doc/xmlrpc.3 releases/xmlrpc-$(VSN)/doc 29 | install -m 644 doc/xmlrpc.txt releases/xmlrpc-$(VSN)/doc 30 | install -m 644 doc/xmlrpc.ps releases/xmlrpc-$(VSN)/doc 31 | install -m 644 doc/xmlrpc.pdf releases/xmlrpc-$(VSN)/doc 32 | mkdir releases/xmlrpc-$(VSN)/ebin 33 | mkdir releases/xmlrpc-$(VSN)/examples 34 | install -m 644 examples/*.erl examples/*.java examples/*.txt examples/README examples/Makefile releases/xmlrpc-$(VSN)/examples 35 | mkdir releases/xmlrpc-$(VSN)/src 36 | install -m 644 src/*.erl src/*.hrl releases/xmlrpc-$(VSN)/src 37 | install -m 644 src/Makefile releases/xmlrpc-$(VSN)/src 38 | (cd releases;tar zcvf xmlrpc-$(VSN).tgz xmlrpc-$(VSN)) 39 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/README -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | edoc-info 2 | erlang.png 3 | stylesheet.css 4 | *.html 5 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/doc/overview.edoc -------------------------------------------------------------------------------- /doc/xmlrpc_spec.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/doc/xmlrpc_spec.txt -------------------------------------------------------------------------------- /ebin/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/ebin/.gitignore -------------------------------------------------------------------------------- /examples/BankClient.java: -------------------------------------------------------------------------------- 1 | import org.apache.xmlrpc.*; 2 | import java.util.*; 3 | 4 | public class BankClient { 5 | public static void main(String[] args) { 6 | if (args.length < 2) { 7 | System.err.println("Usage: BankClient deposit Account Amount"); 8 | System.err.println(" BankClient withdraw Account Amount"); 9 | System.err.println(" BankClient balance Account"); 10 | System.exit(-1); 11 | } 12 | 13 | if (!ask(3020, args)) 14 | if (!ask(3030, args)) 15 | System.err.println("No bank server available"); 16 | } 17 | 18 | private static boolean ask(int port, String[] args) { 19 | try { 20 | // This is obviously not a unique tag. 21 | String tag = new String(Long.toString(System.currentTimeMillis())); 22 | XmlRpcClient xmlrpc = 23 | new XmlRpcClient("http://localhost:"+port+"/"); 24 | Vector params = new Vector(); 25 | 26 | params.addElement(tag); 27 | params.addElement(new String(args[1])); 28 | 29 | if (args[0].equals("deposit")) { 30 | params.addElement(new Integer(Integer.parseInt(args[2]))); 31 | System.out.println(xmlrpc.execute("deposit", params)); 32 | return true; 33 | } 34 | 35 | if (args[0].equals("withdraw")) { 36 | params.addElement(new Integer(Integer.parseInt(args[2]))); 37 | System.out.println(xmlrpc.execute("withdraw", params)); 38 | return true; 39 | } 40 | 41 | if (args[0].equals("balance")) { 42 | System.out.println(xmlrpc.execute("balance", params)); 43 | return true; 44 | } 45 | } catch (Exception e) { 46 | System.err.println(e); 47 | } 48 | 49 | return false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/DateClient.java: -------------------------------------------------------------------------------- 1 | import org.apache.xmlrpc.*; 2 | import java.util.*; 3 | 4 | public class DateClient { 5 | public static void main(String[] args) { 6 | try { 7 | XmlRpcClient xmlrpc = new XmlRpcClient("http://localhost:4567/"); 8 | 9 | // Login 10 | Vector login_params = new Vector(); 11 | login_params.addElement(new String("Slarti")); 12 | login_params.addElement(new String("Bartfast")); 13 | System.out.println(xmlrpc.execute("login", login_params)); 14 | 15 | // Call 'days_since' function 16 | Hashtable ymd = new Hashtable(); 17 | ymd.put("y", new Integer(2001)); 18 | ymd.put("m", new Integer(12)); 19 | ymd.put("d", new Integer(20)); 20 | Hashtable calc = new Hashtable(); 21 | calc.put("days_since", ymd); 22 | Vector calc_params = new Vector(); 23 | calc_params.add(calc); 24 | System.out.println(xmlrpc.execute("calc", calc_params)); 25 | 26 | // Call 'days_since', 'day_of_week' and 'is_leap_year' functions 27 | calc.put("day_of_week", ymd); 28 | calc.put("is_leap_year", new Integer(2000)); 29 | Vector calc_params2 = new Vector(); 30 | calc_params2.addElement(calc); 31 | System.out.println(xmlrpc.execute("calc", calc_params2)); 32 | 33 | // Logout 34 | System.out.println(xmlrpc.execute("logout", new Vector())); 35 | } catch (Exception e) { 36 | System.err.println(e); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/EchoClient.java: -------------------------------------------------------------------------------- 1 | import org.apache.xmlrpc.*; 2 | import java.util.*; 3 | 4 | public class EchoClient { 5 | public static void main(String[] args) { 6 | try { 7 | XmlRpcClient xmlrpc = new XmlRpcClient("http://localhost:4567/"); 8 | Vector params = new Vector(); 9 | params.addElement(new Double(42.5)); 10 | params.addElement(new String("foo")); 11 | params.addElement(new Integer(7)); 12 | System.out.println(xmlrpc.execute("echo", params)); 13 | } catch (Exception e) { 14 | System.err.println(e); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/FibClient.java: -------------------------------------------------------------------------------- 1 | import org.apache.xmlrpc.*; 2 | import java.util.*; 3 | 4 | public class FibClient { 5 | public static void main(String[] args) { 6 | try { 7 | if (args.length != 1) { 8 | System.err.println("Usage: FibClient N"); 9 | System.exit(-1); 10 | } 11 | 12 | XmlRpcClient xmlrpc = new XmlRpcClient("http://localhost:4567/"); 13 | Vector params = new Vector(); 14 | params.addElement(new Integer(Integer.parseInt(args[0]))); 15 | System.out.println(xmlrpc.execute("fib", params)); 16 | } catch (Exception e) { 17 | System.err.println(e); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | # Update the APACHE_XMLRPC_PATH variable to point at your Apache 2 | # XML-RPC installation. This is only necessary if you intend to 3 | # compile the Java clients. 4 | APACHE_XMLRPC_PATH=../test/xmlrpc-1.1.jar 5 | 6 | # Do not change anything below this line. 7 | ERLC=erlc 8 | ERLC_FLAGS=-W 9 | MODULES=date_server echo_server fib_server validator 10 | EBIN_FILES=$(MODULES:%=%.beam) 11 | JAVAC=javac 12 | JAVAC_FLAGS=-classpath $(APACHE_XMLRPC_PATH):. 13 | JAVA_FILES=FibClient EchoClient DateClient BankClient 14 | CLASS_FILES=$(JAVA_FILES:%=%.class) 15 | 16 | all: $(EBIN_FILES) 17 | 18 | java_clients: $(CLASS_FILES) 19 | 20 | %.beam: %.erl 21 | $(ERLC) $(ERLC_FLAGS) $< 22 | 23 | %.class: %.java 24 | $(JAVAC) $(JAVAC_FLAGS) $< 25 | 26 | clean: 27 | rm -f $(EBIN_FILES) $(CLASS_FILES) 28 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | This directory contains five example Erlang XML-RPC servers. Four of 2 | them are desribed in this README file. The fifth server is an example 3 | of robust banking system and is described in a separate tutorial 4 | (robust_banking.txt). 5 | 6 | The servers are small and silly but together they utilize a majority 7 | of the functionality provided by the library. 8 | 9 | The first example (fib_server.erl) calculates Fibonacci values and is 10 | a non-keepalive server. The second example (echo_server.erl) echoes 11 | back any incoming parameters and is a non-keepalive server. The third 12 | example (date_server.erl) calculates calendar values for given dates 13 | and is a keepalive server which uses the state variable to provide 14 | login state and different timeout settings. The fourth example 15 | (validator.erl) is a validation server which can be used to validate 16 | the library using the http://validator.xmlrpc.org/ service. 17 | 18 | Run make to compile the servers. 19 | 20 | You need to replace "../../xmerl/ebin" below to point at your 21 | installation of the xmerl package. 22 | 23 | If you intend to verify inter-operability with the example Java clients 24 | you need to replace "../../apache/xmlrpc/bin/xmlrpc-1.1.jar" below to 25 | point at your installation of the Apache XML-RPC library. Get it at 26 | http://www.apache.org/. You furthermore need a recent installation of 27 | Java/JDK. Get it at http://java.sun.com/. 28 | 29 | Run 'make java_clients' to compile the Java clients. 30 | 31 | Example 1: fib_server.erl 32 | ------------------------- 33 | $ erl -pa ../ebin -pa ../../xmerl/ebin 34 | Erlang (BEAM) emulator version 5.1.2.b2 [source] 35 | 36 | Eshell V5.1.2.b2 (abort with ^G) 37 | 1> {ok, Pid} = xmlrpc:start_link({fib_server, handler}). 38 | {ok,<0.30.0>} 39 | 2> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, foo, []}). 40 | {ok,{response,{fault,-1,"Unknown call: {call,foo,[]}"}}} 41 | 3> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, fib, [0]}). 42 | {ok,{response,[1]}} 43 | 4> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, fib, [4]}). 44 | {ok,{response,[5]}} 45 | 46 | Nothing fancy. Uses a new HTTP connection for each call and handles 47 | unknown method calls gracefully. 48 | 49 | FibClient.java could as well has been used to call the Fibonacci 50 | server: 51 | 52 | $ java -classpath ../../apache/xmlrpc/bin/xmlrpc-1.1.jar:. FibClient 0 53 | 1 54 | $ java -classpath ../../apache/xmlrpc/bin/xmlrpc-1.1.jar:. FibClient 4 55 | 5 56 | 57 | Stop the Fibonacci server: 58 | 59 | 5> xmlrpc:stop(Pid). 60 | stop 61 | 62 | Example 2: echo_server.erl 63 | -------------------------- 64 | $ erl -pa ../ebin -pa ../../xmerl/ebin 65 | Erlang (BEAM) emulator version 5.1.2.b2 [source] 66 | 67 | Eshell V5.1.2.b2 (abort with ^G) 68 | 1> {ok, Pid} = xmlrpc:start_link({echo_server, handler}). 69 | {ok,<0.30.0>} 70 | 2> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, foo, []}). 71 | {ok,{response,{fault,-1,"Unknown call: {call,foo,[]}"}}} 72 | 3> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, echo, [42.0]}). 73 | {ok,{response,[{array, [42.0000]}]}} 74 | 4> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, echo, [2.6, {array, [5, "foo"]}, {struct, [{baz, 1}, {bar, {base64, "SDOMCVN=-kmDC*="}}]}]}). 75 | {ok,{response,[{array,[2.60000, 76 | {array,[5,"foo"]}, 77 | {struct,[{baz,1},{bar,{base64,"SDOMCVN=-kmDC*="}}]}]}]}} 78 | 79 | The parameters are converted from Erlang to XML format before being 80 | sent to the server. When the parameters arrive at the server they are 81 | converted back to Erlang format. The server handler echoes the 82 | parameters back as a response to the client, i.e. in the process 83 | converting the parameters back to XML format. The response arrives at 84 | the client which finally convert the parameters back to Erlang format. 85 | 86 | Nothing fancy. Uses a new HTTP connection for each call and handles 87 | unknown method calls gracefully. 88 | 89 | EchoClient.java could as well has been used to call the echo server: 90 | 91 | $ java -classpath ../../apache/xmlrpc/bin/xmlrpc-1.1.jar:. EchoClient 92 | [42.5, foo, 7] 93 | 94 | Stop the echo server: 95 | 96 | 5> xmlrpc:stop(Pid). 97 | stop 98 | 99 | Example 3: date_server.erl 100 | -------------------------- 101 | $ erl -pa ../ebin -pa ../../xmerl/ebin 102 | Erlang (BEAM) emulator version 5.1.2.b2 [source] 103 | 104 | Eshell V5.1.2.b2 (abort with ^G) 105 | 1> xmlrpc:start_link(4567, 1000, 300000, {date_server, handler}, {false, 0}). 106 | {ok,<0.30.0>} 107 | 2> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, calc, [{struct, [{is_leap_year, 1492}]}]}, true, 300000). 108 | {ok,#Port<0.32>,{response,{fault,-3,"Not authenticated"}}} 109 | 3> xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, login, ["Foo", "Bar"]}, true, 300000). 110 | {ok,#Port<0.40>,{response,{fault,-1,"Invalid authentication"}}} 111 | 4> {ok, S, _} = xmlrpc:call({127, 0, 0, 1}, 4567, "/", {call, login, ["Slarti", "Bartfast"]}, true, 300000). 112 | {ok,#Port<0.42>,{response,["ok"]}} 113 | 5> xmlrpc:call(S, "/", {call, login, ["Slarti", "Bartfast"]}, true, 300000). 114 | {ok,#Port<0.42>,{response,{fault,-2,"Already authenticated"}}} 115 | 6> xmlrpc:call(S, "/", {call, foo, []}, true, 300000). 116 | {ok,#Port<0.42>,{response,{fault,-4,"Unknown call: {call,foo,[]}"}}} 117 | 7> xmlrpc:call(S, "/", {call, calc, [{struct, [{is_leap_year, 1492}]}]}, true, 300000). 118 | {ok,#Port<0.42>,{response,[{array,[3,{struct,[{is_leap_year,true}]}]}]}} 119 | 8> xmlrpc:call(S, "/", {call, calc, [{struct, [{is_leap_year, 2010}]}]}, true, 300000). 120 | {ok,#Port<0.42>,{response,[{array,[4,{struct,[{is_leap_year,false}]}]}]}} 121 | 9> xmlrpc:call(S, "/", {call, calc, [{struct, [{is_leap_year, 2010}, {days_since, {struct, [{y, 2002}, {m, 12}, {d, 10}]}}]}]}, true, 300000). 122 | {ok,#Port<0.42>, 123 | {response,[{array,[5,{struct,[{is_leap_year,false},{days_since,731559}]}]}]}} 124 | 10> xmlrpc:call(S, "/", {call, logout, []}). 125 | {ok,{response,["ok"]}} 126 | 11> xmlrpc:call(S, "/", {call, calc, [{struct, [{is_leap_year, 2010}]}]}, true, 300000). 127 | {error,#Port<0.42>,closed} 128 | 129 | DateClient.java could as well has been used to call the date server: 130 | 131 | $ java -classpath ../../apache/xmlrpc/bin/xmlrpc-1.1.jar:. DateClient 132 | ok 133 | [1, {days_since=731204}] 134 | [2, {days_since=731204, day_of_week=Thursday, is_leap_year=true}] 135 | ok 136 | 137 | Stop the date server: 138 | 139 | 12> xmlrpc:stop(Pid). 140 | stop 141 | 142 | Example 4: validator.erl 143 | -------------------------- 144 | $ erl -pa ../ebin -pa ../../xmerl/ebin 145 | Erlang (BEAM) emulator version 5.1.2.b2 [source] 146 | 147 | Eshell V5.1.2.b2 (abort with ^G) 148 | 1> {ok, Pid} = xmlrpc:start_link({validator, handler}). 149 | {ok,<0.30.0>} 150 | 151 | If your computer is connected to the Internet you can now verify that 152 | the library is compliant with the validation service at 153 | http://validator.xmlrpc.org/. 154 | 155 | Stop the validator server: 156 | 157 | 2> xmlrpc:stop(Pid). 158 | stop 159 | -------------------------------------------------------------------------------- /examples/date_server.erl: -------------------------------------------------------------------------------- 1 | -module(date_server). 2 | -export([handler/2]). 3 | 4 | %% 5 | %% A server which calculates date value(s) such as: 6 | %% 7 | %% * Calculates the number of days since year 0 for a given date. 8 | %% * Calculates if a given date is a leap year. 9 | %% * Calculates the day of the week for a given day. 10 | %% 11 | %% In order to perform the calculation a client must login with 12 | %% username "Slarti" and password "Bartfast". After login the HTTP/1.1 13 | %% keepalive timeout is set to infinity. The server returns a session 14 | %% counter for each call using the state variable. 15 | %% 16 | 17 | handler({false, N}, {call, login, ["Slarti", "Bartfast"]}) -> 18 | {true, infinity, {true, N+1}, {response, ["ok"]}}; 19 | handler({false, _N}, {call, login, [_, _]}) -> 20 | {false, {response, {fault, -1, "Invalid authentication"}}}; 21 | handler({true, N}, {call, login, [_, _]}) -> 22 | {true, infinity, {true, N+1}, 23 | {response, {fault, -2, "Already authenticated"}}}; 24 | handler({true, _N}, {call, logout, []}) -> 25 | {false, {response, ["ok"]}}; 26 | handler({true, N}, {call, calc, [{struct, Elements}]}) -> 27 | {true, infinity, {true, N+1}, 28 | {response, [{array, [N, {struct, calc(Elements)}]}]}}; 29 | handler({false, _N}, {call, calc, _Params}) -> 30 | {false, {response, {fault, -3, "Not authenticated"}}}; 31 | handler({Status, N}, Payload) -> 32 | FaultString = lists:flatten(io_lib:format("Unknown call: ~p", [Payload])), 33 | {true, 300000, {Status, N+1}, {response, {fault, -4, FaultString}}}. 34 | 35 | calc([]) -> []; 36 | calc([{days_since, {struct, Elements}}|Rest]) -> 37 | {Y, M, D} = extract_date(Elements), 38 | [{days_since, calendar:date_to_gregorian_days({Y, M, D})}| 39 | calc(Rest)]; 40 | calc([{is_leap_year, Y}|Rest]) -> 41 | [{is_leap_year, calendar:is_leap_year(Y)}|calc(Rest)]; 42 | calc([{day_of_week, {struct, Elements}}|Rest]) -> 43 | {Y, M, D} = extract_date(Elements), 44 | [{day_of_week, day(calendar:day_of_the_week(Y, M, D))}|calc(Rest)]. 45 | 46 | extract_date(Elements) -> extract_date(Elements, 0, 0, 0). 47 | 48 | extract_date([], Y, M, D) -> {Y, M, D}; 49 | extract_date([{y, Y}|Rest], _, M, D) -> extract_date(Rest, Y, M, D); 50 | extract_date([{m, M}|Rest], Y, _, D) -> extract_date(Rest, Y, M, D); 51 | extract_date([{d, D}|Rest], Y, M, _) -> extract_date(Rest, Y, M, D). 52 | 53 | day(1) -> "Monday"; 54 | day(2) -> "Tuesday"; 55 | day(3) -> "Wednesday"; 56 | day(4) -> "Thursday"; 57 | day(5) -> "Friday"; 58 | day(6) -> "Saturday"; 59 | day(7) -> "Sunday". 60 | -------------------------------------------------------------------------------- /examples/echo_server.erl: -------------------------------------------------------------------------------- 1 | -module(echo_server). 2 | -export([handler/2]). 3 | 4 | %% 5 | %% A server which echoes back any incoming parameters. 6 | %% 7 | 8 | handler(_, {call, echo, Params}) -> 9 | {false, {response, [{array, Params}]}}; 10 | handler(_, Payload) -> 11 | FaultString = lists:flatten(io_lib:format("Unknown call: ~p", [Payload])), 12 | {false, {response, {fault, -1, FaultString}}}. 13 | -------------------------------------------------------------------------------- /examples/fib_server.erl: -------------------------------------------------------------------------------- 1 | -module(fib_server). 2 | -export([handler/2]). 3 | 4 | %% 5 | %% A server which calculates Fibonacci values. 6 | %% 7 | 8 | handler(_State, {call, fib, [N]}) when integer(N) -> 9 | {false, {response, [fib(N)]}}; 10 | handler(_State, Payload) -> 11 | FaultString = lists:flatten(io_lib:format("Unknown call: ~p", [Payload])), 12 | {false, {response, {fault, -1, FaultString}}}. 13 | 14 | fib(0) -> 1; 15 | fib(1) -> 1; 16 | fib(N) -> fib(N-1)+fib(N-2). 17 | -------------------------------------------------------------------------------- /examples/reply.hrl: -------------------------------------------------------------------------------- 1 | -record(reply, {tag, val}). 2 | -------------------------------------------------------------------------------- /examples/robust_bank_client.erl: -------------------------------------------------------------------------------- 1 | -module(robust_bank_client). 2 | -export([deposit/2, withdraw/2, balance/1]). 3 | 4 | deposit(Who, X) -> robust_xmlrpc(deposit, [Who, X]). 5 | withdraw(Who, X) -> robust_xmlrpc(withdraw, [Who, X]). 6 | balance(Who) -> robust_xmlrpc(balance, [Who]). 7 | 8 | robust_xmlrpc(F, A) -> 9 | Tag = mk_tag(), 10 | case call(3020, Tag, F, A) of 11 | {error, Reason} -> call(3030, Tag, F, A); 12 | Result -> Result 13 | end. 14 | 15 | mk_tag() -> 16 | {MegaSecs, Secs, MicroSecs} = erlang:now(), 17 | lists:concat([atom_to_list(node()), 18 | ".", integer_to_list(MegaSecs), 19 | ".", integer_to_list(Secs), 20 | ".", integer_to_list(MicroSecs)]). 21 | 22 | call(Port, Tag, F, A) -> 23 | case xmlrpc:call("localhost", Port, "/", {call, F, [Tag|A]}, false, 24 | 10000) of 25 | {error, Reason} -> {error, Reason}; 26 | {ok, Result} -> 27 | xmlrpc:call("localhost", Port, "/", {call, delete_tag, [Tag]}), 28 | Result 29 | end. 30 | -------------------------------------------------------------------------------- /examples/robust_bank_server.erl: -------------------------------------------------------------------------------- 1 | -module(robust_bank_server). 2 | -export([start/1, stop/1]). 3 | -export([handler/2]). % internal 4 | 5 | -include("reply.hrl"). 6 | 7 | start(Port) -> 8 | mnesia:start(), 9 | xmlrpc:start_link(Port, 100, 60*1000, {?MODULE, handler}, undefined). 10 | 11 | stop(Pid) -> 12 | mnesia:stop(), 13 | xmlrpc:stop(Pid). 14 | 15 | handler(_, {call, deposit, [Tag, Who, X]}) -> 16 | {false, {response, [lookup(Tag, deposit, [Who, X])]}}; 17 | handler(_, {call, withdraw, [Tag, Who, X]}) -> 18 | case lookup(Tag, withdraw, [Who, X]) of 19 | ok -> {false, {response, ["ok"]}}; 20 | {error, Reason} -> 21 | FaultString = lists:flatten(io_lib:format("~p", [Reason])), 22 | {false, {response, {fault, -1, FaultString}}} 23 | end; 24 | handler(_, {call, balance, [Tag, Who]}) -> 25 | case lookup(Tag, balance, [Who]) of 26 | {ok, Result} -> {false, {response, [Result]}}; 27 | {error, Reason} -> 28 | FaultString = lists:flatten(io_lib:format("~p", [Reason])), 29 | {false, {response, {fault, -2, FaultString}}} 30 | end; 31 | handler(_, {call, delete_tag, [Tag]}) -> 32 | mnesia:transaction(fun() -> mnesia:delete({reply, Tag}) end), 33 | {false, {response, ["ok"]}}. 34 | 35 | lookup(Tag, F, A) -> 36 | Fun = fun() -> 37 | case mnesia:read({reply, Tag}) of 38 | [] -> 39 | Val = get_val(F, A), 40 | mnesia:write(#reply{tag = Tag, val = Val}), 41 | Val; 42 | [Reply] -> Reply#reply.val 43 | end 44 | end, 45 | {atomic, Result} = mnesia:transaction(Fun), 46 | Result. 47 | 48 | get_val(deposit, [Who, X]) -> (bank:deposit(Who, X))(); 49 | get_val(withdraw, [Who, X]) -> (bank:withdraw(Who, X))(); 50 | get_val(balance, [Who]) -> (bank:balance(Who))(). 51 | -------------------------------------------------------------------------------- /examples/robust_banking.txt: -------------------------------------------------------------------------------- 1 | This is a companion to Joe Armstrong's tutorial on how to build a 2 | fault-tolerant banking server in Erlang: 3 | 4 | http://www.sics.se/~joe/tutorials/robust_server/robust_server.html 5 | 6 | You *should* read Joe's tutorial before you go any further. You have 7 | been warned! 8 | 9 | 1 A fault-tolerant banking server using XML-RPC 10 | ----------------------------------------------- 11 | It is easy to update Joe's banking server with XML-RPC support. Just 12 | replace the robust_bank_server.erl and robust_bank_client.erl modules 13 | with the drop-in replacements found in this directory. 14 | 15 | Do that. 16 | 17 | The original modules look like this: 18 | http://www.sics.se/~joe/tutorials/robust_server/robust_bank_server.erl 19 | http://www.sics.se/~joe/tutorials/robust_server/robust_bank_client.erl 20 | 21 | They are actually larger than the XML-RPC ditto. 22 | 23 | There is also a Java application (BankClient.java) which can be used 24 | to access the banking server. More on that below. 25 | 26 | 1.1 Initializing the data-base 27 | ------------------------------ 28 | This assumes that there are two nodes called one@enfield and 29 | two@enfield. 30 | 31 | To initialize the system open two terminal windows (on enfield) and 32 | proceed as follows: 33 | 34 | Note: You *must* update the paths to the xmerl and xmlrpc packages. 35 | 36 | ** In terminal 1: 37 | 38 | $ erl -pa xmlrpc-1.11/ebin -pa xmerl-0.18/ebin -sname one -mnesia dir '"one"' 39 | Erlang (BEAM) emulator version 5.2 [source] [hipe] 40 | 41 | Eshell V5.2 (abort with ^G) 42 | 43 | ** In terminal 2: 44 | 45 | $ erl -pa xmlrpc-1.11/ebin -pa xmerl-0.18/ebin -sname two -mnesia dir '"two"' 46 | Erlang (BEAM) emulator version 5.2 [source] [hipe] 47 | 48 | Eshell V5.2 (abort with ^G) 49 | (two@enfield)1> robust_bank_manager:create_schema(). 50 | ok 51 | (two@enfield)2> mnesia:start(). 52 | ok 53 | 54 | ** In terminal 1: 55 | 56 | (one@enfield)1> mnesia:start(). 57 | ok 58 | (one@enfield)2> robust_bank_manager:create_table(). 59 | 60 | 1.2. Now we are ready to run everything 61 | --------------------------------------- 62 | We are now ready to start the banking server on the two nodes: 63 | 64 | ** In terminal 1: 65 | 66 | (one@enfield)3> robust_bank_server:start(3020). 67 | {ok,<0.102.0>} 68 | 69 | ** In terminal 2: 70 | 71 | (two@enfield)3> robust_bank_server:start(3030). 72 | {ok,<0.102.0>} 73 | 74 | The XML-RPC based banking server has now been started. We open a third 75 | terminal window and start an Erlang node to be used as a client: 76 | 77 | ** In terminal 3: 78 | 79 | $ erl -pa xmlrpc-1.11/ebin -pa xmerl-0.18/ebin 80 | Erlang (BEAM) emulator version 5.2 [source] [hipe] 81 | 82 | Eshell V5.2 (abort with ^G) 83 | 1> robust_bank_client:deposit("joe", 7). 84 | {response,[7]} 85 | 2> robust_bank_client:balance("joe"). 86 | {response,[7]} 87 | 88 | Both servers are running - server one replies. Kill server one redo 89 | the query: 90 | 91 | 3> robust_bank_client:balance("joe"). 92 | {response,[7]} 93 | 94 | This time server 2 replies (we killed server one, remember). 95 | 96 | Make a deposit. We make a deposit of 10 units: 97 | 98 | 4> robust_bank_client:deposit("joe", 10). 99 | {response,[17]} 100 | 101 | Only server two is running - so the transaction takes place on server two. 102 | 103 | Restart server one and query the balance: 104 | 105 | 5> robust_bank_client:balance("joe"). 106 | {response,[17]} 107 | 108 | Server one replies with 17 units. Well done server one. 109 | 110 | When server one was restarted - the two servers synced their data and 111 | the changes made to server two were propagated to server one. 112 | 113 | Kill server two and query the balance: 114 | 115 | 6> robust_bank_client:balance("joe"). 116 | {response,[17]} 117 | 118 | Server one replied. 119 | 120 | 1.3 Lets try the Java client 121 | ----------------------------- 122 | $ java -classpath apache/xmlrpc/bin/xmlrpc-1.1.jar:. BankClient deposit joe 22 123 | 39 124 | $ java -classpath apache/xmlrpc/bin/xmlrpc-1.1.jar:. BankClient withdraw joe 2 125 | ok 126 | $ java -classpath apache/xmlrpc/bin/xmlrpc-1.1.jar:. BankClient balance joe 127 | 37 128 | $ java -classpath apache/xmlrpc/bin/xmlrpc-1.1.jar:. BankClient withdraw joe 2000 129 | org.apache.xmlrpc.XmlRpcException: not_enough_money 130 | java.io.IOException: Connection refused 131 | No bank server available 132 | 133 | etc. 134 | 135 | 2 Summing up 136 | ------------ 137 | The intention here was not to make the ultimate fault-tolerant server, 138 | but rather to illustrate how to make a simple functioning server, with 139 | no detail omitted. A production server could be based on this simple 140 | design, but would involve a slightly less simplistic approach. 141 | -------------------------------------------------------------------------------- /examples/validator.erl: -------------------------------------------------------------------------------- 1 | -module(validator). 2 | -export([handler/2]). 3 | 4 | -record(e, {lt = 0, gt = 0, amp = 0, apos = 0, quot = 0}). 5 | 6 | handler(_, {call, 'validator1.arrayOfStructsTest', [{array, Array}]}) -> 7 | {false, {response, [sum_array(Array)]}}; 8 | handler(_, {call, 'validator1.countTheEntities', [String]}) -> 9 | {false, {response, [count(String, #e{})]}}; 10 | handler(_, {call, 'validator1.easyStructTest', [{struct, Elements}]})-> 11 | {false, {response, [sum_struct(Elements)]}}; 12 | handler(_, {call, 'validator1.echoStructTest', [Struct]}) -> 13 | {false, {response, [Struct]}}; 14 | handler(_, {call, 'validator1.manyTypesTest', Params}) -> 15 | {false, {response, [{array, Params}]}}; 16 | handler(_, {call, 'validator1.moderateSizeArrayCheck', [{array, Values}]}) -> 17 | {false, {response, [hd(Values)++hd(lists:reverse(Values))]}}; 18 | handler(_, {call, 'validator1.nestedStructTest', [{struct, Years}]}) -> 19 | {value, {_, {struct, Months}}} = lists:keysearch('2000', 1, Years), 20 | {value, {_, {struct, Days}}} = lists:keysearch('04', 1, Months), 21 | {value, {_, {struct, Elements}}} = lists:keysearch('01', 1, Days), 22 | {false, {response, [sum_struct(Elements)]}}; 23 | handler(_, {call, 'validator1.simpleStructReturnTest', [N]}) -> 24 | Elements = [{times10, N*10}, {times100, N*100}, {times1000, N*1000}], 25 | {false, {response, [{struct, Elements}]}}. 26 | 27 | sum_array([]) -> 0; 28 | sum_array([{struct, Elements}|Rest]) -> 29 | {value, {_, N}} = lists:keysearch('curly', 1, Elements), 30 | N+sum_array(Rest). 31 | 32 | count([], E) -> 33 | {struct, [{ctLeftAngleBrackets, E#e.lt}, {ctRightAngleBrackets, E#e.gt}, 34 | {ctAmpersands, E#e.amp}, {ctApostrophes, E#e.apos}, 35 | {ctQuotes, E#e.quot}]}; 36 | count([$<|Rest], #e{lt = LT} = E) -> count(Rest, E#e{lt = LT+1}); 37 | count([$>|Rest], #e{gt = GT} = E) -> count(Rest, E#e{gt = GT+1}); 38 | count([$&|Rest], #e{amp = AMP} = E) -> count(Rest, E#e{amp = AMP+1}); 39 | count([$'|Rest], #e{apos = APOS} = E) -> count(Rest, E#e{apos = APOS+1}); 40 | count([$"|Rest], #e{quot = QUOT} = E) -> count(Rest, E#e{quot = QUOT+1}); 41 | count([_|Rest], E) -> count(Rest, E). 42 | 43 | sum_struct([]) -> 0; 44 | sum_struct([{moe, N}|Rest]) -> N+sum_struct(Rest); 45 | sum_struct([{larry, N}|Rest]) -> N+sum_struct(Rest); 46 | sum_struct([{curly, N}|Rest]) -> N+sum_struct(Rest); 47 | sum_struct([_|Rest]) -> sum_struct(Rest). 48 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | {clean_files, [ "doc/edoc-info" 3 | , "doc/erlang.png" 4 | , "doc/stylsheet.css" 5 | , "doc/*.html" 6 | ]}. 7 | {src_dirs, ["examples" 8 | , "test" 9 | ]}. 10 | {deps_dir, [".."]}. 11 | -------------------------------------------------------------------------------- /src/log.hrl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/src/log.hrl -------------------------------------------------------------------------------- /src/xmlrpc.app.src: -------------------------------------------------------------------------------- 1 | {application, xmlrpc, [ {description, "XML-RPC server"} 2 | , {vsn, "1.14"} 3 | , {modules, []} 4 | , {registered, []} 5 | , {applications, [kernel,stdlib]} 6 | , {env, []} 7 | ]}. 8 | -------------------------------------------------------------------------------- /src/xmlrpc.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/src/xmlrpc.erl -------------------------------------------------------------------------------- /src/xmlrpc_decode.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/src/xmlrpc_decode.erl -------------------------------------------------------------------------------- /src/xmlrpc_encode.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/src/xmlrpc_encode.erl -------------------------------------------------------------------------------- /src/xmlrpc_http.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/src/xmlrpc_http.erl -------------------------------------------------------------------------------- /src/xmlrpc_tcp_serv.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/src/xmlrpc_tcp_serv.erl -------------------------------------------------------------------------------- /src/xmlrpc_util.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/src/xmlrpc_util.erl -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Update the APACHE_XMLRPC_PATH variable to point at your Apache 2 | # XML-RPC installation. 3 | APACHE_XMLRPC_PATH=xmlrpc-1.1.jar 4 | 5 | # Do not change anything below this line. 6 | ERL=erl 7 | ERLC=erlc 8 | ERLC_FLAGS=-W 9 | MODULES=test 10 | EBIN_FILES=$(MODULES:%=%.beam) 11 | JAVAC=javac 12 | JAVAC_FLAGS=-classpath $(APACHE_XMLRPC_PATH):. 13 | JAVA_FILES=T50 T60 14 | CLASS_FILES=$(JAVA_FILES:%=%.class) 15 | 16 | test: build 17 | $(ERL) -pa ../ebin -pa ../../xmerl/ebin -pa . -s test all -s erlang halt 18 | build: $(EBIN_FILES) $(CLASS_FILES) 19 | 20 | %.beam: %.erl 21 | $(ERLC) $(ERLC_FLAGS) $< 22 | 23 | %.class: %.java 24 | $(JAVAC) $(JAVAC_FLAGS) $< 25 | 26 | clean: 27 | rm -f $(EBIN_FILES) $(CLASS_FILES) 28 | -------------------------------------------------------------------------------- /test/T50.java: -------------------------------------------------------------------------------- 1 | import org.apache.xmlrpc.*; 2 | import java.util.*; 3 | 4 | public class T50 { 5 | public static void main(String[] args) { 6 | try { 7 | XmlRpcClient xmlrpc = new XmlRpcClient("http://localhost:4567/"); 8 | Vector params = new Vector(); 9 | Vector v = new Vector(); 10 | v.addElement(new Integer(42)); 11 | v.addElement(new String("foo")); 12 | params.add(v); 13 | Hashtable h = new Hashtable(); 14 | h.put("bar", new Double(45.5)); 15 | h.put("baz", new Integer(4711)); 16 | params.add(h); 17 | //Object response = xmlrpc.execute("echo", params); 18 | //System.out.println(response.toString()); 19 | Object response = xmlrpc.execute("foo", new Vector()); 20 | System.out.println(response.toString()); 21 | } catch (Exception e) { 22 | System.err.println(e); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/T60.java: -------------------------------------------------------------------------------- 1 | import org.apache.xmlrpc.*; 2 | import java.util.*; 3 | 4 | public class T60 { 5 | public static void main(String[] args) { 6 | try { 7 | XmlRpcClient xmlrpc = new XmlRpcClient("http://localhost:4567/"); 8 | XmlRpc.setKeepAlive(true); 9 | Vector params = new Vector(); 10 | Vector v = new Vector(); 11 | v.addElement(new Integer(42)); 12 | v.addElement(new String("foo")); 13 | params.add(v); 14 | Hashtable h = new Hashtable(); 15 | h.put("bar", new Double(45.5)); 16 | h.put("baz", new Integer(4711)); 17 | params.add(h); 18 | Object response = xmlrpc.execute("echo", params); 19 | System.out.println(response.toString()); 20 | response = xmlrpc.execute("echo", params); 21 | System.out.println(response.toString()); 22 | response = xmlrpc.execute("foo", new Vector()); 23 | System.out.println(response.toString()); 24 | } catch (Exception e) { 25 | System.err.println(e); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/test.erl: -------------------------------------------------------------------------------- 1 | -module(test). 2 | -compile(export_all). 3 | 4 | -include_lib("xmerl/include/xmerl.hrl"). 5 | 6 | all() -> 7 | t10(), 8 | t20(), 9 | t30(), 10 | t40(), 11 | %%t50(), %% xmerl_scan:string/1 returns bad_character_code. broken. 12 | %%t60(), %% xmerl_scan:string/1 returns bad_character_code. broken. 13 | t100(). 14 | 15 | %% Test: Call external XML-RPC servers 16 | 17 | %% Test: Non keep-alive handler <-> Non keep-alive client 18 | 19 | t10() -> 20 | {ok, Pid} = xmlrpc:start_link(4567, 1000, 60000, {test, h10}, undefined), 21 | Fun = fun({call, echo, Params} = Payload) -> 22 | io:format("t10: ~p~n", [Payload]), 23 | {ok, {response, [{array, Params}]}} = 24 | xmlrpc:call("localhost", 4567, "/", Payload) 25 | end, 26 | lists:foreach(Fun, payload_calls()), 27 | {ok, {response, {fault, _, _}}} = 28 | xmlrpc:call("localhost", 4567, "/", {call, baz, []}), 29 | xmlrpc:stop(Pid). 30 | 31 | h10(_State, {call, echo, Params}) -> {false, {response, [{array, Params}]}}; 32 | h10(_State, Payload) -> 33 | FaultString = lists:flatten(io_lib:format("~p", [Payload])), 34 | {false, {response, {fault, -1, FaultString}}}. 35 | 36 | %% Test: Non keep-alive handler <-> Keep-Alive client 37 | 38 | t20() -> 39 | {ok, Pid} = xmlrpc:start_link(4567, 1000, 60000, {test, h20}, undefined), 40 | Params = ["foo"], 41 | Payload = {call, echo, Params}, 42 | io:format("t20: ~p~n", [Payload]), 43 | {ok, Socket, {response, [{array, Params}]}} = 44 | xmlrpc:call("localhost", 4567, "/", Payload, true, 60000), 45 | {error, Socket, closed} = xmlrpc:call(Socket, "/", Payload, true, 60000), 46 | xmlrpc:stop(Pid). 47 | 48 | h20(_State, {call, echo, Params}) -> {false, {response, [{array, Params}]}}. 49 | 50 | %% Test: Keep-Alive handler <-> Non keep-alive client 51 | 52 | t30() -> 53 | {ok, Pid} = xmlrpc:start_link(4567, 1000, 60000, {test, h30}, 0), 54 | Params = ["foo"], 55 | Payload = {call, echo, Params}, 56 | io:format("t30: ~p~n", [Payload]), 57 | {ok, {response, [{array, [0|Params]}]}} = 58 | xmlrpc:call("localhost", 4567, "/", Payload, false, 60000), 59 | {ok, {response, [{array, [0|Params]}]}} = 60 | xmlrpc:call("localhost", 4567, "/", Payload, false, 60000), 61 | {ok, {response, {fault, _, _}}} = 62 | xmlrpc:call("localhost", 4567, "/", {call, baz, []}, false, 60000), 63 | xmlrpc:stop(Pid). 64 | 65 | h30(N, {call, echo, Params}) -> 66 | {true, 60000, N+1, {response, [{array, [N|Params]}]}}; 67 | h30(N, Payload) -> 68 | FaultString = lists:flatten(io_lib:format("~p", [Payload])), 69 | {true, 60000, N, {response, {fault, -1, FaultString}}}. 70 | 71 | %% Test: Keep-Alive handler <-> Keep-Alive client 72 | 73 | t40() -> 74 | {ok, Pid} = xmlrpc:start_link(4567, 1000, 60000, {test, h40}, 0), 75 | Params = ["foo"], 76 | Payload = {call, echo, Params}, 77 | io:format("t40: ~p~n", [Payload]), 78 | {ok, Socket, {response, [{array, [0|Params]}]}} = 79 | xmlrpc:call("localhost", 4567, "/", Payload, true, 60000), 80 | {ok, Socket, {response, [{array, [1|Params]}]}} = 81 | xmlrpc:call(Socket, "/", Payload, true, 60000), 82 | {ok, {response, [{array, [2|Params]}]}} = 83 | xmlrpc:call(Socket, "/", Payload, false, 60000), 84 | {error, closed} = xmlrpc:call(Socket, "/", Payload, false, 60000), 85 | {ok, Socket2, {response, [{array, [0|Params]}]}} = 86 | xmlrpc:call("localhost", 4567, "/", Payload, true, 60000), 87 | {ok, Socket2, {response, [{array, [1|Params]}]}} = 88 | xmlrpc:call(Socket2, "/", Payload, true, 60000), 89 | {ok, Socket2, {response, [{array, [2|Params]}]}} = 90 | xmlrpc:call(Socket2, "/", Payload, true, 60000), 91 | {ok, Socket2, {response, ["ok"]}} = 92 | xmlrpc:call(Socket2, "/", {call, close, []}, true, 60000), 93 | {error, Socket2, closed} = 94 | xmlrpc:call(Socket2, "/", Payload, true, 60000), 95 | xmlrpc:stop(Pid). 96 | 97 | h40(_N, {call, close, []}) -> {false, {response, ["ok"]}}; 98 | h40(N, {call, echo, Params}) -> 99 | {true, 60000, N+1, {response, [{array, [N|Params]}]}}; 100 | h40(N, Payload) -> 101 | FaultString = lists:flatten(io_lib:format("~p", [Payload])), 102 | {true, 60000, N, {response, {fault, -1, FaultString}}}. 103 | 104 | %% Test: Apache XML-RPC client 105 | 106 | t50() -> 107 | {ok, Pid} = xmlrpc:start_link(4567, 1000, 60000, {test, h50}, undefined), 108 | Cmd = "java -classpath xmlrpc-1.1.jar:. T50", 109 | io:format("t50: ~p~n", [Cmd]), 110 | "[[42, foo], {bar=45.5, baz=4711}]\norg.apache.xmlrpc.XmlRpcException: Unknown call: {call,foo,[]}\n" = os:cmd(Cmd), 111 | xmlrpc:stop(Pid). 112 | 113 | h50(_State, {call, echo, Params}) -> {false, {response, [{array, Params}]}}; 114 | h50(_State, Payload) -> 115 | FaultString = lists:flatten(io_lib:format("Unknown call: ~p", [Payload])), 116 | {false, {response, {fault, -1, FaultString}}}. 117 | 118 | %% Test: Keep-Alive Apache XML-RPC client 119 | 120 | t60() -> 121 | {ok, Pid} = xmlrpc:start_link(4567, 1000, 60000, {test, h60}, 0), 122 | Cmd = "java -classpath xmlrpc-1.1.jar:. T60", 123 | io:format("t60: ~p~n", [Cmd]), 124 | "[0, [42, foo], {bar=45.5, baz=4711}]\n[1, [42, foo], {bar=45.5, baz=4711}]\norg.apache.xmlrpc.XmlRpcException: Unknown call: {call,foo,[]}\n" = 125 | os:cmd(Cmd), 126 | xmlrpc:stop(Pid). 127 | 128 | h60(N, {call, echo, Params}) -> 129 | {true, 60000, N+1, {response, [{array, [N|Params]}]}}; 130 | h60(_N, Payload) -> 131 | FaultString = lists:flatten(io_lib:format("Unknown call: ~p", [Payload])), 132 | {false, {response, {fault, -1, FaultString}}}. 133 | 134 | %% Test: Timeouts 135 | 136 | %% Test: Negative testing 137 | 138 | %% Test: Encoding and decoding 139 | 140 | t100() -> 141 | io:format("t100~n"), 142 | encode_decode(payload_calls()++payload_responses()). 143 | 144 | encode_decode([]) -> ok; 145 | encode_decode([Payload|Rest]) -> 146 | case xmlrpc_encode:payload(Payload) of 147 | {ok, EncodedPayload} -> 148 | case xmlrpc_decode:payload(lists:flatten(EncodedPayload)) of 149 | {ok, Payload} -> encode_decode(Rest); 150 | {ok, MismatchedPayload} -> 151 | {error, {mismatch, Payload, EncodedPayload, 152 | MismatchedPayload}}; 153 | {error, Reason} -> 154 | {error, {decode, Payload, EncodedPayload, Reason}} 155 | end; 156 | {error, Reason} -> {error, {encode, Payload, Reason}} 157 | end. 158 | 159 | payload_calls() -> 160 | [{call, echo, []}, 161 | {call, echo, [true]}, 162 | {call, echo, [false]}, 163 | {call, echo, [12]}, 164 | {call, echo, [12.1]}, 165 | {call, echo, ["bar"]}, 166 | {call, echo, [{date, "19980717T14:08:55"}]}, 167 | {call, echo, [{base64, "eW91IGNhbid0IHJlYWQgdGhpcyE="}]}, 168 | {call, echo, [{array, [1, 5.8, "baz"]}]}, 169 | {call, echo, [{struct, [{yes, true}, {no,false}]}]}, 170 | {call, echo, 171 | [{array, [{array, [1, 5.8, "baz", 172 | {array, [{date, "19980717T14:08:55"}, 173 | {base64, "eW91IGNhbid0IHJlYWQgdGhpcyE="}]}, 174 | {struct, [{yes, true}, {no, false}]}]}, 175 | "slartibartfast"]}]}]. 176 | 177 | payload_responses() -> 178 | [{response, [true]}, 179 | {response, [false]}, 180 | {response, [12]}, 181 | {response, [12.1]}, 182 | {response, ["bar"]}, 183 | {response, [{date, "19980717T14:08:55"}]}, 184 | {response, [{base64, "eW91IGNhbid0IHJlYWQgdGhpcyE="}]}, 185 | {response, [{array, [1, 5.8, "baz"]}]}, 186 | {response, [{struct, [{yes, true}, {no,false}]}]}, 187 | {response, 188 | [{array, [{array, [1, 5.8, "baz", 189 | {array, [{date, "19980717T14:08:55"}, 190 | {base64, "eW91IGNhbid0IHJlYWQgdGhpcyE="}]}, 191 | {struct, [{yes, true}, {no, false}]}]}, 192 | "slartibartfast"]}]}, 193 | {response, {fault, 42, "foo"}}]. 194 | -------------------------------------------------------------------------------- /test/xmlrpc-1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etnt/xmlrpc/fb46463b2acadf164ec534d9e2033e194341c507/test/xmlrpc-1.1.jar --------------------------------------------------------------------------------