rest-client 2.0.0.rc2-x64-mingw32 → 2.0.0.rc3-x64-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -127,6 +127,6 @@ Rake::RDocTask.new do |t|
127
127
  t.title = "rest-client, fetch RESTful resources effortlessly"
128
128
  t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
129
129
  t.options << '--charset' << 'utf-8'
130
- t.rdoc_files.include('README.rdoc')
130
+ t.rdoc_files.include('README.md')
131
131
  t.rdoc_files.include('lib/*.rb')
132
132
  end
data/history.md CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  This release is largely API compatible, but makes several breaking changes.
4
4
 
5
- - Drop support for Ruby 1.9.2
5
+ - Drop support for Ruby 1.9
6
+ - Allow mime-types as new as 3.x (requires ruby 2.0)
6
7
  - Respect Content-Type charset header provided by server. Previously,
7
8
  rest-client would not override the string encoding chosen by Net::HTTP. Now
8
9
  responses that specify a charset will yield a body string in that encoding.
@@ -20,8 +21,8 @@ This release is largely API compatible, but makes several breaking changes.
20
21
  inherits from `ExceptionWithResponse`. Previously, HTTP 304, 401, and 404
21
22
  inherited directly from `ExceptionWithResponse` rather than from
22
23
  `RequestFailed`. Now _all_ HTTP status code exceptions inherit from both.
23
- - Rename `:timeout` to `:read_timeout`. When `:timeout` is passed, now set both
24
- `:read_timeout` and `:open_timeout`.
24
+ - Rename the `:timeout` request option to `:read_timeout`. When `:timeout` is
25
+ passed, now set both `:read_timeout` and `:open_timeout`.
25
26
  - Change default HTTP Accept header to `*/*`
26
27
  - Use a more descriptive User-Agent header by default
27
28
  - Drop RC4-MD5 from default cipher list
@@ -37,12 +38,30 @@ This release is largely API compatible, but makes several breaking changes.
37
38
  - `Response#to_i` will now behave like `String#to_i` instead of returning the
38
39
  HTTP response code, which was very surprising behavior.
39
40
  - `Response#body` and `#to_s` will now return a true `String` object rather
40
- than self. Previously there was no easy way to get the true `String` response
41
- instead of the Frankenstein response string object with AbstractResponse
42
- mixed in.
41
+ than self. Previously there was no easy way to get the true `String`
42
+ response instead of the Frankenstein response string object with
43
+ AbstractResponse mixed in.
44
+ - Response objects no longer accept an extra request args hash, but instead
45
+ access request args directly from the request object, which reduces
46
+ confusion and duplication.
43
47
  - Handle multiple HTTP response headers with the same name (except for
44
48
  Set-Cookie, which is special) by joining the values with a comma space,
45
49
  compliant with RFC 7230
50
+ - Rewrite cookie support to be much smarter and to use cookie jars consistently
51
+ for requests, responses, and redirection (resolving long-standing complaints
52
+ about the previously broken behavior):
53
+ - The `:cookies` option may now be a Hash of Strings, an Array of
54
+ HTTP::Cookie objects, or a full HTTP::CookieJar.
55
+ - Add `RestClient::Request#cookie_jar` and reimplement `Request#cookies` to
56
+ be a wrapper around the cookie jar.
57
+ - Still support passing the `:cookies` option in the headers hash, but now
58
+ raise ArgumentError if that option is also passed to `Request#initialize`.
59
+ - Warn if both `:cookies` and a `Cookie` header are supplied.
60
+ - Use the `Request#cookie_jar` as the basis for `Response#cookie_jar`,
61
+ creating a copy of the jar and adding any newly received cookies.
62
+ - When following redirection, also use this same strategy so that cookies
63
+ from the original request are carried through in a standards-compliant way
64
+ by the cookie jar.
46
65
  - Don't set basic auth header if explicit `Authorization` header is specified
47
66
  - Add `:proxy` option to requests, which can be used for thread-safe
48
67
  per-request proxy configuration, overriding `RestClient.proxy`
@@ -54,16 +73,29 @@ This release is largely API compatible, but makes several breaking changes.
54
73
  treat any object that responds to `.read` as a streaming payload and pass it
55
74
  through to `.body_stream=` on the Net:HTTP object. This massively reduces the
56
75
  memory required for large file uploads.
