rest-client 1.5.0 → 1.5.1
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 +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
|