rest-client 1.6.7 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.mailmap +10 -0
  4. data/.rspec +2 -0
  5. data/.rubocop +2 -0
  6. data/.rubocop-disables.yml +386 -0
  7. data/.rubocop.yml +8 -0
  8. data/.travis.yml +62 -0
  9. data/AUTHORS +106 -0
  10. data/Gemfile +11 -0
  11. data/LICENSE +21 -0
  12. data/README.md +901 -0
  13. data/Rakefile +109 -35
  14. data/bin/restclient +11 -12
  15. data/history.md +244 -1
  16. data/lib/restclient.rb +27 -18
  17. data/lib/restclient/abstract_response.rb +197 -51
  18. data/lib/restclient/exceptions.rb +110 -59
  19. data/lib/restclient/params_array.rb +72 -0
  20. data/lib/restclient/payload.rb +74 -75
  21. data/lib/restclient/platform.rb +49 -0
  22. data/lib/restclient/raw_response.rb +21 -6
  23. data/lib/restclient/request.rb +747 -183
  24. data/lib/restclient/resource.rb +22 -13
  25. data/lib/restclient/response.rb +75 -9
  26. data/lib/restclient/utils.rb +274 -0
  27. data/lib/restclient/version.rb +8 -0
  28. data/lib/restclient/windows.rb +8 -0
  29. data/lib/restclient/windows/root_certs.rb +105 -0
  30. data/rest-client.gemspec +32 -0
  31. data/rest-client.windows.gemspec +19 -0
  32. data/spec/ISS.jpg +0 -0
  33. data/spec/helpers.rb +54 -0
  34. data/spec/integration/_lib.rb +1 -0
  35. data/spec/integration/capath_digicert/3513523f.0 +22 -0
  36. data/spec/integration/capath_digicert/399e7759.0 +22 -0
  37. data/spec/integration/capath_digicert/README +8 -0
  38. data/spec/integration/capath_digicert/digicert.crt +22 -0
  39. data/spec/integration/capath_verisign/415660c1.0 +14 -0
  40. data/spec/integration/capath_verisign/7651b327.0 +14 -0
  41. data/spec/integration/capath_verisign/README +8 -0
  42. data/spec/integration/capath_verisign/verisign.crt +14 -0
  43. data/spec/integration/certs/digicert.crt +22 -0
  44. data/spec/integration/httpbin_spec.rb +128 -0
  45. data/spec/integration/integration_spec.rb +118 -0
  46. data/spec/integration/request_spec.rb +109 -7
  47. data/spec/spec_helper.rb +29 -0
  48. data/spec/unit/_lib.rb +1 -0
  49. data/spec/unit/abstract_response_spec.rb +145 -0
  50. data/spec/unit/exceptions_spec.rb +108 -0
  51. data/spec/unit/params_array_spec.rb +36 -0
  52. data/spec/unit/payload_spec.rb +295 -0
  53. data/spec/unit/raw_response_spec.rb +22 -0
  54. data/spec/unit/request2_spec.rb +54 -0
  55. data/spec/unit/request_spec.rb +1238 -0
  56. data/spec/unit/resource_spec.rb +134 -0
  57. data/spec/unit/response_spec.rb +252 -0
  58. data/spec/unit/restclient_spec.rb +80 -0
  59. data/spec/unit/utils_spec.rb +147 -0
  60. data/spec/unit/windows/root_certs_spec.rb +22 -0
  61. metadata +265 -117
  62. data/README.rdoc +0 -285
  63. data/VERSION +0 -1
  64. data/lib/restclient/net_http_ext.rb +0 -55
  65. data/spec/abstract_response_spec.rb +0 -85
  66. data/spec/base.rb +0 -16
  67. data/spec/exceptions_spec.rb +0 -98
  68. data/spec/integration/certs/equifax.crt +0 -19
  69. data/spec/integration_spec.rb +0 -38
  70. data/spec/master_shake.jpg +0 -0
  71. data/spec/payload_spec.rb +0 -234
  72. data/spec/raw_response_spec.rb +0 -17
  73. data/spec/request2_spec.rb +0 -40
  74. data/spec/request_spec.rb +0 -529
  75. data/spec/resource_spec.rb +0 -134
  76. data/spec/response_spec.rb +0 -169
  77. data/spec/restclient_spec.rb +0 -73