57
- - Remove `RestClient::MaxRedirectsReached` in favor of the normal
58
- `ExceptionWithResponse` subclasses. This makes the response accessible on the
59
- exception object as `.response`, making it possible for callers to tell what
60
- has actually happened when the redirect limit is reached.
61
- - When following HTTP redirection, store a list of each previous response on
62
- the response object as `.history`. This makes it possible to access the
63
- original response headers and body before the redirection was followed.
76
+ - Changes to redirection behavior:
77
+ - Remove `RestClient::MaxRedirectsReached` in favor of the normal
78
+ `ExceptionWithResponse` subclasses. This makes the response accessible on
79
+ the exception object as `.response`, making it possible for callers to tell
80
+ what has actually happened when the redirect limit is reached.
81
+ - When following HTTP redirection, store a list of each previous response on
82
+ the response object as `.history`. This makes it possible to access the
83
+ original response headers and body before the redirection was followed.
84
+ - Follow redirection consistently, regardless of whether the HTTP method was
85
+ passed as a symbol or string. Under the hood rest-client now normalizes the
86
+ HTTP request method to a lowercase string.
64
87
  - Add `:before_execution_proc` option to `RestClient::Request`. This makes it
65
88
  possible to add procs like `RestClient.add_before_execution_proc` to a single
66
89
  request without global state.
90
+ - Run tests on Travis's beta OS X support.
91
+ - Make `Request#transmit` a private method, along with a few others.
92
+ - Refactor URI parsing to happen earlier, in Request initialization.
93
+ - When adding URL params, handle URLs that already contain params.
94
+ - Add a few more exception classes for obscure HTTP status codes.
95
+ - Multipart: use a much more robust multipart boundary with greater entropy.
96
+ - Make `RestClient::Payload::Base#inspect` stop pretending to be a String.
97
+ - Add `Request#redacted_uri` and `Request#redacted_url` to display the URI
98
+ with any password redacted.
67
99
 
68
100
  # 2.0.0.rc1
69
101
 
data/lib/restclient.rb CHANGED
@@ -13,6 +13,7 @@ require File.dirname(__FILE__) + '/restclient/abstract_response'
13
13
  require File.dirname(__FILE__) + '/restclient/response'
14
14
  require File.dirname(__FILE__) + '/restclient/raw_response'
15
15
  require File.dirname(__FILE__) + '/restclient/resource'
16
+ require File.dirname(__FILE__) + '/restclient/params_array'
16
17
  require File.dirname(__FILE__) + '/restclient/payload'
17
18
  require File.dirname(__FILE__) + '/restclient/windows'
18
19
 
@@ -93,8 +94,9 @@ module RestClient
93
94
  # A global proxy URL to use for all requests. This can be overridden on a
94
95
  # per-request basis by passing `:proxy` to RestClient::Request.
95
96
  def self.proxy
96
- @proxy
97
+ @proxy ||= nil
97
98
  end
99
+
98
100
  def self.proxy=(value)
99
101
  @proxy = value
100
102
  @proxy_set = true
@@ -106,7 +108,7 @@ module RestClient
106
108
  # @return [Boolean]
107
109
  #
108
110
  def self.proxy_set?
109
- !!@proxy_set
111
+ @proxy_set ||= false
110
112
  end
111
113
 
112
114
  # Setup the log for RestClient calls.
@@ -5,7 +5,7 @@ module RestClient
5
5
 
6
6
  module AbstractResponse
7
7
 
8
- attr_reader :net_http_res, :args, :request
8
+ attr_reader :net_http_res, :request
9
9
 
10
10
  def inspect
11
11
  raise NotImplementedError.new('must override in subclass')
@@ -31,20 +31,28 @@ module RestClient
31
31
  @raw_headers ||= @net_http_res.to_hash
32
32
  end
33
33
 
34
- def response_set_vars(net_http_res, args, request)
34
+ def response_set_vars(net_http_res, request)
35
35
  @net_http_res = net_http_res
36
- @args = args
37
36
  @request = request
38
37
 
39
38
  # prime redirection history
40
39
  history
41
40
  end
42
41
 
43
- # Hash of cookies extracted from response headers
42
+ # Hash of cookies extracted from response headers.
43
+ #
44
+ # NB: This will return only cookies whose domain matches this request, and
45
+ # may not even return all of those cookies if there are duplicate names.
46
+ # Use the full cookie_jar for more nuanced access.
47
+ #
48
+ # @see #cookie_jar
49
+ #
50
+ # @return [Hash]
51
+ #
44
52
  def cookies
45
53
  hash = {}
46
54
 
47
- cookie_jar.cookies.each do |cookie|
55
+ cookie_jar.cookies(@request.uri).each do |cookie|
48
56
  hash[cookie.name] = cookie.value
