rest-client 1.3.1 → 1.4.0.a
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.
Potentially problematic release.
This version of rest-client might be problematic. Click here for more details.
- data/README.rdoc +41 -12
- data/Rakefile +26 -23
- data/VERSION +1 -1
- data/history.md +13 -0
- data/lib/restclient.rb +14 -1
- data/lib/restclient/abstract_response.rb +87 -0
- data/lib/restclient/exceptions.rb +41 -30
- data/lib/restclient/payload.rb +13 -10
- data/lib/restclient/raw_response.rb +7 -6
- data/lib/restclient/request.rb +16 -35
- data/lib/restclient/response.rb +36 -12
- data/spec/{mixin/response_spec.rb → abstract_response_spec.rb} +3 -12
- data/spec/integration_spec.rb +3 -3
- data/spec/payload_spec.rb +9 -0
- data/spec/raw_response_spec.rb +1 -1
- data/spec/request_spec.rb +8 -42
- data/spec/response_spec.rb +68 -10
- metadata +23 -12
- data/lib/restclient/mixin/response.rb +0 -64
data/README.rdoc
CHANGED
@@ -49,11 +49,12 @@ See RestClient::Resource module docs for details.
|
|
49
49
|
|
50
50
|
See RestClient::Resource docs for details.
|
51
51
|
|
52
|
-
== Exceptions
|
52
|
+
== Exceptions (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
|
53
53
|
|
54
54
|
* for results code between 200 and 206 a RestClient::Response will be returned
|
55
|
-
* for results code
|
56
|
-
* for
|
55
|
+
* for results code 301 and 302 the redirection will be followed if the request is a get or a head
|
56
|
+
* for result code 303 the redirection will be followed and the request transformed into a get
|
57
|
+
* for other cases a RestClient::Exception holding the Response will be raised, a specific exception class will be thrown for know error codes
|
57
58
|
|
58
59
|
RestClient.get 'http://example.com/resource'
|
59
60
|
➔ RestClient::ResourceNotFound: RestClient::ResourceNotFound
|
@@ -63,19 +64,19 @@ See RestClient::Resource docs for details.
|
|
63
64
|
rescue => e
|
64
65
|
e.response
|
65
66
|
end
|
66
|
-
➔
|
67
|
+
➔ 404 Resource Not Found | text/html 282 bytes
|
67
68
|
|
68
69
|
== Result handling
|
69
70
|
|
70
71
|
A block can be passed to the RestClient method, this block will then be called with the Response.
|
71
|
-
Response.return! can be called to invoke the default response's behavior
|
72
|
+
Response.return! can be called to invoke the default response's behavior.
|
72
73
|
|
73
74
|
# Don't raise exceptions but return the response
|
74
|
-
RestClient.get('http://example.com/resource'){|response| response}
|
75
|
-
➔
|
75
|
+
RestClient.get('http://example.com/resource'){|response| response }
|
76
|
+
➔ 404 Resource Not Found | text/html 282 bytes
|
76
77
|
|
77
78
|
# Manage a specific error code
|
78
|
-
RestClient.get('http://my-rest-service.com/resource'){ |response|
|
79
|
+
RestClient.get('http://my-rest-service.com/resource'){ |response, &block|
|
79
80
|
case response.code
|
80
81
|
when 200
|
81
82
|
p "It worked !"
|
@@ -83,7 +84,19 @@ Response.return! can be called to invoke the default response's behavior (return
|
|
83
84
|
when 423
|
84
85
|
raise SomeCustomExceptionIfYouWant
|
85
86
|
else
|
86
|
-
response.return!
|
87
|
+
response.return! &block
|
88
|
+
end
|
89
|
+
}
|
90
|
+
|
91
|
+
# Follow redirections for all request types and not only for get and head
|
92
|
+
# RFC : "If the 301 (or 302) status code is received in response to a request other than GET or HEAD,
|
93
|
+
# the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user,
|
94
|
+
# since this might change the conditions under which the request was issued."
|
95
|
+
RestClient.get('http://my-rest-service.com/resource'){ |response, &block|
|
96
|
+
if [301, 302].include? response.code
|
97
|
+
response.follow_redirection &block
|
98
|
+
else
|
99
|
+
response.return! &block
|
87
100
|
end
|
88
101
|
}
|
89
102
|
|
@@ -103,17 +116,17 @@ For cases not covered by the general API, you can use the RestClient::Resource c
|
|
103
116
|
The restclient shell command gives an IRB session with RestClient already loaded:
|
104
117
|
|
105
118
|
$ restclient
|
106
|
-
|
119
|
+
>> RestClient.get 'http://example.com'
|
107
120
|
|
108
121
|
Specify a URL argument for get/post/put/delete on that resource:
|
109
122
|
|
110
123
|
$ restclient http://example.com
|
111
|
-
|
124
|
+
>> put '/resource', 'data'
|
112
125
|
|
113
126
|
Add a user and password for authenticated resources:
|
114
127
|
|
115
128
|
$ restclient https://example.com user pass
|
116
|
-
|
129
|
+
>> delete '/private/resource'
|
117
130
|
|
118
131
|
Create ~/.restclient for named sessions:
|
119
132
|
|
@@ -197,6 +210,22 @@ extract and set headers for them as needed:
|
|
197
210
|
|
198
211
|
Self-signed certificates can be generated with the openssl command-line tool.
|
199
212
|
|
213
|
+
== Hook
|
214
|
+
|
215
|
+
RestClient.add_before_execution_proc add a Proc to be called before each execution, it's handy if you need a direct access to the http request.
|
216
|
+
|
217
|
+
Example:
|
218
|
+
|
219
|
+
# Add oath support using the oauth gem
|
220
|
+
require 'oauth'
|
221
|
+
access_token = ...
|
222
|
+
|
223
|
+
RestClient.add_before_execution_proc do |req, params|
|
224
|
+
access_token.sign! req
|
225
|
+
end
|
226
|
+
|
227
|
+
RestClient.get 'http://example.com'
|
228
|
+
|
200
229
|
== Meta
|
201
230
|
|
202
231
|
Written by Adam Wiggins, major modifications by Blake Mizerany, maintained by Julien Kirch
|
data/Rakefile
CHANGED
@@ -3,16 +3,19 @@ require 'rake'
|
|
3
3
|
require 'jeweler'
|
4
4
|
|
5
5
|
Jeweler::Tasks.new do |s|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
s.name = "rest-client"
|
7
|
+
s.description = "A simple HTTP and REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
8
|
+
s.summary = "Simple HTTP and REST client for Ruby, inspired by microframework syntax for specifying actions."
|
9
|
+
s.author = "Adam Wiggins"
|
10
|
+
s.email = "rest.client@librelist.com"
|
11
|
+
s.homepage = "http://github.com/archiloque/rest-client"
|
12
|
+
s.rubyforge_project = "rest-client"
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"]
|
15
|
+
s.executables = %w(restclient)
|
16
|
+
s.add_dependency("mime-types", ">= 1.16")
|
17
|
+
s.add_development_dependency("webmock", ">= 0.9.1")
|
18
|
+
s.add_development_dependency("rspec")
|
16
19
|
end
|
17
20
|
|
18
21
|
Jeweler::RubyforgeTasks.new
|
@@ -23,21 +26,21 @@ require 'spec/rake/spectask'
|
|
23
26
|
|
24
27
|
desc "Run all specs"
|
25
28
|
Spec::Rake::SpecTask.new('spec') do |t|
|
26
|
-
|
27
|
-
|
29
|
+
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
30
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
28
31
|
end
|
29
32
|
|
30
33
|
desc "Print specdocs"
|
31
34
|
Spec::Rake::SpecTask.new(:doc) do |t|
|
32
|
-
|
33
|
-
|
35
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
36
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
34
37
|
end
|
35
38
|
|
36
39
|
desc "Run all examples with RCov"
|
37
40
|
Spec::Rake::SpecTask.new('rcov') do |t|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
42
|
+
t.rcov = true
|
43
|
+
t.rcov_opts = ['--exclude', 'examples']
|
41
44
|
end
|
42
45
|
|
43
46
|
task :default => :spec
|
@@ -47,11 +50,11 @@ task :default => :spec
|
|
47
50
|
require 'rake/rdoctask'
|
48
51
|
|
49
52
|
Rake::RDocTask.new do |t|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
t.rdoc_dir = 'rdoc'
|
54
|
+
t.title = "rest-client, fetch RESTful resources effortlessly"
|
55
|
+
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
56
|
+
t.options << '--charset' << 'utf-8'
|
57
|
+
t.rdoc_files.include('README.rdoc')
|
58
|
+
t.rdoc_files.include('lib/*.rb')
|
56
59
|
end
|
57
60
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.4.0.a
|
data/history.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 1.4.0
|
2
|
+
|
3
|
+
- Response is no more a String, and the mixin is replaced by an abstract_response, existing calls are redirected to response body with a warning.
|
4
|
+
- enable repeated parameters RestClient.post 'http://example.com/resource', :param1 => ['one', 'two', 'three'], => :param2 => 'foo' (patch provided by Rodrigo Panachi)
|
5
|
+
- fixed the redirect code concerning relative path and query string combination (patch provided by Kevin Read)
|
6
|
+
- redirection code moved to Response so redirection can be customized using the block syntax
|
7
|
+
- only get and head redirections are now followed by default, as stated in the specification
|
8
|
+
- added RestClient.add_before_execution_proc to hack the http request, like for oauth
|
9
|
+
|
10
|
+
The response change may be breaking in rare cases.
|
11
|
+
|
1
12
|
# 1.3.1
|
2
13
|
|
3
14
|
- added compatibility to enable responses in exception to act like Net::HTTPResponse
|
@@ -11,6 +22,8 @@
|
|
11
22
|
- all http error codes have now a corresponding exception class and all of them contain the Reponse -> this means that the raised exception can be different
|
12
23
|
- changed "Content-Disposition: multipart/form-data" to "Content-Disposition: form-data" per RFC 2388 (patch provided by Kyle Crawford)
|
13
24
|
|
25
|
+
The only breaking change should be the exception classes, but as the new classes inherits from the existing ones, the breaking cases should be rare.
|
26
|
+
|
14
27
|
# 1.2.0
|
15
28
|
|
16
29
|
- formatting changed from tabs to spaces
|
data/lib/restclient.rb
CHANGED
@@ -11,7 +11,7 @@ end
|
|
11
11
|
|
12
12
|
require File.dirname(__FILE__) + '/restclient/exceptions'
|
13
13
|
require File.dirname(__FILE__) + '/restclient/request'
|
14
|
-
require File.dirname(__FILE__) + '/restclient/
|
14
|
+
require File.dirname(__FILE__) + '/restclient/abstract_response'
|
15
15
|
require File.dirname(__FILE__) + '/restclient/response'
|
16
16
|
require File.dirname(__FILE__) + '/restclient/raw_response'
|
17
17
|
require File.dirname(__FILE__) + '/restclient/resource'
|
@@ -126,6 +126,7 @@ module RestClient
|
|
126
126
|
else
|
127
127
|
file_logger = Class.new do
|
128
128
|
attr_writer :target_file
|
129
|
+
|
129
130
|
def << obj
|
130
131
|
File.open(@target_file, 'a') { |f| f.puts obj }
|
131
132
|
end
|
@@ -148,4 +149,16 @@ module RestClient
|
|
148
149
|
@@env_log || @@log
|
149
150
|
end
|
150
151
|
|
152
|
+
@@before_execution_procs = []
|
153
|
+
|
154
|
+
# Add a Proc to be called before each request in executed.
|
155
|
+
# The proc parameters will be the http request and the request params.
|
156
|
+
def self.add_before_execution_proc &proc
|
157
|
+
@@before_execution_procs << proc
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.before_execution_procs # :nodoc:
|
161
|
+
@@before_execution_procs
|
162
|
+
end
|
163
|
+
|
151
164
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module RestClient
|
2
|
+
|
3
|
+
class AbstractResponse
|
4
|
+
|
5
|
+
attr_reader :net_http_res, :args
|
6
|
+
|
7
|
+
def initialize net_http_res, args
|
8
|
+
@net_http_res = net_http_res
|
9
|
+
@args = args
|
10
|
+
end
|
11
|
+
|
12
|
+
# HTTP status code
|
13
|
+
def code
|
14
|
+
@code ||= @net_http_res.code.to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
# A hash of the headers, beautified with symbols and underscores.
|
18
|
+
# e.g. "Content-type" will become :content_type.
|
19
|
+
def headers
|
20
|
+
@headers ||= self.class.beautify_headers(@net_http_res.to_hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
# The raw headers.
|
24
|
+
def raw_headers
|
25
|
+
@raw_headers ||= @net_http_res.to_hash
|
26
|
+
end
|
27
|
+
|
28
|
+
# Hash of cookies extracted from response headers
|
29
|
+
def cookies
|
30
|
+
@cookies ||= (self.headers[:set_cookie] || []).inject({}) do |out, cookie_content|
|
31
|
+
# correctly parse comma-separated cookies containing HTTP dates (which also contain a comma)
|
32
|
+
cookie_content.split(/,\s*/).inject([""]) { |array, blob|
|
33
|
+
blob =~ /expires=.+?$/ ? array.push(blob) : array.last.concat(blob)
|
34
|
+
array
|
35
|
+
}.each do |cookie|
|
36
|
+
next if cookie.empty?
|
37
|
+
key, *val = cookie.split(";").first.split("=")
|
38
|
+
out[key] = val.join("=")
|
39
|
+
end
|
40
|
+
out
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the default behavior corresponding to the response code:
|
45
|
+
# the response itself for code in 200..206, redirection for 301 and 302 in get and head cases, redirection for 303 and an exception in other cases
|
46
|
+
def return! &block
|
47
|
+
if (200..206).include? code
|
48
|
+
self
|
49
|
+
elsif [301, 302].include? code
|
50
|
+
unless [:get, :head].include? args[:method]
|
51
|
+
raise Exceptions::EXCEPTIONS_MAP[code], self
|
52
|
+
else
|
53
|
+
follow_redirection &block
|
54
|
+
end
|
55
|
+
elsif code == 303
|
56
|
+
args[:method] = :get
|
57
|
+
args.delete :payload
|
58
|
+
follow_redirection &block
|
59
|
+
elsif Exceptions::EXCEPTIONS_MAP[code]
|
60
|
+
raise Exceptions::EXCEPTIONS_MAP[code], self
|
61
|
+
else
|
62
|
+
raise RequestFailed, self
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
"#{code} #{STATUSES[code]} | #{(headers[:content_type] || '').gsub(/;.*$/, '')} #{size} bytes\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Follow a redirection
|
71
|
+
def follow_redirection &block
|
72
|
+
url = headers[:location]
|
73
|
+
if url !~ /^http/
|
74
|
+
url = URI.parse(args[:url]).merge(url).to_s
|
75
|
+
end
|
76
|
+
args[:url] = url
|
77
|
+
Request.execute args, &block
|
78
|
+
end
|
79
|
+
|
80
|
+
def AbstractResponse.beautify_headers(headers)
|
81
|
+
headers.inject({}) do |out, (key, value)|
|
82
|
+
out[key.gsub(/-/, '_').downcase.to_sym] = %w{set-cookie}.include?(key.downcase) ? value : value.first
|
83
|
+
out
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -1,5 +1,44 @@
|
|
1
1
|
module RestClient
|
2
2
|
|
3
|
+
STATUSES = {100 => 'Continue',
|
4
|
+
101 => 'Switching Protocols',
|
5
|
+
200 => 'OK',
|
6
|
+
201 => 'Created',
|
7
|
+
202 => 'Accepted',
|
8
|
+
203 => 'Non-Authoritative Information',
|
9
|
+
204 => 'No Content',
|
10
|
+
205 => 'Reset Content',
|
11
|
+
206 => 'Partial Content',
|
12
|
+
300 => 'Multiple Choices',
|
13
|
+
301 => 'Moved Permanently',
|
14
|
+
302 => 'Found',
|
15
|
+
303 => 'See Other',
|
16
|
+
304 => 'Not Modified',
|
17
|
+
305 => 'Use Proxy',
|
18
|
+
400 => 'Bad Request',
|
19
|
+
401 => 'Unauthorized',
|
20
|
+
403 => 'Forbidden',
|
21
|
+
404 => 'Resource Not Found',
|
22
|
+
405 => 'Method Not Allowed',
|
23
|
+
406 => 'Not Acceptable',
|
24
|
+
407 => 'Proxy Authentication Required',
|
25
|
+
408 => 'Request Timeout',
|
26
|
+
409 => 'Conflict',
|
27
|
+
410 => 'Gone',
|
28
|
+
411 => 'Length Required',
|
29
|
+
412 => 'Precondition Failed',
|
30
|
+
413 => 'Request Entity Too Large',
|
31
|
+
414 => 'Request-URI Too Long',
|
32
|
+
415 => 'Unsupported Media Type',
|
33
|
+
416 => 'Requested Range Not Satisfiable',
|
34
|
+
417 => 'Expectation Failed',
|
35
|
+
500 => 'Internal Server Error',
|
36
|
+
501 => 'Not Implemented',
|
37
|
+
502 => 'Bad Gateway',
|
38
|
+
503 => 'Service Unavailable',
|
39
|
+
504 => 'Gateway Timeout',
|
40
|
+
505 => 'HTTP Version Not Supported'}
|
41
|
+
|
3
42
|
# Compatibility : make the Response act like a Net::HTTPResponse when needed
|
4
43
|
module ResponseForException
|
5
44
|
def method_missing symbol, *args
|
@@ -34,7 +73,7 @@ module RestClient
|
|
34
73
|
end
|
35
74
|
|
36
75
|
def http_body
|
37
|
-
@response
|
76
|
+
@response.body
|
38
77
|
end
|
39
78
|
|
40
79
|
def inspect
|
@@ -65,35 +104,7 @@ module RestClient
|
|
65
104
|
EXCEPTIONS_MAP = {}
|
66
105
|
end
|
67
106
|
|
68
|
-
|
69
|
-
301 => 'Moved Permanently',
|
70
|
-
302 => 'Found',
|
71
|
-
303 => 'See Other',
|
72
|
-
304 => 'Not Modified',
|
73
|
-
305 => 'Use Proxy',
|
74
|
-
400 => 'Bad Request',
|
75
|
-
401 => 'Unauthorized',
|
76
|
-
403 => 'Forbidden',
|
77
|
-
404 => 'Resource Not Found',
|
78
|
-
405 => 'Method Not Allowed',
|
79
|
-
406 => 'Not Acceptable',
|
80
|
-
407 => 'Proxy Authentication Required',
|
81
|
-
408 => 'Request Timeout',
|
82
|
-
409 => 'Conflict',
|
83
|
-
410 => 'Gone',
|
84
|
-
411 => 'Length Required',
|
85
|
-
412 => 'Precondition Failed',
|
86
|
-
413 => 'Request Entity Too Large',
|
87
|
-
414 => 'Request-URI Too Long',
|
88
|
-
415 => 'Unsupported Media Type',
|
89
|
-
416 => 'Requested Range Not Satisfiable',
|
90
|
-
417 => 'Expectation Failed',
|
91
|
-
500 => 'Internal Server Error',
|
92
|
-
501 => 'Not Implemented',
|
93
|
-
502 => 'Bad Gateway',
|
94
|
-
503 => 'Service Unavailable',
|
95
|
-
504 => 'Gateway Timeout',
|
96
|
-
505 => 'HTTP Version Not Supported'}.each_pair do |code, message|
|
107
|
+
STATUSES.each_pair do |code, message|
|
97
108
|
|
98
109
|
# Compatibility
|
99
110
|
superclass = ([304, 401, 404].include? code) ? ExceptionWithResponse : RequestFailed
|
data/lib/restclient/payload.rb
CHANGED
@@ -54,14 +54,17 @@ module RestClient
|
|
54
54
|
# Flatten parameters by converting hashes of hashes to flat hashes
|
55
55
|
# {keys1 => {keys2 => value}} will be transformed into {keys1[key2] => value}
|
56
56
|
def flatten_params(params, parent_key = nil)
|
57
|
-
result =
|
58
|
-
params.
|
57
|
+
result = []
|
58
|
+
params.each do |key, value|
|
59
59
|
calculated_key = parent_key ? "#{parent_key}[#{escape key}]" : escape(key)
|
60
|
-
value = params[key]
|
61
60
|
if value.is_a? Hash
|
62
|
-
result
|
61
|
+
result << flatten_params(value, calculated_key).flatten
|
62
|
+
elsif value.is_a? Array
|
63
|
+
value.each do |elem|
|
64
|
+
result << [calculated_key, elem]
|
65
|
+
end
|
63
66
|
else
|
64
|
-
result[calculated_key
|
67
|
+
result << [calculated_key, value]
|
65
68
|
end
|
66
69
|
end
|
67
70
|
result
|
@@ -88,15 +91,15 @@ module RestClient
|
|
88
91
|
end
|
89
92
|
|
90
93
|
def short_inspect
|
91
|
-
(size > 100 ? "#{size} byte length" : inspect)
|
94
|
+
(size > 100 ? "#{size} byte(s) length" : inspect)
|
92
95
|
end
|
93
96
|
|
94
97
|
end
|
95
98
|
|
96
99
|
class UrlEncoded < Base
|
97
100
|
def build_stream(params = nil)
|
98
|
-
@stream = StringIO.new(flatten_params(params).
|
99
|
-
"#{
|
101
|
+
@stream = StringIO.new(flatten_params(params).collect do |entry|
|
102
|
+
"#{entry[0]}=#{escape(entry[1])}"
|
100
103
|
end.join("&"))
|
101
104
|
@stream.seek(0)
|
102
105
|
end
|
@@ -116,9 +119,9 @@ module RestClient
|
|
116
119
|
@stream.write(b + EOL)
|
117
120
|
|
118
121
|
if params.is_a? Hash
|
119
|
-
x = flatten_params(params)
|
122
|
+
x = flatten_params(params)
|
120
123
|
else
|
121
|
-
x = params
|
124
|
+
x = params
|
122
125
|
end
|
123
126
|
|
124
127
|
last_index = x.length - 1
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/mixin/response'
|
2
|
-
|
3
1
|
module RestClient
|
4
2
|
# The response from RestClient on a raw request looks like a string, but is
|
5
3
|
# actually one of these. 99% of the time you're making a rest call all you
|
@@ -11,13 +9,12 @@ module RestClient
|
|
11
9
|
# In addition, if you do not use the response as a string, you can access
|
12
10
|
# a Tempfile object at res.file, which contains the path to the raw
|
13
11
|
# downloaded request body.
|
14
|
-
class RawResponse
|
15
|
-
include RestClient::Mixin::Response
|
12
|
+
class RawResponse < AbstractResponse
|
16
13
|
|
17
14
|
attr_reader :file
|
18
15
|
|
19
|
-
def initialize
|
20
|
-
|
16
|
+
def initialize tempfile, net_http_res, args
|
17
|
+
super net_http_res, args
|
21
18
|
@file = tempfile
|
22
19
|
end
|
23
20
|
|
@@ -26,5 +23,9 @@ module RestClient
|
|
26
23
|
@file.read
|
27
24
|
end
|
28
25
|
|
26
|
+
def size
|
27
|
+
File.size file
|
28
|
+
end
|
29
|
+
|
29
30
|
end
|
30
31
|
end
|
data/lib/restclient/request.rb
CHANGED
@@ -21,11 +21,10 @@ module RestClient
|
|
21
21
|
# * :ssl_client_cert, :ssl_client_key, :ssl_ca_file
|
22
22
|
class Request
|
23
23
|
|
24
|
-
attr_reader :method, :url, :
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
|
24
|
+
attr_reader :method, :url, :headers, :cookies,
|
25
|
+
:payload, :user, :password, :timeout,
|
26
|
+
:open_timeout, :raw_response, :verify_ssl, :ssl_client_cert,
|
27
|
+
:ssl_client_key, :ssl_ca_file, :processed_headers, :args
|
29
28
|
|
30
29
|
def self.execute(args, &block)
|
31
30
|
new(args).execute &block
|
@@ -48,20 +47,10 @@ module RestClient
|
|
48
47
|
@ssl_ca_file = args[:ssl_ca_file] || nil
|
49
48
|
@tf = nil # If you are a raw request, this is your tempfile
|
50
49
|
@processed_headers = make_headers headers
|
50
|
+
@args = args
|
51
51
|
end
|
52
52
|
|
53
53
|
def execute &block
|
54
|
-
execute_inner &block
|
55
|
-
rescue Redirect => e
|
56
|
-
@processed_headers.delete "Content-Length"
|
57
|
-
@processed_headers.delete "Content-Type"
|
58
|
-
@url = e.url
|
59
|
-
@method = :get
|
60
|
-
@payload = nil
|
61
|
-
execute &block
|
62
|
-
end
|
63
|
-
|
64
|
-
def execute_inner &block
|
65
54
|
uri = parse_url_with_auth(url)
|
66
55
|
transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, &block
|
67
56
|
end
|
@@ -152,6 +141,10 @@ module RestClient
|
|
152
141
|
net.read_timeout = @timeout if @timeout
|
153
142
|
net.open_timeout = @open_timeout if @open_timeout
|
154
143
|
|
144
|
+
RestClient.before_execution_procs.each do |block|
|
145
|
+
block.call(req, args)
|
146
|
+
end
|
147
|
+
|
155
148
|
log_request
|
156
149
|
|
157
150
|
net.start do |http|
|
@@ -197,32 +190,20 @@ module RestClient
|
|
197
190
|
http_response
|
198
191
|
end
|
199
192
|
|
200
|
-
def process_result res
|
193
|
+
def process_result res, &block
|
201
194
|
if @raw_response
|
202
195
|
# We don't decode raw requests
|
203
|
-
response = RawResponse.new(@tf, res)
|
196
|
+
response = RawResponse.new(@tf, res, args)
|
204
197
|
else
|
205
|
-
response = Response.new(Request.decode(res['content-encoding'], res.body), res)
|
198
|
+
response = Response.new(Request.decode(res['content-encoding'], res.body), res, args)
|
206
199
|
end
|
207
200
|
|
208
|
-
|
209
|
-
|
210
|
-
if (301..303).include? code
|
211
|
-
url = res.header['Location']
|
212
|
-
|
213
|
-
if url !~ /^http/
|
214
|
-
uri = URI.parse(@url)
|
215
|
-
uri.path = "/#{url}".squeeze('/')
|
216
|
-
url = uri.to_s
|
217
|
-
end
|
218
|
-
raise Redirect, url
|
201
|
+
if block_given?
|
202
|
+
block.call response, &block
|
219
203
|
else
|
220
|
-
|
221
|
-
yield response
|
222
|
-
else
|
223
|
-
response.return!
|
224
|
-
end
|
204
|
+
response.return! &block
|
225
205
|
end
|
206
|
+
|
226
207
|
end
|
227
208
|
|
228
209
|
def self.decode content_encoding, body
|
data/lib/restclient/response.rb
CHANGED
@@ -1,19 +1,43 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/mixin/response'
|
2
|
-
|
3
1
|
module RestClient
|
4
|
-
|
5
|
-
#
|
6
|
-
# the body, but on the occassion you want to fetch the headers you can:
|
7
|
-
#
|
8
|
-
# RestClient.get('http://example.com').headers[:content_type]
|
2
|
+
|
3
|
+
# A Response from RestClient, you can access the response body, the code or the headers.
|
9
4
|
#
|
10
|
-
class Response <
|
5
|
+
class Response < AbstractResponse
|
6
|
+
|
7
|
+
attr_reader :body
|
8
|
+
|
9
|
+
def initialize body, net_http_res, args
|
10
|
+
super net_http_res, args
|
11
|
+
@body = body || ""
|
12
|
+
end
|
11
13
|
|
12
|
-
|
14
|
+
def method_missing symbol, *args
|
15
|
+
if body.respond_to? symbol
|
16
|
+
warn "[warning] The Response is no more a String, please update your code"
|
17
|
+
body.send symbol, *args
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def == o
|
24
|
+
if super
|
25
|
+
true
|
26
|
+
else
|
27
|
+
equal_body = (body == o)
|
28
|
+
if equal_body
|
29
|
+
warn "[warning] The Response is no more a String, please update your code"
|
30
|
+
end
|
31
|
+
equal_body
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
body.to_s
|
37
|
+
end
|
13
38
|
|
14
|
-
def
|
15
|
-
|
16
|
-
super(string || "")
|
39
|
+
def size
|
40
|
+
body.size
|
17
41
|
end
|
18
42
|
|
19
43
|
end
|
@@ -1,18 +1,9 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
2
|
|
3
|
-
|
4
|
-
include RestClient::Mixin::Response
|
5
|
-
|
6
|
-
def initialize(body, res)
|
7
|
-
@net_http_res = res
|
8
|
-
@body = body
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
describe RestClient::Mixin::Response do
|
3
|
+
describe RestClient::AbstractResponse do
|
13
4
|
before do
|
14
5
|
@net_http_res = mock('net http response')
|
15
|
-
@response =
|
6
|
+
@response = RestClient::AbstractResponse.new(@net_http_res, {})
|
16
7
|
end
|
17
8
|
|
18
9
|
it "fetches the numeric response code" do
|
data/spec/integration_spec.rb
CHANGED
@@ -10,14 +10,14 @@ describe RestClient do
|
|
10
10
|
stub_request(:get, "www.example.com").to_return(:body => body, :status => 200)
|
11
11
|
response = RestClient.get "www.example.com"
|
12
12
|
response.code.should == 200
|
13
|
-
response.should == body
|
13
|
+
response.body.should == body
|
14
14
|
end
|
15
15
|
|
16
16
|
it "a simple request with gzipped content" do
|
17
17
|
stub_request(:get, "www.example.com").with(:headers => { 'Accept-Encoding' => 'gzip, deflate' }).to_return(:body => "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000", :status => 200, :headers => { 'Content-Encoding' => 'gzip' } )
|
18
18
|
response = RestClient.get "www.example.com"
|
19
19
|
response.code.should == 200
|
20
|
-
response.should == "i'm gziped\n"
|
20
|
+
response.body.should == "i'm gziped\n"
|
21
21
|
end
|
22
22
|
|
23
23
|
it "a 404" do
|
@@ -29,7 +29,7 @@ describe RestClient do
|
|
29
29
|
rescue RestClient::ResourceNotFound => e
|
30
30
|
e.http_code.should == 404
|
31
31
|
e.response.code.should == 404
|
32
|
-
e.response.should == body
|
32
|
+
e.response.body.should == body
|
33
33
|
e.http_body.should == body
|
34
34
|
end
|
35
35
|
end
|
data/spec/payload_spec.rb
CHANGED
@@ -10,6 +10,8 @@ describe RestClient::Payload do
|
|
10
10
|
it "should form properly encoded params" do
|
11
11
|
RestClient::Payload::UrlEncoded.new({:foo => 'bar'}).to_s.
|
12
12
|
should == "foo=bar"
|
13
|
+
["foo=bar&baz=qux", "baz=qux&foo=bar"].should include(
|
14
|
+
RestClient::Payload::UrlEncoded.new({:foo => 'bar', :baz => 'qux'}).to_s)
|
13
15
|
end
|
14
16
|
|
15
17
|
it "should properly handle hashes as parameter" do
|
@@ -26,6 +28,13 @@ describe RestClient::Payload do
|
|
26
28
|
should == "foo[bar]=baz"
|
27
29
|
end
|
28
30
|
|
31
|
+
it "should properyl handle arrays as repeated parameters" do
|
32
|
+
RestClient::Payload::UrlEncoded.new({:foo => ['bar']}).to_s.
|
33
|
+
should == "foo=bar"
|
34
|
+
RestClient::Payload::UrlEncoded.new({:foo => ['bar', 'baz']}).to_s.
|
35
|
+
should == "foo=bar&foo=baz"
|
36
|
+
end
|
37
|
+
|
29
38
|
end
|
30
39
|
|
31
40
|
context "A multipart Payload" do
|
data/spec/raw_response_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe RestClient::RawResponse do
|
|
4
4
|
before do
|
5
5
|
@tf = mock("Tempfile", :read => "the answer is 42", :open => true)
|
6
6
|
@net_http_res = mock('net http response')
|
7
|
-
@response = RestClient::RawResponse.new(@tf, @net_http_res)
|
7
|
+
@response = RestClient::RawResponse.new(@tf, @net_http_res, {})
|
8
8
|
end
|
9
9
|
|
10
10
|
it "behaves like string" do
|
data/spec/request_spec.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/base'
|
2
2
|
|
3
|
+
require 'webmock/rspec'
|
4
|
+
include WebMock
|
5
|
+
|
3
6
|
describe RestClient::Request do
|
4
7
|
before do
|
5
8
|
@request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
|
@@ -45,7 +48,8 @@ describe RestClient::Request do
|
|
45
48
|
res.stub!(:code).and_return("200")
|
46
49
|
res.stub!(:body).and_return('body')
|
47
50
|
res.stub!(:[]).with('content-encoding').and_return(nil)
|
48
|
-
@request.process_result(res).should == 'body'
|
51
|
+
@request.process_result(res).body.should == 'body'
|
52
|
+
@request.process_result(res).to_s.should == 'body'
|
49
53
|
end
|
50
54
|
|
51
55
|
it "doesn't classify successful requests as failed" do
|
@@ -153,7 +157,7 @@ describe RestClient::Request do
|
|
153
157
|
@request.should_receive(:net_http_request_class).with(:put).and_return(klass)
|
154
158
|
klass.should_receive(:new).and_return('result')
|
155
159
|
@request.should_receive(:transmit).with(@uri, 'result', kind_of(RestClient::Payload::Base))
|
156
|
-
@request.
|
160
|
+
@request.execute
|
157
161
|
end
|
158
162
|
|
159
163
|
it "transmits the request with Net::HTTP" do
|
@@ -225,11 +229,6 @@ describe RestClient::Request do
|
|
225
229
|
lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::ServerBrokeConnection)
|
226
230
|
end
|
227
231
|
|
228
|
-
it "execute calls execute_inner" do
|
229
|
-
@request.should_receive(:execute_inner)
|
230
|
-
@request.execute
|
231
|
-
end
|
232
|
-
|
233
232
|
it "class method execute wraps constructor" do
|
234
233
|
req = mock("rest request")
|
235
234
|
RestClient::Request.should_receive(:new).with(1 => 2).and_return(req)
|
@@ -237,39 +236,6 @@ describe RestClient::Request do
|
|
237
236
|
RestClient::Request.execute(1 => 2)
|
238
237
|
end
|
239
238
|
|
240
|
-
describe "redirection" do
|
241
|
-
it "raises a Redirect with the new location when the response is in the 30x range" do
|
242
|
-
res = mock('response', :code => '301', :header => { 'Location' => 'http://new/resource'}, :[] => ['content-encoding' => ''], :body => '' )
|
243
|
-
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://new/resource'}
|
244
|
-
end
|
245
|
-
|
246
|
-
it "handles redirects with relative paths" do
|
247
|
-
res = mock('response', :code => '301', :header => { 'Location' => 'index' }, :[] => ['content-encoding' => ''], :body => '' )
|
248
|
-
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
249
|
-
end
|
250
|
-
|
251
|
-
it "handles redirects with absolute paths" do
|
252
|
-
@request.instance_variable_set('@url', 'http://some/place/else')
|
253
|
-
res = mock('response', :code => '301', :header => { 'Location' => '/index' }, :[] => ['content-encoding' => ''], :body => '' )
|
254
|
-
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
255
|
-
end
|
256
|
-
|
257
|
-
it "uses GET and clears payload and removes possible harmful headers when following 30x redirects" do
|
258
|
-
url = "http://example.com/redirected"
|
259
|
-
|
260
|
-
@request.should_receive(:execute_inner).once.ordered.and_raise(RestClient::Redirect.new(url))
|
261
|
-
|
262
|
-
@request.should_receive(:execute_inner).once.ordered do
|
263
|
-
@request.processed_headers.should_not have_key("Content-Length")
|
264
|
-
@request.url.should == url
|
265
|
-
@request.method.should == :get
|
266
|
-
@request.payload.should be_nil
|
267
|
-
end
|
268
|
-
|
269
|
-
@request.execute
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
239
|
describe "exception" do
|
274
240
|
it "raises Unauthorized when the response is 401" do
|
275
241
|
res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
|
@@ -324,8 +290,8 @@ describe RestClient::Request do
|
|
324
290
|
it "logs a post request with a large payload" do
|
325
291
|
log = RestClient.log = []
|
326
292
|
RestClient::Request.new(:method => :post, :url => 'http://url', :payload => ('x' * 1000)).log_request
|
327
|
-
['RestClient.post "http://url", 1000 byte length, "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000", "Accept"=>"*/*; q=0.5, application/xml"' + "\n",
|
328
|
-
'RestClient.post "http://url", 1000 byte length, "Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000"' + "\n"].should include(log[0])
|
293
|
+
['RestClient.post "http://url", 1000 byte(s) length, "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000", "Accept"=>"*/*; q=0.5, application/xml"' + "\n",
|
294
|
+
'RestClient.post "http://url", 1000 byte(s) length, "Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000"' + "\n"].should include(log[0])
|
329
295
|
end
|
330
296
|
|
331
297
|
it "logs input headers as a hash" do
|
data/spec/response_spec.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/base'
|
2
2
|
|
3
|
+
require 'webmock/rspec'
|
4
|
+
include WebMock
|
5
|
+
|
3
6
|
describe RestClient::Response do
|
4
7
|
before do
|
5
8
|
@net_http_res = mock('net http response', :to_hash => {"Status" => ["200 OK"]})
|
6
|
-
@response = RestClient::Response.new('abc', @net_http_res)
|
9
|
+
@response = RestClient::Response.new('abc', @net_http_res, {})
|
7
10
|
end
|
8
11
|
|
9
12
|
it "behaves like string" do
|
10
|
-
@response.should == 'abc'
|
13
|
+
@response.should.to_s == 'abc'
|
11
14
|
end
|
12
15
|
|
13
16
|
it "accepts nil strings and sets it to empty for the case of HEAD" do
|
14
|
-
RestClient::Response.new(nil, @net_http_res).should == ""
|
17
|
+
RestClient::Response.new(nil, @net_http_res, {}).should.to_s == ""
|
15
18
|
end
|
16
19
|
|
17
20
|
it "test headers and raw headers" do
|
@@ -22,14 +25,14 @@ describe RestClient::Response do
|
|
22
25
|
describe "cookie processing" do
|
23
26
|
it "should correctly deal with one Set-Cookie header with one cookie inside" do
|
24
27
|
net_http_res = mock('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT"]})
|
25
|
-
response = RestClient::Response.new('abc', net_http_res)
|
28
|
+
response = RestClient::Response.new('abc', net_http_res, {})
|
26
29
|
response.headers[:set_cookie].should == ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT"]
|
27
30
|
response.cookies.should == { "main_page" => "main_page_no_rewrite" }
|
28
31
|
end
|
29
32
|
|
30
33
|
it "should correctly deal with multiple cookies [multiple Set-Cookie headers]" do
|
31
34
|
net_http_res = mock('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT", "remember_me=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "user=somebody; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"]})
|
32
|
-
response = RestClient::Response.new('abc', net_http_res)
|
35
|
+
response = RestClient::Response.new('abc', net_http_res, {})
|
33
36
|
response.headers[:set_cookie].should == ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT", "remember_me=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "user=somebody; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"]
|
34
37
|
response.cookies.should == {
|
35
38
|
"main_page" => "main_page_no_rewrite",
|
@@ -40,7 +43,7 @@ describe RestClient::Response do
|
|
40
43
|
|
41
44
|
it "should correctly deal with multiple cookies [one Set-Cookie header with multiple cookies]" do
|
42
45
|
net_http_res = mock('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT, remember_me=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT, user=somebody; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"]})
|
43
|
-
response = RestClient::Response.new('abc', net_http_res)
|
46
|
+
response = RestClient::Response.new('abc', net_http_res, {})
|
44
47
|
response.cookies.should == {
|
45
48
|
"main_page" => "main_page_no_rewrite",
|
46
49
|
"remember_me" => "",
|
@@ -53,20 +56,75 @@ describe RestClient::Response do
|
|
53
56
|
it "should return itself for normal codes" do
|
54
57
|
(200..206).each do |code|
|
55
58
|
net_http_res = mock('net http response', :code => '200')
|
56
|
-
response = RestClient::Response.new('abc', net_http_res)
|
59
|
+
response = RestClient::Response.new('abc', net_http_res, {})
|
57
60
|
response.return!
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
61
64
|
it "should throw an exception for other codes" do
|
62
65
|
RestClient::Exceptions::EXCEPTIONS_MAP.each_key do |code|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
+
unless (200..206).include? code
|
67
|
+
net_http_res = mock('net http response', :code => code.to_i)
|
68
|
+
response = RestClient::Response.new('abc', net_http_res, {})
|
69
|
+
lambda { response.return!}.should raise_error
|
70
|
+
end
|
66
71
|
end
|
67
72
|
end
|
68
73
|
|
69
74
|
end
|
70
75
|
|
76
|
+
describe "redirection" do
|
77
|
+
|
78
|
+
it "follows a redirection when the request is a get" do
|
79
|
+
stub_request(:get, 'http://some/resource').to_return(:body => '', :status => 301, :headers => {'Location' => 'http://new/resource'})
|
80
|
+
stub_request(:get, 'http://new/resource').to_return(:body => 'Foo')
|
81
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :get).body.should == 'Foo'
|
82
|
+
end
|
83
|
+
|
84
|
+
it "doesn't follow a redirection when the request is a post" do
|
85
|
+
net_http_res = mock('net http response', :code => 301)
|
86
|
+
response = RestClient::Response.new('abc', net_http_res, {:method => :post})
|
87
|
+
lambda { response.return!}.should raise_error(RestClient::MovedPermanently)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "doesn't follow a redirection when the request is a put" do
|
91
|
+
net_http_res = mock('net http response', :code => 301)
|
92
|
+
response = RestClient::Response.new('abc', net_http_res, {:method => :put})
|
93
|
+
lambda { response.return!}.should raise_error(RestClient::MovedPermanently)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "follows a redirection when the request is a post and result is a 303" do
|
97
|
+
stub_request(:put, 'http://some/resource').to_return(:body => '', :status => 303, :headers => {'Location' => 'http://new/resource'})
|
98
|
+
stub_request(:get, 'http://new/resource').to_return(:body => 'Foo')
|
99
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :put).body.should == 'Foo'
|
100
|
+
end
|
101
|
+
|
102
|
+
it "follows a redirection when the request is a head" do
|
103
|
+
stub_request(:head, 'http://some/resource').to_return(:body => '', :status => 301, :headers => {'Location' => 'http://new/resource'})
|
104
|
+
stub_request(:head, 'http://new/resource').to_return(:body => 'Foo')
|
105
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :head).body.should == 'Foo'
|
106
|
+
end
|
107
|
+
|
108
|
+
it "handles redirects with relative paths" do
|
109
|
+
stub_request(:get, 'http://some/resource').to_return(:body => '', :status => 301, :headers => {'Location' => 'index'})
|
110
|
+
stub_request(:get, 'http://some/index').to_return(:body => 'Foo')
|
111
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :get).body.should == 'Foo'
|
112
|
+
end
|
113
|
+
|
114
|
+
it "handles redirects with relative path and query string" do
|
115
|
+
stub_request(:get, 'http://some/resource').to_return(:body => '', :status => 301, :headers => {'Location' => 'index?q=1'})
|
116
|
+
stub_request(:get, 'http://some/index?q=1').to_return(:body => 'Foo')
|
117
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :get).body.should == 'Foo'
|
118
|
+
end
|
119
|
+
|
120
|
+
it "follow a redirection when the request is a get and the response is in the 30x range" do
|
121
|
+
stub_request(:get, 'http://some/resource').to_return(:body => '', :status => 301, :headers => {'Location' => 'http://new/resource'})
|
122
|
+
stub_request(:get, 'http://new/resource').to_return(:body => 'Foo')
|
123
|
+
RestClient::Request.execute(:url => 'http://some/resource', :method => :get).body.should == 'Foo'
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
end
|
128
|
+
|
71
129
|
|
72
130
|
end
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
- a
|
10
|
+
version: 1.4.0.a
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Adam Wiggins
|
@@ -10,19 +16,22 @@ autorequire:
|
|
10
16
|
bindir: bin
|
11
17
|
cert_chain: []
|
12
18
|
|
13
|
-
date: 2010-
|
19
|
+
date: 2010-02-22 00:00:00 +01:00
|
14
20
|
default_executable: restclient
|
15
21
|
dependencies:
|
16
22
|
- !ruby/object:Gem::Dependency
|
17
23
|
name: mime-types
|
18
|
-
|
19
|
-
|
20
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
21
26
|
requirements:
|
22
27
|
- - ">="
|
23
28
|
- !ruby/object:Gem::Version
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 16
|
24
32
|
version: "1.16"
|
25
|
-
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
26
35
|
description: "A simple Simple HTTP and REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
27
36
|
email: rest.client@librelist.com
|
28
37
|
executables:
|
@@ -40,7 +49,7 @@ files:
|
|
40
49
|
- lib/rest_client.rb
|
41
50
|
- lib/restclient.rb
|
42
51
|
- lib/restclient/exceptions.rb
|
43
|
-
- lib/restclient/
|
52
|
+
- lib/restclient/abstract_response.rb
|
44
53
|
- lib/restclient/net_http_ext.rb
|
45
54
|
- lib/restclient/payload.rb
|
46
55
|
- lib/restclient/raw_response.rb
|
@@ -51,7 +60,7 @@ files:
|
|
51
60
|
- spec/exceptions_spec.rb
|
52
61
|
- spec/integration_spec.rb
|
53
62
|
- spec/master_shake.jpg
|
54
|
-
- spec/
|
63
|
+
- spec/abstract_response_spec.rb
|
55
64
|
- spec/payload_spec.rb
|
56
65
|
- spec/raw_response_spec.rb
|
57
66
|
- spec/request_spec.rb
|
@@ -72,18 +81,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
81
|
requirements:
|
73
82
|
- - ">="
|
74
83
|
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 0
|
75
86
|
version: "0"
|
76
|
-
version:
|
77
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
88
|
requirements:
|
79
89
|
- - ">="
|
80
90
|
- !ruby/object:Gem::Version
|
91
|
+
segments:
|
92
|
+
- 0
|
81
93
|
version: "0"
|
82
|
-
version:
|
83
94
|
requirements: []
|
84
95
|
|
85
96
|
rubyforge_project: rest-client
|
86
|
-
rubygems_version: 1.3.
|
97
|
+
rubygems_version: 1.3.6
|
87
98
|
signing_key:
|
88
99
|
specification_version: 3
|
89
100
|
summary: Simple REST client for Ruby, inspired by microframework syntax for specifying actions.
|
@@ -91,7 +102,7 @@ test_files:
|
|
91
102
|
- spec/base.rb
|
92
103
|
- spec/exceptions_spec.rb
|
93
104
|
- spec/integration_spec.rb
|
94
|
-
- spec/
|
105
|
+
- spec/abstract_response_spec.rb
|
95
106
|
- spec/payload_spec.rb
|
96
107
|
- spec/raw_response_spec.rb
|
97
108
|
- spec/request_spec.rb
|
@@ -1,64 +0,0 @@
|
|
1
|
-
module RestClient
|
2
|
-
module Mixin
|
3
|
-
module Response
|
4
|
-
attr_reader :net_http_res
|
5
|
-
|
6
|
-
# HTTP status code
|
7
|
-
def code
|
8
|
-
@code ||= @net_http_res.code.to_i
|
9
|
-
end
|
10
|
-
|
11
|
-
# A hash of the headers, beautified with symbols and underscores.
|
12
|
-
# e.g. "Content-type" will become :content_type.
|
13
|
-
def headers
|
14
|
-
@headers ||= self.class.beautify_headers(@net_http_res.to_hash)
|
15
|
-
end
|
16
|
-
|
17
|
-
# The raw headers.
|
18
|
-
def raw_headers
|
19
|
-
@raw_headers ||= @net_http_res.to_hash
|
20
|
-
end
|
21
|
-
|
22
|
-
# Hash of cookies extracted from response headers
|
23
|
-
def cookies
|
24
|
-
@cookies ||= (self.headers[:set_cookie] || []).inject({}) do |out, cookie_content|
|
25
|
-
# correctly parse comma-separated cookies containing HTTP dates (which also contain a comma)
|
26
|
-
cookie_content.split(/,\s*/).inject([""]) { |array, blob|
|
27
|
-
blob =~ /expires=.+?$/ ? array.push(blob) : array.last.concat(blob)
|
28
|
-
array
|
29
|
-
}.each do |cookie|
|
30
|
-
next if cookie.empty?
|
31
|
-
key, *val = cookie.split(";").first.split("=")
|
32
|
-
out[key] = val.join("=")
|
33
|
-
end
|
34
|
-
out
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Return the default behavior corresponding to the response code:
|
39
|
-
# the response itself for code in 200..206 and an exception in other cases
|
40
|
-
def return!
|
41
|
-
if (200..206).include? code
|
42
|
-
self
|
43
|
-
elsif Exceptions::EXCEPTIONS_MAP[code]
|
44
|
-
raise Exceptions::EXCEPTIONS_MAP[code], self
|
45
|
-
else
|
46
|
-
raise RequestFailed, self
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.included(receiver)
|
51
|
-
receiver.extend(RestClient::Mixin::Response::ClassMethods)
|
52
|
-
end
|
53
|
-
|
54
|
-
module ClassMethods
|
55
|
-
def beautify_headers(headers)
|
56
|
-
headers.inject({}) do |out, (key, value)|
|
57
|
-
out[key.gsub(/-/, '_').downcase.to_sym] = %w{set-cookie}.include?(key.downcase) ? value : value.first
|
58
|
-
out
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|