data/lib/restclient.rb CHANGED
@@ -1,22 +1,20 @@
1
- require 'uri'
2
- require 'zlib'
1
+ require 'net/http'
2
+ require 'openssl'
3
3
  require 'stringio'
4
+ require 'uri'
4
5
 
5
- begin
6
- require 'net/https'
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/net_http_ext'
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
- class << self
96
- attr_accessor :proxy
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, :args
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
- # Hash of cookies extracted from response headers
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
- @cookies ||= (self.headers[:set_cookie] || {}).inject({}) do |out, cookie_content|
28
- out.merge parse_cookie(cookie_content)
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
- # the response itself for code in 200..206, redirection for 301, 302 and 307 in get and head cases, redirection for 303 and an exception in other cases
34
- def return! request = nil, result = nil, & block
35
- if (200..207).include? 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
36
116
  self
37
- elsif [301, 302, 307].include? code
38
- unless [:get, :head].include? args[:method]
39
- raise Exceptions::EXCEPTIONS_MAP[code].new(self, code)
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
- follow_redirection(request, result, & block)
123
+ raise exception_with_response
42
124
  end
43
- elsif code == 303
44
- args[:method] = :get
45
- args.delete :payload
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 RequestFailed.new(self, code)
129
+ raise exception_with_response
51
130
  end
52
131
  end
53
132
 
54
133
  def to_i
55
- code
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
- def follow_redirection request = nil, result = nil, & block
64
- url = headers[:location]
65
- if url !~ /^http/
66
- url = URI.parse(args[:url]).merge(url).to_s
67
- end
68
- args[:url] = url
69
- if request
70
- if request.max_redirects == 0
71
- raise MaxRedirectsReached
72
- end
73
- args[:password] = request.password
74
- args[:user] = request.user
75
- args[:headers] = request.headers
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
- def AbstractResponse.beautify_headers(headers)
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
- out[key.gsub(/-/, '_').downcase.to_sym] = %w{ set-cookie }.include?(key.downcase) ? value : value.first
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
- # Parse a cookie value and return its content in an Hash
95
- def parse_cookie cookie_content
96
- out = {}
97
- CGI::Cookie::parse(cookie_content).each do |key, cookie|
98
- unless ['expires', 'path'].include? key
99
- out[CGI::escape(key)] = cookie.value[0] ? (CGI::escape(cookie.value[0]) || '') : ''
100
- end
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 => 'Resource Not Found',
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 => 'Request Entity Too Large',
39
- 414 => 'Request-URI Too Long',
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 => 'Requested Range Not Satisfiable',
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
- # Compatibility : make the Response act like a Net::HTTPResponse when needed
65
- module ResponseForException
66
- def method_missing symbol, *args
67
- if net_http_res.respond_to? symbol
68
- warn "[warning] The response contained in an RestClient::Exception is now a RestClient::Response instead of a Net::HTTPResponse, please update your code"
69
- net_http_res.send symbol, *args
70
- else
71
- super
72
- end
73
- end
74
- end
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
- attr_writer :message
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 http_body
104
- @response.body if @response
129
+ def http_headers
130
+ @response.headers if @response
105
131
  end
106
132
 
107
- def inspect
108
- "#{message}: #{http_body}"
133
+ def http_body
134
+ @response.body if @response
109
135
  end
110
136
 
111
137
  def to_s
112
- inspect
138
+ message
113
139
  end
114
-
140
+
115
141
  def message
116
- @message || self.class.name
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 message
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
- # We will a create an exception for each status code, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
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
- # Compatibility
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 message.delete(' \-\''), klass
181
+ klass_constant = const_set(message.delete(' \-\''), klass)
151
182
  Exceptions::EXCEPTIONS_MAP[code] = klass_constant
152
183
  end
153
184
 
154
- # A redirect was encountered; caught by execute to retry with the new url.
155
- class Redirect < Exception
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
- message = 'Redirect'
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
- attr_accessor :url
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
- def initialize(url)
162
- @url = url
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