49
57
  end
50
58
 
@@ -56,28 +64,40 @@ module RestClient
56
64
  # @return [HTTP::CookieJar]
57
65
  #
58
66
  def cookie_jar
59
- return @cookie_jar if @cookie_jar
67
+ return @cookie_jar if defined?(@cookie_jar) && @cookie_jar
60
68
 
61
- jar = HTTP::CookieJar.new
69
+ jar = @request.cookie_jar.dup
62
70
  headers.fetch(:set_cookie, []).each do |cookie|
63
- jar.parse(cookie, @request.url)
71
+ jar.parse(cookie, @request.uri)
64
72
  end
65
73
 
66
74
  @cookie_jar = jar
67
75
  end
68
76
 
69
77
  # Return the default behavior corresponding to the response code:
70
- # 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
78
+ #
79
+ # For 20x status codes: return the response itself
80
+ #
81
+ # For 30x status codes:
82
+ # 301, 302, 307: redirect GET / HEAD if there is a Location header
83
+ # 303: redirect, changing method to GET, if there is a Location header
84
+ #
85
+ # For all other responses, raise a response exception
86
+ #
71
87
  def return!(&block)
72
- if (200..207).include? code
88
+ case code
89
+ when 200..207
73
90
  self
74
- elsif [301, 302, 307].include? code
75
- unless [:get, :head].include? args[:method]
76
- raise exception_with_response
77
- else
91
+ when 301, 302, 307
92
+ case request.method
93
+ when 'get', 'head'
94
+ check_max_redirects
78
95
  follow_redirection(&block)
96
+ else
97
+ raise exception_with_response
79
98
  end
80
- elsif code == 303
99
+ when 303
100
+ check_max_redirects
81
101
  follow_get_redirection(&block)
82
102
  else
83
103
  raise exception_with_response
@@ -96,13 +116,13 @@ module RestClient
96
116
  # Follow a redirection response by making a new HTTP request to the
97
117
  # redirection target.
98
118
  def follow_redirection(&block)
99
- _follow_redirection(@args.dup, &block)
119
+ _follow_redirection(request.args.dup, &block)
100
120
  end
101
121
 
102
122
  # Follow a redirection response, but change the HTTP method to GET and drop
103
123
  # the payload from the original request.
104
124
  def follow_get_redirection(&block)
105
- new_args = @args.dup
125
+ new_args = request.args.dup
106
126
  new_args[:method] = :get
107
127
  new_args.delete(:payload)
108
128
 
@@ -132,7 +152,7 @@ module RestClient
132
152
  #
133
153
  def self.beautify_headers(headers)
134
154
  headers.inject({}) do |out, (key, value)|
135
- key_sym = key.gsub(/-/, '_').downcase.to_sym
155
+ key_sym = key.tr('-', '_').downcase.to_sym
136
156
 
137
157
  # Handle Set-Cookie specially since it cannot be joined by comma.
138
158
  if key.downcase == 'set-cookie'
@@ -158,24 +178,24 @@ module RestClient
158
178
  # parse location header and merge into existing URL
159
179
  url = headers[:location]
160
180
 
181
+ # cannot follow redirection if there is no location header
182
+ unless url
183
+ raise exception_with_response
184
+ end
185
+
161
186
  # handle relative redirects
162
187
  unless url.start_with?('http')
163
188
  url = URI.parse(request.url).merge(url).to_s
164
189
  end
165
190
  new_args[:url] = url
166
191
 
167
- if request.max_redirects <= 0
168
- raise exception_with_response
169
- end
170
192
  new_args[:password] = request.password
171
193
  new_args[:user] = request.user
172
194
  new_args[:headers] = request.headers
173
195
  new_args[:max_redirects] = request.max_redirects - 1
174
196
 
175
- # TODO: figure out what to do with original :cookie, :cookies values
176
- new_args[:headers]['Cookie'] = HTTP::Cookie.cookie_value(
177
- cookie_jar.cookies(new_args.fetch(:url)))
178
-
197
+ # pass through our new cookie jar
198
+ new_args[:cookies] = cookie_jar
179
199
 
180
200
  # prepare new request
181
201
  new_req = Request.new(new_args)
@@ -187,6 +207,12 @@ module RestClient
187
207
  new_req.execute(&block)
188
208
  end
189
209
 
210
+ def check_max_redirects
211
+ if request.max_redirects <= 0
212
+ raise exception_with_response
213
+ end
214
+ end
215
+
190
216
  def exception_with_response
