alinta-rest-client 2.2.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.mailmap +10 -0
- data/.rspec +2 -0
- data/.rubocop +2 -0
- data/.rubocop-disables.yml +393 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +49 -0
- data/AUTHORS +106 -0
- data/Gemfile +11 -0
- data/LICENSE +21 -0
- data/README.md +896 -0
- data/Rakefile +140 -0
- data/bin/restclient +92 -0
- data/history.md +357 -0
- data/lib/rest-client.rb +2 -0
- data/lib/rest_client.rb +2 -0
- data/lib/restclient.rb +183 -0
- data/lib/restclient/abstract_response.rb +252 -0
- data/lib/restclient/exceptions.rb +244 -0
- data/lib/restclient/params_array.rb +72 -0
- data/lib/restclient/payload.rb +234 -0
- data/lib/restclient/platform.rb +49 -0
- data/lib/restclient/raw_response.rb +49 -0
- data/lib/restclient/request.rb +875 -0
- data/lib/restclient/resource.rb +178 -0
- data/lib/restclient/response.rb +90 -0
- 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/244b5494.0 +19 -0
- data/spec/integration/capath_digicert/81b9768f.0 +19 -0
- data/spec/integration/capath_digicert/README +8 -0
- data/spec/integration/capath_digicert/digicert.crt +19 -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 +19 -0
- data/spec/integration/certs/verisign.crt +14 -0
- data/spec/integration/httpbin_spec.rb +128 -0
- data/spec/integration/integration_spec.rb +118 -0
- data/spec/integration/request_spec.rb +127 -0
- 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 +318 -0
data/lib/rest-client.rb
ADDED
data/lib/rest_client.rb
ADDED
data/lib/restclient.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'openssl'
|
3
|
+
require 'stringio'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
require File.dirname(__FILE__) + '/restclient/version'
|
7
|
+
require File.dirname(__FILE__) + '/restclient/platform'
|
8
|
+
require File.dirname(__FILE__) + '/restclient/exceptions'
|
9
|
+
require File.dirname(__FILE__) + '/restclient/utils'
|
10
|
+
require File.dirname(__FILE__) + '/restclient/request'
|
11
|
+
require File.dirname(__FILE__) + '/restclient/abstract_response'
|
12
|
+
require File.dirname(__FILE__) + '/restclient/response'
|
13
|
+
require File.dirname(__FILE__) + '/restclient/raw_response'
|
14
|
+
require File.dirname(__FILE__) + '/restclient/resource'
|
15
|
+
require File.dirname(__FILE__) + '/restclient/params_array'
|
16
|
+
require File.dirname(__FILE__) + '/restclient/payload'
|
17
|
+
require File.dirname(__FILE__) + '/restclient/windows'
|
18
|
+
|
19
|
+
# This module's static methods are the entry point for using the REST client.
|
20
|
+
#
|
21
|
+
# # GET
|
22
|
+
# xml = RestClient.get 'http://example.com/resource'
|
23
|
+
# jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
|
24
|
+
#
|
25
|
+
# # authentication and SSL
|
26
|
+
# RestClient.get 'https://user:password@example.com/private/resource'
|
27
|
+
#
|
28
|
+
# # POST or PUT with a hash sends parameters as a urlencoded form body
|
29
|
+
# RestClient.post 'http://example.com/resource', :param1 => 'one'
|
30
|
+
#
|
31
|
+
# # nest hash parameters
|
32
|
+
# RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
|
33
|
+
#
|
34
|
+
# # POST and PUT with raw payloads
|
35
|
+
# RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
|
36
|
+
# RestClient.post 'http://example.com/resource.xml', xml_doc
|
37
|
+
# RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
|
38
|
+
#
|
39
|
+
# # DELETE
|
40
|
+
# RestClient.delete 'http://example.com/resource'
|
41
|
+
#
|
42
|
+
# # retreive the response http code and headers
|
43
|
+
# res = RestClient.get 'http://example.com/some.jpg'
|
44
|
+
# res.code # => 200
|
45
|
+
# res.headers[:content_type] # => 'image/jpg'
|
46
|
+
#
|
47
|
+
# # HEAD
|
48
|
+
# RestClient.head('http://example.com').headers
|
49
|
+
#
|
50
|
+
# To use with a proxy, just set RestClient.proxy to the proper http proxy:
|
51
|
+
#
|
52
|
+
# RestClient.proxy = "http://proxy.example.com/"
|
53
|
+
#
|
54
|
+
# Or inherit the proxy from the environment:
|
55
|
+
#
|
56
|
+
# RestClient.proxy = ENV['http_proxy']
|
57
|
+
#
|
58
|
+
# For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call:
|
59
|
+
#
|
60
|
+
# >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz'
|
61
|
+
# => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
|
62
|
+
#
|
63
|
+
module RestClient
|
64
|
+
|
65
|
+
def self.get(url, headers={}, &block)
|
66
|
+
Request.execute(:method => :get, :url => url, :headers => headers, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.post(url, payload, headers={}, &block)
|
70
|
+
Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.patch(url, payload, headers={}, &block)
|
74
|
+
Request.execute(:method => :patch, :url => url, :payload => payload, :headers => headers, &block)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.put(url, payload, headers={}, &block)
|
78
|
+
Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers, &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.delete(url, headers={}, &block)
|
82
|
+
Request.execute(:method => :delete, :url => url, :headers => headers, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.head(url, headers={}, &block)
|
86
|
+
Request.execute(:method => :head, :url => url, :headers => headers, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.options(url, headers={}, &block)
|
90
|
+
Request.execute(:method => :options, :url => url, :headers => headers, &block)
|
91
|
+
end
|
92
|
+
|
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
|
111
|
+
end
|
112
|
+
|
113
|
+
# Setup the log for RestClient calls.
|
114
|
+
# Value should be a logger but can can be stdout, stderr, or a filename.
|
115
|
+
# You can also configure logging by the environment variable RESTCLIENT_LOG.
|
116
|
+
def self.log= log
|
117
|
+
@@log = create_log log
|
118
|
+
end
|
119
|
+
|
120
|
+
# Create a log that respond to << like a logger
|
121
|
+
# param can be 'stdout', 'stderr', a string (then we will log to that file) or a logger (then we return it)
|
122
|
+
def self.create_log param
|
123
|
+
if param
|
124
|
+
if param.is_a? String
|
125
|
+
if param == 'stdout'
|
126
|
+
stdout_logger = Class.new do
|
127
|
+
def << obj
|
128
|
+
STDOUT.puts obj
|
129
|
+
end
|
130
|
+
end
|
131
|
+
stdout_logger.new
|
132
|
+
elsif param == 'stderr'
|
133
|
+
stderr_logger = Class.new do
|
134
|
+
def << obj
|
135
|
+
STDERR.puts obj
|
136
|
+
end
|
137
|
+
end
|
138
|
+
stderr_logger.new
|
139
|
+
else
|
140
|
+
file_logger = Class.new do
|
141
|
+
attr_writer :target_file
|
142
|
+
|
143
|
+
def << obj
|
144
|
+
File.open(@target_file, 'a') { |f| f.puts obj }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
logger = file_logger.new
|
148
|
+
logger.target_file = param
|
149
|
+
logger
|
150
|
+
end
|
151
|
+
else
|
152
|
+
param
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@@env_log = create_log ENV['RESTCLIENT_LOG']
|
158
|
+
|
159
|
+
@@log = nil
|
160
|
+
|
161
|
+
def self.log # :nodoc:
|
162
|
+
@@env_log || @@log
|
163
|
+
end
|
164
|
+
|
165
|
+
@@before_execution_procs = []
|
166
|
+
|
167
|
+
# Add a Proc to be called before each request in executed.
|
168
|
+
# The proc parameters will be the http request and the request params.
|
169
|
+
def self.add_before_execution_proc &proc
|
170
|
+
raise ArgumentError.new('block is required') unless proc
|
171
|
+
@@before_execution_procs << proc
|
172
|
+
end
|
173
|
+
|
174
|
+
# Reset the procs to be called before each request is executed.
|
175
|
+
def self.reset_before_execution_procs
|
176
|
+
@@before_execution_procs = []
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.before_execution_procs # :nodoc:
|
180
|
+
@@before_execution_procs
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'http-cookie'
|
3
|
+
|
4
|
+
module RestClient
|
5
|
+
|
6
|
+
module AbstractResponse
|
7
|
+
|
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
|
28
|
+
|
29
|
+
# HTTP status code
|
30
|
+
def code
|
31
|
+
@code ||= @net_http_res.code.to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
def history
|
35
|
+
@history ||= request.redirection_history || []
|
36
|
+
end
|
37
|
+
|
38
|
+
# A hash of the headers, beautified with symbols and underscores.
|
39
|
+
# e.g. "Content-type" will become :content_type.
|
40
|
+
def headers
|
41
|
+
@headers ||= AbstractResponse.beautify_headers(@net_http_res.to_hash)
|
42
|
+
end
|
43
|
+
|
44
|
+
# The raw headers.
|
45
|
+
def raw_headers
|
46
|
+
@raw_headers ||= @net_http_res.to_hash
|
47
|
+
end
|
48
|
+
|
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
|
+
#
|
78
|
+
def cookies
|
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)
|
98
|
+
end
|
99
|
+
|
100
|
+
@cookie_jar = jar
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return the default behavior corresponding to the response code:
|
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
|
116
|
+
self
|
117
|
+
when 301, 302, 307
|
118
|
+
case request.method
|
119
|
+
when 'get', 'head'
|
120
|
+
check_max_redirects
|
121
|
+
follow_redirection(&block)
|
122
|
+
else
|
123
|
+
raise exception_with_response
|
124
|
+
end
|
125
|
+
when 303
|
126
|
+
check_max_redirects
|
127
|
+
follow_get_redirection(&block)
|
128
|
+
else
|
129
|
+
raise exception_with_response
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_i
|
134
|
+
warn('warning: calling Response#to_i is not recommended')
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def description
|
139
|
+
"#{code} #{STATUSES[code]} | #{(headers[:content_type] || '').gsub(/;.*$/, '')} #{size} bytes\n"
|
140
|
+
end
|
141
|
+
|
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)
|
156
|
+
end
|
157
|
+
|
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)
|
180
|
+
headers.inject({}) do |out, (key, value)|
|
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
|
+
|
190
|
+
out
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
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
|
239
|
+
end
|
240
|
+
end
|
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
|
252
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
module RestClient
|
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
|
+
#
|
17
|
+
STATUSES = {100 => 'Continue',
|
18
|
+
101 => 'Switching Protocols',
|
19
|
+
102 => 'Processing', #WebDAV
|
20
|
+
|
21
|
+
200 => 'OK',
|
22
|
+
201 => 'Created',
|
23
|
+
202 => 'Accepted',
|
24
|
+
203 => 'Non-Authoritative Information', # http/1.1
|
25
|
+
204 => 'No Content',
|
26
|
+
205 => 'Reset Content',
|
27
|
+
206 => 'Partial Content',
|
28
|
+
207 => 'Multi-Status', #WebDAV
|
29
|
+
208 => 'Already Reported', # RFC5842
|
30
|
+
226 => 'IM Used', # RFC3229
|
31
|
+
|
32
|
+
300 => 'Multiple Choices',
|
33
|
+
301 => 'Moved Permanently',
|
34
|
+
302 => 'Found',
|
35
|
+
303 => 'See Other', # http/1.1
|
36
|
+
304 => 'Not Modified',
|
37
|
+
305 => 'Use Proxy', # http/1.1
|
38
|
+
306 => 'Switch Proxy', # no longer used
|
39
|
+
307 => 'Temporary Redirect', # http/1.1
|
40
|
+
308 => 'Permanent Redirect', # RFC7538
|
41
|
+
|
42
|
+
400 => 'Bad Request',
|
43
|
+
401 => 'Unauthorized',
|
44
|
+
402 => 'Payment Required',
|
45
|
+
403 => 'Forbidden',
|
46
|
+
404 => 'Not Found',
|
47
|
+
405 => 'Method Not Allowed',
|
48
|
+
406 => 'Not Acceptable',
|
49
|
+
407 => 'Proxy Authentication Required',
|
50
|
+
408 => 'Request Timeout',
|
51
|
+
409 => 'Conflict',
|
52
|
+
410 => 'Gone',
|
53
|
+
411 => 'Length Required',
|
54
|
+
412 => 'Precondition Failed',
|
55
|
+
413 => 'Payload Too Large', # RFC7231 (renamed, see below)
|
56
|
+
414 => 'URI Too Long', # RFC7231 (renamed, see below)
|
57
|
+
415 => 'Unsupported Media Type',
|
58
|
+
416 => 'Range Not Satisfiable', # RFC7233 (renamed, see below)
|
59
|
+
417 => 'Expectation Failed',
|
60
|
+
418 => 'I\'m A Teapot', #RFC2324
|
61
|
+
421 => 'Too Many Connections From This IP',
|
62
|
+
422 => 'Unprocessable Entity', #WebDAV
|
63
|
+
423 => 'Locked', #WebDAV
|
64
|
+
424 => 'Failed Dependency', #WebDAV
|
65
|
+
425 => 'Unordered Collection', #WebDAV
|
66
|
+
426 => 'Upgrade Required',
|
67
|
+
428 => 'Precondition Required', #RFC6585
|
68
|
+
429 => 'Too Many Requests', #RFC6585
|
69
|
+
431 => 'Request Header Fields Too Large', #RFC6585
|
70
|
+
449 => 'Retry With', #Microsoft
|
71
|
+
450 => 'Blocked By Windows Parental Controls', #Microsoft
|
72
|
+
|
73
|
+
500 => 'Internal Server Error',
|
74
|
+
501 => 'Not Implemented',
|
75
|
+
502 => 'Bad Gateway',
|
76
|
+
503 => 'Service Unavailable',
|
77
|
+
504 => 'Gateway Timeout',
|
78
|
+
505 => 'HTTP Version Not Supported',
|
79
|
+
506 => 'Variant Also Negotiates',
|
80
|
+
507 => 'Insufficient Storage', #WebDAV
|
81
|
+
508 => 'Loop Detected', # RFC5842
|
82
|
+
509 => 'Bandwidth Limit Exceeded', #Apache
|
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
|
+
|
102
|
+
|
103
|
+
# This is the base RestClient exception class. Rescue it if you want to
|
104
|
+
# catch any exception that your request might raise
|
105
|
+
# You can get the status code by e.http_code, or see anything about the
|
106
|
+
# response via e.response.
|
107
|
+
# For example, the entire result body (which is
|
108
|
+
# probably an HTML error page) is e.response.
|
109
|
+
class Exception < RuntimeError
|
110
|
+
attr_accessor :response
|
111
|
+
attr_accessor :original_exception
|
112
|
+
attr_writer :message
|
113
|
+
|
114
|
+
def initialize response = nil, initial_response_code = nil
|
115
|
+
@response = response
|
116
|
+
@message = nil
|
117
|
+
@initial_response_code = initial_response_code
|
118
|
+
end
|
119
|
+
|
120
|
+
def http_code
|
121
|
+
# return integer for compatibility
|
122
|
+
if @response
|
123
|
+
@response.code.to_i
|
124
|
+
else
|
125
|
+
@initial_response_code
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def http_headers
|
130
|
+
@response.headers if @response
|
131
|
+
end
|
132
|
+
|
133
|
+
def http_body
|
134
|
+
@response.body if @response
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_s
|
138
|
+
message
|
139
|
+
end
|
140
|
+
|
141
|
+
def message
|
142
|
+
@message || default_message
|
143
|
+
end
|
144
|
+
|
145
|
+
def default_message
|
146
|
+
self.class.name
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Compatibility
|
151
|
+
class ExceptionWithResponse < RestClient::Exception
|
152
|
+
end
|
153
|
+
|
154
|
+
# The request failed with an error code not managed by the code
|
155
|
+
class RequestFailed < ExceptionWithResponse
|
156
|
+
|
157
|
+
def default_message
|
158
|
+
"HTTP status code #{http_code}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_s
|
162
|
+
message
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
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
|
+
#
|
171
|
+
module Exceptions
|
172
|
+
# Map http status codes to the corresponding exception class
|
173
|
+
EXCEPTIONS_MAP = {}
|
174
|
+
end
|
175
|
+
|
176
|
+
# Create HTTP status exception classes
|
177
|
+
STATUSES.each_pair do |code, message|
|
178
|
+
klass = Class.new(RequestFailed) do
|
179
|
+
send(:define_method, :default_message) {"#{http_code ? "#{http_code} " : ''}#{message}"}
|
180
|
+
end
|
181
|
+
klass_constant = const_set(message.delete(' \-\''), klass)
|
182
|
+
Exceptions::EXCEPTIONS_MAP[code] = klass_constant
|
183
|
+
end
|
184
|
+
|
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
|
192
|
+
|
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
|
209
|
+
|
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
|
217
|
+
|
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
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# The server broke the connection prior to the request completing. Usually
|
229
|
+
# this means it crashed, or sometimes that your network connection was
|
230
|
+
# severed before it could complete.
|
231
|
+
class ServerBrokeConnection < RestClient::Exception
|
232
|
+
def initialize(message = 'Server broke connection')
|
233
|
+
super nil, nil
|
234
|
+
self.message = message
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class SSLCertificateNotVerified < RestClient::Exception
|
239
|
+
def initialize(message = 'SSL certificate not verified')
|
240
|
+
super nil, nil
|
241
|
+
self.message = message
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|