xmlrpc-streaming 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +35 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +35 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/lib/stream_parser_mixin.rb +261 -0
- data/lib/stream_writer.rb +168 -0
- data/lib/xmlrpc-streaming.rb +256 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/xmlrpc-streaming_spec.rb +126 -0
- data/xmlrpc-streaming.gemspec +65 -0
- metadata +120 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rspec", "~> 2.3.0"
|
10
|
+
gem "bundler", "~> 1.0.0"
|
11
|
+
gem "jeweler", "~> 1.6.4"
|
12
|
+
gem "libxml4r"
|
13
|
+
gem "nokogiri"
|
14
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
libxml-ruby (2.2.2)
|
11
|
+
libxml-ruby (2.2.2-x86-mingw32)
|
12
|
+
libxml4r (0.2.6)
|
13
|
+
libxml-ruby (>= 1.1.3)
|
14
|
+
nokogiri (1.5.0)
|
15
|
+
nokogiri (1.5.0-x86-mingw32)
|
16
|
+
rake (0.9.2.2)
|
17
|
+
rspec (2.3.0)
|
18
|
+
rspec-core (~> 2.3.0)
|
19
|
+
rspec-expectations (~> 2.3.0)
|
20
|
+
rspec-mocks (~> 2.3.0)
|
21
|
+
rspec-core (2.3.1)
|
22
|
+
rspec-expectations (2.3.0)
|
23
|
+
diff-lcs (~> 1.1.2)
|
24
|
+
rspec-mocks (2.3.0)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
x86-mingw32
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
bundler (~> 1.0.0)
|
32
|
+
jeweler (~> 1.6.4)
|
33
|
+
libxml4r
|
34
|
+
nokogiri
|
35
|
+
rspec (~> 2.3.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Sal Scotto
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
= xmlrpc-streaming
|
2
|
+
|
3
|
+
This package will enhance the built-in XMLRPC client to ruby
|
4
|
+
It changes the following:
|
5
|
+
* Base64 now accepts an IO object to its constructor
|
6
|
+
* The XMLRPC request will now be streamed to the server. This
|
7
|
+
will be a little faster and generate much less garbage. It should
|
8
|
+
also fix issues where large Base64 objects sent to the server can cause out of memory errors
|
9
|
+
* Base64 now has a to_io methods that will give access to the underlying data as a IO object
|
10
|
+
note: this is the raw data.
|
11
|
+
* set_writer will be ignored if this module has been loaded.
|
12
|
+
* the parser will now try the following, nokogiri, libxml and finally fallback to REML
|
13
|
+
* This module can be used completely transparently, just require the lib and nothing else needs done
|
14
|
+
|
15
|
+
== Usage
|
16
|
+
|
17
|
+
require 'xmlrpc-streaming'
|
18
|
+
|
19
|
+
.. Normal XMLRPC Code here
|
20
|
+
|
21
|
+
== Contributing to xmlrpc-streaming
|
22
|
+
|
23
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
24
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
25
|
+
* Fork the project
|
26
|
+
* Start a feature/bugfix branch
|
27
|
+
* Commit and push until you are happy with your contribution
|
28
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
29
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
30
|
+
|
31
|
+
== Copyright
|
32
|
+
|
33
|
+
Copyright (c) 2011 Sal Scotto. See LICENSE.txt for
|
34
|
+
further details.
|
35
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "xmlrpc-streaming"
|
18
|
+
gem.homepage = "http://github.com/washu/xmlrpc-streaming"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Xmlrpc client that uses streaming for binary data}
|
21
|
+
gem.description = %Q{The built in xmlrpc client doesnt handle large binary data well, this client addresses the problem}
|
22
|
+
gem.email = "sal.scotto@gmail.com"
|
23
|
+
gem.authors = ["Sal Scotto"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => :spec
|
35
|
+
|
36
|
+
require 'rake/rdoctask'
|
37
|
+
Rake::RDocTask.new do |rdoc|
|
38
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
39
|
+
|
40
|
+
rdoc.rdoc_dir = 'rdoc'
|
41
|
+
rdoc.title = "xmlrpc-streaming #{version}"
|
42
|
+
rdoc.rdoc_files.include('README*')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'xmlrpc/parser'
|
2
|
+
|
3
|
+
module XMLRPC
|
4
|
+
module StreamParserMixin2
|
5
|
+
attr_reader :params
|
6
|
+
attr_reader :method_name
|
7
|
+
attr_reader :fault
|
8
|
+
attr_accessor :use_streams
|
9
|
+
def initialize(*a)
|
10
|
+
super(*a)
|
11
|
+
@params = []
|
12
|
+
@values = []
|
13
|
+
@val_stack = []
|
14
|
+
|
15
|
+
@names = []
|
16
|
+
@name = []
|
17
|
+
|
18
|
+
@structs = []
|
19
|
+
@struct = {}
|
20
|
+
|
21
|
+
@method_name = nil
|
22
|
+
@fault = nil
|
23
|
+
|
24
|
+
@data = nil
|
25
|
+
@working_tag = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def startElement(name, attrs=[])
|
29
|
+
@data = nil
|
30
|
+
case name
|
31
|
+
when "value"
|
32
|
+
@value = nil
|
33
|
+
when "nil"
|
34
|
+
raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER
|
35
|
+
@value = :nil
|
36
|
+
when "array"
|
37
|
+
@val_stack << @values
|
38
|
+
@values = []
|
39
|
+
when "struct"
|
40
|
+
@names << @name
|
41
|
+
@name = []
|
42
|
+
@structs << @struct
|
43
|
+
@struct = {}
|
44
|
+
end
|
45
|
+
@working_tag = name
|
46
|
+
end
|
47
|
+
|
48
|
+
def endElement(name)
|
49
|
+
@data ||= ""
|
50
|
+
if name.eql?("base64") and @use_streams
|
51
|
+
# Decode the file data into a new temp file and set the response as a stream
|
52
|
+
# the caller will get an IO Object as a result. Only do this if we flagged ourselves
|
53
|
+
# as 'recevied an io stream'
|
54
|
+
elsif name.eql?("base64") and not @use_streams
|
55
|
+
@data = Convert.base64(@data)
|
56
|
+
end
|
57
|
+
case name
|
58
|
+
when "string"
|
59
|
+
@value = @data
|
60
|
+
when "i4", "int"
|
61
|
+
@value = Convert.int(@data)
|
62
|
+
when "boolean"
|
63
|
+
@value = Convert.boolean(@data)
|
64
|
+
when "double"
|
65
|
+
@value = Convert.double(@data)
|
66
|
+
when "dateTime.iso8601"
|
67
|
+
@value = Convert.dateTime(@data)
|
68
|
+
when "base64"
|
69
|
+
@value = @data
|
70
|
+
when "value"
|
71
|
+
@value = @data if @value.nil?
|
72
|
+
@values << (@value == :nil ? nil : @value)
|
73
|
+
when "array"
|
74
|
+
@value = @values
|
75
|
+
@values = @val_stack.pop
|
76
|
+
when "struct"
|
77
|
+
@value = Convert.struct(@struct)
|
78
|
+
|
79
|
+
@name = @names.pop
|
80
|
+
@struct = @structs.pop
|
81
|
+
when "name"
|
82
|
+
@name[0] = @data
|
83
|
+
when "member"
|
84
|
+
@struct[@name[0]] = @values.pop
|
85
|
+
|
86
|
+
when "param"
|
87
|
+
@params << @values[0]
|
88
|
+
@values = []
|
89
|
+
|
90
|
+
when "fault"
|
91
|
+
@fault = Convert.fault(@values[0])
|
92
|
+
|
93
|
+
when "methodName"
|
94
|
+
@method_name = @data
|
95
|
+
end
|
96
|
+
|
97
|
+
@data = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def character(data)
|
101
|
+
if @data
|
102
|
+
@data << data
|
103
|
+
else
|
104
|
+
if @working_tag.eql?("base64")
|
105
|
+
@data = Tempfile.new('xmlrpc-stream-base64-data')
|
106
|
+
@data.binmode
|
107
|
+
else
|
108
|
+
@data = data
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end # module StreamParserMixin
|
113
|
+
|
114
|
+
module XMLParser
|
115
|
+
class AbstractStreamParser2
|
116
|
+
def use_streams=(arg)
|
117
|
+
@use_streams = arg
|
118
|
+
end
|
119
|
+
def parseMethodResponse(str)
|
120
|
+
parser = @parser_class.new
|
121
|
+
parser.user_streams = @use_streams
|
122
|
+
parser.parse(str)
|
123
|
+
raise "No valid method response!" if parser.method_name != nil
|
124
|
+
if parser.fault != nil
|
125
|
+
# is a fault structure
|
126
|
+
[false, parser.fault]
|
127
|
+
else
|
128
|
+
# is a normal return value
|
129
|
+
raise "Missing return value!" if parser.params.size == 0
|
130
|
+
raise "Too many return values. Only one allowed!" if parser.params.size > 1
|
131
|
+
[true, parser.params[0]]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def parseMethodCall(str)
|
136
|
+
parser = @parser_class.new
|
137
|
+
parser.user_streams = @use_streams
|
138
|
+
parser.parse(str)
|
139
|
+
raise "No valid method call - missing method name!" if parser.method_name.nil?
|
140
|
+
[parser.method_name, parser.params]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
module XMLParser
|
146
|
+
class NokogiriStreamParser < AbstractStreamParser2
|
147
|
+
def initialize
|
148
|
+
require 'nokogiri'
|
149
|
+
@parser_class = Class.new(Nokogiri::XML::SAX::Document) do
|
150
|
+
include StreamParserMixin2
|
151
|
+
alias :cdata_block :character
|
152
|
+
alias :characters :character
|
153
|
+
alias :end_element :endElement
|
154
|
+
def start_element(name,attrs)
|
155
|
+
startElement(name)
|
156
|
+
end
|
157
|
+
def start_element_namespace(name, attrs = nil, prefix = nil, uri = nil, ns = nil)
|
158
|
+
startElement(name)
|
159
|
+
end
|
160
|
+
def end_element_namespace(name, prefix = nil, uri = nil)
|
161
|
+
endElement(name)
|
162
|
+
end
|
163
|
+
def method_missing(*a)
|
164
|
+
end
|
165
|
+
def parse(str)
|
166
|
+
parser = Nokogiri::XML::SAX::Parser.new(self)
|
167
|
+
parser.parse(str)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
module XMLParser
|
175
|
+
class LibXmlStreamParser < AbstractStreamParser2
|
176
|
+
def initialize()
|
177
|
+
require "libxml"
|
178
|
+
@parser_class = StreamCallback
|
179
|
+
end
|
180
|
+
class StreamCallback
|
181
|
+
include StreamParserMixin2
|
182
|
+
Entities = {
|
183
|
+
"lt" => "<",
|
184
|
+
"gt" => ">",
|
185
|
+
"amp" => "&",
|
186
|
+
"quot" => '"',
|
187
|
+
"apos" => "'"
|
188
|
+
}
|
189
|
+
|
190
|
+
def on_cdata_block(cdata)
|
191
|
+
character(cdata)
|
192
|
+
end
|
193
|
+
|
194
|
+
def on_characters(chars)
|
195
|
+
character(chars)
|
196
|
+
end
|
197
|
+
|
198
|
+
def on_reference (name)
|
199
|
+
str = Entities[name]
|
200
|
+
if str
|
201
|
+
character(str)
|
202
|
+
else
|
203
|
+
raise "Unknown Entity"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def on_start_element_ns (name, attributes, prefix, uri, namespaces)
|
208
|
+
startElement(name)
|
209
|
+
end
|
210
|
+
|
211
|
+
def on_end_element_ns(name, prefix, uri)
|
212
|
+
endElement(name)
|
213
|
+
end
|
214
|
+
|
215
|
+
def method_missing(*a)
|
216
|
+
end
|
217
|
+
|
218
|
+
def parse(str)
|
219
|
+
parser = LibXML::XML::SaxParser.io(str)
|
220
|
+
parser.extend(LibXML::XML::SaxParser::Callbacks)
|
221
|
+
parser.callbacks = self
|
222
|
+
parser.parse
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
module XMLParser
|
229
|
+
class REXMLStreamParser2 < AbstractStreamParser2
|
230
|
+
def initialize()
|
231
|
+
require "rexml/document"
|
232
|
+
@parser_class = StreamListener
|
233
|
+
end
|
234
|
+
class StreamListener
|
235
|
+
include StreamParserMixin2
|
236
|
+
|
237
|
+
alias :tag_start :startElement
|
238
|
+
alias :tag_end :endElement
|
239
|
+
alias :text :character
|
240
|
+
alias :cdata :character
|
241
|
+
|
242
|
+
def method_missing(*a)
|
243
|
+
# ignore
|
244
|
+
end
|
245
|
+
|
246
|
+
def parse(str)
|
247
|
+
parser = REXML::Document.parse_stream(str, self)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
module XMLParser
|
253
|
+
def self.parser_instance(klass)
|
254
|
+
begin
|
255
|
+
klass.new
|
256
|
+
rescue LoadError => e
|
257
|
+
puts e
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
#
|
2
|
+
# Stream Writer, will write out the XMLRPC data to an IO object
|
3
|
+
#
|
4
|
+
|
5
|
+
module XMLRPC
|
6
|
+
class StreamWriter
|
7
|
+
|
8
|
+
WRITE_BUFFER_SIZE = 33972
|
9
|
+
# Create a write with a given IO
|
10
|
+
def initialize(io)
|
11
|
+
@io = io
|
12
|
+
end
|
13
|
+
def has_streams?
|
14
|
+
@had_a_stream ||= false
|
15
|
+
@had_a_stream
|
16
|
+
end
|
17
|
+
def methodCall(name, *params)
|
18
|
+
@io << '<?xml version="1.0" ?><methodCall><methodName>'
|
19
|
+
@io << name
|
20
|
+
@io << '</methodName><params>'
|
21
|
+
params.each do |param|
|
22
|
+
@io << "<param>"
|
23
|
+
conv2value(param)
|
24
|
+
@io << "</param>"
|
25
|
+
end
|
26
|
+
@io << '</params></methodCall>'
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# escape some text
|
33
|
+
def text(txt)
|
34
|
+
cleaned = txt.dup
|
35
|
+
cleaned.gsub!(/&/, '&')
|
36
|
+
cleaned.gsub!(/</, '<')
|
37
|
+
cleaned.gsub!(/>/, '>')
|
38
|
+
cleaned
|
39
|
+
end
|
40
|
+
|
41
|
+
# write a tag with value tags around it
|
42
|
+
def write_tag(tag,value)
|
43
|
+
@io << "<value><#{tag}>#{text(value)}</#{tag}></value>"
|
44
|
+
end
|
45
|
+
|
46
|
+
# write teh tag directly without the value tags
|
47
|
+
def write_elem(tag, value)
|
48
|
+
@io << "<#{tag}>#{text(value)}</#{tag}>"
|
49
|
+
end
|
50
|
+
|
51
|
+
def write_with_children(tag,sub = nil)
|
52
|
+
@io << "<value><#{tag}>"
|
53
|
+
@io<< "<#{sub}>" if sub
|
54
|
+
yield if block_given?
|
55
|
+
@io<< "</#{sub}>" if sub
|
56
|
+
@io << "</#{tag}></value>"
|
57
|
+
end
|
58
|
+
|
59
|
+
# write base64 data to the output stream
|
60
|
+
def write_base64(data_stream)
|
61
|
+
write_with_children "base64" do
|
62
|
+
while (buf = data_stream.read(WRITE_BUFFER_SIZE)) != nil do
|
63
|
+
@io << [buf].pack('m').chop
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def conv2value(param)
|
69
|
+
case param
|
70
|
+
when Fixnum, Bignum
|
71
|
+
# XML-RPC's int is 32bit int, and Fixnum also may be beyond 32bit
|
72
|
+
if Config::ENABLE_BIGINT
|
73
|
+
write_tag "i4",param.to_s
|
74
|
+
else
|
75
|
+
if param >= -(2**31) and param <= (2**31-1)
|
76
|
+
write_tag "i4", param.to_s
|
77
|
+
else
|
78
|
+
raise "Bignum is too big! Must be signed 32-bit integer!"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
when TrueClass, FalseClass
|
82
|
+
write_tag "boolean", param ? "1" : "0"
|
83
|
+
|
84
|
+
when Symbol
|
85
|
+
write_tag "string", param.to_s
|
86
|
+
|
87
|
+
when String
|
88
|
+
write_tag "string", param
|
89
|
+
|
90
|
+
when NilClass
|
91
|
+
@io << "<nil/>" if Config::ENABLE_NIL_CREATE
|
92
|
+
raise "Wrong type NilClass. Not allowed!" unless Config::ENABLE_NIL_CREATE
|
93
|
+
|
94
|
+
when Float
|
95
|
+
write_tag "double", param.to_s
|
96
|
+
|
97
|
+
when Struct
|
98
|
+
write_with_children "struct" do
|
99
|
+
param.members.each do |key|
|
100
|
+
value = param[key]
|
101
|
+
@io << "<member>"
|
102
|
+
write_elem("name",key.to_s)
|
103
|
+
con2value(value)
|
104
|
+
@io << "</member>"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
when Hash
|
109
|
+
write_with_children "struct" do
|
110
|
+
param.each do |key, value|
|
111
|
+
@io << "<member>"
|
112
|
+
write_elem("name", key.to_s)
|
113
|
+
conv2value(value)
|
114
|
+
@io << "</member>"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
when Array
|
119
|
+
write_with_children "array","data" do
|
120
|
+
param.each do |elem|
|
121
|
+
conv2value(elem)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
when Time, Date, ::DateTime
|
126
|
+
write_tag "dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S")
|
127
|
+
|
128
|
+
when XMLRPC::DateTime
|
129
|
+
write_tag "dateTime.iso8601",format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a)
|
130
|
+
|
131
|
+
when XMLRPC::Base64
|
132
|
+
write_base64(param.to_io)
|
133
|
+
|
134
|
+
when IO, respond_to?(:read)
|
135
|
+
@had_a_stream = true
|
136
|
+
write_base64(param)
|
137
|
+
|
138
|
+
else
|
139
|
+
if XMLRPC::Config::ENABLE_MARSHALLING and param.class.included_modules.include? XMLRPC::Marshallable
|
140
|
+
# convert Ruby object into Hash
|
141
|
+
ret = {"___class___" => param.class.name}
|
142
|
+
param.instance_variables.each do |v|
|
143
|
+
name = v[1..-1]
|
144
|
+
val = param.instance_variable_get(v)
|
145
|
+
|
146
|
+
if val.nil?
|
147
|
+
ret[name] = val if XLMRPC::Config::ENABLE_NIL_CREATE
|
148
|
+
else
|
149
|
+
ret[name] = val
|
150
|
+
end
|
151
|
+
end
|
152
|
+
return conv2value(ret)
|
153
|
+
else
|
154
|
+
ok, pa = wrong_type(param)
|
155
|
+
if ok
|
156
|
+
return conv2value(pa)
|
157
|
+
else
|
158
|
+
raise "Wrong type!"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def wrong_type(value)
|
165
|
+
false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
=begin
|
2
|
+
= xmlrpc-streaming.rb
|
3
|
+
Copyright (C) 2011 by Sal Scotto (sal.scotto@gmail.com)
|
4
|
+
Released under the same terms of license as Ruby.
|
5
|
+
|
6
|
+
== XMLRPC::Client License
|
7
|
+
We override several key methods of the original XMLRPC::Client by Michael Neumann
|
8
|
+
Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
9
|
+
Released under the same terms of license as Ruby.
|
10
|
+
|
11
|
+
== Description
|
12
|
+
This class extends a few key methods of the XMLRPC::Client library
|
13
|
+
it treats base64 data differently. It allows you pass in any object
|
14
|
+
that supports a #read(bytes) method to be used instead of having to provide a
|
15
|
+
base64 encoded string for binary data. The problem it is trying to solve
|
16
|
+
is the case of sending a large binary blob over xmlrpc, which consumes large
|
17
|
+
amounts of ram to not only encoded and represent, but also to decode.
|
18
|
+
To use transparently. Please NOTE, we this module overwrite ignores the parser
|
19
|
+
settings in the config. We first try Nokogiri, if that isnt available we then
|
20
|
+
try LibXML, if that fails we fallback to REML. In any case we only
|
21
|
+
use StreamingParsers
|
22
|
+
|
23
|
+
require 'xmlrpc/client'
|
24
|
+
require 'xmlrpc-streaming'
|
25
|
+
|
26
|
+
== Instance Methods
|
27
|
+
--- XMLRPC::Client#set_debug( output_stream)
|
28
|
+
Invokes the call with debug output sent to the provided stream
|
29
|
+
|
30
|
+
== Differences
|
31
|
+
Any place you would normally get or send an XMLRPC::Base64 object
|
32
|
+
you can instead subsitute an object that supports #read(bytes) in it places
|
33
|
+
=end
|
34
|
+
|
35
|
+
|
36
|
+
require 'stringio'
|
37
|
+
require 'rbconfig'
|
38
|
+
require 'xmlrpc/base64'
|
39
|
+
require 'xmlrpc/client'
|
40
|
+
require 'tempfile'
|
41
|
+
require 'stream_writer'
|
42
|
+
require 'stream_parser_mixin'
|
43
|
+
|
44
|
+
# Add a to_io method to existing base64 class
|
45
|
+
module XMLRPC
|
46
|
+
class Base64
|
47
|
+
|
48
|
+
def initialize(str, state = :dec)
|
49
|
+
@state = state
|
50
|
+
@str = nil
|
51
|
+
@stream = false
|
52
|
+
case state
|
53
|
+
when :enc
|
54
|
+
if str.respond_to?(:read)
|
55
|
+
@str = str
|
56
|
+
@stream = true
|
57
|
+
else
|
58
|
+
@str = XMLRPC::Base64.decode(str)
|
59
|
+
end
|
60
|
+
when :dec
|
61
|
+
@str = str
|
62
|
+
if str.respond_to?(:read)
|
63
|
+
@stream = true
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise ArgumentError, "wrong argument; either :enc or :dec"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create an IO stream out of the data if it isnt a stream already
|
71
|
+
# side effect: will call rewind on the stream if it is rewindable
|
72
|
+
def to_io
|
73
|
+
if @stream
|
74
|
+
if @str.respond_to?(:rewind)
|
75
|
+
@str.rewind
|
76
|
+
end
|
77
|
+
@str
|
78
|
+
else
|
79
|
+
StringIO.new(@str)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get the decoded string
|
84
|
+
# if theunderlying string is a stream it will rewind before the call
|
85
|
+
def decoded
|
86
|
+
if @stream
|
87
|
+
if @str.respond_to?(:rewind)
|
88
|
+
@str.rewind
|
89
|
+
end
|
90
|
+
@str.read
|
91
|
+
else
|
92
|
+
@str
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Get the encoded string
|
97
|
+
# if the underlying string is a stream will call rewind first
|
98
|
+
def encoded
|
99
|
+
if @stream
|
100
|
+
if @str.respond_to?(:rewind)
|
101
|
+
@str.rewind
|
102
|
+
end
|
103
|
+
Base64.encode(@str.read)
|
104
|
+
else
|
105
|
+
Base64.encode(@str)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
module XMLRPC
|
112
|
+
class Client
|
113
|
+
|
114
|
+
=begin
|
115
|
+
set_debug_stream stream
|
116
|
+
will enable HTTP debuggin to the passed in stream
|
117
|
+
=end
|
118
|
+
def set_debug_stream(stream)
|
119
|
+
@debug_stream = stream
|
120
|
+
end
|
121
|
+
|
122
|
+
def call2_async(method, *args)
|
123
|
+
data = do_rpc(true,method,*args)
|
124
|
+
parser().parseMethodResponse(data)
|
125
|
+
end
|
126
|
+
|
127
|
+
def call2(method, *args)
|
128
|
+
data = do_rpc(false,method,*args)
|
129
|
+
parser().parseMethodResponse(data)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Stream our Request over to the server and save the results in a tempfile
|
135
|
+
def post_request(client,path,header,request_file,sink)
|
136
|
+
# Post via stream
|
137
|
+
req = Net::HTTP::Post.new(path,header)
|
138
|
+
req.body_stream = request_file
|
139
|
+
sink.binmode
|
140
|
+
resp = client.request(req) do |res|
|
141
|
+
res.read_body do |b|
|
142
|
+
sink.write(b)
|
143
|
+
end
|
144
|
+
sink.rewind
|
145
|
+
sink.size
|
146
|
+
sink
|
147
|
+
end
|
148
|
+
resp
|
149
|
+
end
|
150
|
+
|
151
|
+
def do_rpc(async,method,*args)
|
152
|
+
header = {
|
153
|
+
"User-Agent" => USER_AGENT,
|
154
|
+
"Content-Type" => "text/xml; charset=utf-8",
|
155
|
+
"Connection" => (async ? "close" : "keep-alive")
|
156
|
+
}
|
157
|
+
header["Cookie"] = @cookie if @cookie
|
158
|
+
header.update(@http_header_extra) if @http_header_extra
|
159
|
+
if @auth != nil
|
160
|
+
# add authorization header
|
161
|
+
header["Authorization"] = @auth
|
162
|
+
end
|
163
|
+
resp = nil
|
164
|
+
@http_last_response = nil
|
165
|
+
|
166
|
+
# Construct the request data
|
167
|
+
request_message = Tempfile.new('xmlrpc-stream-request')
|
168
|
+
data = Tempfile.new("xmlrpc-response-body")
|
169
|
+
# Use the streamwrite to write the temp file
|
170
|
+
writer = StreamWriter.new(request_message)
|
171
|
+
writer.methodCall(method,*args)
|
172
|
+
libxmlparser = nil
|
173
|
+
unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
174
|
+
libxmlparser = XMLRPC::XMLParser.parser_instance XMLRPC::XMLParser::LibXmlStreamParser
|
175
|
+
end
|
176
|
+
nokogiri = XMLRPC::XMLParser.parser_instance XMLRPC::XMLParser::NokogiriStreamParser
|
177
|
+
if nokogiri
|
178
|
+
set_parser(nokogiri)
|
179
|
+
elsif libxmlparser
|
180
|
+
set_parser(libxmlparser)
|
181
|
+
else # couldnt load one of the other parser so use REXML
|
182
|
+
set_parser(XMLParser::REXMLStreamParser2.new)
|
183
|
+
end
|
184
|
+
@parser.use_streams = writer.has_streams?
|
185
|
+
request_message.close
|
186
|
+
content_length = request_message.size
|
187
|
+
request_message.open
|
188
|
+
# get the data size of the request
|
189
|
+
header["Content-Length"] = content_length.to_s
|
190
|
+
|
191
|
+
# temp garbage will grow but GC will handle it over the course of
|
192
|
+
# the download/upload so you shouldnt get alocation errors with big files
|
193
|
+
|
194
|
+
resp = nil
|
195
|
+
if async
|
196
|
+
# use a new HTTP object for each call
|
197
|
+
Net::HTTP.version_1_2
|
198
|
+
http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
|
199
|
+
http.use_ssl = @use_ssl if @use_ssl
|
200
|
+
http.read_timeout = @timeout
|
201
|
+
http.open_timeout = @timeout
|
202
|
+
http.set_debug_output @debug_stream if @debug_stream
|
203
|
+
http.start {
|
204
|
+
resp = post_request(http,@path,header,request_message,data)
|
205
|
+
}
|
206
|
+
else
|
207
|
+
@http.set_debug_output @debug_stream if @debug_stream
|
208
|
+
@http.start if not @http.started?
|
209
|
+
resp = post_request(@http,@path,header,request_message,data)
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
@http_last_response = resp
|
214
|
+
|
215
|
+
if resp.code == "401"
|
216
|
+
# Authorization Required
|
217
|
+
data.unlink
|
218
|
+
raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}"
|
219
|
+
elsif resp.code[0,1] != "2"
|
220
|
+
data.unlink
|
221
|
+
raise "HTTP-Error: #{resp.code} #{resp.message}"
|
222
|
+
end
|
223
|
+
|
224
|
+
ct = parse_content_type(resp["Content-Type"]).first
|
225
|
+
# Some implmentations return application/xml, for faults so lets allow
|
226
|
+
# them for poor implmentations of servers
|
227
|
+
if ct !~ /\/xml$/
|
228
|
+
data.unlink
|
229
|
+
raise "Wrong content-type (received '#{ct}' but expected 'text/xml') *Use set_debug_stream for details"
|
230
|
+
end
|
231
|
+
|
232
|
+
expected = resp["Content-Length"] || "<unknown>"
|
233
|
+
if data.nil? or data.size == 0
|
234
|
+
s = data.size
|
235
|
+
data.unlink
|
236
|
+
raise "Wrong size. Was #{s}, should be #{expected} #{data.read}"
|
237
|
+
elsif expected != "<unknown>" and expected.to_i != data.size and resp["Transfer-Encoding"].nil?
|
238
|
+
s = data.size
|
239
|
+
data.unlink
|
240
|
+
raise "Wrong size. Was #{s}, should be #{expected} #{data.read}"
|
241
|
+
end
|
242
|
+
|
243
|
+
# Copy any cookies sent
|
244
|
+
set_cookies = resp.get_fields("Set-Cookie")
|
245
|
+
if set_cookies and !set_cookies.empty?
|
246
|
+
require 'webrick/cookie'
|
247
|
+
@cookie = set_cookies.collect do |set_cookie|
|
248
|
+
cookie = WEBrick::Cookie.parse_set_cookie(set_cookie)
|
249
|
+
WEBrick::Cookie.new(cookie.name, cookie.value).to_s
|
250
|
+
end.join("; ")
|
251
|
+
end
|
252
|
+
# Return the TempFile
|
253
|
+
return data
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'xmlrpc-streaming'
|
5
|
+
# Requires supporting files with custom matchers and macros, etc,
|
6
|
+
# in ./support/ and its subdirectories.
|
7
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
8
|
+
|
9
|
+
RSpec::Matchers.define :answer_to do |method|
|
10
|
+
match do |obj|
|
11
|
+
obj.respond_to?(method)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "XmlrpcStreaming" do
|
4
|
+
|
5
|
+
it "should create an instance with has values" do
|
6
|
+
client = XMLRPC::Client.new2 "http://me@test.com/RPC2"
|
7
|
+
client.user.should == "me"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should send a body async" do
|
11
|
+
client = XMLRPC::Client.new2 "http://time.xmlrpc.com/RPC2"
|
12
|
+
proxy = client.proxy_async("currentTime")
|
13
|
+
t = proxy.getCurrentTime.to_time
|
14
|
+
t.should_not == Time.now
|
15
|
+
end
|
16
|
+
it "should send a body sync" do
|
17
|
+
client = XMLRPC::Client.new2 "http://time.xmlrpc.com/RPC2"
|
18
|
+
proxy = client.proxy("currentTime")
|
19
|
+
proxy.getCurrentTime.to_time.should_not == Time.now
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should add a to_io method to base64 class" do
|
23
|
+
base64 = XMLRPC::Base64.new("dkjfkdjhfkdjhfkdhfkjdhkj")
|
24
|
+
base64.to_io.should answer_to(:read)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should add a base64 initializer that handles IO objects" do
|
28
|
+
base64 = XMLRPC::Base64.new(File.open(File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')))
|
29
|
+
base64.should_not be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should set base64 to_io to return an IO object when given one" do
|
33
|
+
base64 = XMLRPC::Base64.new(File.open(File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')))
|
34
|
+
base64.to_io.should be_kind_of(IO)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should call wordpress for testing" do
|
38
|
+
client = XMLRPC::Client.new2 "http://salsxmltest.wordpress.com/xmlrpc.php"
|
39
|
+
m = client.call "wp.getUsersBlogs", "washu214", "abc123", File.open(File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb'))
|
40
|
+
m.should_not be_empty
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should upload a base64 object to wordpress" do
|
44
|
+
pending("Create a blog post with a large image")
|
45
|
+
#blogid 28060656
|
46
|
+
#https://salsxmltest.wordpress.com/xmlrpc.php
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should encode data the same as the original encoder" do
|
50
|
+
creater = XMLRPC::Create.new
|
51
|
+
io_block = ''
|
52
|
+
streamer = XMLRPC::StreamWriter.new io_block
|
53
|
+
doc = creater.methodCall "test", [1,2,3], { :key=> 1, :d => 'v', :x => ['a','b'] }
|
54
|
+
# Strip the \n off of the document
|
55
|
+
doc.chop!
|
56
|
+
streamer.methodCall "test", [1,2,3], { :key=> 1, :d => 'v', :x => ['a','b'] }
|
57
|
+
io_block.should == doc
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should encode data the same as the original with a time object" do
|
61
|
+
creater = XMLRPC::Create.new
|
62
|
+
io_block = ''
|
63
|
+
streamer = XMLRPC::StreamWriter.new io_block
|
64
|
+
doc = creater.methodCall "test", Time.now
|
65
|
+
# Strip the \n off of the document
|
66
|
+
doc.chop!
|
67
|
+
streamer.methodCall "test", Time.now
|
68
|
+
io_block.should == doc
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should encode data the same as the original with a Date object" do
|
72
|
+
creater = XMLRPC::Create.new
|
73
|
+
io_block = ''
|
74
|
+
streamer = XMLRPC::StreamWriter.new io_block
|
75
|
+
doc = creater.methodCall "test", Date.today
|
76
|
+
# Strip the \n off of the document
|
77
|
+
doc.chop!
|
78
|
+
streamer.methodCall "test", Date.today
|
79
|
+
io_block.should == doc
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should encode data the same as the original with a Marshalable object" do
|
83
|
+
klass = Class.new do
|
84
|
+
include XMLRPC::Marshallable
|
85
|
+
attr_accessor :name, :date
|
86
|
+
end
|
87
|
+
Object.const_set 'Testable', klass
|
88
|
+
creater = XMLRPC::Create.new
|
89
|
+
io_block = ''
|
90
|
+
streamer = XMLRPC::StreamWriter.new io_block
|
91
|
+
doc = creater.methodCall "test", Testable.new
|
92
|
+
# Strip the \n off of the document
|
93
|
+
doc.chop!
|
94
|
+
streamer.methodCall "test", Testable.new
|
95
|
+
io_block.should == doc
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should encode data the same as the original with a Base64 object" do
|
99
|
+
creater = XMLRPC::Create.new
|
100
|
+
io_block = ''
|
101
|
+
b64 = XMLRPC::Base64.new 'testing junk'
|
102
|
+
streamer = XMLRPC::StreamWriter.new io_block
|
103
|
+
doc = creater.methodCall "test", b64
|
104
|
+
# Strip the \n out as the original will include a \n after a base64 object
|
105
|
+
doc.gsub!(/\n/,'')
|
106
|
+
streamer.methodCall "test", b64
|
107
|
+
io_block.should == doc
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should encode data the same as the original with a Base64 object" do
|
111
|
+
creater = XMLRPC::Create.new
|
112
|
+
io_block = ''
|
113
|
+
b64 = XMLRPC::Base64.new StringIO.new 'testing junk'
|
114
|
+
streamer = XMLRPC::StreamWriter.new io_block
|
115
|
+
doc = creater.methodCall "test", b64
|
116
|
+
# Strip the \n out as the original will include a \n after a base64 object
|
117
|
+
doc.gsub!(/\n/,'')
|
118
|
+
streamer.methodCall "test", b64
|
119
|
+
io_block.should == doc
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should upload a large binary object and not run out of memory" do
|
123
|
+
pending("add large file upload test")
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{xmlrpc-streaming}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sal Scotto"]
|
12
|
+
s.date = %q{2011-11-15}
|
13
|
+
s.description = %q{The built in xmlrpc client doesnt handle large binary data well, this client addresses the problem}
|
14
|
+
s.email = %q{sal.scotto@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"lib/stream_parser_mixin.rb",
|
29
|
+
"lib/stream_writer.rb",
|
30
|
+
"lib/xmlrpc-streaming.rb",
|
31
|
+
"spec/spec_helper.rb",
|
32
|
+
"spec/xmlrpc-streaming_spec.rb",
|
33
|
+
"xmlrpc-streaming.gemspec"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/washu/xmlrpc-streaming}
|
36
|
+
s.licenses = ["MIT"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.7.2}
|
39
|
+
s.summary = %q{Xmlrpc client that uses streaming for binary data}
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
s.specification_version = 3
|
43
|
+
|
44
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
46
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
47
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
48
|
+
s.add_development_dependency(%q<libxml4r>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<nokogiri>, [">= 0"])
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
52
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
53
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
54
|
+
s.add_dependency(%q<libxml4r>, [">= 0"])
|
55
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
59
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
60
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
61
|
+
s.add_dependency(%q<libxml4r>, [">= 0"])
|
62
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xmlrpc-streaming
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sal Scotto
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-15 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &24667536 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.3.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *24667536
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: bundler
|
27
|
+
requirement: &24667200 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *24667200
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: jeweler
|
38
|
+
requirement: &24666864 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.6.4
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *24666864
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: libxml4r
|
49
|
+
requirement: &24666564 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *24666564
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: nokogiri
|
60
|
+
requirement: &24666264 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *24666264
|
69
|
+
description: The built in xmlrpc client doesnt handle large binary data well, this
|
70
|
+
client addresses the problem
|
71
|
+
email: sal.scotto@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files:
|
75
|
+
- LICENSE.txt
|
76
|
+
- README.rdoc
|
77
|
+
files:
|
78
|
+
- .document
|
79
|
+
- .rspec
|
80
|
+
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.rdoc
|
84
|
+
- Rakefile
|
85
|
+
- VERSION
|
86
|
+
- lib/stream_parser_mixin.rb
|
87
|
+
- lib/stream_writer.rb
|
88
|
+
- lib/xmlrpc-streaming.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
- spec/xmlrpc-streaming_spec.rb
|
91
|
+
- xmlrpc-streaming.gemspec
|
92
|
+
homepage: http://github.com/washu/xmlrpc-streaming
|
93
|
+
licenses:
|
94
|
+
- MIT
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
hash: -927155007
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 1.7.2
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: Xmlrpc client that uses streaming for binary data
|
120
|
+
test_files: []
|