191
217
  begin
192
218
  klass = Exceptions::EXCEPTIONS_MAP.fetch(code)
@@ -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,6 +37,7 @@ 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
40
+ 308 => 'Permanent Redirect', # RFC7538
24
41
 
25
42
  400 => 'Bad Request',
26
43
  401 => 'Unauthorized',
@@ -35,10 +52,10 @@ 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
60
  418 => 'I\'m A Teapot', #RFC2324
44
61
  421 => 'Too Many Connections From This IP',
@@ -61,11 +78,28 @@ module RestClient
61
78
  505 => 'HTTP Version Not Supported',
62
79
  506 => 'Variant Also Negotiates',
63
80
  507 => 'Insufficient Storage', #WebDAV
81
+ 508 => 'Loop Detected', # RFC5842
64
82
  509 => 'Bandwidth Limit Exceeded', #Apache
65
83
  510 => 'Not Extended',
66
84
  511 => 'Network Authentication Required', # RFC6585
67
85
  }
68
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
+
69
103
  # This is the base RestClient exception class. Rescue it if you want to
70
104
  # catch any exception that your request might raise
71
105
  # You can get the status code by e.http_code, or see anything about the
@@ -148,8 +182,13 @@ module RestClient
148
182
  Exceptions::EXCEPTIONS_MAP[code] = klass_constant
149
183
  end
150
184
 
151
- # Backwards compatibility. "Not Found" is the actual text in the RFCs.
152
- ResourceNotFound = NotFound
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
153
192
 
154
193
  module Exceptions
155
194
  # We have to split the Exceptions module like we do here because the
@@ -197,7 +236,7 @@ module RestClient
197
236
  end
198
237
 
199
238
  class SSLCertificateNotVerified < Exception
200
- def initialize(message)
239
+ def initialize(message = 'SSL certificate not verified')
201
240
  super nil, nil
202
241
  self.message = message
203
242
  end
@@ -0,0 +1,72 @@
1
+ module RestClient
2
+
3
+ # The ParamsArray class is used to represent an ordered list of [key, value]
4
+ # pairs. Use this when you need to include a key multiple times or want
5
+ # explicit control over parameter ordering.
6
+ #
7
+ # Most of the request payload & parameter functions normally accept a Hash of
8
+ # keys => values, which does not allow for duplicated keys.
9
+ #
10
+ # @see RestClient::Utils.encode_query_string
11
+ # @see RestClient::Utils.flatten_params
12
+ #
13
+ class ParamsArray
14
+ include Enumerable
15
+
16
+ # @param array [Array<Array>] An array of parameter key,value pairs. These
17
+ # pairs may be 2 element arrays [key, value] or single element hashes
18
+ # {key => value}. They may also be single element arrays to represent a
19
+ # key with no value.
20
+ #
21
+ # @example
22
+ # >> ParamsArray.new([[:foo, 123], [:foo, 456], [:bar, 789]])
23
+ # This will be encoded as "foo=123&foo=456&bar=789"
24
+ #
25
+ # @example
26
+ # >> ParamsArray.new({foo: 123, bar: 456})
27
+ # This is valid, but there's no reason not to just use the Hash directly
28
+ # instead of a ParamsArray.
29
+ #
30
+ #
31
+ def initialize(array)
32
+ @array = process_input(array)
33
+ end
34
+
35
+ def each(*args, &blk)
36
+ @array.each(*args, &blk)
37
+ end
38
+
39
+ def empty?
40
+ @array.empty?
41
+ end
42
+
43
+ private
44
+
45
+ def process_input(array)
46
+ array.map {|v| process_pair(v) }
47
+ end
48
+
49
+ # A pair may be:
50
+ # - A single element hash, e.g. {foo: 'bar'}
51
+ # - A two element array, e.g. ['foo', 'bar']
52
+ # - A one element array, e.g. ['foo']
53
+ #
54
+ def process_pair(pair)
55
+ case pair
56
+ when Hash
57
+ if pair.length != 1
58
+ raise ArgumentError.new("Bad # of fields for pair: #{pair.inspect}")
59
+ end
60
+ pair.to_a.fetch(0)
61
+ when Array
62
+ if pair.length > 2
63
+ raise ArgumentError.new("Bad # of fields for pair: #{pair.inspect}")
64
+ end
65
+ [pair.fetch(0), pair[1]]
66
+ else
67
+ # recurse, converting any non-array to an array
68
+ process_pair(pair.to_a)
69
+ end
70
+ end
71
+ end
72
+ end