rest 1.2.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +6 -2
- data/README.md +27 -2
- data/Rakefile +1 -0
- data/lib/rest/client.rb +66 -43
- data/lib/rest/errors.rb +43 -0
- data/lib/rest/version.rb +1 -1
- data/lib/rest/wrappers/base_wrapper.rb +44 -20
- data/lib/rest/wrappers/excon_wrapper.rb +111 -0
- data/lib/rest/wrappers/net_http_persistent_wrapper.rb +37 -21
- data/lib/rest/wrappers/rest_client_wrapper.rb +12 -6
- data/lib/rest/wrappers/typhoeus_wrapper.rb +34 -20
- data/rest.gemspec +3 -1
- data/test/test_base.rb +9 -4
- data/test/test_performance.rb +5 -4
- data/test/test_rest.rb +68 -5
- data/test/tmp.rb +24 -11
- metadata +37 -3
data/Gemfile.lock
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rest (1.2.
|
4
|
+
rest (1.2.2)
|
5
|
+
net-http-persistent
|
5
6
|
rest-client (>= 0.3.0)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
11
|
+
excon (0.14.3)
|
10
12
|
ffi (1.0.11)
|
11
13
|
mime-types (1.19)
|
14
|
+
minitest (3.2.0)
|
12
15
|
net-http-persistent (2.7)
|
13
16
|
quicky (0.0.1)
|
14
17
|
rake (0.9.2.2)
|
@@ -24,7 +27,8 @@ PLATFORMS
|
|
24
27
|
ruby
|
25
28
|
|
26
29
|
DEPENDENCIES
|
27
|
-
|
30
|
+
excon
|
31
|
+
minitest
|
28
32
|
quicky
|
29
33
|
rake
|
30
34
|
rest!
|
data/README.md
CHANGED
@@ -1,8 +1,22 @@
|
|
1
1
|
Rest Wrapper
|
2
2
|
-------------
|
3
3
|
|
4
|
-
HTTP/REST client wrapper that provides a standard interface for making http requests using different http
|
5
|
-
If no client is specified it will choose the best
|
4
|
+
HTTP/REST client wrapper that provides a standard interface for making http requests using different http
|
5
|
+
clients. If no client is specified **it will choose the best http client** you have installed based on
|
6
|
+
our performance tests.
|
7
|
+
|
8
|
+
Features
|
9
|
+
========
|
10
|
+
|
11
|
+
* All clients behave exactly the same:
|
12
|
+
* Same error behavior
|
13
|
+
* Same 30X redirect behavior
|
14
|
+
* Same response object methods
|
15
|
+
* Same way to access and manipulate requests and responses such as body, headers, code, etc.
|
16
|
+
* Chooses best client you have installed on your system based on what we have found performs the best.
|
17
|
+
* Currently net_http_persistent and typhoeus are nearly the same, but since net_http_persistent doesn't have a binary
|
18
|
+
dependency, it wins.
|
19
|
+
|
6
20
|
|
7
21
|
Getting Started
|
8
22
|
==============
|
@@ -70,3 +84,14 @@ The response object you get back will always be consistent and will have the fol
|
|
70
84
|
response.code
|
71
85
|
response.body
|
72
86
|
|
87
|
+
|
88
|
+
Exceptions
|
89
|
+
======
|
90
|
+
|
91
|
+
If it didn't get a response for whatever reason, you will get a Rest::ClientError
|
92
|
+
|
93
|
+
If status code is 40X or 50X, it will raise an exception with the following methods.
|
94
|
+
|
95
|
+
err.code
|
96
|
+
err.response (which has body: err.response.body)
|
97
|
+
|
data/Rakefile
CHANGED
data/lib/rest/client.rb
CHANGED
@@ -5,26 +5,12 @@ require 'logger'
|
|
5
5
|
# The purpose of this is so that users who can't install binaries easily (like windoze users)
|
6
6
|
# can have fallbacks that work.
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
class ClientError < StandardError
|
11
|
-
|
12
|
-
end
|
8
|
+
require_relative 'errors'
|
13
9
|
|
14
|
-
|
15
|
-
class TimeoutError < ClientError
|
16
|
-
def initialize(msg=nil)
|
17
|
-
msg ||= "HTTP Request Timed out."
|
18
|
-
super(msg)
|
19
|
-
end
|
20
|
-
end
|
10
|
+
module Rest
|
21
11
|
|
22
12
|
require_relative 'wrappers/base_wrapper'
|
23
13
|
|
24
|
-
#def self.puts(s)
|
25
|
-
# Kernel.puts("rest gem: #{s}")
|
26
|
-
#end
|
27
|
-
|
28
14
|
class Client
|
29
15
|
|
30
16
|
attr_accessor :options, :logger, :gem
|
@@ -42,7 +28,11 @@ module Rest
|
|
42
28
|
choose_best_gem()
|
43
29
|
end
|
44
30
|
|
45
|
-
if @gem == :
|
31
|
+
if @gem == :excon
|
32
|
+
require_relative 'wrappers/excon_wrapper'
|
33
|
+
@wrapper = Rest::Wrappers::ExconWrapper.new(self)
|
34
|
+
@logger.debug "Using excon gem."
|
35
|
+
elsif @gem == :typhoeus
|
46
36
|
require_relative 'wrappers/typhoeus_wrapper'
|
47
37
|
@wrapper = Rest::Wrappers::TyphoeusWrapper.new
|
48
38
|
@logger.debug "Using typhoeus gem."
|
@@ -60,13 +50,13 @@ module Rest
|
|
60
50
|
def choose_best_gem
|
61
51
|
begin
|
62
52
|
raise LoadError
|
63
|
-
|
64
|
-
|
53
|
+
require 'typhoeus'
|
54
|
+
@gem = :typhoeus
|
65
55
|
rescue LoadError => ex
|
66
56
|
begin
|
67
57
|
# try net-http-persistent
|
68
|
-
|
69
|
-
|
58
|
+
require 'net/http/persistent'
|
59
|
+
@gem = :net_http_persistent
|
70
60
|
rescue LoadError => ex
|
71
61
|
end
|
72
62
|
end
|
@@ -78,7 +68,7 @@ module Rest
|
|
78
68
|
|
79
69
|
def get(url, req_hash={})
|
80
70
|
res = nil
|
81
|
-
perform_op do
|
71
|
+
res = perform_op(:get, req_hash) do
|
82
72
|
res = @wrapper.get(url, req_hash)
|
83
73
|
end
|
84
74
|
return res
|
@@ -86,38 +76,71 @@ module Rest
|
|
86
76
|
|
87
77
|
# This will attempt to perform the operation with an exponential backoff on 503 errors.
|
88
78
|
# Amazon services throw 503
|
89
|
-
|
90
|
-
|
79
|
+
# todo: just make perform_op a method and have it call the wrapper. The block is a waste now.
|
80
|
+
def perform_op(method, req_hash, options={}, &blk)
|
81
|
+
set_defaults(options)
|
82
|
+
max_retries = options[:max_retries] || 5
|
83
|
+
max_follows = options[:max_follows] || 10
|
84
|
+
if options[:follow_count] && options[:follow_count] >= max_follows
|
85
|
+
raise Rest::RestError "Too many follows. #{options[:follow_count]}"
|
86
|
+
end
|
91
87
|
current_retry = 0
|
88
|
+
current_follow = 0
|
92
89
|
success = false
|
93
90
|
res = nil
|
94
|
-
while current_retry < max_retries do
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
91
|
+
while current_retry < max_retries && current_follow < max_follows do
|
92
|
+
begin
|
93
|
+
res = yield blk
|
94
|
+
res.tries = current_retry + 1
|
95
|
+
if res.code >= 300 && res.code < 400
|
96
|
+
# try new location
|
97
|
+
#p res.headers
|
98
|
+
loc = res.headers["location"]
|
99
|
+
@logger.debug "#{res.code} Received. Trying new location: #{loc}"
|
100
|
+
if loc.nil?
|
101
|
+
raise InvalidResponseError.new("No location header received with #{res.code} status code!")
|
102
|
+
end
|
103
|
+
# options.merge({:max_follows=>options[:max_follows-1]}
|
104
|
+
options[:follow_count] ||= 0
|
105
|
+
options[:follow_count] += 1
|
106
|
+
res = perform_op(method, req_hash, options) do
|
107
|
+
res = @wrapper.send(method, loc, req_hash)
|
108
|
+
end
|
109
|
+
#puts 'X: ' + res.inspect
|
110
|
+
return res
|
111
|
+
end
|
112
|
+
# If it's here, then it's all good
|
109
113
|
break
|
114
|
+
rescue Rest::HttpError => ex
|
115
|
+
if ex.code == 503
|
116
|
+
pow = (4 ** (current_retry)) * 100 # milliseconds
|
117
|
+
#puts 'pow=' + pow.to_s
|
118
|
+
s = Random.rand * pow
|
119
|
+
#puts 's=' + s.to_s
|
120
|
+
sleep_secs = 1.0 * s / 1000.0
|
121
|
+
#puts 'sleep for ' + sleep_secs.to_s
|
122
|
+
current_retry += 1
|
123
|
+
@logger.debug "#{ex.code} Received. Retrying #{current_retry} out of #{max_retries} max in #{sleep_secs} seconds."
|
124
|
+
sleep sleep_secs
|
125
|
+
else
|
126
|
+
raise ex
|
127
|
+
end
|
110
128
|
end
|
111
129
|
end
|
112
130
|
res
|
113
131
|
end
|
114
132
|
|
133
|
+
def set_defaults(options)
|
134
|
+
options[:max_retries] ||= (@options[:max_retries] || 5)
|
135
|
+
options[:max_follows] ||= (@options[:max_follows] || 10)
|
136
|
+
end
|
137
|
+
|
115
138
|
# req_hash options:
|
116
139
|
# - :body => post body
|
117
140
|
#
|
118
141
|
def post(url, req_hash={})
|
119
142
|
res = nil
|
120
|
-
perform_op do
|
143
|
+
res = perform_op(:post, req_hash) do
|
121
144
|
res = @wrapper.post(url, req_hash)
|
122
145
|
end
|
123
146
|
return res
|
@@ -125,7 +148,7 @@ module Rest
|
|
125
148
|
|
126
149
|
def put(url, req_hash={})
|
127
150
|
res = nil
|
128
|
-
perform_op do
|
151
|
+
res = perform_op(:put, req_hash) do
|
129
152
|
res = @wrapper.put(url, req_hash)
|
130
153
|
end
|
131
154
|
return res
|
@@ -133,7 +156,7 @@ module Rest
|
|
133
156
|
|
134
157
|
def delete(url, req_hash={})
|
135
158
|
res = nil
|
136
|
-
perform_op do
|
159
|
+
res = perform_op(:delete, req_hash) do
|
137
160
|
res = @wrapper.delete(url, req_hash)
|
138
161
|
end
|
139
162
|
return res
|
@@ -141,7 +164,7 @@ module Rest
|
|
141
164
|
|
142
165
|
def post_file(url, req_hash={})
|
143
166
|
res = nil
|
144
|
-
perform_op do
|
167
|
+
res = perform_op(:post_file, req_hash) do
|
145
168
|
res = @wrapper.post_file(url, req_hash)
|
146
169
|
end
|
147
170
|
return res
|
data/lib/rest/errors.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
module Rest
|
3
|
+
|
4
|
+
# Base Rest error class
|
5
|
+
class RestError < StandardError
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
class HttpError < RestError
|
10
|
+
def initialize(response)
|
11
|
+
super("#{response.code} Error")
|
12
|
+
@response = response
|
13
|
+
end
|
14
|
+
|
15
|
+
def response
|
16
|
+
@response
|
17
|
+
end
|
18
|
+
def code
|
19
|
+
response.code
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"HTTP #{code} Error. #{response.body}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# If it didn't even get a response, it will be a ClientError
|
28
|
+
class ClientError < RestError
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class TimeoutError < ClientError
|
33
|
+
def initialize(msg=nil)
|
34
|
+
msg ||= "HTTP Request Timed out."
|
35
|
+
super(msg)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class InvalidResponseError < RestError
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/lib/rest/version.rb
CHANGED
@@ -1,29 +1,53 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
module Rest
|
2
|
+
class BaseWrapper
|
3
|
+
|
4
|
+
def post_file(url, req_hash={})
|
5
|
+
response = nil
|
6
|
+
begin
|
7
|
+
if req_hash[:body]
|
8
|
+
req_hash = req_hash.merge(req_hash[:body])
|
9
|
+
req_hash.delete(:body)
|
10
|
+
end
|
11
|
+
|
12
|
+
headers = {}
|
13
|
+
if req_hash[:headers]
|
14
|
+
headers = req_hash[:headers]
|
15
|
+
req_hash.delete(:headers)
|
16
|
+
end
|
10
17
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
18
|
+
r2 = RestClient.post(url, req_hash, headers)
|
19
|
+
response = Rest::Wrappers::RestClientResponseWrapper.new(r2)
|
20
|
+
rescue RestClient::Exception => ex
|
21
|
+
raise Rest::Wrappers::RestClientExceptionWrapper.new(ex)
|
15
22
|
end
|
23
|
+
response
|
24
|
+
end
|
25
|
+
|
26
|
+
# if body is a hash, it will convert it to json
|
27
|
+
def to_json_parts(h)
|
28
|
+
h[:body] = h[:body].to_json if h[:body] && h[:body].is_a?(Hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
# if wrapper has a close/shutdown, override this
|
32
|
+
def close
|
16
33
|
|
17
|
-
r2 = RestClient.post(url, req_hash, headers)
|
18
|
-
response = Rest::Wrappers::RestClientResponseWrapper.new(r2)
|
19
|
-
rescue RestClient::Exception => ex
|
20
|
-
raise Rest::Wrappers::RestClientExceptionWrapper.new(ex)
|
21
34
|
end
|
22
|
-
response
|
23
35
|
end
|
24
36
|
|
25
|
-
|
26
|
-
|
37
|
+
class BaseResponseWrapper
|
38
|
+
attr_accessor :tries
|
39
|
+
|
40
|
+
# Provide a headers_orig method in your wrapper to allow this to work
|
41
|
+
def headers
|
42
|
+
new_h = {}
|
43
|
+
headers_orig.each_pair do |k,v|
|
44
|
+
if v.is_a?(Array) && v.size == 1
|
45
|
+
v = v[0]
|
46
|
+
end
|
47
|
+
new_h[k.downcase] = v
|
48
|
+
end
|
49
|
+
new_h
|
50
|
+
end
|
27
51
|
|
28
52
|
end
|
29
53
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'excon'
|
2
|
+
|
3
|
+
module Rest
|
4
|
+
|
5
|
+
module Wrappers
|
6
|
+
class ExconExceptionWrapper < ClientError
|
7
|
+
def initialize(ex)
|
8
|
+
super(ex.message)
|
9
|
+
@ex = ex
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class ExconResponseWrapper < BaseResponseWrapper
|
14
|
+
def initialize(response)
|
15
|
+
@response = response
|
16
|
+
end
|
17
|
+
|
18
|
+
def code
|
19
|
+
@response.status
|
20
|
+
end
|
21
|
+
|
22
|
+
def body
|
23
|
+
@response.body
|
24
|
+
end
|
25
|
+
|
26
|
+
def headers_orig
|
27
|
+
@response.headers
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class ExconWrapper < BaseWrapper
|
33
|
+
|
34
|
+
def initialize(client)
|
35
|
+
@client = client
|
36
|
+
# Would need to pass in base url to use persistent connection.
|
37
|
+
#@http = Excon.new("")
|
38
|
+
end
|
39
|
+
|
40
|
+
def default_headers
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
|
44
|
+
def get(url, req_hash={})
|
45
|
+
response = nil
|
46
|
+
begin
|
47
|
+
uri = URI(url)
|
48
|
+
req_hash[:method] = :get
|
49
|
+
req_hash[:url] = url
|
50
|
+
req_hash[:headers] ||= default_headers
|
51
|
+
req_hash[:query] = req_hash[:params] if req_hash[:params]
|
52
|
+
#p req_hash
|
53
|
+
response = excon_request(url, req_hash)
|
54
|
+
rescue RestClient::Exception => ex
|
55
|
+
#p ex
|
56
|
+
raise ExconExceptionWrapper.new(ex)
|
57
|
+
end
|
58
|
+
response
|
59
|
+
end
|
60
|
+
|
61
|
+
def excon_request(url, req_hash)
|
62
|
+
conn = Excon.new(url)
|
63
|
+
r2 = conn.request(req_hash)
|
64
|
+
response = ExconResponseWrapper.new(r2)
|
65
|
+
if response.code >= 400
|
66
|
+
raise HttpError.new(response)
|
67
|
+
end
|
68
|
+
response
|
69
|
+
end
|
70
|
+
|
71
|
+
def post(url, req_hash={})
|
72
|
+
response = nil
|
73
|
+
begin
|
74
|
+
req_hash[:method] = :post
|
75
|
+
req_hash[:url] = url
|
76
|
+
to_json_parts(req_hash)
|
77
|
+
response = excon_request(url, req_hash)
|
78
|
+
rescue RestClient::Exception => ex
|
79
|
+
raise HttpError.new(ex)
|
80
|
+
end
|
81
|
+
response
|
82
|
+
end
|
83
|
+
|
84
|
+
def put(url, req_hash={})
|
85
|
+
response = nil
|
86
|
+
begin
|
87
|
+
req_hash[:method] = :put
|
88
|
+
req_hash[:url] = url
|
89
|
+
response = excon_request(url, req_hash)
|
90
|
+
rescue RestClient::Exception => ex
|
91
|
+
raise RestClientExceptionWrapper.new(ex)
|
92
|
+
end
|
93
|
+
response
|
94
|
+
end
|
95
|
+
|
96
|
+
def delete(url, req_hash={})
|
97
|
+
response = nil
|
98
|
+
begin
|
99
|
+
req_hash[:method] = :delete
|
100
|
+
req_hash[:url] = url
|
101
|
+
response = excon_request(url, req_hash)
|
102
|
+
rescue RestClient::Exception => ex
|
103
|
+
raise RestClientExceptionWrapper.new(ex)
|
104
|
+
end
|
105
|
+
response
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -10,7 +10,7 @@ module Rest
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
class NetHttpPersistentResponseWrapper
|
13
|
+
class NetHttpPersistentResponseWrapper < BaseResponseWrapper
|
14
14
|
def initialize(response)
|
15
15
|
@response = response
|
16
16
|
end
|
@@ -23,6 +23,10 @@ module Rest
|
|
23
23
|
@response.body
|
24
24
|
end
|
25
25
|
|
26
|
+
def headers_orig
|
27
|
+
@response.to_hash
|
28
|
+
end
|
29
|
+
|
26
30
|
end
|
27
31
|
|
28
32
|
class NetHttpPersistentWrapper < BaseWrapper
|
@@ -36,7 +40,7 @@ module Rest
|
|
36
40
|
|
37
41
|
def default_headers
|
38
42
|
{
|
39
|
-
"Accept-Encoding" => "gzip, deflate",
|
43
|
+
#"Accept-Encoding" => "gzip, deflate",
|
40
44
|
#"Accept" => "*/*; q=0.5, application/xml"
|
41
45
|
}
|
42
46
|
end
|
@@ -53,37 +57,49 @@ module Rest
|
|
53
57
|
def get(url, req_hash={}, options={})
|
54
58
|
@client.logger.debug "limit #{options[:limit]}"
|
55
59
|
options[:limit] ||= 10
|
56
|
-
|
60
|
+
r = nil
|
57
61
|
begin
|
58
62
|
|
59
63
|
uri = URI(url)
|
60
64
|
#p uri
|
61
65
|
#p uri.path
|
66
|
+
#p uri.request_uri
|
67
|
+
#puts "query: " + uri.query.inspect
|
68
|
+
#puts "fragment: " + uri.fragment.inspect
|
69
|
+
if req_hash[:params]
|
70
|
+
new_q = URI.encode_www_form(req_hash[:params])
|
71
|
+
if uri.query
|
72
|
+
new_q = uri.query + "&" + new_q
|
73
|
+
end
|
74
|
+
#puts "new_q: " + new_q
|
75
|
+
uri.query = new_q
|
76
|
+
end
|
77
|
+
#p uri.request_uri
|
62
78
|
post = Net::HTTP::Get.new fix_path(uri.request_uri)
|
63
79
|
add_headers(post, req_hash, default_headers)
|
64
80
|
response = http.request uri, post
|
65
81
|
@client.logger.debug response.class.name
|
82
|
+
r = NetHttpPersistentResponseWrapper.new(response)
|
66
83
|
case response
|
67
|
-
when Net::
|
68
|
-
|
69
|
-
response = get(response['location'], req_hash, {limit: options[:limit]-1})
|
70
|
-
when Net::HTTPMovedPermanently
|
71
|
-
@client.logger.debug "moved to #{response['location']}"
|
72
|
-
response = get(response['location'], req_hash, {limit: options[:limit]-1})
|
73
|
-
end
|
74
|
-
response = NetHttpPersistentResponseWrapper.new(response)
|
75
|
-
rescue Net::HTTPClientError => ex
|
76
|
-
if ex.code == 404
|
77
|
-
return NetHttpPersistentResponseWrapper.new(ex)
|
78
|
-
end
|
79
|
-
raise NetHttpPersistentExceptionWrapper.new(ex)
|
80
|
-
rescue Net::HTTPServerError => ex
|
81
|
-
if ex.code == 404
|
82
|
-
return NetHttpPersistentResponseWrapper.new(ex)
|
84
|
+
when Net::HTTPClientError, Net::HTTPServerError
|
85
|
+
raise Rest::HttpError.new(r)
|
83
86
|
end
|
84
|
-
|
87
|
+
# when Net::HTTPRedirection
|
88
|
+
# @client.logger.debug "moved to #{response['location']}"
|
89
|
+
# response = get(response['location'], req_hash, {limit: options[:limit]-1})
|
90
|
+
# when Net::HTTPMovedPermanently
|
91
|
+
# @client.logger.debug "moved to #{response['location']}"
|
92
|
+
# response = get(response['location'], req_hash, {limit: options[:limit]-1})
|
93
|
+
#end
|
94
|
+
#rescue Net::HTTPClientError, Net::HTTPServerError => ex
|
95
|
+
# raise NetHttpPersistentExceptionWrapper.new(ex)
|
96
|
+
#rescue Net::HTTPServerError => ex
|
97
|
+
# if ex.code == 404
|
98
|
+
# return NetHttpPersistentResponseWrapper.new(ex)
|
99
|
+
# end
|
100
|
+
# raise NetHttpPersistentExceptionWrapper.new(ex)
|
85
101
|
end
|
86
|
-
|
102
|
+
r
|
87
103
|
end
|
88
104
|
|
89
105
|
def fix_path(path)
|
@@ -3,14 +3,16 @@ require 'rest_client'
|
|
3
3
|
module Rest
|
4
4
|
|
5
5
|
module Wrappers
|
6
|
-
class RestClientExceptionWrapper <
|
6
|
+
class RestClientExceptionWrapper < HttpError
|
7
|
+
attr_reader :ex
|
8
|
+
|
7
9
|
def initialize(ex)
|
8
|
-
super(ex.
|
10
|
+
super(ex.response)
|
9
11
|
@ex = ex
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
13
|
-
class RestClientResponseWrapper
|
15
|
+
class RestClientResponseWrapper < BaseResponseWrapper
|
14
16
|
def initialize(response)
|
15
17
|
@response = response
|
16
18
|
end
|
@@ -23,6 +25,10 @@ module Rest
|
|
23
25
|
@response.body
|
24
26
|
end
|
25
27
|
|
28
|
+
def headers_orig
|
29
|
+
@response.headers
|
30
|
+
end
|
31
|
+
|
26
32
|
end
|
27
33
|
|
28
34
|
class RestClientWrapper < BaseWrapper
|
@@ -43,9 +49,9 @@ module Rest
|
|
43
49
|
response = RestClientResponseWrapper.new(r2)
|
44
50
|
rescue RestClient::Exception => ex
|
45
51
|
#p ex
|
46
|
-
if ex.http_code == 404
|
47
|
-
|
48
|
-
end
|
52
|
+
#if ex.http_code == 404
|
53
|
+
# return RestClientResponseWrapper.new(ex.response)
|
54
|
+
#end
|
49
55
|
raise RestClientExceptionWrapper.new(ex)
|
50
56
|
end
|
51
57
|
response
|
@@ -11,6 +11,26 @@ module Rest
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
class TyphoeusResponseWrapper < BaseResponseWrapper
|
15
|
+
|
16
|
+
def initialize(response)
|
17
|
+
@response = response
|
18
|
+
end
|
19
|
+
|
20
|
+
def code
|
21
|
+
@response.code
|
22
|
+
end
|
23
|
+
|
24
|
+
def body
|
25
|
+
@response.body
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers_orig
|
29
|
+
@response.headers_hash
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
14
34
|
class TyphoeusWrapper < BaseWrapper
|
15
35
|
|
16
36
|
def default_typhoeus_options
|
@@ -28,29 +48,28 @@ module Rest
|
|
28
48
|
# puts "REQ_HASH=" + req_hash.inspect
|
29
49
|
response = Typhoeus::Request.get(url, req_hash)
|
30
50
|
#p response
|
51
|
+
response = handle_response(response)
|
52
|
+
response
|
53
|
+
end
|
54
|
+
|
55
|
+
def handle_response(response)
|
31
56
|
if response.timed_out?
|
32
57
|
raise TyphoeusTimeoutError.new(response)
|
33
58
|
end
|
34
|
-
|
35
|
-
response
|
59
|
+
r = TyphoeusResponseWrapper.new(response)
|
60
|
+
if response.code >= 400
|
61
|
+
raise Rest::HttpError.new(r)
|
62
|
+
end
|
63
|
+
r
|
36
64
|
end
|
37
65
|
|
38
|
-
|
39
|
-
def to_json_parts(h)
|
40
|
-
h[:body] = h[:body].to_json if h[:body] && h[:body].is_a?(Hash)
|
41
|
-
end
|
66
|
+
|
42
67
|
|
43
68
|
def post(url, req_hash={})
|
44
69
|
req_hash = default_typhoeus_options.merge(req_hash)
|
45
|
-
# puts "REQ_HASH=" + req_hash.inspect
|
46
|
-
|
47
|
-
# Convert body to json - NEED TO TEST THIS MORE
|
48
70
|
to_json_parts(req_hash)
|
49
71
|
response = Typhoeus::Request.post(url, req_hash)
|
50
|
-
|
51
|
-
if response.timed_out?
|
52
|
-
raise TyphoeusTimeoutError.new(response)
|
53
|
-
end
|
72
|
+
response = handle_response(response)
|
54
73
|
response
|
55
74
|
end
|
56
75
|
|
@@ -58,19 +77,14 @@ module Rest
|
|
58
77
|
req_hash = default_typhoeus_options.merge(req_hash)
|
59
78
|
# puts "REQ_HASH=" + req_hash.inspect
|
60
79
|
response = Typhoeus::Request.put(url, req_hash)
|
61
|
-
|
62
|
-
if response.timed_out?
|
63
|
-
raise TyphoeusTimeoutError.new(response)
|
64
|
-
end
|
80
|
+
response = handle_response(response)
|
65
81
|
response
|
66
82
|
end
|
67
83
|
|
68
84
|
def delete(url, req_hash={})
|
69
85
|
req_hash = default_typhoeus_options.merge(req_hash)
|
70
86
|
response = Typhoeus::Request.delete(url, req_hash)
|
71
|
-
|
72
|
-
raise TyphoeusTimeoutError.new(response)
|
73
|
-
end
|
87
|
+
response = handle_response(response)
|
74
88
|
response
|
75
89
|
end
|
76
90
|
|
data/rest.gemspec
CHANGED
@@ -17,13 +17,15 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.required_rubygems_version = ">= 1.3.6"
|
18
18
|
gem.required_ruby_version = Gem::Requirement.new(">= 1.9")
|
19
19
|
gem.add_runtime_dependency "rest-client", ">= 0.3.0"
|
20
|
+
gem.add_runtime_dependency "net-http-persistent"
|
20
21
|
|
21
22
|
gem.add_development_dependency "test-unit"
|
23
|
+
gem.add_development_dependency "minitest"
|
22
24
|
gem.add_development_dependency "rake"
|
23
25
|
gem.add_development_dependency "uber_config"
|
24
26
|
gem.add_development_dependency "typhoeus"
|
25
27
|
gem.add_development_dependency "quicky"
|
26
|
-
gem.add_development_dependency "
|
28
|
+
gem.add_development_dependency "excon"
|
27
29
|
|
28
30
|
end
|
29
31
|
|
data/test/test_base.rb
CHANGED
@@ -8,13 +8,18 @@ rescue Exception => ex
|
|
8
8
|
raise ex
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
11
|
class TestBase < Test::Unit::TestCase
|
13
|
-
|
12
|
+
|
13
|
+
def setup
|
14
14
|
puts 'setup'
|
15
|
-
|
16
|
-
@rest = Rest::Client.new(:gem=>:rest_client)
|
15
|
+
@rest = Rest::Client.new(:gem => :net_http_persistent)
|
17
16
|
@rest.logger.level = Logger::DEBUG
|
17
|
+
@request_bin = "http://requestb.in/13t6hs51"
|
18
18
|
|
19
19
|
end
|
20
|
+
|
21
|
+
def bin
|
22
|
+
@request_bin
|
23
|
+
end
|
24
|
+
|
20
25
|
end
|
data/test/test_performance.rb
CHANGED
@@ -5,19 +5,20 @@ require 'quicky'
|
|
5
5
|
|
6
6
|
require_relative 'test_base'
|
7
7
|
|
8
|
-
class
|
8
|
+
class TestPerformance < TestBase
|
9
9
|
def setup
|
10
10
|
super
|
11
11
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_get_performance
|
15
|
+
puts 'test_get_performance'
|
15
16
|
|
16
|
-
times =
|
17
|
+
times = 100
|
17
18
|
|
18
19
|
quicky = Quicky::Timer.new
|
19
20
|
|
20
|
-
to_run = [:typhoeus, :rest_client, :net_http_persistent]
|
21
|
+
to_run = [:typhoeus, :rest_client, :net_http_persistent, :excon]
|
21
22
|
to_run.each do |gem|
|
22
23
|
run_perf(quicky, times, gem)
|
23
24
|
end
|
@@ -32,7 +33,7 @@ class TestTests < TestBase
|
|
32
33
|
puts "Starting #{gem} test..."
|
33
34
|
client = Rest::Client.new(:gem => gem)
|
34
35
|
quicky.loop(gem, times) do
|
35
|
-
client.get("http://
|
36
|
+
client.get("http://rest-test.iron.io/code/200")
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
data/test/test_rest.rb
CHANGED
@@ -5,7 +5,7 @@ require 'test/unit'
|
|
5
5
|
require 'yaml'
|
6
6
|
require_relative 'test_base'
|
7
7
|
|
8
|
-
class
|
8
|
+
class TestRest < TestBase
|
9
9
|
def setup
|
10
10
|
super
|
11
11
|
|
@@ -17,7 +17,7 @@ class TestTests < TestBase
|
|
17
17
|
p response
|
18
18
|
p response.code
|
19
19
|
assert response.code == 200
|
20
|
-
|
20
|
+
p response.body
|
21
21
|
assert response.body.include?("Social Coding")
|
22
22
|
end
|
23
23
|
|
@@ -25,6 +25,9 @@ class TestTests < TestBase
|
|
25
25
|
response = @rest.get("http://rest-test.iron.io/code/503?switch_after=3&switch_to=200")
|
26
26
|
p response
|
27
27
|
p response.code
|
28
|
+
p response.tries == 3
|
29
|
+
assert response.code == 200
|
30
|
+
|
28
31
|
end
|
29
32
|
|
30
33
|
def test_gets
|
@@ -35,8 +38,64 @@ class TestTests < TestBase
|
|
35
38
|
'User-Agent' => "someagent"
|
36
39
|
}
|
37
40
|
body = {"foo" => "bar"}
|
38
|
-
response = @rest.get("
|
41
|
+
response = @rest.get("#{bin}?param1=x")
|
42
|
+
|
43
|
+
# params as hash
|
44
|
+
response = @rest.get("#{bin}?x=y#frag", :params => {:param2 => "abc"})
|
45
|
+
response = @rest.get("#{bin}", :params => {param3: "xyz"})
|
46
|
+
response = @rest.get("#{bin}")
|
47
|
+
|
48
|
+
response = @rest.get("http://rest-test.iron.io/code/200")
|
49
|
+
assert response.code == 200
|
50
|
+
assert response.body.include?("200")
|
51
|
+
p response.headers
|
52
|
+
assert response.headers.is_a?(Hash)
|
53
|
+
|
54
|
+
end
|
39
55
|
|
56
|
+
def test_404
|
57
|
+
begin
|
58
|
+
response = @rest.get("http://rest-test.iron.io/code/404")
|
59
|
+
assert false, "shouldn't get here"
|
60
|
+
rescue Rest::HttpError => ex
|
61
|
+
puts "EX: " + ex.inspect
|
62
|
+
p ex.backtrace
|
63
|
+
assert ex.is_a?(Rest::HttpError)
|
64
|
+
assert ex.response
|
65
|
+
assert ex.response.body
|
66
|
+
assert ex.code == 404
|
67
|
+
assert ex.response.body.include?("404")
|
68
|
+
assert ex.to_s.include?("404")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_400
|
73
|
+
begin
|
74
|
+
response = @rest.get("http://rest-test.iron.io/code/400")
|
75
|
+
assert false, "shouldn't get here"
|
76
|
+
rescue Rest::HttpError => ex
|
77
|
+
puts "EX: #{ex}"
|
78
|
+
p ex.backtrace
|
79
|
+
assert ex.is_a?(Rest::HttpError)
|
80
|
+
assert ex.response
|
81
|
+
assert ex.response.body
|
82
|
+
assert ex.code == 400
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_500
|
87
|
+
puts '500'
|
88
|
+
begin
|
89
|
+
response = @rest.get("http://rest-test.iron.io/code/500")
|
90
|
+
assert false, "shouldn't get here"
|
91
|
+
rescue Rest::HttpError => ex
|
92
|
+
puts "EX: " + ex.inspect
|
93
|
+
p ex.backtrace
|
94
|
+
assert ex.is_a?(Rest::HttpError)
|
95
|
+
assert ex.response
|
96
|
+
assert ex.response.body
|
97
|
+
assert ex.code == 500
|
98
|
+
end
|
40
99
|
end
|
41
100
|
|
42
101
|
def test_post_with_headers
|
@@ -48,12 +107,16 @@ class TestTests < TestBase
|
|
48
107
|
'User-Agent' => "someagent"
|
49
108
|
}
|
50
109
|
body = {"foo" => "bar"}
|
51
|
-
response = @rest.post("
|
110
|
+
response = @rest.post("#{bin}",
|
111
|
+
:body => body,
|
112
|
+
:headers => headers)
|
113
|
+
p response
|
114
|
+
response = @rest.post("http://rest-test.iron.io/code/200",
|
52
115
|
:body => body,
|
53
116
|
:headers => headers)
|
54
117
|
p response
|
55
118
|
|
56
|
-
response = @rest.post("
|
119
|
+
response = @rest.post("#{bin}",
|
57
120
|
:body => "some string body",
|
58
121
|
:headers => headers)
|
59
122
|
p response
|
data/test/tmp.rb
CHANGED
@@ -1,14 +1,27 @@
|
|
1
|
+
gem 'test-unit'
|
2
|
+
require 'test/unit'
|
1
3
|
require 'yaml'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
4
|
+
require_relative 'test_base'
|
5
|
+
|
6
|
+
class TestTemp < TestBase
|
7
|
+
def setup
|
8
|
+
super
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_500
|
13
|
+
puts '500'
|
14
|
+
begin
|
15
|
+
puts 'in block'
|
16
|
+
response = @rest.get("http://rest-test.iron.io/code/500")
|
17
|
+
assert false, "shouldn't get here"
|
18
|
+
rescue => ex
|
19
|
+
p ex
|
20
|
+
assert ex.is_a?(Rest::HttpError)
|
21
|
+
assert ex.response
|
22
|
+
assert ex.response.body
|
23
|
+
assert ex.code == 500
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
11
27
|
|
12
|
-
response = @rest.get("http://smooth-sword-1395.herokuapp.com/code/503?switch_after=3&switch_to=200")
|
13
|
-
p response
|
14
|
-
p response.code
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 0.3.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: net-http-persistent
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: test-unit
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -43,6 +59,22 @@ dependencies:
|
|
43
59
|
- - ! '>='
|
44
60
|
- !ruby/object:Gem::Version
|
45
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: minitest
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
46
78
|
- !ruby/object:Gem::Dependency
|
47
79
|
name: rake
|
48
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,7 +140,7 @@ dependencies:
|
|
108
140
|
- !ruby/object:Gem::Version
|
109
141
|
version: '0'
|
110
142
|
- !ruby/object:Gem::Dependency
|
111
|
-
name:
|
143
|
+
name: excon
|
112
144
|
requirement: !ruby/object:Gem::Requirement
|
113
145
|
none: false
|
114
146
|
requirements:
|
@@ -138,8 +170,10 @@ files:
|
|
138
170
|
- Rakefile
|
139
171
|
- lib/rest.rb
|
140
172
|
- lib/rest/client.rb
|
173
|
+
- lib/rest/errors.rb
|
141
174
|
- lib/rest/version.rb
|
142
175
|
- lib/rest/wrappers/base_wrapper.rb
|
176
|
+
- lib/rest/wrappers/excon_wrapper.rb
|
143
177
|
- lib/rest/wrappers/net_http_persistent_wrapper.rb
|
144
178
|
- lib/rest/wrappers/rest_client_wrapper.rb
|
145
179
|
- lib/rest/wrappers/typhoeus_wrapper.rb
|