rest-client 1.5.1 → 1.6.0.a
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rest-client might be problematic. Click here for more details.
- data/README.rdoc +13 -11
- data/VERSION +1 -1
- data/history.md +12 -0
- data/lib/rest-client.rb +2 -0
- data/lib/restclient.rb +0 -3
- data/lib/restclient/abstract_response.rb +14 -9
- data/lib/restclient/exceptions.rb +28 -6
- data/lib/restclient/payload.rb +33 -9
- data/lib/restclient/request.rb +42 -15
- data/spec/abstract_response_spec.rb +6 -1
- data/spec/payload_spec.rb +51 -0
- data/spec/request2_spec.rb +17 -0
- data/spec/request_spec.rb +2 -2
- data/spec/resource_spec.rb +10 -10
- data/spec/response_spec.rb +24 -5
- metadata +10 -6
data/README.rdoc
CHANGED
@@ -13,7 +13,9 @@ of specifying actions: get, put, post, delete.
|
|
13
13
|
|
14
14
|
RestClient.get 'http://example.com/resource'
|
15
15
|
|
16
|
-
RestClient.get '
|
16
|
+
RestClient.get 'http://example.com/resource', {:params => {:id => 50, 'foo' => 'bar'}}
|
17
|
+
|
18
|
+
RestClient.get 'https://user:password@example.com/private/resource', {:accept => :json}
|
17
19
|
|
18
20
|
RestClient.post 'http://example.com/resource', :param1 => 'one', :nested => { :param2 => 'two' }
|
19
21
|
|
@@ -77,8 +79,8 @@ See RestClient::Resource docs for details.
|
|
77
79
|
|
78
80
|
== Exceptions (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
|
79
81
|
|
80
|
-
* for results code between 200 and
|
81
|
-
* for results code 301
|
82
|
+
* for results code between 200 and 207 a RestClient::Response will be returned
|
83
|
+
* for results code 301, 302 or 307 the redirection will be followed if the request is a get or a head
|
82
84
|
* for result code 303 the redirection will be followed and the request transformed into a get
|
83
85
|
* for other cases a RestClient::Exception holding the Response will be raised, a specific exception class will be thrown for know error codes
|
84
86
|
|
@@ -98,11 +100,11 @@ A block can be passed to the RestClient method, this block will then be called w
|
|
98
100
|
Response.return! can be called to invoke the default response's behavior.
|
99
101
|
|
100
102
|
# Don't raise exceptions but return the response
|
101
|
-
RestClient.get('http://example.com/resource'){|response| response }
|
103
|
+
RestClient.get('http://example.com/resource'){|response, request| response }
|
102
104
|
➔ 404 Resource Not Found | text/html 282 bytes
|
103
105
|
|
104
106
|
# Manage a specific error code
|
105
|
-
RestClient.get('http://my-rest-service.com/resource'){ |response, &block|
|
107
|
+
RestClient.get('http://my-rest-service.com/resource'){ |response, request, &block|
|
106
108
|
case response.code
|
107
109
|
when 200
|
108
110
|
p "It worked !"
|
@@ -110,19 +112,19 @@ Response.return! can be called to invoke the default response's behavior.
|
|
110
112
|
when 423
|
111
113
|
raise SomeCustomExceptionIfYouWant
|
112
114
|
else
|
113
|
-
response.return! &block
|
115
|
+
response.return!(request, &block)
|
114
116
|
end
|
115
117
|
}
|
116
118
|
|
117
119
|
# Follow redirections for all request types and not only for get and head
|
118
|
-
# RFC : "If the 301
|
120
|
+
# RFC : "If the 301, 302 or 307 status code is received in response to a request other than GET or HEAD,
|
119
121
|
# the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user,
|
120
122
|
# since this might change the conditions under which the request was issued."
|
121
|
-
RestClient.get('http://my-rest-service.com/resource'){ |response, &block|
|
122
|
-
if [301, 302].include? response.code
|
123
|
-
response.follow_redirection &block
|
123
|
+
RestClient.get('http://my-rest-service.com/resource'){ |response, request, &block|
|
124
|
+
if [301, 302, 307].include? response.code
|
125
|
+
response.follow_redirection(request, &block)
|
124
126
|
else
|
125
|
-
response.return! &block
|
127
|
+
response.return!(request, &block)
|
126
128
|
end
|
127
129
|
}
|
128
130
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.6.0.a
|
data/history.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# 1.6.0
|
2
|
+
|
3
|
+
- forgot to include rest-client.rb in the gem
|
4
|
+
- user, password and user-defined headers should survive a redirect
|
5
|
+
- added all missing status codes
|
6
|
+
- added parameter passing for get request using the :param key in header
|
7
|
+
- the warning about the logger when using a string was a bad idea
|
8
|
+
- multipart parameters names should not be escaped
|
9
|
+
- remove the cookie escaping introduced by migrating to CGI cookie parsing in 1.5.1
|
10
|
+
- add a streamed payload type (patch provided by Caleb Land)
|
11
|
+
- Exception#http_body works even when no response
|
12
|
+
|
1
13
|
# 1.5.1
|
2
14
|
|
3
15
|
- only converts headers keys which are Symbols
|
data/lib/rest-client.rb
ADDED
data/lib/restclient.rb
CHANGED
@@ -92,9 +92,6 @@ module RestClient
|
|
92
92
|
# Value should be a logger but can can be stdout, stderr, or a filename.
|
93
93
|
# You can also configure logging by the environment variable RESTCLIENT_LOG.
|
94
94
|
def self.log= log
|
95
|
-
if log.is_a? String
|
96
|
-
warn "[warning] You should set the log with a logger"
|
97
|
-
end
|
98
95
|
@@log = create_log log
|
99
96
|
end
|
100
97
|
|
@@ -27,7 +27,7 @@ module RestClient
|
|
27
27
|
@cookies ||= (self.headers[:set_cookie] || {}).inject({}) do |out, cookie_content|
|
28
28
|
CGI::Cookie::parse(cookie_content).each do |key, cookie|
|
29
29
|
unless ['expires', 'path'].include? key
|
30
|
-
out[key] = cookie.value[0] || ''
|
30
|
+
out[CGI::escape(key)] = cookie.value[0] ? (CGI::escape(cookie.value[0]) || '') : ''
|
31
31
|
end
|
32
32
|
end
|
33
33
|
out
|
@@ -35,20 +35,20 @@ module RestClient
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# Return the default behavior corresponding to the response code:
|
38
|
-
# the response itself for code in 200..206, redirection for 301 and
|
39
|
-
def return! &block
|
40
|
-
if (200..
|
38
|
+
# 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
|
39
|
+
def return! request = nil, &block
|
40
|
+
if (200..207).include? code
|
41
41
|
self
|
42
|
-
elsif [301, 302].include? code
|
42
|
+
elsif [301, 302, 307].include? code
|
43
43
|
unless [:get, :head].include? args[:method]
|
44
44
|
raise Exceptions::EXCEPTIONS_MAP[code], self
|
45
45
|
else
|
46
|
-
follow_redirection(&block)
|
46
|
+
follow_redirection(request, &block)
|
47
47
|
end
|
48
48
|
elsif code == 303
|
49
49
|
args[:method] = :get
|
50
50
|
args.delete :payload
|
51
|
-
follow_redirection(&block)
|
51
|
+
follow_redirection(request, &block)
|
52
52
|
elsif Exceptions::EXCEPTIONS_MAP[code]
|
53
53
|
raise Exceptions::EXCEPTIONS_MAP[code], self
|
54
54
|
else
|
@@ -65,18 +65,23 @@ module RestClient
|
|
65
65
|
end
|
66
66
|
|
67
67
|
# Follow a redirection
|
68
|
-
def follow_redirection &block
|
68
|
+
def follow_redirection request = nil, &block
|
69
69
|
url = headers[:location]
|
70
70
|
if url !~ /^http/
|
71
71
|
url = URI.parse(args[:url]).merge(url).to_s
|
72
72
|
end
|
73
73
|
args[:url] = url
|
74
|
+
if request
|
75
|
+
args[:password] = request.password
|
76
|
+
args[:user] = request.user
|
77
|
+
args[:headers] = request.headers
|
78
|
+
end
|
74
79
|
Request.execute args, &block
|
75
80
|
end
|
76
81
|
|
77
82
|
def AbstractResponse.beautify_headers(headers)
|
78
83
|
headers.inject({}) do |out, (key, value)|
|
79
|
-
out[key.gsub(/-/, '_').downcase.to_sym] = %w{set-cookie}.include?(key.downcase) ? value : value.first
|
84
|
+
out[key.gsub(/-/, '_').downcase.to_sym] = %w{ set-cookie }.include?(key.downcase) ? value : value.first
|
80
85
|
out
|
81
86
|
end
|
82
87
|
end
|
@@ -2,21 +2,29 @@ module RestClient
|
|
2
2
|
|
3
3
|
STATUSES = {100 => 'Continue',
|
4
4
|
101 => 'Switching Protocols',
|
5
|
+
102 => 'Processing', #WebDAV
|
6
|
+
|
5
7
|
200 => 'OK',
|
6
8
|
201 => 'Created',
|
7
9
|
202 => 'Accepted',
|
8
|
-
203 => 'Non-Authoritative Information',
|
10
|
+
203 => 'Non-Authoritative Information', # http/1.1
|
9
11
|
204 => 'No Content',
|
10
12
|
205 => 'Reset Content',
|
11
13
|
206 => 'Partial Content',
|
14
|
+
207 => 'Multi-Status', #WebDAV
|
15
|
+
|
12
16
|
300 => 'Multiple Choices',
|
13
17
|
301 => 'Moved Permanently',
|
14
18
|
302 => 'Found',
|
15
|
-
303 => 'See Other',
|
19
|
+
303 => 'See Other', # http/1.1
|
16
20
|
304 => 'Not Modified',
|
17
|
-
305 => 'Use Proxy',
|
21
|
+
305 => 'Use Proxy', # http/1.1
|
22
|
+
306 => 'Switch Proxy', # no longer used
|
23
|
+
307 => 'Temporary Redirect', # http/1.1
|
24
|
+
|
18
25
|
400 => 'Bad Request',
|
19
26
|
401 => 'Unauthorized',
|
27
|
+
402 => 'Payment Required',
|
20
28
|
403 => 'Forbidden',
|
21
29
|
404 => 'Resource Not Found',
|
22
30
|
405 => 'Method Not Allowed',
|
@@ -32,12 +40,26 @@ module RestClient
|
|
32
40
|
415 => 'Unsupported Media Type',
|
33
41
|
416 => 'Requested Range Not Satisfiable',
|
34
42
|
417 => 'Expectation Failed',
|
43
|
+
418 => 'I\'m A Teapot',
|
44
|
+
421 => 'Too Many Connections From This IP',
|
45
|
+
422 => 'Unprocessable Entity', #WebDAV
|
46
|
+
423 => 'Locked', #WebDAV
|
47
|
+
424 => 'Failed Dependency', #WebDAV
|
48
|
+
425 => 'Unordered Collection', #WebDAV
|
49
|
+
426 => 'Upgrade Required',
|
50
|
+
449 => 'Retry With', #Microsoft
|
51
|
+
450 => 'Blocked By Windows Parental Controls', #Microsoft
|
52
|
+
|
35
53
|
500 => 'Internal Server Error',
|
36
54
|
501 => 'Not Implemented',
|
37
55
|
502 => 'Bad Gateway',
|
38
56
|
503 => 'Service Unavailable',
|
39
57
|
504 => 'Gateway Timeout',
|
40
|
-
505 => 'HTTP Version Not Supported'
|
58
|
+
505 => 'HTTP Version Not Supported',
|
59
|
+
506 => 'Variant Also Negotiates',
|
60
|
+
507 => 'Insufficient Storage', #WebDAV
|
61
|
+
509 => 'Bandwidth Limit Exceeded', #Apache
|
62
|
+
510 => 'Not Extended'}
|
41
63
|
|
42
64
|
# Compatibility : make the Response act like a Net::HTTPResponse when needed
|
43
65
|
module ResponseForException
|
@@ -73,7 +95,7 @@ module RestClient
|
|
73
95
|
end
|
74
96
|
|
75
97
|
def http_body
|
76
|
-
@response.body
|
98
|
+
@response.body if @response
|
77
99
|
end
|
78
100
|
|
79
101
|
def inspect
|
@@ -111,7 +133,7 @@ module RestClient
|
|
111
133
|
klass = Class.new(superclass) do
|
112
134
|
send(:define_method, :message) {message}
|
113
135
|
end
|
114
|
-
klass_constant = const_set message.
|
136
|
+
klass_constant = const_set message.delete(' \-\''), klass
|
115
137
|
Exceptions::EXCEPTIONS_MAP[code] = klass_constant
|
116
138
|
end
|
117
139
|
|
data/lib/restclient/payload.rb
CHANGED
@@ -9,6 +9,8 @@ module RestClient
|
|
9
9
|
def generate(params)
|
10
10
|
if params.is_a?(String)
|
11
11
|
Base.new(params)
|
12
|
+
elsif params.respond_to?(:read)
|
13
|
+
Streamed.new(params)
|
12
14
|
elsif params
|
13
15
|
if params.delete(:multipart) == true || has_file?(params)
|
14
16
|
Multipart.new(params)
|
@@ -47,16 +49,12 @@ module RestClient
|
|
47
49
|
|
48
50
|
alias :to_s :read
|
49
51
|
|
50
|
-
def escape(v)
|
51
|
-
URI.escape(v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
52
|
-
end
|
53
|
-
|
54
52
|
# Flatten parameters by converting hashes of hashes to flat hashes
|
55
53
|
# {keys1 => {keys2 => value}} will be transformed into [keys1[key2], value]
|
56
54
|
def flatten_params(params, parent_key = nil)
|
57
55
|
result = []
|
58
56
|
params.each do |key, value|
|
59
|
-
calculated_key = parent_key ? "#{parent_key}[#{
|
57
|
+
calculated_key = parent_key ? "#{parent_key}[#{handle_key(key)}]" : handle_key(key)
|
60
58
|
if value.is_a? Hash
|
61
59
|
result += flatten_params(value, calculated_key)
|
62
60
|
elsif value.is_a? Array
|
@@ -83,7 +81,7 @@ module RestClient
|
|
83
81
|
end
|
84
82
|
|
85
83
|
def headers
|
86
|
-
{
|
84
|
+
{'Content-Length' => size.to_s}
|
87
85
|
end
|
88
86
|
|
89
87
|
def size
|
@@ -108,16 +106,37 @@ module RestClient
|
|
108
106
|
|
109
107
|
end
|
110
108
|
|
109
|
+
class Streamed < Base
|
110
|
+
def build_stream(params = nil)
|
111
|
+
@stream = params
|
112
|
+
end
|
113
|
+
|
114
|
+
def size
|
115
|
+
if @stream.respond_to?(:size)
|
116
|
+
@stream.size
|
117
|
+
elsif @stream.is_a?(IO)
|
118
|
+
@stream.stat.size
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
alias :length :size
|
123
|
+
end
|
124
|
+
|
111
125
|
class UrlEncoded < Base
|
112
126
|
def build_stream(params = nil)
|
113
127
|
@stream = StringIO.new(flatten_params(params).collect do |entry|
|
114
|
-
"#{entry[0]}=#{
|
128
|
+
"#{entry[0]}=#{handle_key(entry[1])}"
|
115
129
|
end.join("&"))
|
116
130
|
@stream.seek(0)
|
117
131
|
end
|
118
132
|
|
133
|
+
# for UrlEncoded escape the keys
|
134
|
+
def handle_key key
|
135
|
+
URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
136
|
+
end
|
137
|
+
|
119
138
|
def headers
|
120
|
-
super.merge({
|
139
|
+
super.merge({'Content-Type' => 'application/x-www-form-urlencoded'})
|
121
140
|
end
|
122
141
|
end
|
123
142
|
|
@@ -139,7 +158,7 @@ module RestClient
|
|
139
158
|
|
140
159
|
last_index = x.length - 1
|
141
160
|
x.each_with_index do |a, index|
|
142
|
-
k, v = *a
|
161
|
+
k, v = * a
|
143
162
|
if v.respond_to?(:read) && v.respond_to?(:path)
|
144
163
|
create_file_field(@stream, k, v)
|
145
164
|
else
|
@@ -184,6 +203,11 @@ module RestClient
|
|
184
203
|
@boundary ||= rand(1_000_000).to_s
|
185
204
|
end
|
186
205
|
|
206
|
+
# for Multipart do not escape the keys
|
207
|
+
def handle_key key
|
208
|
+
key
|
209
|
+
end
|
210
|
+
|
187
211
|
def headers
|
188
212
|
super.merge({'Content-Type' => %Q{multipart/form-data; boundary=#{boundary}}})
|
189
213
|
end
|
data/lib/restclient/request.rb
CHANGED
@@ -27,14 +27,18 @@ module RestClient
|
|
27
27
|
:open_timeout, :raw_response, :verify_ssl, :ssl_client_cert,
|
28
28
|
:ssl_client_key, :ssl_ca_file, :processed_headers, :args
|
29
29
|
|
30
|
-
def self.execute(args, &block)
|
31
|
-
new(args).execute(&block)
|
30
|
+
def self.execute(args, & block)
|
31
|
+
new(args).execute(& block)
|
32
32
|
end
|
33
33
|
|
34
34
|
def initialize args
|
35
35
|
@method = args[:method] or raise ArgumentError, "must pass :method"
|
36
|
-
@url = args[:url] or raise ArgumentError, "must pass :url"
|
37
36
|
@headers = args[:headers] || {}
|
37
|
+
if args[:url]
|
38
|
+
@url = process_get_params(args[:url], headers)
|
39
|
+
else
|
40
|
+
raise ArgumentError, "must pass :url"
|
41
|
+
end
|
38
42
|
@cookies = @headers.delete(:cookies) || args[:cookies] || {}
|
39
43
|
@payload = Payload.generate(args[:payload])
|
40
44
|
@user = args[:user]
|
@@ -51,14 +55,37 @@ module RestClient
|
|
51
55
|
@args = args
|
52
56
|
end
|
53
57
|
|
54
|
-
def execute &block
|
58
|
+
def execute & block
|
55
59
|
uri = parse_url_with_auth(url)
|
56
|
-
transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, &block
|
60
|
+
transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, & block
|
61
|
+
end
|
62
|
+
|
63
|
+
# Extract the query parameters for get request and append them to the url
|
64
|
+
def process_get_params url, headers
|
65
|
+
if method == :get
|
66
|
+
get_params = {}
|
67
|
+
headers.delete_if do |key, value|
|
68
|
+
if 'params' == key.to_s.downcase && value.is_a?(Hash)
|
69
|
+
get_params.merge! value
|
70
|
+
true
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
unless get_params.empty?
|
76
|
+
query_string = get_params.collect { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
|
77
|
+
url + "?#{query_string}"
|
78
|
+
else
|
79
|
+
url
|
80
|
+
end
|
81
|
+
else
|
82
|
+
url
|
83
|
+
end
|
57
84
|
end
|
58
85
|
|
59
86
|
def make_headers user_headers
|
60
87
|
unless @cookies.empty?
|
61
|
-
user_headers[:cookie] = @cookies.map {|(key, val)| "#{key.to_s}=#{val}" }.sort.join(';')
|
88
|
+
user_headers[:cookie] = @cookies.map { |(key, val)| "#{key.to_s}=#{CGI::unescape(val)}" }.sort.join(';')
|
62
89
|
end
|
63
90
|
headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
|
64
91
|
headers.merge!(@payload.headers) if @payload
|
@@ -107,7 +134,7 @@ module RestClient
|
|
107
134
|
end
|
108
135
|
end
|
109
136
|
|
110
|
-
def transmit uri, req, payload, &block
|
137
|
+
def transmit uri, req, payload, & block
|
111
138
|
setup_credentials req
|
112
139
|
|
113
140
|
net = net_http_class.new(uri.host, uri.port)
|
@@ -139,7 +166,7 @@ module RestClient
|
|
139
166
|
net.start do |http|
|
140
167
|
res = http.request(req, payload) { |http_response| fetch_body(http_response) }
|
141
168
|
log_response res
|
142
|
-
process_result res, &block
|
169
|
+
process_result res, & block
|
143
170
|
end
|
144
171
|
rescue EOFError
|
145
172
|
raise RestClient::ServerBrokeConnection
|
@@ -179,7 +206,7 @@ module RestClient
|
|
179
206
|
http_response
|
180
207
|
end
|
181
208
|
|
182
|
-
def process_result res, &block
|
209
|
+
def process_result res, & block
|
183
210
|
if @raw_response
|
184
211
|
# We don't decode raw requests
|
185
212
|
response = RawResponse.new(@tf, res, args)
|
@@ -188,9 +215,9 @@ module RestClient
|
|
188
215
|
end
|
189
216
|
|
190
217
|
if block_given?
|
191
|
-
block.call
|
218
|
+
block.call(response, self, & block)
|
192
219
|
else
|
193
|
-
response.return!(&block)
|
220
|
+
response.return!(self, & block)
|
194
221
|
end
|
195
222
|
|
196
223
|
end
|
@@ -212,7 +239,7 @@ module RestClient
|
|
212
239
|
out = []
|
213
240
|
out << "RestClient.#{method} #{url.inspect}"
|
214
241
|
out << payload.short_inspect if payload
|
215
|
-
out << processed_headers.to_a.sort.map{|(k,v)| [k.inspect, v.inspect].join("=>")}.join(", ")
|
242
|
+
out << processed_headers.to_a.sort.map { |(k, v)| [k.inspect, v.inspect].join("=>") }.join(", ")
|
216
243
|
RestClient.log << out.join(', ') + "\n"
|
217
244
|
end
|
218
245
|
end
|
@@ -228,7 +255,7 @@ module RestClient
|
|
228
255
|
def stringify_headers headers
|
229
256
|
headers.inject({}) do |result, (key, value)|
|
230
257
|
if key.is_a? Symbol
|
231
|
-
key = key.to_s.split(/_/).map{|w| w.capitalize}.join('-')
|
258
|
+
key = key.to_s.split(/_/).map { |w| w.capitalize }.join('-')
|
232
259
|
end
|
233
260
|
if 'CONTENT-TYPE' == key.upcase
|
234
261
|
target_value = value.to_s
|
@@ -240,7 +267,7 @@ module RestClient
|
|
240
267
|
else
|
241
268
|
target_values = value.to_s.split ','
|
242
269
|
end
|
243
|
-
result[key] = target_values.map{ |ext| MIME::Types.type_for_extension(ext.to_s.strip)}.join(', ')
|
270
|
+
result[key] = target_values.map { |ext| MIME::Types.type_for_extension(ext.to_s.strip) }.join(', ')
|
244
271
|
else
|
245
272
|
result[key] = value.to_s
|
246
273
|
end
|
@@ -249,7 +276,7 @@ module RestClient
|
|
249
276
|
end
|
250
277
|
|
251
278
|
def default_headers
|
252
|
-
{
|
279
|
+
{:accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate'}
|
253
280
|
end
|
254
281
|
|
255
282
|
end
|
@@ -53,7 +53,12 @@ describe RestClient::AbstractResponse do
|
|
53
53
|
|
54
54
|
it "extract strange cookies" do
|
55
55
|
@net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/'])
|
56
|
-
@response.cookies.should == { 'session_id' => 'ZJ
|
56
|
+
@response.cookies.should == { 'session_id' => 'ZJ%2FHQVH6YE+rVkTpn0zvTQ%3D%3D' }
|
57
|
+
end
|
58
|
+
|
59
|
+
it "doesn't escape cookies" do
|
60
|
+
@net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca; path=/'])
|
61
|
+
@response.cookies.should == { 'session_id' => 'BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca' }
|
57
62
|
end
|
58
63
|
|
59
64
|
it "can access the net http result directly" do
|
data/spec/payload_spec.rb
CHANGED
@@ -14,6 +14,11 @@ describe RestClient::Payload do
|
|
14
14
|
RestClient::Payload::UrlEncoded.new({:foo => 'bar', :baz => 'qux'}).to_s)
|
15
15
|
end
|
16
16
|
|
17
|
+
it "should escape parameters" do
|
18
|
+
RestClient::Payload::UrlEncoded.new({'foo ' => 'bar'}).to_s.
|
19
|
+
should == "foo%20=bar"
|
20
|
+
end
|
21
|
+
|
17
22
|
it "should properly handle hashes as parameter" do
|
18
23
|
RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz' }}).to_s.
|
19
24
|
should == "foo[bar]=baz"
|
@@ -74,6 +79,17 @@ bar\r
|
|
74
79
|
EOS
|
75
80
|
end
|
76
81
|
|
82
|
+
it "should not escape parameters names" do
|
83
|
+
m = RestClient::Payload::Multipart.new([["bar ", "baz"]])
|
84
|
+
m.to_s.should == <<-EOS
|
85
|
+
--#{m.boundary}\r
|
86
|
+
Content-Disposition: form-data; name="bar "\r
|
87
|
+
\r
|
88
|
+
baz\r
|
89
|
+
--#{m.boundary}--\r
|
90
|
+
EOS
|
91
|
+
end
|
92
|
+
|
77
93
|
it "should form properly separated multipart data" do
|
78
94
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
79
95
|
m = RestClient::Payload::Multipart.new({:foo => f})
|
@@ -141,6 +157,33 @@ Content-Type: text/plain\r
|
|
141
157
|
|
142
158
|
end
|
143
159
|
|
160
|
+
context "streamed payloads" do
|
161
|
+
it "should properly determine the size of file payloads" do
|
162
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
163
|
+
payload = RestClient::Payload.generate(f)
|
164
|
+
payload.size.should == 22_545
|
165
|
+
payload.length.should == 22_545
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should properly determine the size of other kinds of streaming payloads" do
|
169
|
+
s = StringIO.new 'foo'
|
170
|
+
payload = RestClient::Payload.generate(s)
|
171
|
+
payload.size.should == 3
|
172
|
+
payload.length.should == 3
|
173
|
+
|
174
|
+
begin
|
175
|
+
f = Tempfile.new "rest-client"
|
176
|
+
f.write 'foo bar'
|
177
|
+
|
178
|
+
payload = RestClient::Payload.generate(f)
|
179
|
+
payload.size.should == 7
|
180
|
+
payload.length.should == 7
|
181
|
+
ensure
|
182
|
+
f.close
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
144
187
|
context "Payload generation" do
|
145
188
|
it "should recognize standard urlencoded params" do
|
146
189
|
RestClient::Payload.generate({"foo" => 'bar'}).should be_kind_of(RestClient::Payload::UrlEncoded)
|
@@ -164,5 +207,13 @@ Content-Type: text/plain\r
|
|
164
207
|
RestClient::Payload.generate({"foo" => {"file" => f}}).should be_kind_of(RestClient::Payload::Multipart)
|
165
208
|
end
|
166
209
|
|
210
|
+
it "should recognize file payloads that can be streamed" do
|
211
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
212
|
+
RestClient::Payload.generate(f).should be_kind_of(RestClient::Payload::Streamed)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should recognize other payloads that can be streamed" do
|
216
|
+
RestClient::Payload.generate(StringIO.new('foo')).should be_kind_of(RestClient::Payload::Streamed)
|
217
|
+
end
|
167
218
|
end
|
168
219
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
require 'webmock/rspec'
|
4
|
+
include WebMock
|
5
|
+
|
6
|
+
describe RestClient::Request do
|
7
|
+
|
8
|
+
it "manage params for get requests" do
|
9
|
+
stub_request(:get, 'http://some/resource?a=b&c=d').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar'}).to_return(:body => 'foo', :status => 200)
|
10
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => {:a => :b, 'c' => 'd'}}).body.should == 'foo'
|
11
|
+
|
12
|
+
stub_request(:get, 'http://some/resource').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar', 'params' => 'a'}).to_return(:body => 'foo', :status => 200)
|
13
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => :a}).body.should == 'foo'
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
data/spec/request_spec.rb
CHANGED
@@ -59,7 +59,7 @@ describe RestClient::Request do
|
|
59
59
|
end
|
60
60
|
|
61
61
|
it "doesn't classify successful requests as failed" do
|
62
|
-
203.upto(
|
62
|
+
203.upto(207) do |code|
|
63
63
|
res = mock("result")
|
64
64
|
res.stub!(:code).and_return(code.to_s)
|
65
65
|
res.stub!(:body).and_return("")
|
@@ -276,7 +276,7 @@ describe RestClient::Request do
|
|
276
276
|
describe "block usage" do
|
277
277
|
it "returns what asked to" do
|
278
278
|
res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
|
279
|
-
@request.process_result(res){|response| "foo"}.should == "foo"
|
279
|
+
@request.process_result(res){|response, request| "foo"}.should == "foo"
|
280
280
|
end
|
281
281
|
end
|
282
282
|
|
data/spec/resource_spec.rb
CHANGED
@@ -5,32 +5,32 @@ include WebMock
|
|
5
5
|
|
6
6
|
describe RestClient::Resource do
|
7
7
|
before do
|
8
|
-
@resource = RestClient::Resource.new('http://some/resource', :user => 'jane', :password => 'mypass', :headers => {
|
8
|
+
@resource = RestClient::Resource.new('http://some/resource', :user => 'jane', :password => 'mypass', :headers => {'X-Something' => '1'})
|
9
9
|
end
|
10
10
|
|
11
11
|
context "Resource delegation" do
|
12
12
|
it "GET" do
|
13
|
-
RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {
|
13
|
+
RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {'X-Something' => '1'}, :user => 'jane', :password => 'mypass')
|
14
14
|
@resource.get
|
15
15
|
end
|
16
16
|
|
17
17
|
it "POST" do
|
18
|
-
RestClient::Request.should_receive(:execute).with(:method => :post, :url => 'http://some/resource', :payload => 'abc', :headers => {
|
18
|
+
RestClient::Request.should_receive(:execute).with(:method => :post, :url => 'http://some/resource', :payload => 'abc', :headers => {:content_type => 'image/jpg', 'X-Something' => '1'}, :user => 'jane', :password => 'mypass')
|
19
19
|
@resource.post 'abc', :content_type => 'image/jpg'
|
20
20
|
end
|
21
21
|
|
22
22
|
it "PUT" do
|
23
|
-
RestClient::Request.should_receive(:execute).with(:method => :put, :url => 'http://some/resource', :payload => 'abc', :headers => {
|
23
|
+
RestClient::Request.should_receive(:execute).with(:method => :put, :url => 'http://some/resource', :payload => 'abc', :headers => {:content_type => 'image/jpg', 'X-Something' => '1'}, :user => 'jane', :password => 'mypass')
|
24
24
|
@resource.put 'abc', :content_type => 'image/jpg'
|
25
25
|
end
|
26
26
|
|
27
27
|
it "DELETE" do
|
28
|
-
RestClient::Request.should_receive(:execute).with(:method => :delete, :url => 'http://some/resource', :headers => {
|
28
|
+
RestClient::Request.should_receive(:execute).with(:method => :delete, :url => 'http://some/resource', :headers => {'X-Something' => '1'}, :user => 'jane', :password => 'mypass')
|
29
29
|
@resource.delete
|
30
30
|
end
|
31
31
|
|
32
32
|
it "overrides resource headers" do
|
33
|
-
RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {
|
33
|
+
RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {'X-Something' => '2'}, :user => 'jane', :password => 'mypass')
|
34
34
|
@resource.get 'X-Something' => '2'
|
35
35
|
end
|
36
36
|
end
|
@@ -79,20 +79,20 @@ describe RestClient::Resource do
|
|
79
79
|
describe 'block' do
|
80
80
|
it 'can use block when creating the resource' do
|
81
81
|
stub_request(:get, 'www.example.com').to_return(:body => '', :status => 404)
|
82
|
-
resource = RestClient::Resource.new('www.example.com'){|response| 'foo'}
|
82
|
+
resource = RestClient::Resource.new('www.example.com') { |response, request| 'foo' }
|
83
83
|
resource.get.should == 'foo'
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'can use block when executing the resource' do
|
87
87
|
stub_request(:get, 'www.example.com').to_return(:body => '', :status => 404)
|
88
88
|
resource = RestClient::Resource.new('www.example.com')
|
89
|
-
resource.get{|response| 'foo'}.should == 'foo'
|
89
|
+
resource.get { |response, request| 'foo' }.should == 'foo'
|
90
90
|
end
|
91
91
|
|
92
92
|
it 'execution block override resource block' do
|
93
93
|
stub_request(:get, 'www.example.com').to_return(:body => '', :status => 404)
|
94
|
-
resource = RestClient::Resource.new('www.example.com'){|response| 'foo'}
|
95
|
-
resource.get{|response| 'bar'}.should == 'bar'
|
94
|
+
resource = RestClient::Resource.new('www.example.com') { |response, request| 'foo' }
|
95
|
+
resource.get { |response, request| 'bar' }.should == 'bar'
|
96
96
|
end
|
97
97
|
|
98
98
|
end
|
data/spec/response_spec.rb
CHANGED
@@ -6,6 +6,7 @@ include WebMock
|
|
6
6
|
describe RestClient::Response do
|
7
7
|
before do
|
8
8
|
@net_http_res = mock('net http response', :to_hash => {"Status" => ["200 OK"]}, :code => 200)
|
9
|
+
@request = mock('http request', :user => nil, :password => nil)
|
9
10
|
@response = RestClient::Response.create('abc', @net_http_res, {})
|
10
11
|
end
|
11
12
|
|
@@ -59,13 +60,13 @@ describe RestClient::Response do
|
|
59
60
|
(200..206).each do |code|
|
60
61
|
net_http_res = mock('net http response', :code => '200')
|
61
62
|
response = RestClient::Response.create('abc', net_http_res, {})
|
62
|
-
response.return!
|
63
|
+
response.return! @request
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
66
67
|
it "should throw an exception for other codes" do
|
67
68
|
RestClient::Exceptions::EXCEPTIONS_MAP.each_key do |code|
|
68
|
-
unless (200..
|
69
|
+
unless (200..207).include? code
|
69
70
|
net_http_res = mock('net http response', :code => code.to_i)
|
70
71
|
response = RestClient::Response.create('abc', net_http_res, {})
|
71
72
|
lambda { response.return!}.should raise_error
|
@@ -83,16 +84,34 @@ describe RestClient::Response do
|
|
83
84
|
RestClient::Request.execute(:url => 'http://some/resource', :method => :get).body.should == 'Foo'
|
84
85
|
end
|
85
86
|
|
86
|
-
it "
|
87
|
+
it "follows a redirection and keep the parameters" do
|
88
|
+
stub_request(:get, 'http://foo:bar@some/resource').with(:headers => {'Accept' => 'application/json'}).to_return(:body => '', :status => 301, :headers => {'Location' => 'http://new/resource'})
|
89
|
+
stub_request(:get, 'http://foo:bar@new/resource').with(:headers => {'Accept' => 'application/json'}).to_return(:body => 'Foo')
|
90
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :user => 'foo', :password => 'bar', :headers => {:accept => :json}).body.should == 'Foo'
|
91
|
+
end
|
92
|
+
|
93
|
+
it "doesn't follow a 301 when the request is a post" do
|
87
94
|
net_http_res = mock('net http response', :code => 301)
|
88
95
|
response = RestClient::Response.create('abc', net_http_res, {:method => :post})
|
89
|
-
lambda { response.return!}.should raise_error(RestClient::MovedPermanently)
|
96
|
+
lambda { response.return!(@request)}.should raise_error(RestClient::MovedPermanently)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "doesn't follow a 302 when the request is a post" do
|
100
|
+
net_http_res = mock('net http response', :code => 302)
|
101
|
+
response = RestClient::Response.create('abc', net_http_res, {:method => :post})
|
102
|
+
lambda { response.return!(@request)}.should raise_error(RestClient::Found)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "doesn't follow a 307 when the request is a post" do
|
106
|
+
net_http_res = mock('net http response', :code => 307)
|
107
|
+
response = RestClient::Response.create('abc', net_http_res, {:method => :post})
|
108
|
+
lambda { response.return!(@request)}.should raise_error(RestClient::TemporaryRedirect)
|
90
109
|
end
|
91
110
|
|
92
111
|
it "doesn't follow a redirection when the request is a put" do
|
93
112
|
net_http_res = mock('net http response', :code => 301)
|
94
113
|
response = RestClient::Response.create('abc', net_http_res, {:method => :put})
|
95
|
-
lambda { response.return!}.should raise_error(RestClient::MovedPermanently)
|
114
|
+
lambda { response.return!(@request)}.should raise_error(RestClient::MovedPermanently)
|
96
115
|
end
|
97
116
|
|
98
117
|
it "follows a redirection when the request is a post and result is a 303" do
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 10
|
5
|
+
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
- a
|
11
|
+
version: 1.6.0.a
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- Adam Wiggins
|
@@ -16,7 +17,7 @@ autorequire:
|
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date: 2010-
|
20
|
+
date: 2010-06-28 00:00:00 +02:00
|
20
21
|
default_executable: restclient
|
21
22
|
dependencies:
|
22
23
|
- !ruby/object:Gem::Dependency
|
@@ -49,6 +50,7 @@ files:
|
|
49
50
|
- VERSION
|
50
51
|
- bin/restclient
|
51
52
|
- lib/rest_client.rb
|
53
|
+
- lib/rest-client.rb
|
52
54
|
- lib/restclient.rb
|
53
55
|
- lib/restclient/exceptions.rb
|
54
56
|
- lib/restclient/abstract_response.rb
|
@@ -66,6 +68,7 @@ files:
|
|
66
68
|
- spec/payload_spec.rb
|
67
69
|
- spec/raw_response_spec.rb
|
68
70
|
- spec/request_spec.rb
|
71
|
+
- spec/request2_spec.rb
|
69
72
|
- spec/resource_spec.rb
|
70
73
|
- spec/response_spec.rb
|
71
74
|
- spec/restclient_spec.rb
|
@@ -115,6 +118,7 @@ test_files:
|
|
115
118
|
- spec/payload_spec.rb
|
116
119
|
- spec/raw_response_spec.rb
|
117
120
|
- spec/request_spec.rb
|
121
|
+
- spec/request2_spec.rb
|
118
122
|
- spec/resource_spec.rb
|
119
123
|
- spec/response_spec.rb
|
120
124
|
- spec/restclient_spec.rb
|