rubysl-xmlrpc 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +14 -6
- data/.travis.yml +5 -6
- data/lib/rubysl/xmlrpc.rb +1 -0
- data/lib/rubysl/xmlrpc/version.rb +1 -1
- data/lib/rubysl/xmlrpc/xmlrpc.rb +301 -0
- data/lib/xmlrpc.rb +1 -0
- data/lib/xmlrpc/.document +1 -0
- data/lib/xmlrpc/README.rdoc +300 -0
- data/lib/xmlrpc/base64.rb +27 -46
- data/lib/xmlrpc/client.rb +320 -343
- data/lib/xmlrpc/config.rb +18 -16
- data/lib/xmlrpc/create.rb +108 -112
- data/lib/xmlrpc/datetime.rb +59 -72
- data/lib/xmlrpc/httpserver.rb +38 -43
- data/lib/xmlrpc/marshal.rb +6 -16
- data/lib/xmlrpc/parser.rb +267 -242
- data/lib/xmlrpc/server.rb +297 -372
- data/lib/xmlrpc/utils.rb +40 -34
- data/rubysl-xmlrpc.gemspec +0 -1
- metadata +16 -24
data/lib/xmlrpc/httpserver.rb
CHANGED
@@ -1,21 +1,19 @@
|
|
1
|
-
#
|
2
|
-
# Implements a simple HTTP-server by using John W. Small's (jsmall@laser.net)
|
3
|
-
# ruby-generic-server.
|
4
|
-
#
|
5
1
|
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
6
2
|
#
|
7
|
-
# $Id
|
3
|
+
# $Id$
|
8
4
|
#
|
9
5
|
|
10
6
|
|
11
7
|
require "gserver"
|
12
8
|
|
9
|
+
# Implements a simple HTTP-server by using John W. Small's (jsmall@laser.net)
|
10
|
+
# ruby-generic-server: GServer.
|
13
11
|
class HttpServer < GServer
|
14
12
|
|
15
13
|
##
|
16
|
-
# handle_obj specifies the object, that receives calls
|
17
|
-
# and ip_auth_handler
|
18
|
-
def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4,
|
14
|
+
# +handle_obj+ specifies the object, that receives calls from +request_handler+
|
15
|
+
# and +ip_auth_handler+
|
16
|
+
def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4,
|
19
17
|
stdlog = $stdout, audit = true, debug = true)
|
20
18
|
@handler = handle_obj
|
21
19
|
super(port, host, maxConnections, stdlog, audit, debug)
|
@@ -23,19 +21,16 @@ class HttpServer < GServer
|
|
23
21
|
|
24
22
|
private
|
25
23
|
|
26
|
-
# Constants -----------------------------------------------
|
27
|
-
|
28
24
|
CRLF = "\r\n"
|
29
25
|
HTTP_PROTO = "HTTP/1.0"
|
30
26
|
SERVER_NAME = "HttpServer (Ruby #{RUBY_VERSION})"
|
31
27
|
|
28
|
+
# Default header for the server name
|
32
29
|
DEFAULT_HEADER = {
|
33
30
|
"Server" => SERVER_NAME
|
34
31
|
}
|
35
32
|
|
36
|
-
|
37
|
-
# Mapping of status code and error message
|
38
|
-
#
|
33
|
+
# Mapping of status codes and error messages
|
39
34
|
StatusCodeMapping = {
|
40
35
|
200 => "OK",
|
41
36
|
400 => "Bad Request",
|
@@ -45,28 +40,26 @@ private
|
|
45
40
|
500 => "Internal Server Error"
|
46
41
|
}
|
47
42
|
|
48
|
-
# Classes -------------------------------------------------
|
49
|
-
|
50
43
|
class Request
|
51
44
|
attr_reader :data, :header, :method, :path, :proto
|
52
|
-
|
45
|
+
|
53
46
|
def initialize(data, method=nil, path=nil, proto=nil)
|
54
47
|
@header, @data = Table.new, data
|
55
48
|
@method, @path, @proto = method, path, proto
|
56
49
|
end
|
57
|
-
|
50
|
+
|
58
51
|
def content_length
|
59
52
|
len = @header['Content-Length']
|
60
53
|
return nil if len.nil?
|
61
|
-
return len.to_i
|
54
|
+
return len.to_i
|
62
55
|
end
|
63
|
-
|
56
|
+
|
64
57
|
end
|
65
|
-
|
58
|
+
|
66
59
|
class Response
|
67
60
|
attr_reader :header
|
68
61
|
attr_accessor :body, :status, :status_message
|
69
|
-
|
62
|
+
|
70
63
|
def initialize(status=200)
|
71
64
|
@status = status
|
72
65
|
@status_message = nil
|
@@ -74,15 +67,12 @@ private
|
|
74
67
|
end
|
75
68
|
end
|
76
69
|
|
77
|
-
|
78
|
-
##
|
79
|
-
# a case-insensitive Hash class for HTTP header
|
80
|
-
#
|
70
|
+
# A case-insensitive Hash class for HTTP header
|
81
71
|
class Table
|
82
72
|
include Enumerable
|
83
73
|
|
84
74
|
def initialize(hash={})
|
85
|
-
@hash = hash
|
75
|
+
@hash = hash
|
86
76
|
update(hash)
|
87
77
|
end
|
88
78
|
|
@@ -103,17 +93,17 @@ private
|
|
103
93
|
@hash.each {|k,v| yield k.capitalize, v }
|
104
94
|
end
|
105
95
|
|
96
|
+
# Output the Hash table for the HTTP header
|
106
97
|
def writeTo(port)
|
107
98
|
each { |k,v| port << "#{k}: #{v}" << CRLF }
|
108
99
|
end
|
109
100
|
end # class Table
|
110
101
|
|
111
102
|
|
112
|
-
#
|
113
|
-
|
114
|
-
def http_header(header=nil)
|
103
|
+
# Generates a Hash with the HTTP headers
|
104
|
+
def http_header(header=nil) # :doc:
|
115
105
|
new_header = Table.new(DEFAULT_HEADER)
|
116
|
-
new_header.update(header) unless header.nil?
|
106
|
+
new_header.update(header) unless header.nil?
|
117
107
|
|
118
108
|
new_header["Connection"] = "close"
|
119
109
|
new_header["Date"] = http_date(Time.now)
|
@@ -121,13 +111,16 @@ private
|
|
121
111
|
new_header
|
122
112
|
end
|
123
113
|
|
124
|
-
|
114
|
+
# Returns a string which represents the time as rfc1123-date of HTTP-date
|
115
|
+
def http_date( aTime ) # :doc:
|
125
116
|
aTime.gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" )
|
126
117
|
end
|
127
118
|
|
128
|
-
|
119
|
+
# Returns a string which includes the status code message as,
|
120
|
+
# http headers, and body for the response.
|
121
|
+
def http_resp(status_code, status_message=nil, header=nil, body=nil) # :doc:
|
129
122
|
status_message ||= StatusCodeMapping[status_code]
|
130
|
-
|
123
|
+
|
131
124
|
str = ""
|
132
125
|
str << "#{HTTP_PROTO} #{status_code} #{status_message}" << CRLF
|
133
126
|
http_header(header).writeTo(str)
|
@@ -136,9 +129,11 @@ private
|
|
136
129
|
str
|
137
130
|
end
|
138
131
|
|
139
|
-
#
|
140
|
-
|
141
|
-
|
132
|
+
# Handles the HTTP request and writes the response back to the client, +io+.
|
133
|
+
#
|
134
|
+
# If an Exception is raised while handling the request, the client will receive
|
135
|
+
# a 500 "Internal Server Error" message.
|
136
|
+
def serve(io) # :doc:
|
142
137
|
# perform IP authentification
|
143
138
|
unless @handler.ip_auth_handler(io)
|
144
139
|
io << http_resp(403, "Forbidden")
|
@@ -149,28 +144,28 @@ private
|
|
149
144
|
if io.gets =~ /^(\S+)\s+(\S+)\s+(\S+)/
|
150
145
|
request = Request.new(io, $1, $2, $3)
|
151
146
|
else
|
152
|
-
io << http_resp(400, "Bad Request")
|
147
|
+
io << http_resp(400, "Bad Request")
|
153
148
|
return
|
154
149
|
end
|
155
|
-
|
150
|
+
|
156
151
|
# parse HTTP headers
|
157
152
|
while (line=io.gets) !~ /^(\n|\r)/
|
158
153
|
if line =~ /^([\w-]+):\s*(.*)$/
|
159
|
-
|
154
|
+
request.header[$1] = $2.strip
|
160
155
|
end
|
161
156
|
end
|
162
157
|
|
163
|
-
io.binmode
|
158
|
+
io.binmode
|
164
159
|
response = Response.new
|
165
160
|
|
166
161
|
# execute request handler
|
167
162
|
@handler.request_handler(request, response)
|
168
|
-
|
163
|
+
|
169
164
|
# write response back to the client
|
170
165
|
io << http_resp(response.status, response.status_message,
|
171
|
-
response.header, response.body)
|
166
|
+
response.header, response.body)
|
172
167
|
|
173
|
-
rescue Exception
|
168
|
+
rescue Exception
|
174
169
|
io << http_resp(500, "Internal Server Error")
|
175
170
|
end
|
176
171
|
|
data/lib/xmlrpc/marshal.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
#
|
2
|
-
# Marshalling of XML-RPC methodCall and methodResponse
|
3
|
-
#
|
4
2
|
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
5
3
|
#
|
6
|
-
# $Id
|
4
|
+
# $Id$
|
7
5
|
#
|
8
6
|
|
9
7
|
require "xmlrpc/parser"
|
@@ -11,13 +9,12 @@ require "xmlrpc/create"
|
|
11
9
|
require "xmlrpc/config"
|
12
10
|
require "xmlrpc/utils"
|
13
11
|
|
14
|
-
module XMLRPC
|
12
|
+
module XMLRPC # :nodoc:
|
15
13
|
|
14
|
+
# Marshalling of XMLRPC::Create#methodCall and XMLRPC::Create#methodResponse
|
16
15
|
class Marshal
|
17
16
|
include ParserWriterChooseMixin
|
18
17
|
|
19
|
-
# class methods -------------------------------
|
20
|
-
|
21
18
|
class << self
|
22
19
|
|
23
20
|
def dump_call( methodName, *params )
|
@@ -41,8 +38,6 @@ module XMLRPC
|
|
41
38
|
|
42
39
|
end # class self
|
43
40
|
|
44
|
-
# instance methods ----------------------------
|
45
|
-
|
46
41
|
def initialize( parser = nil, writer = nil )
|
47
42
|
set_parser( parser )
|
48
43
|
set_writer( writer )
|
@@ -52,20 +47,16 @@ module XMLRPC
|
|
52
47
|
create.methodCall( methodName, *params )
|
53
48
|
end
|
54
49
|
|
55
|
-
def dump_response( param )
|
50
|
+
def dump_response( param )
|
56
51
|
create.methodResponse( ! param.kind_of?( XMLRPC::FaultException ) , param )
|
57
52
|
end
|
58
53
|
|
59
|
-
|
60
|
-
# returns [ methodname, params ]
|
61
|
-
#
|
54
|
+
# Returns <code>[ methodname, params ]</code>
|
62
55
|
def load_call( stringOrReadable )
|
63
56
|
parser.parseMethodCall( stringOrReadable )
|
64
57
|
end
|
65
58
|
|
66
|
-
|
67
|
-
# returns paramOrFault
|
68
|
-
#
|
59
|
+
# Returns +paramOrFault+
|
69
60
|
def load_response( stringOrReadable )
|
70
61
|
parser.parseMethodResponse( stringOrReadable )[1]
|
71
62
|
end
|
@@ -73,4 +64,3 @@ module XMLRPC
|
|
73
64
|
end # class Marshal
|
74
65
|
|
75
66
|
end
|
76
|
-
|
data/lib/xmlrpc/parser.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
#
|
2
|
-
# Parser for XML-RPC call and response
|
3
|
-
#
|
4
1
|
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
5
2
|
#
|
6
|
-
# $Id
|
3
|
+
# $Id$
|
7
4
|
#
|
8
5
|
|
9
6
|
|
@@ -12,7 +9,6 @@ require "xmlrpc/base64"
|
|
12
9
|
require "xmlrpc/datetime"
|
13
10
|
|
14
11
|
|
15
|
-
# add some methods to NQXML::Node
|
16
12
|
module NQXML
|
17
13
|
class Node
|
18
14
|
|
@@ -49,42 +45,62 @@ module NQXML
|
|
49
45
|
end # class Node
|
50
46
|
end # module NQXML
|
51
47
|
|
52
|
-
module XMLRPC
|
48
|
+
module XMLRPC # :nodoc:
|
53
49
|
|
50
|
+
# Raised when the remote procedure returns a fault-structure, which has two
|
51
|
+
# accessor-methods +faultCode+ an Integer, and +faultString+ a String.
|
54
52
|
class FaultException < StandardError
|
55
53
|
attr_reader :faultCode, :faultString
|
56
54
|
|
57
|
-
|
58
|
-
|
55
|
+
# Creates a new XMLRPC::FaultException instance.
|
56
|
+
#
|
57
|
+
# +faultString+ is passed to StandardError as the +msg+ of the Exception.
|
59
58
|
def initialize(faultCode, faultString)
|
60
59
|
@faultCode = faultCode
|
61
60
|
@faultString = faultString
|
61
|
+
super(@faultString)
|
62
62
|
end
|
63
|
-
|
64
|
-
#
|
63
|
+
|
64
|
+
# The +faultCode+ and +faultString+ of the exception in a Hash.
|
65
65
|
def to_h
|
66
66
|
{"faultCode" => @faultCode, "faultString" => @faultString}
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
+
# Helper class used to convert types.
|
70
71
|
module Convert
|
72
|
+
|
73
|
+
# Converts a String to an Integer
|
74
|
+
#
|
75
|
+
# See also String.to_i
|
71
76
|
def self.int(str)
|
72
77
|
str.to_i
|
73
78
|
end
|
74
79
|
|
80
|
+
# Converts a String to +true+ or +false+
|
81
|
+
#
|
82
|
+
# Raises an exception if +str+ is not +0+ or +1+
|
75
83
|
def self.boolean(str)
|
76
84
|
case str
|
77
85
|
when "0" then false
|
78
86
|
when "1" then true
|
79
87
|
else
|
80
|
-
raise "RPC-value of type boolean is wrong"
|
88
|
+
raise "RPC-value of type boolean is wrong"
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
92
|
+
# Converts a String to a Float
|
93
|
+
#
|
94
|
+
# See also String.to_f
|
84
95
|
def self.double(str)
|
85
96
|
str.to_f
|
86
97
|
end
|
87
98
|
|
99
|
+
# Converts a the given +str+ to a +dateTime.iso8601+ formatted date.
|
100
|
+
#
|
101
|
+
# Raises an exception if the String isn't in +dateTime.iso8601+ format.
|
102
|
+
#
|
103
|
+
# See also, XMLRPC::DateTime
|
88
104
|
def self.dateTime(str)
|
89
105
|
case str
|
90
106
|
when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/
|
@@ -115,14 +131,18 @@ module XMLRPC
|
|
115
131
|
end
|
116
132
|
end
|
117
133
|
|
134
|
+
# Decodes the given +str+ using XMLRPC::Base64.decode
|
118
135
|
def self.base64(str)
|
119
136
|
XMLRPC::Base64.decode(str)
|
120
137
|
end
|
121
138
|
|
139
|
+
# Converts the given +hash+ to a marshalled object.
|
140
|
+
#
|
141
|
+
# Returns the given +hash+ if an exception occurs.
|
122
142
|
def self.struct(hash)
|
123
|
-
# convert to
|
143
|
+
# convert to marshalled object
|
124
144
|
klass = hash["___class___"]
|
125
|
-
if klass.nil? or Config::ENABLE_MARSHALLING == false
|
145
|
+
if klass.nil? or Config::ENABLE_MARSHALLING == false
|
126
146
|
hash
|
127
147
|
else
|
128
148
|
begin
|
@@ -130,10 +150,10 @@ module XMLRPC
|
|
130
150
|
klass.split("::").each {|const| mod = mod.const_get(const.strip)}
|
131
151
|
|
132
152
|
obj = mod.allocate
|
133
|
-
|
153
|
+
|
134
154
|
hash.delete "___class___"
|
135
|
-
hash.each {|key, value|
|
136
|
-
obj.instance_variable_set("@#{ key }", value) if key =~ /^([
|
155
|
+
hash.each {|key, value|
|
156
|
+
obj.instance_variable_set("@#{ key }", value) if key =~ /^([a-zA-Z_]\w*)$/
|
137
157
|
}
|
138
158
|
obj
|
139
159
|
rescue
|
@@ -142,12 +162,21 @@ module XMLRPC
|
|
142
162
|
end
|
143
163
|
end
|
144
164
|
|
165
|
+
# Converts the given +hash+ to an XMLRPC::FaultException object by passing
|
166
|
+
# the +faultCode+ and +faultString+ attributes of the Hash to
|
167
|
+
# XMLRPC::FaultException.new
|
168
|
+
#
|
169
|
+
# Raises an Exception if the given +hash+ doesn't meet the requirements.
|
170
|
+
# Those requirements being:
|
171
|
+
# * 2 keys
|
172
|
+
# * <code>'faultCode'</code> key is an Integer
|
173
|
+
# * <code>'faultString'</code> key is a String
|
145
174
|
def self.fault(hash)
|
146
|
-
if hash.kind_of? Hash and hash.size == 2 and
|
147
|
-
hash.has_key? "faultCode" and hash.has_key? "faultString" and
|
175
|
+
if hash.kind_of? Hash and hash.size == 2 and
|
176
|
+
hash.has_key? "faultCode" and hash.has_key? "faultString" and
|
148
177
|
hash["faultCode"].kind_of? Integer and hash["faultString"].kind_of? String
|
149
178
|
|
150
|
-
XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"])
|
179
|
+
XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"])
|
151
180
|
else
|
152
181
|
raise "wrong fault-structure: #{hash.inspect}"
|
153
182
|
end
|
@@ -155,224 +184,221 @@ module XMLRPC
|
|
155
184
|
|
156
185
|
end # module Convert
|
157
186
|
|
187
|
+
# Parser for XML-RPC call and response
|
158
188
|
module XMLParser
|
159
189
|
|
160
190
|
class AbstractTreeParser
|
161
191
|
|
162
192
|
def parseMethodResponse(str)
|
163
|
-
|
193
|
+
methodResponse_document(createCleanedTree(str))
|
164
194
|
end
|
165
195
|
|
166
196
|
def parseMethodCall(str)
|
167
|
-
|
197
|
+
methodCall_document(createCleanedTree(str))
|
168
198
|
end
|
169
199
|
|
170
200
|
private
|
171
201
|
|
172
|
-
#
|
173
|
-
# remove all whitespaces but in the tags i4, int, boolean....
|
202
|
+
# Removes all whitespaces but in the tags i4, i8, int, boolean....
|
174
203
|
# and all comments
|
175
|
-
#
|
176
204
|
def removeWhitespacesAndComments(node)
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
205
|
+
remove = []
|
206
|
+
childs = node.childNodes.to_a
|
207
|
+
childs.each do |nd|
|
208
|
+
case _nodeType(nd)
|
209
|
+
when :TEXT
|
182
210
|
# TODO: add nil?
|
183
|
-
unless %w(i4 int boolean string double dateTime.iso8601 base64).include? node.nodeName
|
211
|
+
unless %w(i4 i8 int boolean string double dateTime.iso8601 base64).include? node.nodeName
|
184
212
|
|
185
|
-
if node.nodeName == "value"
|
213
|
+
if node.nodeName == "value"
|
186
214
|
if not node.childNodes.to_a.detect {|n| _nodeType(n) == :ELEMENT}.nil?
|
187
|
-
remove << nd if nd.nodeValue.strip == ""
|
215
|
+
remove << nd if nd.nodeValue.strip == ""
|
188
216
|
end
|
189
217
|
else
|
190
218
|
remove << nd if nd.nodeValue.strip == ""
|
191
219
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
220
|
+
end
|
221
|
+
when :COMMENT
|
222
|
+
remove << nd
|
223
|
+
else
|
224
|
+
removeWhitespacesAndComments(nd)
|
225
|
+
end
|
226
|
+
end
|
199
227
|
|
200
|
-
|
228
|
+
remove.each { |i| node.removeChild(i) }
|
201
229
|
end
|
202
230
|
|
203
231
|
|
204
232
|
def nodeMustBe(node, name)
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
233
|
+
cmp = case name
|
234
|
+
when Array
|
235
|
+
name.include?(node.nodeName)
|
236
|
+
when String
|
237
|
+
name == node.nodeName
|
238
|
+
else
|
239
|
+
raise "error"
|
240
|
+
end
|
213
241
|
|
214
|
-
|
215
|
-
|
216
|
-
|
242
|
+
if not cmp then
|
243
|
+
raise "wrong xml-rpc (name)"
|
244
|
+
end
|
217
245
|
|
218
|
-
|
246
|
+
node
|
219
247
|
end
|
220
248
|
|
221
|
-
#
|
222
|
-
# returns, when successfully the only child-node
|
223
|
-
#
|
249
|
+
# Returns, when successfully the only child-node
|
224
250
|
def hasOnlyOneChild(node, name=nil)
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
251
|
+
if node.childNodes.to_a.size != 1
|
252
|
+
raise "wrong xml-rpc (size)"
|
253
|
+
end
|
254
|
+
if name != nil then
|
255
|
+
nodeMustBe(node.firstChild, name)
|
256
|
+
end
|
231
257
|
end
|
232
258
|
|
233
259
|
|
234
260
|
def assert(b)
|
235
|
-
|
236
|
-
|
237
|
-
|
261
|
+
if not b then
|
262
|
+
raise "assert-fail"
|
263
|
+
end
|
238
264
|
end
|
239
265
|
|
240
|
-
#
|
266
|
+
# The node `node` has empty string or string
|
241
267
|
def text_zero_one(node)
|
242
|
-
|
268
|
+
nodes = node.childNodes.to_a.size
|
243
269
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
270
|
+
if nodes == 1
|
271
|
+
text(node.firstChild)
|
272
|
+
elsif nodes == 0
|
273
|
+
""
|
274
|
+
else
|
275
|
+
raise "wrong xml-rpc (size)"
|
276
|
+
end
|
251
277
|
end
|
252
|
-
|
278
|
+
|
253
279
|
|
254
280
|
def integer(node)
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
281
|
+
#TODO: check string for float because to_i returnsa
|
282
|
+
# 0 when wrong string
|
283
|
+
nodeMustBe(node, %w(i4 i8 int))
|
284
|
+
hasOnlyOneChild(node)
|
285
|
+
|
286
|
+
Convert.int(text(node.firstChild))
|
261
287
|
end
|
262
288
|
|
263
289
|
def boolean(node)
|
264
|
-
|
265
|
-
|
266
|
-
|
290
|
+
nodeMustBe(node, "boolean")
|
291
|
+
hasOnlyOneChild(node)
|
292
|
+
|
267
293
|
Convert.boolean(text(node.firstChild))
|
268
294
|
end
|
269
295
|
|
270
296
|
def v_nil(node)
|
271
297
|
nodeMustBe(node, "nil")
|
272
|
-
|
298
|
+
assert( node.childNodes.to_a.size == 0 )
|
273
299
|
nil
|
274
300
|
end
|
275
301
|
|
276
302
|
def string(node)
|
277
|
-
|
278
|
-
|
303
|
+
nodeMustBe(node, "string")
|
304
|
+
text_zero_one(node)
|
279
305
|
end
|
280
306
|
|
281
307
|
def double(node)
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
308
|
+
#TODO: check string for float because to_f returnsa
|
309
|
+
# 0.0 when wrong string
|
310
|
+
nodeMustBe(node, "double")
|
311
|
+
hasOnlyOneChild(node)
|
312
|
+
|
313
|
+
Convert.double(text(node.firstChild))
|
288
314
|
end
|
289
315
|
|
290
316
|
def dateTime(node)
|
291
|
-
|
292
|
-
|
293
|
-
|
317
|
+
nodeMustBe(node, "dateTime.iso8601")
|
318
|
+
hasOnlyOneChild(node)
|
319
|
+
|
294
320
|
Convert.dateTime( text(node.firstChild) )
|
295
321
|
end
|
296
322
|
|
297
323
|
def base64(node)
|
298
|
-
|
299
|
-
|
300
|
-
|
324
|
+
nodeMustBe(node, "base64")
|
325
|
+
#hasOnlyOneChild(node)
|
326
|
+
|
301
327
|
Convert.base64(text_zero_one(node))
|
302
328
|
end
|
303
329
|
|
304
330
|
def member(node)
|
305
|
-
|
306
|
-
|
331
|
+
nodeMustBe(node, "member")
|
332
|
+
assert( node.childNodes.to_a.size == 2 )
|
307
333
|
|
308
|
-
|
334
|
+
[ name(node[0]), value(node[1]) ]
|
309
335
|
end
|
310
336
|
|
311
337
|
def name(node)
|
312
|
-
|
313
|
-
|
314
|
-
|
338
|
+
nodeMustBe(node, "name")
|
339
|
+
#hasOnlyOneChild(node)
|
340
|
+
text_zero_one(node)
|
315
341
|
end
|
316
342
|
|
317
343
|
def array(node)
|
318
|
-
|
319
|
-
|
320
|
-
|
344
|
+
nodeMustBe(node, "array")
|
345
|
+
hasOnlyOneChild(node, "data")
|
346
|
+
data(node.firstChild)
|
321
347
|
end
|
322
348
|
|
323
349
|
def data(node)
|
324
|
-
|
350
|
+
nodeMustBe(node, "data")
|
325
351
|
|
326
|
-
|
327
|
-
|
328
|
-
|
352
|
+
node.childNodes.to_a.collect do |val|
|
353
|
+
value(val)
|
354
|
+
end
|
329
355
|
end
|
330
356
|
|
331
357
|
def param(node)
|
332
|
-
|
333
|
-
|
334
|
-
|
358
|
+
nodeMustBe(node, "param")
|
359
|
+
hasOnlyOneChild(node, "value")
|
360
|
+
value(node.firstChild)
|
335
361
|
end
|
336
|
-
|
362
|
+
|
337
363
|
def methodResponse(node)
|
338
|
-
|
339
|
-
|
340
|
-
|
364
|
+
nodeMustBe(node, "methodResponse")
|
365
|
+
hasOnlyOneChild(node, %w(params fault))
|
366
|
+
child = node.firstChild
|
341
367
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
368
|
+
case child.nodeName
|
369
|
+
when "params"
|
370
|
+
[ true, params(child,false) ]
|
371
|
+
when "fault"
|
372
|
+
[ false, fault(child) ]
|
373
|
+
else
|
374
|
+
raise "unexpected error"
|
375
|
+
end
|
350
376
|
|
351
377
|
end
|
352
378
|
|
353
379
|
def methodName(node)
|
354
|
-
|
355
|
-
|
356
|
-
|
380
|
+
nodeMustBe(node, "methodName")
|
381
|
+
hasOnlyOneChild(node)
|
382
|
+
text(node.firstChild)
|
357
383
|
end
|
358
384
|
|
359
385
|
def params(node, call=true)
|
360
|
-
|
386
|
+
nodeMustBe(node, "params")
|
361
387
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
388
|
+
if call
|
389
|
+
node.childNodes.to_a.collect do |n|
|
390
|
+
param(n)
|
391
|
+
end
|
392
|
+
else # response (only one param)
|
393
|
+
hasOnlyOneChild(node)
|
394
|
+
param(node.firstChild)
|
395
|
+
end
|
370
396
|
end
|
371
397
|
|
372
398
|
def fault(node)
|
373
|
-
|
374
|
-
|
375
|
-
|
399
|
+
nodeMustBe(node, "fault")
|
400
|
+
hasOnlyOneChild(node, "value")
|
401
|
+
f = value(node.firstChild)
|
376
402
|
Convert.fault(f)
|
377
403
|
end
|
378
404
|
|
@@ -380,76 +406,76 @@ module XMLRPC
|
|
380
406
|
|
381
407
|
# _nodeType is defined in the subclass
|
382
408
|
def text(node)
|
383
|
-
|
384
|
-
|
385
|
-
|
409
|
+
assert( _nodeType(node) == :TEXT )
|
410
|
+
assert( node.hasChildNodes == false )
|
411
|
+
assert( node.nodeValue != nil )
|
386
412
|
|
387
|
-
|
413
|
+
node.nodeValue.to_s
|
388
414
|
end
|
389
415
|
|
390
416
|
def struct(node)
|
391
|
-
|
417
|
+
nodeMustBe(node, "struct")
|
392
418
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
419
|
+
hash = {}
|
420
|
+
node.childNodes.to_a.each do |me|
|
421
|
+
n, v = member(me)
|
422
|
+
hash[n] = v
|
423
|
+
end
|
398
424
|
|
399
425
|
Convert.struct(hash)
|
400
|
-
|
426
|
+
end
|
401
427
|
|
402
428
|
|
403
429
|
def value(node)
|
404
|
-
|
405
|
-
|
406
|
-
if nodes == 0
|
430
|
+
nodeMustBe(node, "value")
|
431
|
+
nodes = node.childNodes.to_a.size
|
432
|
+
if nodes == 0
|
407
433
|
return ""
|
408
|
-
elsif nodes > 1
|
409
|
-
|
434
|
+
elsif nodes > 1
|
435
|
+
raise "wrong xml-rpc (size)"
|
410
436
|
end
|
411
437
|
|
412
|
-
|
438
|
+
child = node.firstChild
|
413
439
|
|
414
|
-
|
415
|
-
|
440
|
+
case _nodeType(child)
|
441
|
+
when :TEXT
|
416
442
|
text_zero_one(node)
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
when "nil"
|
443
|
+
when :ELEMENT
|
444
|
+
case child.nodeName
|
445
|
+
when "i4", "i8", "int" then integer(child)
|
446
|
+
when "boolean" then boolean(child)
|
447
|
+
when "string" then string(child)
|
448
|
+
when "double" then double(child)
|
449
|
+
when "dateTime.iso8601" then dateTime(child)
|
450
|
+
when "base64" then base64(child)
|
451
|
+
when "struct" then struct(child)
|
452
|
+
when "array" then array(child)
|
453
|
+
when "nil"
|
428
454
|
if Config::ENABLE_NIL_PARSER
|
429
455
|
v_nil(child)
|
430
456
|
else
|
431
457
|
raise "wrong/unknown XML-RPC type 'nil'"
|
432
458
|
end
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
459
|
+
else
|
460
|
+
raise "wrong/unknown XML-RPC type"
|
461
|
+
end
|
462
|
+
else
|
463
|
+
raise "wrong type of node"
|
464
|
+
end
|
439
465
|
|
440
466
|
end
|
441
467
|
|
442
468
|
def methodCall(node)
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
469
|
+
nodeMustBe(node, "methodCall")
|
470
|
+
assert( (1..2).include?( node.childNodes.to_a.size ) )
|
471
|
+
name = methodName(node[0])
|
472
|
+
|
473
|
+
if node.childNodes.to_a.size == 2 then
|
474
|
+
pa = params(node[1])
|
475
|
+
else # no parameters given
|
476
|
+
pa = []
|
477
|
+
end
|
478
|
+
[name, pa]
|
453
479
|
end
|
454
480
|
|
455
481
|
end # module TreeParserMixin
|
@@ -461,7 +487,7 @@ module XMLRPC
|
|
461
487
|
raise "No valid method response!" if parser.method_name != nil
|
462
488
|
if parser.fault != nil
|
463
489
|
# is a fault structure
|
464
|
-
[false, parser.fault]
|
490
|
+
[false, parser.fault]
|
465
491
|
else
|
466
492
|
# is a normal return value
|
467
493
|
raise "Missing return value!" if parser.params.size == 0
|
@@ -508,7 +534,7 @@ module XMLRPC
|
|
508
534
|
@value = nil
|
509
535
|
when "nil"
|
510
536
|
raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER
|
511
|
-
@value = :nil
|
537
|
+
@value = :nil
|
512
538
|
when "array"
|
513
539
|
@val_stack << @values
|
514
540
|
@values = []
|
@@ -517,7 +543,7 @@ module XMLRPC
|
|
517
543
|
@name = []
|
518
544
|
|
519
545
|
@structs << @struct
|
520
|
-
@struct = {}
|
546
|
+
@struct = {}
|
521
547
|
end
|
522
548
|
end
|
523
549
|
|
@@ -526,7 +552,7 @@ module XMLRPC
|
|
526
552
|
case name
|
527
553
|
when "string"
|
528
554
|
@value = @data
|
529
|
-
when "i4", "int"
|
555
|
+
when "i4", "i8", "int"
|
530
556
|
@value = Convert.int(@data)
|
531
557
|
when "boolean"
|
532
558
|
@value = Convert.boolean(@data)
|
@@ -538,7 +564,7 @@ module XMLRPC
|
|
538
564
|
@value = Convert.base64(@data)
|
539
565
|
when "value"
|
540
566
|
@value = @data if @value.nil?
|
541
|
-
@values << (@value == :nil ? nil : @value)
|
567
|
+
@values << (@value == :nil ? nil : @value)
|
542
568
|
when "array"
|
543
569
|
@value = @values
|
544
570
|
@values = @val_stack.pop
|
@@ -548,9 +574,9 @@ module XMLRPC
|
|
548
574
|
@name = @names.pop
|
549
575
|
@struct = @structs.pop
|
550
576
|
when "name"
|
551
|
-
@name[0] = @data
|
577
|
+
@name[0] = @data
|
552
578
|
when "member"
|
553
|
-
@struct[@name[0]] = @values.pop
|
579
|
+
@struct[@name[0]] = @values.pop
|
554
580
|
|
555
581
|
when "param"
|
556
582
|
@params << @values[0]
|
@@ -560,7 +586,7 @@ module XMLRPC
|
|
560
586
|
@fault = Convert.fault(@values[0])
|
561
587
|
|
562
588
|
when "methodName"
|
563
|
-
@method_name = @data
|
589
|
+
@method_name = @data
|
564
590
|
end
|
565
591
|
|
566
592
|
@data = nil
|
@@ -576,7 +602,6 @@ module XMLRPC
|
|
576
602
|
|
577
603
|
end # module StreamParserMixin
|
578
604
|
|
579
|
-
# ---------------------------------------------------------------------------
|
580
605
|
class XMLStreamParser < AbstractStreamParser
|
581
606
|
def initialize
|
582
607
|
require "xmlparser"
|
@@ -585,14 +610,14 @@ module XMLRPC
|
|
585
610
|
}
|
586
611
|
end
|
587
612
|
end # class XMLStreamParser
|
588
|
-
|
613
|
+
|
589
614
|
class NQXMLStreamParser < AbstractStreamParser
|
590
615
|
def initialize
|
591
616
|
require "nqxml/streamingparser"
|
592
617
|
@parser_class = XMLRPCParser
|
593
618
|
end
|
594
619
|
|
595
|
-
class XMLRPCParser
|
620
|
+
class XMLRPCParser
|
596
621
|
include StreamParserMixin
|
597
622
|
|
598
623
|
def parse(str)
|
@@ -614,59 +639,59 @@ module XMLRPC
|
|
614
639
|
end # class XMLRPCParser
|
615
640
|
|
616
641
|
end # class NQXMLStreamParser
|
617
|
-
|
642
|
+
|
618
643
|
class XMLTreeParser < AbstractTreeParser
|
619
644
|
|
620
645
|
def initialize
|
621
646
|
require "xmltreebuilder"
|
622
647
|
|
623
|
-
# The new XMLParser library (0.6.2+) uses a slightly different DOM implementation.
|
648
|
+
# The new XMLParser library (0.6.2+) uses a slightly different DOM implementation.
|
624
649
|
# The following code removes the differences between both versions.
|
625
|
-
if defined? XML::DOM::Builder
|
650
|
+
if defined? XML::DOM::Builder
|
626
651
|
return if defined? XML::DOM::Node::DOCUMENT # code below has been already executed
|
627
652
|
klass = XML::DOM::Node
|
628
|
-
klass.const_set(
|
629
|
-
klass.const_set(
|
630
|
-
klass.const_set(
|
631
|
-
klass.const_set(
|
653
|
+
klass.const_set(:DOCUMENT, klass::DOCUMENT_NODE)
|
654
|
+
klass.const_set(:TEXT, klass::TEXT_NODE)
|
655
|
+
klass.const_set(:COMMENT, klass::COMMENT_NODE)
|
656
|
+
klass.const_set(:ELEMENT, klass::ELEMENT_NODE)
|
632
657
|
end
|
633
658
|
end
|
634
659
|
|
635
660
|
private
|
636
661
|
|
637
662
|
def _nodeType(node)
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
663
|
+
tp = node.nodeType
|
664
|
+
if tp == XML::SimpleTree::Node::TEXT then :TEXT
|
665
|
+
elsif tp == XML::SimpleTree::Node::COMMENT then :COMMENT
|
666
|
+
elsif tp == XML::SimpleTree::Node::ELEMENT then :ELEMENT
|
667
|
+
else :ELSE
|
668
|
+
end
|
644
669
|
end
|
645
670
|
|
646
671
|
|
647
672
|
def methodResponse_document(node)
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
673
|
+
assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
|
674
|
+
hasOnlyOneChild(node, "methodResponse")
|
675
|
+
|
676
|
+
methodResponse(node.firstChild)
|
652
677
|
end
|
653
678
|
|
654
679
|
def methodCall_document(node)
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
680
|
+
assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
|
681
|
+
hasOnlyOneChild(node, "methodCall")
|
682
|
+
|
683
|
+
methodCall(node.firstChild)
|
659
684
|
end
|
660
685
|
|
661
686
|
def createCleanedTree(str)
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
687
|
+
doc = XML::SimpleTreeBuilder.new.parse(str)
|
688
|
+
doc.documentElement.normalize
|
689
|
+
removeWhitespacesAndComments(doc)
|
690
|
+
doc
|
666
691
|
end
|
667
692
|
|
668
693
|
end # class XMLParser
|
669
|
-
|
694
|
+
|
670
695
|
class NQXMLTreeParser < AbstractTreeParser
|
671
696
|
|
672
697
|
def initialize
|
@@ -676,32 +701,32 @@ module XMLRPC
|
|
676
701
|
private
|
677
702
|
|
678
703
|
def _nodeType(node)
|
679
|
-
|
704
|
+
node.nodeType
|
680
705
|
end
|
681
706
|
|
682
707
|
def methodResponse_document(node)
|
683
|
-
|
708
|
+
methodResponse(node)
|
684
709
|
end
|
685
710
|
|
686
711
|
def methodCall_document(node)
|
687
|
-
|
712
|
+
methodCall(node)
|
688
713
|
end
|
689
714
|
|
690
715
|
def createCleanedTree(str)
|
691
|
-
doc = ::NQXML::TreeParser.new(str).document.rootNode
|
692
|
-
|
693
|
-
|
716
|
+
doc = ::NQXML::TreeParser.new(str).document.rootNode
|
717
|
+
removeWhitespacesAndComments(doc)
|
718
|
+
doc
|
694
719
|
end
|
695
720
|
|
696
721
|
end # class NQXMLTreeParser
|
697
|
-
|
722
|
+
|
698
723
|
class REXMLStreamParser < AbstractStreamParser
|
699
724
|
def initialize
|
700
725
|
require "rexml/document"
|
701
726
|
@parser_class = StreamListener
|
702
727
|
end
|
703
728
|
|
704
|
-
class StreamListener
|
729
|
+
class StreamListener
|
705
730
|
include StreamParserMixin
|
706
731
|
|
707
732
|
alias :tag_start :startElement
|
@@ -714,12 +739,12 @@ module XMLRPC
|
|
714
739
|
end
|
715
740
|
|
716
741
|
def parse(str)
|
717
|
-
|
718
|
-
|
719
|
-
end
|
742
|
+
REXML::Document.parse_stream(str, self)
|
743
|
+
end
|
744
|
+
end
|
720
745
|
|
721
746
|
end
|
722
|
-
|
747
|
+
|
723
748
|
class XMLScanStreamParser < AbstractStreamParser
|
724
749
|
def initialize
|
725
750
|
require "xmlscan/parser"
|
@@ -743,7 +768,7 @@ module XMLRPC
|
|
743
768
|
end
|
744
769
|
|
745
770
|
alias :on_stag :startElement
|
746
|
-
|
771
|
+
alias :on_etag :endElement
|
747
772
|
|
748
773
|
def on_stag_end(name); end
|
749
774
|
|
@@ -751,7 +776,7 @@ module XMLRPC
|
|
751
776
|
startElement(name)
|
752
777
|
endElement(name)
|
753
778
|
end
|
754
|
-
|
779
|
+
|
755
780
|
def on_chardata(str)
|
756
781
|
character(str)
|
757
782
|
end
|
@@ -784,16 +809,16 @@ module XMLRPC
|
|
784
809
|
# valid_name?
|
785
810
|
# valid_chardata?
|
786
811
|
# valid_char?
|
787
|
-
# parse_error
|
812
|
+
# parse_error
|
788
813
|
|
789
814
|
end
|
790
815
|
end
|
791
|
-
|
816
|
+
|
792
817
|
XMLParser = XMLTreeParser
|
793
818
|
NQXMLParser = NQXMLTreeParser
|
794
819
|
|
795
|
-
Classes = [XMLStreamParser, XMLTreeParser,
|
796
|
-
NQXMLStreamParser, NQXMLTreeParser,
|
820
|
+
Classes = [XMLStreamParser, XMLTreeParser,
|
821
|
+
NQXMLStreamParser, NQXMLTreeParser,
|
797
822
|
REXMLStreamParser, XMLScanStreamParser]
|
798
823
|
|
799
824
|
# yields an instance of each installed parser
|