Skip to content

Commit a7463e8

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 a7463e8

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-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: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# coding: utf-8
2+
# frozen_string_literal: true
3+
4+
require "test/unit"
5+
require "rack/test"
6+
require "xmlrpc/server"
7+
require 'xmlrpc/create'
8+
require 'xmlrpc/parser'
9+
require 'xmlrpc/utils'
10+
11+
module TestXMLRPC
12+
class Test_Rack < Test::Unit::TestCase
13+
include Rack::Test::Methods
14+
include XMLRPC::ParserWriterChooseMixin
15+
16+
def app
17+
s = XMLRPC::RackServer.new
18+
19+
s.add_handler("test.add") do |a,b|
20+
a + b
21+
end
22+
23+
s.add_handler("test.div") do |a,b|
24+
if b == 0
25+
raise XMLRPC::FaultException.new(1, "division by zero")
26+
else
27+
a / b
28+
end
29+
end
30+
31+
s.set_default_handler do |name, *args|
32+
raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
33+
" or wrong number of parameters!")
34+
end
35+
36+
s.add_introspection
37+
38+
return s
39+
end
40+
41+
def test_rack
42+
# simple call
43+
assert_call_success(9, "test.add", 4, 5)
44+
45+
# fault exception
46+
assert_call_failure(1, "division by zero", "test.div", 1, 0)
47+
48+
# introspection
49+
assert_call_success(["test.add", "test.div", "system.listMethods", "system.methodSignature", "system.methodHelp"], "system.listMethods")
50+
51+
# default handler (missing handler)
52+
assert_call_failure(-99, "Method test.nonexisting missing or wrong number of parameters!", "test.nonexisting")
53+
54+
# default handler (wrong number of arguments)
55+
assert_call_failure(-99, "Method test.add missing or wrong number of parameters!", "test.add", 1, 2, 3)
56+
57+
# multibyte characters
58+
assert_call_success("あいうえおかきくけこ", 'test.add', "あいうえお", "かきくけこ")
59+
end
60+
61+
def assert_call_success(expect, methodname, *args)
62+
request = create().methodCall(methodname, *args)
63+
post("/", request, 'CONTENT_TYPE' => 'text/xml')
64+
ok, param = parser().parseMethodResponse(last_response.body)
65+
assert(ok)
66+
assert_equal(expect, param)
67+
end
68+
69+
def assert_call_failure(code, string, methodname, *args)
70+
request = create().methodCall(methodname, *args)
71+
post("/", request, 'CONTENT_TYPE' => 'text/xml')
72+
ok, param = parser().parseMethodResponse(last_response.body)
73+
refute(ok)
74+
assert_kind_of(XMLRPC::FaultException, param)
75+
assert_equal(code, param.faultCode)
76+
assert_equal(string, param.faultString)
77+
end
78+
end
79+
end

0 commit comments

Comments
 (0)