rest-client 1.8.0 → 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 +5 -5
- data/.gitignore +2 -0
- data/.mailmap +10 -0
- data/.rspec +2 -1
- data/.rubocop +2 -0
- data/.rubocop-disables.yml +386 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +56 -8
- data/AUTHORS +26 -1
- data/README.md +901 -0
- data/Rakefile +27 -3
- data/bin/restclient +3 -5
- data/history.md +181 -0
- data/lib/restclient/abstract_response.rb +172 -55
- data/lib/restclient/exceptions.rb +96 -55
- data/lib/restclient/params_array.rb +72 -0
- data/lib/restclient/payload.rb +70 -74
- data/lib/restclient/platform.rb +19 -0
- data/lib/restclient/raw_response.rb +21 -7
- data/lib/restclient/request.rb +540 -281
- data/lib/restclient/resource.rb +19 -9
- data/lib/restclient/response.rb +75 -6
- data/lib/restclient/utils.rb +274 -0
- data/lib/restclient/version.rb +2 -1
- data/lib/restclient.rb +21 -3
- data/rest-client.gemspec +12 -10
- 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/digicert.crt +20 -17
- data/spec/integration/certs/digicert.crt +20 -17
- data/spec/integration/httpbin_spec.rb +128 -0
- data/spec/integration/integration_spec.rb +97 -14
- data/spec/integration/request_spec.rb +25 -2
- data/spec/spec_helper.rb +28 -1
- data/spec/unit/_lib.rb +1 -0
- data/spec/unit/abstract_response_spec.rb +95 -38
- data/spec/unit/exceptions_spec.rb +41 -28
- data/spec/unit/params_array_spec.rb +36 -0
- data/spec/unit/payload_spec.rb +118 -68
- data/spec/unit/raw_response_spec.rb +10 -6
- data/spec/unit/request2_spec.rb +34 -12
- data/spec/unit/request_spec.rb +745 -424
- data/spec/unit/resource_spec.rb +31 -27
- data/spec/unit/response_spec.rb +134 -57
- data/spec/unit/restclient_spec.rb +16 -15
- data/spec/unit/utils_spec.rb +147 -0
- data/spec/unit/windows/root_certs_spec.rb +3 -3
- metadata +79 -29
- data/README.rdoc +0 -324
- data/spec/integration/capath_digicert/244b5494.0 +0 -19
- data/spec/integration/capath_digicert/81b9768f.0 +0 -19
- data/spec/unit/master_shake.jpg +0 -0
@@ -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
|
40
|
+
308 => 'Permanent Redirect', # RFC7538
|
24
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,10 +52,10 @@ 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
60
|
418 => 'I\'m A Teapot', #RFC2324
|
44
61
|
421 => 'Too Many Connections From This IP',
|
@@ -61,22 +78,27 @@ 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
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
+
|
80
102
|
|
81
103
|
# This is the base RestClient exception class. Rescue it if you want to
|
82
104
|
# catch any exception that your request might raise
|
@@ -86,15 +108,13 @@ module RestClient
|
|
86
108
|
# probably an HTML error page) is e.response.
|
87
109
|
class Exception < RuntimeError
|
88
110
|
attr_accessor :response
|
111
|
+
attr_accessor :original_exception
|
89
112
|
attr_writer :message
|
90
113
|
|
91
114
|
def initialize response = nil, initial_response_code = nil
|
92
115
|
@response = response
|
93
116
|
@message = nil
|
94
117
|
@initial_response_code = initial_response_code
|
95
|
-
|
96
|
-
# compatibility: this make the exception behave like a Net::HTTPResponse
|
97
|
-
response.extend ResponseForException if response
|
98
118
|
end
|
99
119
|
|
100
120
|
def http_code
|
@@ -106,32 +126,35 @@ module RestClient
|
|
106
126
|
end
|
107
127
|
end
|
108
128
|
|
109
|
-
def
|
110
|
-
@response.
|
129
|
+
def http_headers
|
130
|
+
@response.headers if @response
|
111
131
|
end
|
112
132
|
|
113
|
-
def
|
114
|
-
|
133
|
+
def http_body
|
134
|
+
@response.body if @response
|
115
135
|
end
|
116
136
|
|
117
137
|
def to_s
|
118
|
-
|
138
|
+
message
|
119
139
|
end
|
120
140
|
|
121
141
|
def message
|
122
|
-
@message ||
|
142
|
+
@message || default_message
|
123
143
|
end
|
124
144
|
|
145
|
+
def default_message
|
146
|
+
self.class.name
|
147
|
+
end
|
125
148
|
end
|
126
149
|
|
127
150
|
# Compatibility
|
128
|
-
class ExceptionWithResponse < Exception
|
151
|
+
class ExceptionWithResponse < RestClient::Exception
|
129
152
|
end
|
130
153
|
|
131
154
|
# The request failed with an error code not managed by the code
|
132
155
|
class RequestFailed < ExceptionWithResponse
|
133
156
|
|
134
|
-
def
|
157
|
+
def default_message
|
135
158
|
"HTTP status code #{http_code}"
|
136
159
|
end
|
137
160
|
|
@@ -140,64 +163,82 @@ module RestClient
|
|
140
163
|
end
|
141
164
|
end
|
142
165
|
|
143
|
-
#
|
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
|
+
#
|
144
171
|
module Exceptions
|
145
172
|
# Map http status codes to the corresponding exception class
|
146
173
|
EXCEPTIONS_MAP = {}
|
147
174
|
end
|
148
175
|
|
176
|
+
# Create HTTP status exception classes
|
149
177
|
STATUSES.each_pair do |code, message|
|
150
|
-
|
151
|
-
|
152
|
-
superclass = ([304, 401, 404].include? code) ? ExceptionWithResponse : RequestFailed
|
153
|
-
klass = Class.new(superclass) do
|
154
|
-
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}"}
|
155
180
|
end
|
156
|
-
klass_constant = const_set
|
181
|
+
klass_constant = const_set(message.delete(' \-\''), klass)
|
157
182
|
Exceptions::EXCEPTIONS_MAP[code] = klass_constant
|
158
183
|
end
|
159
184
|
|
160
|
-
#
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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)
|
165
190
|
end
|
191
|
+
end
|
166
192
|
|
167
|
-
|
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
|
168
209
|
|
169
|
-
|
170
|
-
|
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
|
171
216
|
end
|
172
|
-
end
|
173
217
|
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
177
224
|
end
|
178
225
|
end
|
179
226
|
|
227
|
+
|
180
228
|
# The server broke the connection prior to the request completing. Usually
|
181
229
|
# this means it crashed, or sometimes that your network connection was
|
182
230
|
# severed before it could complete.
|
183
|
-
class ServerBrokeConnection < Exception
|
231
|
+
class ServerBrokeConnection < RestClient::Exception
|
184
232
|
def initialize(message = 'Server broke connection')
|
185
233
|
super nil, nil
|
186
234
|
self.message = message
|
187
235
|
end
|
188
236
|
end
|
189
237
|
|
190
|
-
class SSLCertificateNotVerified < Exception
|
191
|
-
def initialize(message)
|
238
|
+
class SSLCertificateNotVerified < RestClient::Exception
|
239
|
+
def initialize(message = 'SSL certificate not verified')
|
192
240
|
super nil, nil
|
193
241
|
self.message = message
|
194
242
|
end
|
195
243
|
end
|
196
244
|
end
|
197
|
-
|
198
|
-
class RestClient::Request
|
199
|
-
# backwards compatibility
|
200
|
-
Redirect = RestClient::Redirect
|
201
|
-
Unauthorized = RestClient::Unauthorized
|
202
|
-
RequestFailed = RestClient::RequestFailed
|
203
|
-
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
|
data/lib/restclient/payload.rb
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
require 'tempfile'
|
2
|
+
require 'securerandom'
|
2
3
|
require 'stringio'
|
3
|
-
|
4
|
+
|
5
|
+
begin
|
6
|
+
# Use mime/types/columnar if available, for reduced memory usage
|
7
|
+
require 'mime/types/columnar'
|
8
|
+
rescue LoadError
|
9
|
+
require 'mime/types'
|
10
|
+
end
|
4
11
|
|
5
12
|
module RestClient
|
6
13
|
module Payload
|
7
14
|
extend self
|
8
15
|
|
9
16
|
def generate(params)
|
10
|
-
if params.is_a?(
|
17
|
+
if params.is_a?(RestClient::Payload::Base)
|
18
|
+
# pass through Payload objects unchanged
|
19
|
+
params
|
20
|
+
elsif params.is_a?(String)
|
11
21
|
Base.new(params)
|
12
22
|
elsif params.is_a?(Hash)
|
13
23
|
if params.delete(:multipart) == true || has_file?(params)
|
@@ -15,6 +25,12 @@ module RestClient
|
|
15
25
|
else
|
16
26
|
UrlEncoded.new(params)
|
17
27
|
end
|
28
|
+
elsif params.is_a?(ParamsArray)
|
29
|
+
if _has_file?(params)
|
30
|
+
Multipart.new(params)
|
31
|
+
else
|
32
|
+
UrlEncoded.new(params)
|
33
|
+
end
|
18
34
|
elsif params.respond_to?(:read)
|
19
35
|
Streamed.new(params)
|
20
36
|
else
|
@@ -23,28 +39,20 @@ module RestClient
|
|
23
39
|
end
|
24
40
|
|
25
41
|
def has_file?(params)
|
26
|
-
params.
|
27
|
-
|
28
|
-
when Hash
|
29
|
-
has_file?(v)
|
30
|
-
when Array
|
31
|
-
has_file_array?(v)
|
32
|
-
else
|
33
|
-
v.respond_to?(:path) && v.respond_to?(:read)
|
34
|
-
end
|
42
|
+
unless params.is_a?(Hash)
|
43
|
+
raise ArgumentError.new("Must pass Hash, not #{params.inspect}")
|
35
44
|
end
|
45
|
+
_has_file?(params)
|
36
46
|
end
|
37
47
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
v.respond_to?(:path) && v.respond_to?(:read)
|
47
|
-
end
|
48
|
+
def _has_file?(obj)
|
49
|
+
case obj
|
50
|
+
when Hash, ParamsArray
|
51
|
+
obj.any? {|_, v| _has_file?(v) }
|
52
|
+
when Array
|
53
|
+
obj.any? {|v| _has_file?(v) }
|
54
|
+
else
|
55
|
+
obj.respond_to?(:path) && obj.respond_to?(:read)
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
@@ -58,40 +66,13 @@ module RestClient
|
|
58
66
|
@stream.seek(0)
|
59
67
|
end
|
60
68
|
|
61
|
-
def read(
|
62
|
-
@stream.read(
|
63
|
-
end
|
64
|
-
|
65
|
-
alias :to_s :read
|
66
|
-
|
67
|
-
# Flatten parameters by converting hashes of hashes to flat hashes
|
68
|
-
# {keys1 => {keys2 => value}} will be transformed into [keys1[key2], value]
|
69
|
-
def flatten_params(params, parent_key = nil)
|
70
|
-
result = []
|
71
|
-
params.each do |key, value|
|
72
|
-
calculated_key = parent_key ? "#{parent_key}[#{handle_key(key)}]" : handle_key(key)
|
73
|
-
if value.is_a? Hash
|
74
|
-
result += flatten_params(value, calculated_key)
|
75
|
-
elsif value.is_a? Array
|
76
|
-
result += flatten_params_array(value, calculated_key)
|
77
|
-
else
|
78
|
-
result << [calculated_key, value]
|
79
|
-
end
|
80
|
-
end
|
81
|
-
result
|
69
|
+
def read(*args)
|
70
|
+
@stream.read(*args)
|
82
71
|
end
|
83
72
|
|
84
|
-
def
|
85
|
-
result =
|
86
|
-
|
87
|
-
if elem.is_a? Hash
|
88
|
-
result += flatten_params(elem, calculated_key)
|
89
|
-
elsif elem.is_a? Array
|
90
|
-
result += flatten_params_array(elem, calculated_key)
|
91
|
-
else
|
92
|
-
result << ["#{calculated_key}[]", elem]
|
93
|
-
end
|
94
|
-
end
|
73
|
+
def to_s
|
74
|
+
result = read
|
75
|
+
@stream.seek(0)
|
95
76
|
result
|
96
77
|
end
|
97
78
|
|
@@ -109,14 +90,20 @@ module RestClient
|
|
109
90
|
@stream.close unless @stream.closed?
|
110
91
|
end
|
111
92
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
93
|
+
def closed?
|
94
|
+
@stream.closed?
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s_inspect
|
98
|
+
to_s.inspect
|
116
99
|
end
|
117
100
|
|
118
101
|
def short_inspect
|
119
|
-
|
102
|
+
if size && size > 500
|
103
|
+
"#{size} byte(s) length"
|
104
|
+
else
|
105
|
+
to_s_inspect
|
106
|
+
end
|
120
107
|
end
|
121
108
|
|
122
109
|
end
|
@@ -134,42 +121,36 @@ module RestClient
|
|
134
121
|
end
|
135
122
|
end
|
136
123
|
|
124
|
+
# TODO (breaks compatibility): ought to use mime_for() to autodetect the
|
125
|
+
# Content-Type for stream objects that have a filename.
|
126
|
+
|
137
127
|
alias :length :size
|
138
128
|
end
|
139
129
|
|
140
130
|
class UrlEncoded < Base
|
141
131
|
def build_stream(params = nil)
|
142
|
-
@stream = StringIO.new(
|
143
|
-
"#{entry[0]}=#{handle_key(entry[1])}"
|
144
|
-
end.join("&"))
|
132
|
+
@stream = StringIO.new(Utils.encode_query_string(params))
|
145
133
|
@stream.seek(0)
|
146
134
|
end
|
147
135
|
|
148
|
-
# for UrlEncoded escape the keys
|
149
|
-
def handle_key key
|
150
|
-
Parser.escape(key.to_s, Escape)
|
151
|
-
end
|
152
|
-
|
153
136
|
def headers
|
154
137
|
super.merge({'Content-Type' => 'application/x-www-form-urlencoded'})
|
155
138
|
end
|
156
|
-
|
157
|
-
Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
158
|
-
Escape = Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
|
159
139
|
end
|
160
140
|
|
161
141
|
class Multipart < Base
|
162
142
|
EOL = "\r\n"
|
163
143
|
|
164
144
|
def build_stream(params)
|
165
|
-
b =
|
145
|
+
b = '--' + boundary
|
166
146
|
|
167
|
-
@stream = Tempfile.new(
|
147
|
+
@stream = Tempfile.new('rest-client.multipart.')
|
168
148
|
@stream.binmode
|
169
149
|
@stream.write(b + EOL)
|
170
150
|
|
171
|
-
|
172
|
-
|
151
|
+
case params
|
152
|
+
when Hash, ParamsArray
|
153
|
+
x = Utils.flatten_params(params)
|
173
154
|
else
|
174
155
|
x = params
|
175
156
|
end
|
@@ -218,10 +199,25 @@ module RestClient
|
|
218
199
|
end
|
219
200
|
|
220
201
|
def boundary
|
221
|
-
@boundary
|
202
|
+
return @boundary if defined?(@boundary) && @boundary
|
203
|
+
|
204
|
+
# Use the same algorithm used by WebKit: generate 16 random
|
205
|
+
# alphanumeric characters, replacing `+` `/` with `A` `B` (included in
|
206
|
+
# the list twice) to round out the set of 64.
|
207
|
+
s = SecureRandom.base64(12)
|
208
|
+
s.tr!('+/', 'AB')
|
209
|
+
|
210
|
+
@boundary = '----RubyFormBoundary' + s
|
222
211
|
end
|
223
212
|
|
224
213
|
# for Multipart do not escape the keys
|
214
|
+
#
|
215
|
+
# Ostensibly multipart keys MAY be percent encoded per RFC 7578, but in
|
216
|
+
# practice no major browser that I'm aware of uses percent encoding.
|
217
|
+
#
|
218
|
+
# Further discussion of multipart encoding:
|
219
|
+
# https://github.com/rest-client/rest-client/pull/403#issuecomment-156976930
|
220
|
+
#
|
225
221
|
def handle_key key
|
226
222
|
key
|
227
223
|
end
|
data/lib/restclient/platform.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
1
3
|
module RestClient
|
2
4
|
module Platform
|
3
5
|
# Return true if we are running on a darwin-based Ruby platform. This will
|
@@ -26,5 +28,22 @@ module RestClient
|
|
26
28
|
# defined on mri >= 1.9
|
27
29
|
RUBY_ENGINE == 'jruby'
|
28
30
|
end
|
31
|
+
|
32
|
+
def self.architecture
|
33
|
+
"#{RbConfig::CONFIG['host_os']} #{RbConfig::CONFIG['host_cpu']}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.ruby_agent_version
|
37
|
+
case RUBY_ENGINE
|
38
|
+
when 'jruby'
|
39
|
+
"jruby/#{JRUBY_VERSION} (#{RUBY_VERSION}p#{RUBY_PATCHLEVEL})"
|
40
|
+
else
|
41
|
+
"#{RUBY_ENGINE}/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.default_user_agent
|
46
|
+
"rest-client/#{VERSION} (#{architecture}) #{ruby_agent_version}"
|
47
|
+
end
|
29
48
|
end
|
30
49
|
end
|
@@ -13,22 +13,36 @@ module RestClient
|
|
13
13
|
|
14
14
|
include AbstractResponse
|
15
15
|
|
16
|
-
attr_reader :file, :request
|
16
|
+
attr_reader :file, :request, :start_time, :end_time
|
17
17
|
|
18
|
-
def
|
19
|
-
@
|
20
|
-
|
18
|
+
def inspect
|
19
|
+
"<RestClient::RawResponse @code=#{code.inspect}, @file=#{file.inspect}, @request=#{request.inspect}>"
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Tempfile] tempfile The temporary file containing the body
|
23
|
+
# @param [Net::HTTPResponse] net_http_res
|
24
|
+
# @param [RestClient::Request] request
|
25
|
+
# @param [Time] start_time
|
26
|
+
def initialize(tempfile, net_http_res, request, start_time=nil)
|
21
27
|
@file = tempfile
|
22
|
-
|
28
|
+
|
29
|
+
# reopen the tempfile so we can read it
|
30
|
+
@file.open
|
31
|
+
|
32
|
+
response_set_vars(net_http_res, request, start_time)
|
23
33
|
end
|
24
34
|
|
25
35
|
def to_s
|
26
|
-
|
36
|
+
body
|
37
|
+
end
|
38
|
+
|
39
|
+
def body
|
40
|
+
@file.rewind
|
27
41
|
@file.read
|
28
42
|
end
|
29
43
|
|
30
44
|
def size
|
31
|
-
|
45
|
+
file.size
|
32
46
|
end
|
33
47
|
|
34
48
|
end
|