em-xmlrpc-client 1.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ vendor/bundle
6
+ doc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in em-xmlrpc-client.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ em-xmlrpc-client (1.0.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ addressable (2.2.6)
10
+ crack (0.1.8)
11
+ em-http-request (0.3.0)
12
+ addressable (>= 2.0.0)
13
+ escape_utils
14
+ eventmachine (>= 0.12.9)
15
+ escape_utils (0.2.3)
16
+ eventmachine (0.12.10)
17
+ rr (1.0.3)
18
+ webmock (1.6.4)
19
+ addressable (~> 2.2, > 2.2.5)
20
+ crack (>= 0.1.7)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ em-http-request
27
+ em-xmlrpc-client!
28
+ eventmachine
29
+ rr
30
+ webmock
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Christopher J. Bottaro
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,31 @@
1
+ = em-xmlrpc-client
2
+
3
+ This gem monkey patches Ruby's built in XMLRPC::Client to be EventMachine + Fiber aware. If you are not using EventMachine (or the reactor isn't running), then it will fallback to using XMLRPC::Client's default implemenation (using Net::HTTP).
4
+
5
+ == Example
6
+
7
+ require "eventmachine"
8
+ require "em-xmlrpc-client"
9
+
10
+ EM.run do
11
+ Fiber.new do
12
+ client = XMLRPC::Client.new2("http://blah.com/api")
13
+ result = client.call("someMethod", "arg1", 123) # Uses em-http-request.
14
+ end.resume
15
+ end
16
+
17
+
18
+ client = XMLRPC::Client.new2("http://blah.com/api")
19
+ result = client.call("someMethod", "arg1", 123) # Uses Net::HTTP like normal.
20
+
21
+ == Dependencies
22
+
23
+ {em-http-request}[https://github.com/igrigorik/em-http-request] is required if you want to use the EventMachine + Fiber aware implementation.
24
+
25
+ == Author
26
+
27
+ Christopher J. Bottaro
28
+
29
+ {\https://github.com/cjbottaro}[https://github.com/cjbottaro]
30
+
31
+ @cjbottaro
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "em-xmlrpc-client/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "em-xmlrpc-client"
7
+ s.version = Em::Xmlrpc::Client::VERSION
8
+ s.authors = ["Christopher J. Bottaro"]
9
+ s.email = ["cjbottaro@alumni.cs.utexas.edu"]
10
+ s.homepage = ""
11
+ s.summary = %q{Evented and fibered XMLRPC Client}
12
+ s.description = %q{Monkey patches Ruby's standard XMLRPC Client to use EventMachine and fibers}
13
+
14
+ s.rubyforge_project = "em-xmlrpc-client"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "eventmachine"
23
+ s.add_development_dependency "em-http-request"
24
+ s.add_development_dependency "webmock"
25
+ s.add_development_dependency "rr"
26
+
27
+ # s.add_runtime_dependency "rest-client"
28
+ end
@@ -0,0 +1,7 @@
1
+ module Em
2
+ module Xmlrpc
3
+ module Client
4
+ VERSION = "1.0.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,189 @@
1
+ require "em-xmlrpc-client/version"
2
+ require "xmlrpc/client"
3
+
4
+ module XMLRPC
5
+ class Client
6
+
7
+ def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil,
8
+ user=nil, password=nil, use_ssl=nil, timeout=nil)
9
+
10
+ @http_header_extra = nil
11
+ @http_last_response = nil
12
+ @cookie = nil
13
+
14
+ @host = host || "localhost"
15
+ @path = path || "/RPC2"
16
+ @proxy_host = proxy_host
17
+ @proxy_port = proxy_port
18
+ @proxy_host ||= 'localhost' if @proxy_port != nil
19
+ @proxy_port ||= 8080 if @proxy_host != nil
20
+ @use_ssl = use_ssl || false
21
+ @timeout = timeout || 30
22
+
23
+ if use_ssl
24
+ require "net/https"
25
+ @port = port || 443
26
+ else
27
+ @port = port || 80
28
+ end
29
+
30
+ @user, @password = user, password
31
+
32
+ set_auth
33
+
34
+ # convert ports to integers
35
+ @port = @port.to_i if @port != nil
36
+ @proxy_port = @proxy_port.to_i if @proxy_port != nil
37
+
38
+ if defined?(EM) and EM.reactor_running?
39
+ require "em-http"
40
+ require "ostruct"
41
+ require "fiber"
42
+ else
43
+ # HTTP object for synchronous calls
44
+ Net::HTTP.version_1_2
45
+ @http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
46
+ @http.use_ssl = @use_ssl if @use_ssl
47
+ @http.read_timeout = @timeout
48
+ @http.open_timeout = @timeout
49
+ end
50
+
51
+ @parser = nil
52
+ @create = nil
53
+ end
54
+
55
+ def do_rpc(request, async=false)
56
+ header = {
57
+ "User-Agent" => USER_AGENT,
58
+ "Content-Type" => "text/xml; charset=utf-8",
59
+ "Content-Length" => request.size.to_s,
60
+ "Connection" => (async ? "close" : "keep-alive")
61
+ }
62
+
63
+ header["Cookie"] = @cookie if @cookie
64
+ header.update(@http_header_extra) if @http_header_extra
65
+
66
+ if @auth != nil
67
+ # add authorization header
68
+ header["Authorization"] = @auth
69
+ end
70
+
71
+ @http_last_response = nil
72
+
73
+ if defined?(EM) and EM.reactor_running?
74
+ resp = do_rpc_em_http(async, request, header)
75
+ else
76
+ resp = do_rpc_net_http(async, request, header)
77
+ end
78
+
79
+ @http_last_response = resp
80
+
81
+ data = resp.body
82
+
83
+ if resp.code == "401"
84
+ # Authorization Required
85
+ raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}"
86
+ elsif resp.code[0,1] != "2"
87
+ raise "HTTP-Error: #{resp.code} #{resp.message}"
88
+ end
89
+
90
+ ct = parse_content_type(resp["Content-Type"]).first
91
+ if ct != "text/xml"
92
+ if ct == "text/html"
93
+ raise "Wrong content-type (received '#{ct}' but expected 'text/xml'): \n#{data}"
94
+ else
95
+ raise "Wrong content-type (received '#{ct}' but expected 'text/xml')"
96
+ end
97
+ end
98
+
99
+ expected = resp["Content-Length"] || "<unknown>"
100
+ if data.nil? or data.size == 0
101
+ raise "Wrong size. Was #{data.size}, should be #{expected}"
102
+ elsif expected != "<unknown>" and expected.to_i != data.size and resp["Transfer-Encoding"].nil?
103
+ raise "Wrong size. Was #{data.size}, should be #{expected}"
104
+ end
105
+
106
+ set_cookies = resp.get_fields("Set-Cookie")
107
+ if set_cookies and !set_cookies.empty?
108
+ require 'webrick/cookie'
109
+ @cookie = set_cookies.collect do |set_cookie|
110
+ cookie = WEBrick::Cookie.parse_set_cookie(set_cookie)
111
+ WEBrick::Cookie.new(cookie.name, cookie.value).to_s
112
+ end.join("; ")
113
+ end
114
+
115
+ return data
116
+ end
117
+
118
+ def timeout=(new_timeout)
119
+ @timeout = new_timeout
120
+ unless defined?(EM) and EM.reactor_running?
121
+ @http.read_timeout = @timeout
122
+ @http.open_timeout = @timeout
123
+ end
124
+ end
125
+
126
+ def do_rpc_em_http(async, request, header)
127
+ fiber = Fiber.current
128
+ http = EM::HttpRequest.new("http://#{@host}:#{@port}#{@path}").post :body => request, :timeout => @timeout
129
+ http.callback{ fiber.resume }
130
+ http.errback do
131
+ # Unfortunately, we can't determine exactly what the error is using EventMachine < 1.0.
132
+ error = RuntimeError.new("connection or timeout error")
133
+ fiber.resume(error)
134
+ end
135
+
136
+ e = Fiber.yield and raise(e)
137
+
138
+ # Ducktype our response object.
139
+ resp = OpenStruct.new :code => http.response_header.http_status.to_s,
140
+ :message => http.response_header.http_reason,
141
+ :header => http.response_header.to_hash,
142
+ :body => http.response.to_s
143
+
144
+ resp.header["Content-Type"] = resp.header["CONTENT_TYPE"]
145
+ resp.header["Content-Length"] = resp.header["CONTENT_LENGTH"]
146
+ resp.header["Transfer-Encoding"] = resp.header["TRANSFER_ENCODING"]
147
+ resp.header["Set-Cookie"] = resp.header["SET_COOKIE"]
148
+
149
+ def resp.[](name)
150
+ header[name]
151
+ end
152
+ def resp.get_fields(name)
153
+ value = header[name]
154
+ value and [value].flatten
155
+ end
156
+
157
+ resp
158
+ end
159
+
160
+ def do_rpc_net_http(async, request, header)
161
+ resp = nil
162
+
163
+ if async
164
+ # use a new HTTP object for each call
165
+ Net::HTTP.version_1_2
166
+ http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
167
+ http.use_ssl = @use_ssl if @use_ssl
168
+ http.read_timeout = @timeout
169
+ http.open_timeout = @timeout
170
+
171
+ # post request
172
+ http.start {
173
+ resp = http.post2(@path, request, header)
174
+ }
175
+ else
176
+ # reuse the HTTP object for each call => connection alive is possible
177
+ # we must start connection explicitely first time so that http.request
178
+ # does not assume that we don't want keepalive
179
+ @http.start if not @http.started?
180
+
181
+ # post request
182
+ resp = @http.post2(@path, request, header)
183
+ end
184
+
185
+ resp
186
+ end
187
+
188
+ end
189
+ end
@@ -0,0 +1,53 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Fri, 29 Jul 2011 17:42:24 GMT
3
+ Server: Apache
4
+ X-Powered-By: PHP/5.2.11
5
+ Content-Length: 988
6
+ Connection: close
7
+ Content-Type: text/xml; charset=UTF-8
8
+
9
+ <?xml version="1.0" encoding="UTF-8"?>
10
+ <methodResponse>
11
+ <params>
12
+ <param>
13
+ <value><struct>
14
+ <member><name>zoneId</name>
15
+ <value><int>55</int></value>
16
+ </member>
17
+ <member><name>publisherId</name>
18
+ <value><int>6</int></value>
19
+ </member>
20
+ <member><name>zoneName</name>
21
+ <value><string>Telegraph ATW</string></value>
22
+ </member>
23
+ <member><name>type</name>
24
+ <value><int>0</int></value>
25
+ </member>
26
+ <member><name>width</name>
27
+ <value><int>150</int></value>
28
+ </member>
29
+ <member><name>height</name>
30
+ <value><int>150</int></value>
31
+ </member>
32
+ <member><name>capping</name>
33
+ <value><int>0</int></value>
34
+ </member>
35
+ <member><name>sessionCapping</name>
36
+ <value><int>0</int></value>
37
+ </member>
38
+ <member><name>block</name>
39
+ <value><int>0</int></value>
40
+ </member>
41
+ <member><name>comments</name>
42
+ <value><string></string></value>
43
+ </member>
44
+ <member><name>append</name>
45
+ <value><string></string></value>
46
+ </member>
47
+ <member><name>prepend</name>
48
+ <value><string></string></value>
49
+ </member>
50
+ </struct></value>
51
+ </param>
52
+ </params>
53
+ </methodResponse>
data/test/helper.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'eventmachine'
15
+ require 'em-http'
16
+ require 'webmock/test_unit' # This needs to come after requiring 'em-http'.
17
+ require 'rr'
18
+ require 'em-xmlrpc-client'
19
+
20
+ class Test::Unit::TestCase
21
+ include RR::Adapters::TestUnit
22
+ end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ class TestEmXmlrpcClient < Test::Unit::TestCase
4
+
5
+ def test_em_http
6
+ host = "localhost"
7
+ path = "api/v2/xmlrpc"
8
+ stub_request(:post, "http://#{host}/#{path}").to_return(File.read("test/data/response"))
9
+ expected = {"zoneId"=>55, "publisherId"=>6, "zoneName"=>"Telegraph ATW", "type"=>0, "width"=>150, "height"=>150, "capping"=>0, "sessionCapping"=>0, "block"=>0, "comments"=>"", "append"=>"", "prepend"=>""}
10
+
11
+ EM.run do
12
+ Fiber.new do
13
+ client = XMLRPC::Client.new2("http://#{host}/#{path}")
14
+ mock.proxy(client).do_rpc_em_http(anything, anything, anything)
15
+ mock(client).do_rpc_net_http.never
16
+ actual = client.call("ox.getZone", "phpads4e32f100507466.86347358", 54)
17
+ assert_equal expected, actual
18
+ EM.stop
19
+ end.resume
20
+ end
21
+ end
22
+
23
+ def test_net_http
24
+ host = "localhost"
25
+ path = "api/v2/xmlrpc"
26
+ stub_request(:post, "http://#{host}/#{path}").to_return(File.read("test/data/response"))
27
+ expected = {"zoneId"=>55, "publisherId"=>6, "zoneName"=>"Telegraph ATW", "type"=>0, "width"=>150, "height"=>150, "capping"=>0, "sessionCapping"=>0, "block"=>0, "comments"=>"", "append"=>"", "prepend"=>""}
28
+
29
+ client = XMLRPC::Client.new2("http://#{host}/#{path}")
30
+ mock(client).do_rpc_em_http.never
31
+ mock.proxy(client).do_rpc_net_http(anything, anything, anything)
32
+ actual = client.call("ox.getZone", "phpads4e32f100507466.86347358", 54)
33
+ assert_equal expected, actual
34
+ end
35
+
36
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-xmlrpc-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christopher J. Bottaro
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: &70204197997120 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70204197997120
25
+ - !ruby/object:Gem::Dependency
26
+ name: em-http-request
27
+ requirement: &70204197996700 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70204197996700
36
+ - !ruby/object:Gem::Dependency
37
+ name: webmock
38
+ requirement: &70204197996280 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70204197996280
47
+ - !ruby/object:Gem::Dependency
48
+ name: rr
49
+ requirement: &70204197995860 !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: *70204197995860
58
+ description: Monkey patches Ruby's standard XMLRPC Client to use EventMachine and
59
+ fibers
60
+ email:
61
+ - cjbottaro@alumni.cs.utexas.edu
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .document
67
+ - .gitignore
68
+ - Gemfile
69
+ - Gemfile.lock
70
+ - LICENSE.txt
71
+ - README.rdoc
72
+ - Rakefile
73
+ - VERSION
74
+ - em-xmlrpc-client.gemspec
75
+ - lib/em-xmlrpc-client.rb
76
+ - lib/em-xmlrpc-client/version.rb
77
+ - test/data/response
78
+ - test/helper.rb
79
+ - test/test_em-xmlrpc-client.rb
80
+ homepage: ''
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ segments:
93
+ - 0
94
+ hash: -159327133412347017
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: -159327133412347017
104
+ requirements: []
105
+ rubyforge_project: em-xmlrpc-client
106
+ rubygems_version: 1.8.11
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Evented and fibered XMLRPC Client
110
+ test_files:
111
+ - test/data/response
112
+ - test/helper.rb
113
+ - test/test_em-xmlrpc-client.rb