Skip to content

Commit c3f00ce

Browse files
committed
Add XMLRPC::RackServer
This is a simple Rack middleware that acts as an XMLRPC server. This can be used in any Rack compatible webserver.
1 parent e18b4cb commit c3f00ce

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ group :development do
66
gem "bundler"
77
gem "libxml-ruby", platforms: [:ruby, :jruby]
88
gem "nokogiri", platforms: [:ruby, :jruby]
9+
gem "rack"
10+
gem "rack-test"
911
gem "rake"
1012
gem "test-unit"
1113
end

lib/xmlrpc/server.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,49 @@ def http_write(body, status, header)
532532
end
533533

534534

535+
# Implements a XML-RPC server, which works with Rack
536+
class RackServer < BasicServer
537+
538+
# This method processes a XML-RPC method call and sends the answer
539+
# back to the client.
540+
def call(env)
541+
length = env['CONTENT_LENGTH'].to_i
542+
543+
return http_error(405, "Method Not Allowed") unless env['REQUEST_METHOD'] == "POST"
544+
return http_error(400, "Bad Request") unless parse_content_type(env['CONTENT_TYPE']).first == "text/xml"
545+
return http_error(411, "Length Required") unless length > 0
546+
547+
req = Rack::Request.new(env)
548+
data = req.body.read(length)
549+
550+
return http_error(400, "Bad Request") if data.nil? or data.bytesize != length
551+
552+
[200, { "Content-type" => "text/xml; charset=utf-8" }, [process(data)]]
553+
end
554+
555+
556+
private
557+
558+
def http_error(status, message)
559+
err = "#{status} #{message}"
560+
msg = <<-"MSGEND"
561+
<html>
562+
<head>
563+
<title>#{err}</title>
564+
</head>
565+
<body>
566+
<h1>#{err}</h1>
567+
<p>Unexpected error occurred while processing XML-RPC request!</p>
568+
</body>
569+
</html>
570+
MSGEND
571+
572+
[status, { "Content-Type" => "text/html" }, [msg]]
573+
end
574+
575+
end
576+
577+
535578
class WEBrickServlet < BasicServer; end # forward declaration
536579

537580
# Implements a standalone XML-RPC server. The method XMLRPC::Server#serve is

test/test_rack_server.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# frozen_string_literal: true
2+
3+
require "test/unit"
4+
require "rack/test"
5+
require "xmlrpc/server"
6+
require 'xmlrpc/create'
7+
require 'xmlrpc/parser'
8+
9+
class TestRack < Test::Unit::TestCase
10+
include Rack::Test::Methods
11+
12+
def app
13+
s = XMLRPC::RackServer.new
14+
15+
s.add_handler("test.add") do |a,b|
16+
a + b
17+
end
18+
19+
s.add_handler("test.div") do |a,b|
20+
if b == 0
21+
raise XMLRPC::FaultException.new(1, "division by zero")
22+
else
23+
a / b
24+
end
25+
end
26+
27+
s.set_default_handler do |name, *args|
28+
raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
29+
" or wrong number of parameters!")
30+
end
31+
32+
s.add_introspection
33+
34+
s
35+
end
36+
37+
def test_successful_call
38+
assert_equal([true, 9], call("test.add", 4, 5))
39+
end
40+
41+
def test_fault_exception
42+
assert_equal([false, XMLRPC::FaultException.new(1, "division by zero")], call("test.div", 1, 0))
43+
end
44+
45+
def test_introspection
46+
methods = ["test.add", "test.div", "system.listMethods", "system.methodSignature", "system.methodHelp"]
47+
assert_equal([true, methods], call("system.listMethods"))
48+
end
49+
50+
def test_missing_handler
51+
assert_equal([false, XMLRPC::FaultException.new(-99, "Method test.nonexisting missing or wrong number of parameters!")], call("test.nonexisting"))
52+
end
53+
54+
def test_wrong_number_of_arguments
55+
assert_equal([false, XMLRPC::FaultException.new(-99, "Method test.add missing or wrong number of parameters!")], call("test.add", 1, 2, 3))
56+
end
57+
58+
def test_multibyte_characters
59+
assert_equal([true, "あいうえおかきくけこ"], call('test.add', "あいうえお", "かきくけこ"))
60+
end
61+
62+
def call(methodname, *args)
63+
create = XMLRPC::Create.new(XMLRPC::Config.default_writer.new)
64+
parser = XMLRPC::Config.default_parser.new
65+
66+
request = create.methodCall(methodname, *args)
67+
post("/", request, 'CONTENT_TYPE' => 'text/xml')
68+
assert(last_response.ok?)
69+
parser.parseMethodResponse(last_response.body)
70+
end
71+
end

0 commit comments

Comments
 (0)