rest-client 1.5.0 → 1.5.1
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 +2 -2
- data/Rakefile +10 -1
- data/VERSION +1 -1
- data/bin/restclient +1 -1
- data/history.md +8 -0
- data/lib/restclient/abstract_response.rb +9 -11
- data/lib/restclient/exceptions.rb +6 -2
- data/lib/restclient/request.rb +20 -10
- data/lib/restclient/response.rb +1 -1
- data/spec/abstract_response_spec.rb +5 -0
- data/spec/integration/certs/equifax.crt +19 -0
- data/spec/integration/certs/verisign.crt +14 -0
- data/spec/integration/request_spec.rb +25 -0
- data/spec/request_spec.rb +17 -2
- metadata +14 -3
data/README.rdoc
CHANGED
@@ -39,7 +39,7 @@ of specifying actions: get, put, post, delete.
|
|
39
39
|
:group => 'those_guys'
|
40
40
|
},
|
41
41
|
:upload => {
|
42
|
-
:file => File.new(path)
|
42
|
+
:file => File.new(path, 'rb')
|
43
43
|
}
|
44
44
|
})
|
45
45
|
|
@@ -47,7 +47,7 @@ of specifying actions: get, put, post, delete.
|
|
47
47
|
|
48
48
|
Yeah, that's right! This does multipart sends for you!
|
49
49
|
|
50
|
-
RestClient.post '/data', :myfile => File.new("/path/to/image.jpg")
|
50
|
+
RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb')
|
51
51
|
|
52
52
|
This does two things for you:
|
53
53
|
|
data/Rakefile
CHANGED
@@ -25,11 +25,20 @@ Jeweler::RubyforgeTasks.new
|
|
25
25
|
require 'spec/rake/spectask'
|
26
26
|
|
27
27
|
desc "Run all specs"
|
28
|
-
|
28
|
+
task :spec => ["spec:unit", "spec:integration"]
|
29
|
+
|
30
|
+
desc "Run unit specs"
|
31
|
+
Spec::Rake::SpecTask.new('spec:unit') do |t|
|
29
32
|
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
30
33
|
t.spec_files = FileList['spec/*_spec.rb']
|
31
34
|
end
|
32
35
|
|
36
|
+
desc "Run integration specs"
|
37
|
+
Spec::Rake::SpecTask.new('spec:integration') do |t|
|
38
|
+
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
39
|
+
t.spec_files = FileList['spec/integration/*_spec.rb']
|
40
|
+
end
|
41
|
+
|
33
42
|
desc "Print specdocs"
|
34
43
|
Spec::Rake::SpecTask.new(:doc) do |t|
|
35
44
|
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.5.
|
1
|
+
1.5.1
|
data/bin/restclient
CHANGED
data/history.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# 1.5.1
|
2
|
+
|
3
|
+
- only converts headers keys which are Symbols
|
4
|
+
- use CGI for cookie parsing instead of custom code
|
5
|
+
- unescape user and password before using them (patch provided by Lars Gierth)
|
6
|
+
- expand ~ in ~/.restclientrc (patch provided by Mike Fletcher)
|
7
|
+
- ssl verification raise an exception when the ca certificate is incorrect (patch provided by Braintree)
|
8
|
+
|
1
9
|
# 1.5.0
|
2
10
|
|
3
11
|
- the response is now a String with the Response module a.k.a. the change in 1.4.0 was a mistake (Response.body is returning self for compatability)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
1
3
|
module RestClient
|
2
4
|
|
3
5
|
module AbstractResponse
|
@@ -22,15 +24,11 @@ module RestClient
|
|
22
24
|
|
23
25
|
# Hash of cookies extracted from response headers
|
24
26
|
def cookies
|
25
|
-
@cookies ||= (self.headers[:set_cookie] ||
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
}.each do |cookie|
|
31
|
-
next if cookie.empty?
|
32
|
-
key, *val = cookie.split(";").first.split("=")
|
33
|
-
out[key] = val.join("=")
|
27
|
+
@cookies ||= (self.headers[:set_cookie] || {}).inject({}) do |out, cookie_content|
|
28
|
+
CGI::Cookie::parse(cookie_content).each do |key, cookie|
|
29
|
+
unless ['expires', 'path'].include? key
|
30
|
+
out[key] = cookie.value[0] || ''
|
31
|
+
end
|
34
32
|
end
|
35
33
|
out
|
36
34
|
end
|
@@ -45,12 +43,12 @@ module RestClient
|
|
45
43
|
unless [:get, :head].include? args[:method]
|
46
44
|
raise Exceptions::EXCEPTIONS_MAP[code], self
|
47
45
|
else
|
48
|
-
follow_redirection
|
46
|
+
follow_redirection(&block)
|
49
47
|
end
|
50
48
|
elsif code == 303
|
51
49
|
args[:method] = :get
|
52
50
|
args.delete :payload
|
53
|
-
follow_redirection
|
51
|
+
follow_redirection(&block)
|
54
52
|
elsif Exceptions::EXCEPTIONS_MAP[code]
|
55
53
|
raise Exceptions::EXCEPTIONS_MAP[code], self
|
56
54
|
else
|
@@ -134,8 +134,12 @@ module RestClient
|
|
134
134
|
message = 'Server broke connection'
|
135
135
|
end
|
136
136
|
|
137
|
-
|
138
|
-
|
137
|
+
class SSLCertificateNotVerified < Exception
|
138
|
+
def initialize(message)
|
139
|
+
super(nil)
|
140
|
+
self.message = message
|
141
|
+
end
|
142
|
+
end
|
139
143
|
end
|
140
144
|
|
141
145
|
# backwards compatibility
|
data/lib/restclient/request.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'tempfile'
|
2
2
|
require 'mime/types'
|
3
|
+
require 'cgi'
|
3
4
|
|
4
5
|
module RestClient
|
5
6
|
# This class is used internally by RestClient to send the request, but you can also
|
@@ -27,7 +28,7 @@ module RestClient
|
|
27
28
|
:ssl_client_key, :ssl_ca_file, :processed_headers, :args
|
28
29
|
|
29
30
|
def self.execute(args, &block)
|
30
|
-
new(args).execute
|
31
|
+
new(args).execute(&block)
|
31
32
|
end
|
32
33
|
|
33
34
|
def initialize args
|
@@ -84,8 +85,8 @@ module RestClient
|
|
84
85
|
|
85
86
|
def parse_url_with_auth(url)
|
86
87
|
uri = parse_url(url)
|
87
|
-
@user = uri.user if uri.user
|
88
|
-
@password = uri.password if uri.password
|
88
|
+
@user = CGI.unescape(uri.user) if uri.user
|
89
|
+
@password = CGI.unescape(uri.password) if uri.password
|
89
90
|
uri
|
90
91
|
end
|
91
92
|
|
@@ -115,6 +116,13 @@ module RestClient
|
|
115
116
|
net.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
116
117
|
elsif @verify_ssl.is_a? Integer
|
117
118
|
net.verify_mode = @verify_ssl
|
119
|
+
net.verify_callback = lambda do |preverify_ok, ssl_context|
|
120
|
+
if (!preverify_ok) || ssl_context.error != 0
|
121
|
+
err_msg = "SSL Verification failed -- Preverify: #{preverify_ok}, Error: #{ssl_context.error_string} (#{ssl_context.error})"
|
122
|
+
raise SSLCertificateNotVerified.new(err_msg)
|
123
|
+
end
|
124
|
+
true
|
125
|
+
end
|
118
126
|
end
|
119
127
|
net.cert = @ssl_client_cert if @ssl_client_cert
|
120
128
|
net.key = @ssl_client_key if @ssl_client_key
|
@@ -182,7 +190,7 @@ module RestClient
|
|
182
190
|
if block_given?
|
183
191
|
block.call response, &block
|
184
192
|
else
|
185
|
-
response.return!
|
193
|
+
response.return!(&block)
|
186
194
|
end
|
187
195
|
|
188
196
|
end
|
@@ -219,20 +227,22 @@ module RestClient
|
|
219
227
|
# Return a hash of headers whose keys are capitalized strings
|
220
228
|
def stringify_headers headers
|
221
229
|
headers.inject({}) do |result, (key, value)|
|
222
|
-
|
223
|
-
|
230
|
+
if key.is_a? Symbol
|
231
|
+
key = key.to_s.split(/_/).map{|w| w.capitalize}.join('-')
|
232
|
+
end
|
233
|
+
if 'CONTENT-TYPE' == key.upcase
|
224
234
|
target_value = value.to_s
|
225
|
-
result[
|
226
|
-
elsif 'ACCEPT' ==
|
235
|
+
result[key] = MIME::Types.type_for_extension target_value
|
236
|
+
elsif 'ACCEPT' == key.upcase
|
227
237
|
# Accept can be composed of several comma-separated values
|
228
238
|
if value.is_a? Array
|
229
239
|
target_values = value
|
230
240
|
else
|
231
241
|
target_values = value.to_s.split ','
|
232
242
|
end
|
233
|
-
result[
|
243
|
+
result[key] = target_values.map{ |ext| MIME::Types.type_for_extension(ext.to_s.strip)}.join(', ')
|
234
244
|
else
|
235
|
-
result[
|
245
|
+
result[key] = value.to_s
|
236
246
|
end
|
237
247
|
result
|
238
248
|
end
|
data/lib/restclient/response.rb
CHANGED
@@ -51,6 +51,11 @@ describe RestClient::AbstractResponse do
|
|
51
51
|
@response.cookies.should == { 'session_id' => '1' }
|
52
52
|
end
|
53
53
|
|
54
|
+
it "extract strange cookies" do
|
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/HQVH6YE rVkTpn0zvTQ==' }
|
57
|
+
end
|
58
|
+
|
54
59
|
it "can access the net http result directly" do
|
55
60
|
@response.net_http_res.should == @net_http_res
|
56
61
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
|
3
|
+
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
|
4
|
+
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
|
5
|
+
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
|
6
|
+
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
|
7
|
+
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
|
8
|
+
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
|
9
|
+
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
|
10
|
+
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
|
11
|
+
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
|
12
|
+
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
|
13
|
+
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
|
14
|
+
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
|
15
|
+
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
|
16
|
+
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
|
17
|
+
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
|
18
|
+
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
|
19
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,14 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
|
3
|
+
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
|
4
|
+
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
|
5
|
+
MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
|
6
|
+
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
|
7
|
+
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
|
8
|
+
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
|
9
|
+
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
|
10
|
+
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
|
11
|
+
CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
|
12
|
+
lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
|
13
|
+
AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
|
14
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../base'
|
2
|
+
|
3
|
+
describe RestClient::Request do
|
4
|
+
describe "ssl verification" do
|
5
|
+
it "is successful with the correct ca_file" do
|
6
|
+
request = RestClient::Request.new(
|
7
|
+
:method => :get,
|
8
|
+
:url => 'https://www.google.com',
|
9
|
+
:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
|
10
|
+
:ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "verisign.crt")
|
11
|
+
)
|
12
|
+
expect { request.execute }.to_not raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it "is unsuccessful with an incorrect ca_file" do
|
16
|
+
request = RestClient::Request.new(
|
17
|
+
:method => :get,
|
18
|
+
:url => 'https://www.google.com',
|
19
|
+
:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
|
20
|
+
:ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "equifax.crt")
|
21
|
+
)
|
22
|
+
expect { request.execute }.to raise_error(RestClient::SSLCertificateNotVerified)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/request_spec.rb
CHANGED
@@ -86,6 +86,13 @@ describe RestClient::Request do
|
|
86
86
|
@request.password.should == 'pass1'
|
87
87
|
end
|
88
88
|
|
89
|
+
it "extracts with escaping the username and password when parsing http://user:password@example.com/" do
|
90
|
+
URI.stub!(:parse).and_return(mock('uri', :user => 'joe%20', :password => 'pass1'))
|
91
|
+
@request.parse_url_with_auth('http://joe%20:pass1@example.com/resource')
|
92
|
+
@request.user.should == 'joe '
|
93
|
+
@request.password.should == 'pass1'
|
94
|
+
end
|
95
|
+
|
89
96
|
it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
|
90
97
|
URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
|
91
98
|
@request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
|
@@ -98,8 +105,8 @@ describe RestClient::Request do
|
|
98
105
|
it "correctly formats cookies provided to the constructor" do
|
99
106
|
URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
|
100
107
|
@request = RestClient::Request.new(:method => 'get', :url => 'example.com', :cookies => {:session_id => '1', :user_id => "someone" })
|
101
|
-
@request.should_receive(:default_headers).and_return({'
|
102
|
-
|
108
|
+
@request.should_receive(:default_headers).and_return({'Foo' => 'bar'})
|
109
|
+
@request.make_headers({}).should == { 'Foo' => 'bar', 'Cookie' => 'session_id=1;user_id=someone'}
|
103
110
|
end
|
104
111
|
|
105
112
|
it "determines the Net::HTTP class to instantiate by the method name" do
|
@@ -152,6 +159,13 @@ describe RestClient::Request do
|
|
152
159
|
headers['Accept'].should == 'application/json'
|
153
160
|
end
|
154
161
|
|
162
|
+
it "only convert symbols in header" do
|
163
|
+
@request.should_receive(:default_headers).and_return({})
|
164
|
+
headers = @request.make_headers({:foo_bar => 'value', "bar_bar" => 'value'})
|
165
|
+
headers['Foo-Bar'].should == 'value'
|
166
|
+
headers['bar_bar'].should == 'value'
|
167
|
+
end
|
168
|
+
|
155
169
|
it "converts header values to strings" do
|
156
170
|
@request.make_headers('A' => 1)['A'].should == '1'
|
157
171
|
end
|
@@ -398,6 +412,7 @@ describe RestClient::Request do
|
|
398
412
|
:payload => 'payload',
|
399
413
|
:verify_ssl => mode )
|
400
414
|
@net.should_receive(:verify_mode=).with(mode)
|
415
|
+
@net.should_receive(:verify_callback=)
|
401
416
|
@http.stub!(:request)
|
402
417
|
@request.stub!(:process_result)
|
403
418
|
@request.stub!(:response_log)
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 1
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 1
|
7
8
|
- 5
|
8
|
-
-
|
9
|
-
version: 1.5.
|
9
|
+
- 1
|
10
|
+
version: 1.5.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Adam Wiggins
|
@@ -22,9 +23,11 @@ dependencies:
|
|
22
23
|
name: mime-types
|
23
24
|
prerelease: false
|
24
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
25
27
|
requirements:
|
26
28
|
- - ">="
|
27
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 47
|
28
31
|
segments:
|
29
32
|
- 1
|
30
33
|
- 16
|
@@ -66,6 +69,9 @@ files:
|
|
66
69
|
- spec/resource_spec.rb
|
67
70
|
- spec/response_spec.rb
|
68
71
|
- spec/restclient_spec.rb
|
72
|
+
- spec/integration/certs/equifax.crt
|
73
|
+
- spec/integration/certs/verisign.crt
|
74
|
+
- spec/integration/request_spec.rb
|
69
75
|
- history.md
|
70
76
|
has_rdoc: true
|
71
77
|
homepage: http://github.com/archiloque/rest-client
|
@@ -77,23 +83,27 @@ rdoc_options:
|
|
77
83
|
require_paths:
|
78
84
|
- lib
|
79
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
80
87
|
requirements:
|
81
88
|
- - ">="
|
82
89
|
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
83
91
|
segments:
|
84
92
|
- 0
|
85
93
|
version: "0"
|
86
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
87
96
|
requirements:
|
88
97
|
- - ">="
|
89
98
|
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
90
100
|
segments:
|
91
101
|
- 0
|
92
102
|
version: "0"
|
93
103
|
requirements: []
|
94
104
|
|
95
105
|
rubyforge_project: rest-client
|
96
|
-
rubygems_version: 1.3.
|
106
|
+
rubygems_version: 1.3.7
|
97
107
|
signing_key:
|
98
108
|
specification_version: 3
|
99
109
|
summary: Simple REST client for Ruby, inspired by microframework syntax for specifying actions.
|
@@ -108,3 +118,4 @@ test_files:
|
|
108
118
|
- spec/resource_spec.rb
|
109
119
|
- spec/response_spec.rb
|
110
120
|
- spec/restclient_spec.rb
|
121
|
+
- spec/integration/request_spec.rb
|