rest-client 1.6.7 → 2.1.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 +7 -0
- data/.gitignore +9 -0
- data/.mailmap +10 -0
- data/.rspec +2 -0
- data/.rubocop +2 -0
- data/.rubocop-disables.yml +386 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +62 -0
- data/AUTHORS +106 -0
- data/Gemfile +11 -0
- data/LICENSE +21 -0
- data/README.md +901 -0
- data/Rakefile +109 -35
- data/bin/restclient +11 -12
- data/history.md +244 -1
- data/lib/restclient.rb +27 -18
- data/lib/restclient/abstract_response.rb +197 -51
- data/lib/restclient/exceptions.rb +110 -59
- data/lib/restclient/params_array.rb +72 -0
- data/lib/restclient/payload.rb +74 -75
- data/lib/restclient/platform.rb +49 -0
- data/lib/restclient/raw_response.rb +21 -6
- data/lib/restclient/request.rb +747 -183
- data/lib/restclient/resource.rb +22 -13
- data/lib/restclient/response.rb +75 -9
- data/lib/restclient/utils.rb +274 -0
- data/lib/restclient/version.rb +8 -0
- data/lib/restclient/windows.rb +8 -0
- data/lib/restclient/windows/root_certs.rb +105 -0
- data/rest-client.gemspec +32 -0
- data/rest-client.windows.gemspec +19 -0
- data/spec/ISS.jpg +0 -0
- data/spec/helpers.rb +54 -0
- data/spec/integration/_lib.rb +1 -0
- data/spec/integration/capath_digicert/3513523f.0 +22 -0
- data/spec/integration/capath_digicert/399e7759.0 +22 -0
- data/spec/integration/capath_digicert/README +8 -0
- data/spec/integration/capath_digicert/digicert.crt +22 -0
- data/spec/integration/capath_verisign/415660c1.0 +14 -0
- data/spec/integration/capath_verisign/7651b327.0 +14 -0
- data/spec/integration/capath_verisign/README +8 -0
- data/spec/integration/capath_verisign/verisign.crt +14 -0
- data/spec/integration/certs/digicert.crt +22 -0
- data/spec/integration/httpbin_spec.rb +128 -0
- data/spec/integration/integration_spec.rb +118 -0
- data/spec/integration/request_spec.rb +109 -7
- data/spec/spec_helper.rb +29 -0
- data/spec/unit/_lib.rb +1 -0
- data/spec/unit/abstract_response_spec.rb +145 -0
- data/spec/unit/exceptions_spec.rb +108 -0
- data/spec/unit/params_array_spec.rb +36 -0
- data/spec/unit/payload_spec.rb +295 -0
- data/spec/unit/raw_response_spec.rb +22 -0
- data/spec/unit/request2_spec.rb +54 -0
- data/spec/unit/request_spec.rb +1238 -0
- data/spec/unit/resource_spec.rb +134 -0
- data/spec/unit/response_spec.rb +252 -0
- data/spec/unit/restclient_spec.rb +80 -0
- data/spec/unit/utils_spec.rb +147 -0
- data/spec/unit/windows/root_certs_spec.rb +22 -0
- metadata +265 -117
- data/README.rdoc +0 -285
- data/VERSION +0 -1
- data/lib/restclient/net_http_ext.rb +0 -55
- data/spec/abstract_response_spec.rb +0 -85
- data/spec/base.rb +0 -16
- data/spec/exceptions_spec.rb +0 -98
- data/spec/integration/certs/equifax.crt +0 -19
- data/spec/integration_spec.rb +0 -38
- data/spec/master_shake.jpg +0 -0
- data/spec/payload_spec.rb +0 -234
- data/spec/raw_response_spec.rb +0 -17
- data/spec/request2_spec.rb +0 -40
- data/spec/request_spec.rb +0 -529
- data/spec/resource_spec.rb +0 -134
- data/spec/response_spec.rb +0 -169
- data/spec/restclient_spec.rb +0 -73
data/lib/restclient.rb
CHANGED
@@ -1,22 +1,20 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'net/http'
|
2
|
+
require 'openssl'
|
3
3
|
require 'stringio'
|
4
|
+
require 'uri'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
rescue LoadError => e
|
8
|
-
raise e unless RUBY_PLATFORM =~ /linux/
|
9
|
-
raise LoadError, "no such file to load -- net/https. Try running apt-get install libopenssl-ruby"
|
10
|
-
end
|
11
|
-
|
6
|
+
require File.dirname(__FILE__) + '/restclient/version'
|
7
|
+
require File.dirname(__FILE__) + '/restclient/platform'
|
12
8
|
require File.dirname(__FILE__) + '/restclient/exceptions'
|
9
|
+
require File.dirname(__FILE__) + '/restclient/utils'
|
13
10
|
require File.dirname(__FILE__) + '/restclient/request'
|
14
11
|
require File.dirname(__FILE__) + '/restclient/abstract_response'
|
15
12
|
require File.dirname(__FILE__) + '/restclient/response'
|
16
13
|
require File.dirname(__FILE__) + '/restclient/raw_response'
|
17
14
|
require File.dirname(__FILE__) + '/restclient/resource'
|
15
|
+
require File.dirname(__FILE__) + '/restclient/params_array'
|
18
16
|
require File.dirname(__FILE__) + '/restclient/payload'
|
19
|
-
require File.dirname(__FILE__) + '/restclient/
|
17
|
+
require File.dirname(__FILE__) + '/restclient/windows'
|
20
18
|
|
21
19
|
# This module's static methods are the entry point for using the REST client.
|
22
20
|
#
|
@@ -92,8 +90,24 @@ module RestClient
|
|
92
90
|
Request.execute(:method => :options, :url => url, :headers => headers, &block)
|
93
91
|
end
|
94
92
|
|
95
|
-
|
96
|
-
|
93
|
+
# A global proxy URL to use for all requests. This can be overridden on a
|
94
|
+
# per-request basis by passing `:proxy` to RestClient::Request.
|
95
|
+
def self.proxy
|
96
|
+
@proxy ||= nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.proxy=(value)
|
100
|
+
@proxy = value
|
101
|
+
@proxy_set = true
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return whether RestClient.proxy was set explicitly. We use this to
|
105
|
+
# differentiate between no value being set and a value explicitly set to nil.
|
106
|
+
#
|
107
|
+
# @return [Boolean]
|
108
|
+
#
|
109
|
+
def self.proxy_set?
|
110
|
+
@proxy_set ||= false
|
97
111
|
end
|
98
112
|
|
99
113
|
# Setup the log for RestClient calls.
|
@@ -103,12 +117,6 @@ module RestClient
|
|
103
117
|
@@log = create_log log
|
104
118
|
end
|
105
119
|
|
106
|
-
def self.version
|
107
|
-
version_path = File.dirname(__FILE__) + "/../VERSION"
|
108
|
-
return File.read(version_path).chomp if File.file?(version_path)
|
109
|
-
"0.0.0"
|
110
|
-
end
|
111
|
-
|
112
120
|
# Create a log that respond to << like a logger
|
113
121
|
# param can be 'stdout', 'stderr', a string (then we will log to that file) or a logger (then we return it)
|
114
122
|
def self.create_log param
|
@@ -159,6 +167,7 @@ module RestClient
|
|
159
167
|
# Add a Proc to be called before each request in executed.
|
160
168
|
# The proc parameters will be the http request and the request params.
|
161
169
|
def self.add_before_execution_proc &proc
|
170
|
+
raise ArgumentError.new('block is required') unless proc
|
162
171
|
@@before_execution_procs << proc
|
163
172
|
end
|
164
173
|
|
@@ -1,16 +1,40 @@
|
|
1
1
|
require 'cgi'
|
2
|
+
require 'http-cookie'
|
2
3
|
|
3
4
|
module RestClient
|
4
5
|
|
5
6
|
module AbstractResponse
|
6
7
|
|
7
|
-
attr_reader :net_http_res, :
|
8
|
+
attr_reader :net_http_res, :request, :start_time, :end_time, :duration
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
raise NotImplementedError.new('must override in subclass')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Logger from the request, potentially nil.
|
15
|
+
def log
|
16
|
+
request.log
|
17
|
+
end
|
18
|
+
|
19
|
+
def log_response
|
20
|
+
return unless log
|
21
|
+
|
22
|
+
code = net_http_res.code
|
23
|
+
res_name = net_http_res.class.to_s.gsub(/\ANet::HTTP/, '')
|
24
|
+
content_type = (net_http_res['Content-type'] || '').gsub(/;.*\z/, '')
|
25
|
+
|
26
|
+
log << "# => #{code} #{res_name} | #{content_type} #{size} bytes, #{sprintf('%.2f', duration)}s\n"
|
27
|
+
end
|
8
28
|
|
9
29
|
# HTTP status code
|
10
30
|
def code
|
11
31
|
@code ||= @net_http_res.code.to_i
|
12
32
|
end
|
13
33
|
|
34
|
+
def history
|
35
|
+
@history ||= request.redirection_history || []
|
36
|
+
end
|
37
|
+
|
14
38
|
# A hash of the headers, beautified with symbols and underscores.
|
15
39
|
# e.g. "Content-type" will become :content_type.
|
16
40
|
def headers
|
@@ -22,85 +46,207 @@ module RestClient
|
|
22
46
|
@raw_headers ||= @net_http_res.to_hash
|
23
47
|
end
|
24
48
|
|
25
|
-
#
|
49
|
+
# @param [Net::HTTPResponse] net_http_res
|
50
|
+
# @param [RestClient::Request] request
|
51
|
+
# @param [Time] start_time
|
52
|
+
def response_set_vars(net_http_res, request, start_time)
|
53
|
+
@net_http_res = net_http_res
|
54
|
+
@request = request
|
55
|
+
@start_time = start_time
|
56
|
+
@end_time = Time.now
|
57
|
+
|
58
|
+
if @start_time
|
59
|
+
@duration = @end_time - @start_time
|
60
|
+
else
|
61
|
+
@duration = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# prime redirection history
|
65
|
+
history
|
66
|
+
end
|
67
|
+
|
68
|
+
# Hash of cookies extracted from response headers.
|
69
|
+
#
|
70
|
+
# NB: This will return only cookies whose domain matches this request, and
|
71
|
+
# may not even return all of those cookies if there are duplicate names.
|
72
|
+
# Use the full cookie_jar for more nuanced access.
|
73
|
+
#
|
74
|
+
# @see #cookie_jar
|
75
|
+
#
|
76
|
+
# @return [Hash]
|
77
|
+
#
|
26
78
|
def cookies
|
27
|
-
|
28
|
-
|
79
|
+
hash = {}
|
80
|
+
|
81
|
+
cookie_jar.cookies(@request.uri).each do |cookie|
|
82
|
+
hash[cookie.name] = cookie.value
|
83
|
+
end
|
84
|
+
|
85
|
+
hash
|
86
|
+
end
|
87
|
+
|
88
|
+
# Cookie jar extracted from response headers.
|
89
|
+
#
|
90
|
+
# @return [HTTP::CookieJar]
|
91
|
+
#
|
92
|
+
def cookie_jar
|
93
|
+
return @cookie_jar if defined?(@cookie_jar) && @cookie_jar
|
94
|
+
|
95
|
+
jar = @request.cookie_jar.dup
|
96
|
+
headers.fetch(:set_cookie, []).each do |cookie|
|
97
|
+
jar.parse(cookie, @request.uri)
|
29
98
|
end
|
99
|
+
|
100
|
+
@cookie_jar = jar
|
30
101
|
end
|
31
102
|
|
32
103
|
# Return the default behavior corresponding to the response code:
|
33
|
-
#
|
34
|
-
|
35
|
-
|
104
|
+
#
|
105
|
+
# For 20x status codes: return the response itself
|
106
|
+
#
|
107
|
+
# For 30x status codes:
|
108
|
+
# 301, 302, 307: redirect GET / HEAD if there is a Location header
|
109
|
+
# 303: redirect, changing method to GET, if there is a Location header
|
110
|
+
#
|
111
|
+
# For all other responses, raise a response exception
|
112
|
+
#
|
113
|
+
def return!(&block)
|
114
|
+
case code
|
115
|
+
when 200..207
|
36
116
|
self
|
37
|
-
|
38
|
-
|
39
|
-
|
117
|
+
when 301, 302, 307
|
118
|
+
case request.method
|
119
|
+
when 'get', 'head'
|
120
|
+
check_max_redirects
|
121
|
+
follow_redirection(&block)
|
40
122
|
else
|
41
|
-
|
123
|
+
raise exception_with_response
|
42
124
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
follow_redirection(request, result, & block)
|
47
|
-
elsif Exceptions::EXCEPTIONS_MAP[code]
|
48
|
-
raise Exceptions::EXCEPTIONS_MAP[code].new(self, code)
|
125
|
+
when 303
|
126
|
+
check_max_redirects
|
127
|
+
follow_get_redirection(&block)
|
49
128
|
else
|
50
|
-
raise
|
129
|
+
raise exception_with_response
|
51
130
|
end
|
52
131
|
end
|
53
132
|
|
54
133
|
def to_i
|
55
|
-
|
134
|
+
warn('warning: calling Response#to_i is not recommended')
|
135
|
+
super
|
56
136
|
end
|
57
137
|
|
58
138
|
def description
|
59
139
|
"#{code} #{STATUSES[code]} | #{(headers[:content_type] || '').gsub(/;.*$/, '')} #{size} bytes\n"
|
60
140
|
end
|
61
141
|
|
62
|
-
# Follow a redirection
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
args[:max_redirects] = request.max_redirects - 1
|
77
|
-
# pass any cookie set in the result
|
78
|
-
if result && result['set-cookie']
|
79
|
-
args[:headers][:cookies] = (args[:headers][:cookies] || {}).merge(parse_cookie(result['set-cookie']))
|
80
|
-
end
|
81
|
-
end
|
82
|
-
Request.execute args, &block
|
142
|
+
# Follow a redirection response by making a new HTTP request to the
|
143
|
+
# redirection target.
|
144
|
+
def follow_redirection(&block)
|
145
|
+
_follow_redirection(request.args.dup, &block)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Follow a redirection response, but change the HTTP method to GET and drop
|
149
|
+
# the payload from the original request.
|
150
|
+
def follow_get_redirection(&block)
|
151
|
+
new_args = request.args.dup
|
152
|
+
new_args[:method] = :get
|
153
|
+
new_args.delete(:payload)
|
154
|
+
|
155
|
+
_follow_redirection(new_args, &block)
|
83
156
|
end
|
84
157
|
|
85
|
-
|
158
|
+
# Convert headers hash into canonical form.
|
159
|
+
#
|
160
|
+
# Header names will be converted to lowercase symbols with underscores
|
161
|
+
# instead of hyphens.
|
162
|
+
#
|
163
|
+
# Headers specified multiple times will be joined by comma and space,
|
164
|
+
# except for Set-Cookie, which will always be an array.
|
165
|
+
#
|
166
|
+
# Per RFC 2616, if a server sends multiple headers with the same key, they
|
167
|
+
# MUST be able to be joined into a single header by a comma. However,
|
168
|
+
# Set-Cookie (RFC 6265) cannot because commas are valid within cookie
|
169
|
+
# definitions. The newer RFC 7230 notes (3.2.2) that Set-Cookie should be
|
170
|
+
# handled as a special case.
|
171
|
+
#
|
172
|
+
# http://tools.ietf.org/html/rfc2616#section-4.2
|
173
|
+
# http://tools.ietf.org/html/rfc7230#section-3.2.2
|
174
|
+
# http://tools.ietf.org/html/rfc6265
|
175
|
+
#
|
176
|
+
# @param headers [Hash]
|
177
|
+
# @return [Hash]
|
178
|
+
#
|
179
|
+
def self.beautify_headers(headers)
|
86
180
|
headers.inject({}) do |out, (key, value)|
|
87
|
-
|
181
|
+
key_sym = key.tr('-', '_').downcase.to_sym
|
182
|
+
|
183
|
+
# Handle Set-Cookie specially since it cannot be joined by comma.
|
184
|
+
if key.downcase == 'set-cookie'
|
185
|
+
out[key_sym] = value
|
186
|
+
else
|
187
|
+
out[key_sym] = value.join(', ')
|
188
|
+
end
|
189
|
+
|
88
190
|
out
|
89
191
|
end
|
90
192
|
end
|
91
193
|
|
92
194
|
private
|
93
195
|
|
94
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
196
|
+
# Follow a redirection
|
197
|
+
#
|
198
|
+
# @param new_args [Hash] Start with this hash of arguments for the
|
199
|
+
# redirection request. The hash will be mutated, so be sure to dup any
|
200
|
+
# existing hash that should not be modified.
|
201
|
+
#
|
202
|
+
def _follow_redirection(new_args, &block)
|
203
|
+
|
204
|
+
# parse location header and merge into existing URL
|
205
|
+
url = headers[:location]
|
206
|
+
|
207
|
+
# cannot follow redirection if there is no location header
|
208
|
+
unless url
|
209
|
+
raise exception_with_response
|
210
|
+
end
|
211
|
+
|
212
|
+
# handle relative redirects
|
213
|
+
unless url.start_with?('http')
|
214
|
+
url = URI.parse(request.url).merge(url).to_s
|
215
|
+
end
|
216
|
+
new_args[:url] = url
|
217
|
+
|
218
|
+
new_args[:password] = request.password
|
219
|
+
new_args[:user] = request.user
|
220
|
+
new_args[:headers] = request.headers
|
221
|
+
new_args[:max_redirects] = request.max_redirects - 1
|
222
|
+
|
223
|
+
# pass through our new cookie jar
|
224
|
+
new_args[:cookies] = cookie_jar
|
225
|
+
|
226
|
+
# prepare new request
|
227
|
+
new_req = Request.new(new_args)
|
228
|
+
|
229
|
+
# append self to redirection history
|
230
|
+
new_req.redirection_history = history + [self]
|
231
|
+
|
232
|
+
# execute redirected request
|
233
|
+
new_req.execute(&block)
|
234
|
+
end
|
235
|
+
|
236
|
+
def check_max_redirects
|
237
|
+
if request.max_redirects <= 0
|
238
|
+
raise exception_with_response
|
101
239
|
end
|
102
|
-
out
|
103
240
|
end
|
104
|
-
end
|
105
241
|
|
242
|
+
def exception_with_response
|
243
|
+
begin
|
244
|
+
klass = Exceptions::EXCEPTIONS_MAP.fetch(code)
|
245
|
+
rescue KeyError
|
246
|
+
raise RequestFailed.new(self, code)
|
247
|
+
end
|
248
|
+
|
249
|
+
raise klass.new(self, code)
|
250
|
+
end
|
251
|
+
end
|
106
252
|
end
|
@@ -1,5 +1,19 @@
|
|
1
1
|
module RestClient
|
2
2
|
|
3
|
+
# Hash of HTTP status code => message.
|
4
|
+
#
|
5
|
+
# 1xx: Informational - Request received, continuing process
|
6
|
+
# 2xx: Success - The action was successfully received, understood, and
|
7
|
+
# accepted
|
8
|
+
# 3xx: Redirection - Further action must be taken in order to complete the
|
9
|
+
# request
|
10
|
+
# 4xx: Client Error - The request contains bad syntax or cannot be fulfilled
|
11
|
+
# 5xx: Server Error - The server failed to fulfill an apparently valid
|
12
|
+
# request
|
13
|
+
#
|
14
|
+
# @see
|
15
|
+
# http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
16
|
+
#
|
3
17
|
STATUSES = {100 => 'Continue',
|
4
18
|
101 => 'Switching Protocols',
|
5
19
|
102 => 'Processing', #WebDAV
|
@@ -12,6 +26,8 @@ module RestClient
|
|
12
26
|
205 => 'Reset Content',
|
13
27
|
206 => 'Partial Content',
|
14
28
|
207 => 'Multi-Status', #WebDAV
|
29
|
+
208 => 'Already Reported', # RFC5842
|
30
|
+
226 => 'IM Used', # RFC3229
|
15
31
|
|
16
32
|
300 => 'Multiple Choices',
|
17
33
|
301 => 'Moved Permanently',
|
@@ -21,12 +37,13 @@ module RestClient
|
|
21
37
|
305 => 'Use Proxy', # http/1.1
|
22
38
|
306 => 'Switch Proxy', # no longer used
|
23
39
|
307 => 'Temporary Redirect', # http/1.1
|
24
|
-
|
40
|
+
308 => 'Permanent Redirect', # RFC7538
|
41
|
+
|
25
42
|
400 => 'Bad Request',
|
26
43
|
401 => 'Unauthorized',
|
27
44
|
402 => 'Payment Required',
|
28
45
|
403 => 'Forbidden',
|
29
|
-
404 => '
|
46
|
+
404 => 'Not Found',
|
30
47
|
405 => 'Method Not Allowed',
|
31
48
|
406 => 'Not Acceptable',
|
32
49
|
407 => 'Proxy Authentication Required',
|
@@ -35,18 +52,21 @@ module RestClient
|
|
35
52
|
410 => 'Gone',
|
36
53
|
411 => 'Length Required',
|
37
54
|
412 => 'Precondition Failed',
|
38
|
-
413 => '
|
39
|
-
414 => '
|
55
|
+
413 => 'Payload Too Large', # RFC7231 (renamed, see below)
|
56
|
+
414 => 'URI Too Long', # RFC7231 (renamed, see below)
|
40
57
|
415 => 'Unsupported Media Type',
|
41
|
-
416 => '
|
58
|
+
416 => 'Range Not Satisfiable', # RFC7233 (renamed, see below)
|
42
59
|
417 => 'Expectation Failed',
|
43
|
-
418 => 'I\'m A Teapot',
|
60
|
+
418 => 'I\'m A Teapot', #RFC2324
|
44
61
|
421 => 'Too Many Connections From This IP',
|
45
62
|
422 => 'Unprocessable Entity', #WebDAV
|
46
63
|
423 => 'Locked', #WebDAV
|
47
64
|
424 => 'Failed Dependency', #WebDAV
|
48
65
|
425 => 'Unordered Collection', #WebDAV
|
49
|
-
426 => 'Upgrade Required',
|
66
|
+
426 => 'Upgrade Required',
|
67
|
+
428 => 'Precondition Required', #RFC6585
|
68
|
+
429 => 'Too Many Requests', #RFC6585
|
69
|
+
431 => 'Request Header Fields Too Large', #RFC6585
|
50
70
|
449 => 'Retry With', #Microsoft
|
51
71
|
450 => 'Blocked By Windows Parental Controls', #Microsoft
|
52
72
|
|
@@ -58,20 +78,27 @@ module RestClient
|
|
58
78
|
505 => 'HTTP Version Not Supported',
|
59
79
|
506 => 'Variant Also Negotiates',
|
60
80
|
507 => 'Insufficient Storage', #WebDAV
|
81
|
+
508 => 'Loop Detected', # RFC5842
|
61
82
|
509 => 'Bandwidth Limit Exceeded', #Apache
|
62
|
-
510 => 'Not Extended'
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
83
|
+
510 => 'Not Extended',
|
84
|
+
511 => 'Network Authentication Required', # RFC6585
|
85
|
+
}
|
86
|
+
|
87
|
+
STATUSES_COMPATIBILITY = {
|
88
|
+
# The RFCs all specify "Not Found", but "Resource Not Found" was used in
|
89
|
+
# earlier RestClient releases.
|
90
|
+
404 => ['ResourceNotFound'],
|
91
|
+
|
92
|
+
# HTTP 413 was renamed to "Payload Too Large" in RFC7231.
|
93
|
+
413 => ['RequestEntityTooLarge'],
|
94
|
+
|
95
|
+
# HTTP 414 was renamed to "URI Too Long" in RFC7231.
|
96
|
+
414 => ['RequestURITooLong'],
|
97
|
+
|
98
|
+
# HTTP 416 was renamed to "Range Not Satisfiable" in RFC7233.
|
99
|
+
416 => ['RequestedRangeNotSatisfiable'],
|
100
|
+
}
|
101
|
+
|
75
102
|
|
76
103
|
# This is the base RestClient exception class. Rescue it if you want to
|
77
104
|
# catch any exception that your request might raise
|
@@ -81,14 +108,13 @@ module RestClient
|
|
81
108
|
# probably an HTML error page) is e.response.
|
82
109
|
class Exception < RuntimeError
|
83
110
|
attr_accessor :response
|
84
|
-
|
111
|
+
attr_accessor :original_exception
|
112
|
+
attr_writer :message
|
85
113
|
|
86
114
|
def initialize response = nil, initial_response_code = nil
|
87
115
|
@response = response
|
116
|
+
@message = nil
|
88
117
|
@initial_response_code = initial_response_code
|
89
|
-
|
90
|
-
# compatibility: this make the exception behave like a Net::HTTPResponse
|
91
|
-
response.extend ResponseForException if response
|
92
118
|
end
|
93
119
|
|
94
120
|
def http_code
|
@@ -100,32 +126,35 @@ module RestClient
|
|
100
126
|
end
|
101
127
|
end
|
102
128
|
|
103
|
-
def
|
104
|
-
@response.
|
129
|
+
def http_headers
|
130
|
+
@response.headers if @response
|
105
131
|
end
|
106
132
|
|
107
|
-
def
|
108
|
-
|
133
|
+
def http_body
|
134
|
+
@response.body if @response
|
109
135
|
end
|
110
136
|
|
111
137
|
def to_s
|
112
|
-
|
138
|
+
message
|
113
139
|
end
|
114
|
-
|
140
|
+
|
115
141
|
def message
|
116
|
-
@message ||
|
142
|
+
@message || default_message
|
117
143
|
end
|
118
144
|
|
145
|
+
def default_message
|
146
|
+
self.class.name
|
147
|
+
end
|
119
148
|
end
|
120
149
|
|
121
150
|
# Compatibility
|
122
|
-
class ExceptionWithResponse < Exception
|
151
|
+
class ExceptionWithResponse < RestClient::Exception
|
123
152
|
end
|
124
153
|
|
125
154
|
# The request failed with an error code not managed by the code
|
126
155
|
class RequestFailed < ExceptionWithResponse
|
127
156
|
|
128
|
-
def
|
157
|
+
def default_message
|
129
158
|
"HTTP status code #{http_code}"
|
130
159
|
end
|
131
160
|
|
@@ -134,60 +163,82 @@ module RestClient
|
|
134
163
|
end
|
135
164
|
end
|
136
165
|
|
137
|
-
#
|
166
|
+
# RestClient exception classes. TODO: move all exceptions into this module.
|
167
|
+
#
|
168
|
+
# We will a create an exception for each status code, see
|
169
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
170
|
+
#
|
138
171
|
module Exceptions
|
139
172
|
# Map http status codes to the corresponding exception class
|
140
173
|
EXCEPTIONS_MAP = {}
|
141
174
|
end
|
142
175
|
|
176
|
+
# Create HTTP status exception classes
|
143
177
|
STATUSES.each_pair do |code, message|
|
144
|
-
|
145
|
-
|
146
|
-
superclass = ([304, 401, 404].include? code) ? ExceptionWithResponse : RequestFailed
|
147
|
-
klass = Class.new(superclass) do
|
148
|
-
send(:define_method, :message) {"#{http_code ? "#{http_code} " : ''}#{message}"}
|
178
|
+
klass = Class.new(RequestFailed) do
|
179
|
+
send(:define_method, :default_message) {"#{http_code ? "#{http_code} " : ''}#{message}"}
|
149
180
|
end
|
150
|
-
klass_constant = const_set
|
181
|
+
klass_constant = const_set(message.delete(' \-\''), klass)
|
151
182
|
Exceptions::EXCEPTIONS_MAP[code] = klass_constant
|
152
183
|
end
|
153
184
|
|
154
|
-
#
|
155
|
-
|
185
|
+
# Create HTTP status exception classes used for backwards compatibility
|
186
|
+
STATUSES_COMPATIBILITY.each_pair do |code, compat_list|
|
187
|
+
klass = Exceptions::EXCEPTIONS_MAP.fetch(code)
|
188
|
+
compat_list.each do |old_name|
|
189
|
+
const_set(old_name, klass)
|
190
|
+
end
|
191
|
+
end
|
156
192
|
|
157
|
-
|
193
|
+
module Exceptions
|
194
|
+
# We have to split the Exceptions module like we do here because the
|
195
|
+
# EXCEPTIONS_MAP is under Exceptions, but we depend on
|
196
|
+
# RestClient::RequestTimeout below.
|
197
|
+
|
198
|
+
# Base class for request timeouts.
|
199
|
+
#
|
200
|
+
# NB: Previous releases of rest-client would raise RequestTimeout both for
|
201
|
+
# HTTP 408 responses and for actual connection timeouts.
|
202
|
+
class Timeout < RestClient::RequestTimeout
|
203
|
+
def initialize(message=nil, original_exception=nil)
|
204
|
+
super(nil, nil)
|
205
|
+
self.message = message if message
|
206
|
+
self.original_exception = original_exception if original_exception
|
207
|
+
end
|
208
|
+
end
|
158
209
|
|
159
|
-
|
210
|
+
# Timeout when connecting to a server. Typically wraps Net::OpenTimeout (in
|
211
|
+
# ruby 2.0 or greater).
|
212
|
+
class OpenTimeout < Timeout
|
213
|
+
def default_message
|
214
|
+
'Timed out connecting to server'
|
215
|
+
end
|
216
|
+
end
|
160
217
|
|
161
|
-
|
162
|
-
|
218
|
+
# Timeout when reading from a server. Typically wraps Net::ReadTimeout (in
|
219
|
+
# ruby 2.0 or greater).
|
220
|
+
class ReadTimeout < Timeout
|
221
|
+
def default_message
|
222
|
+
'Timed out reading data from server'
|
223
|
+
end
|
163
224
|
end
|
164
225
|
end
|
165
226
|
|
166
|
-
class MaxRedirectsReached < Exception
|
167
|
-
message = 'Maximum number of redirect reached'
|
168
|
-
end
|
169
227
|
|
170
228
|
# The server broke the connection prior to the request completing. Usually
|
171
229
|
# this means it crashed, or sometimes that your network connection was
|
172
230
|
# severed before it could complete.
|
173
|
-
class ServerBrokeConnection < Exception
|
231
|
+
class ServerBrokeConnection < RestClient::Exception
|
174
232
|
def initialize(message = 'Server broke connection')
|
175
233
|
super nil, nil
|
176
234
|
self.message = message
|
177
235
|
end
|
178
236
|
end
|
179
237
|
|
180
|
-
class SSLCertificateNotVerified < Exception
|
181
|
-
def initialize(message)
|
238
|
+
class SSLCertificateNotVerified < RestClient::Exception
|
239
|
+
def initialize(message = 'SSL certificate not verified')
|
182
240
|
super nil, nil
|
183
241
|
self.message = message
|
184
242
|
end
|
185
243
|
end
|
186
244
|
end
|
187
|
-
|
188
|
-
# backwards compatibility
|
189
|
-
class RestClient::Request
|
190
|
-
Redirect = RestClient::Redirect
|
191
|
-
Unauthorized = RestClient::Unauthorized
|
192
|
-
RequestFailed = RestClient::RequestFailed
|
193
|
-
end
|