rest-client 1.6.14 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +6 -6
- data/.rspec +2 -1
- data/.rubocop-disables.yml +384 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +46 -1
- data/AUTHORS +28 -5
- data/Gemfile +5 -1
- data/LICENSE +21 -0
- data/README.md +784 -0
- data/Rakefile +95 -12
- data/bin/restclient +11 -12
- data/history.md +180 -16
- data/lib/restclient.rb +25 -11
- data/lib/restclient/abstract_response.rb +171 -51
- data/lib/restclient/exceptions.rb +102 -56
- data/lib/restclient/params_array.rb +72 -0
- data/lib/restclient/payload.rb +43 -74
- data/lib/restclient/platform.rb +22 -2
- data/lib/restclient/raw_response.rb +7 -3
- data/lib/restclient/request.rb +672 -179
- data/lib/restclient/resource.rb +6 -7
- data/lib/restclient/response.rb +64 -10
- data/lib/restclient/utils.rb +235 -0
- data/lib/restclient/version.rb +2 -1
- data/lib/restclient/windows.rb +8 -0
- data/lib/restclient/windows/root_certs.rb +105 -0
- data/rest-client.gemspec +16 -11
- data/rest-client.windows.gemspec +19 -0
- data/spec/helpers.rb +22 -0
- data/spec/integration/_lib.rb +1 -0
- data/spec/integration/capath_verisign/415660c1.0 +14 -0
- data/spec/integration/capath_verisign/7651b327.0 +14 -0
- data/spec/integration/capath_verisign/README +8 -0
- data/spec/integration/capath_verisign/verisign.crt +14 -0
- data/spec/integration/httpbin_spec.rb +87 -0
- data/spec/integration/integration_spec.rb +125 -0
- data/spec/integration/request_spec.rb +72 -20
- data/spec/spec_helper.rb +29 -0
- data/spec/unit/_lib.rb +1 -0
- data/spec/unit/abstract_response_spec.rb +145 -0
- data/spec/unit/exceptions_spec.rb +108 -0
- data/spec/{master_shake.jpg → unit/master_shake.jpg} +0 -0
- data/spec/unit/params_array_spec.rb +36 -0
- data/spec/{payload_spec.rb → unit/payload_spec.rb} +73 -54
- data/spec/{raw_response_spec.rb → unit/raw_response_spec.rb} +5 -4
- data/spec/unit/request2_spec.rb +54 -0
- data/spec/unit/request_spec.rb +1250 -0
- data/spec/unit/resource_spec.rb +134 -0
- data/spec/unit/response_spec.rb +241 -0
- data/spec/unit/restclient_spec.rb +79 -0
- data/spec/unit/utils_spec.rb +147 -0
- data/spec/unit/windows/root_certs_spec.rb +22 -0
- metadata +143 -53
- data/README.rdoc +0 -300
- data/lib/restclient/net_http_ext.rb +0 -55
- data/spec/abstract_response_spec.rb +0 -85
- data/spec/base.rb +0 -13
- data/spec/exceptions_spec.rb +0 -98
- data/spec/integration_spec.rb +0 -38
- data/spec/request2_spec.rb +0 -35
- data/spec/request_spec.rb +0 -528
- data/spec/resource_spec.rb +0 -136
- data/spec/response_spec.rb +0 -169
- data/spec/restclient_spec.rb +0 -73
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'webmock/rspec'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
require_relative './helpers'
|
5
|
+
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.raise_errors_for_deprecations!
|
9
|
+
|
10
|
+
# Run specs in random order to surface order dependencies. If you find an
|
11
|
+
# order dependency and want to debug it, you can fix the order by providing
|
12
|
+
# the seed, which is printed after each run.
|
13
|
+
# --seed 1234
|
14
|
+
config.order = 'random'
|
15
|
+
|
16
|
+
# always run with ruby warnings enabled
|
17
|
+
# TODO: figure out why this is so obscenely noisy (rspec bug?)
|
18
|
+
# config.warnings = true
|
19
|
+
|
20
|
+
# add helpers
|
21
|
+
config.include Helpers, :include_helpers
|
22
|
+
|
23
|
+
config.mock_with :rspec do |mocks|
|
24
|
+
mocks.yield_receiver_to_any_instance_implementation_blocks = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# always run with ruby warnings enabled (see above)
|
29
|
+
$VERBOSE = true
|
data/spec/unit/_lib.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative '../spec_helper'
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require_relative '_lib'
|
2
|
+
|
3
|
+
describe RestClient::AbstractResponse, :include_helpers do
|
4
|
+
|
5
|
+
class MyAbstractResponse
|
6
|
+
|
7
|
+
include RestClient::AbstractResponse
|
8
|
+
|
9
|
+
attr_accessor :size
|
10
|
+
|
11
|
+
def initialize net_http_res, request
|
12
|
+
@net_http_res = net_http_res
|
13
|
+
@request = request
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
before do
|
19
|
+
@net_http_res = double('net http response')
|
20
|
+
@request = request_double(url: 'http://example.com', method: 'get')
|
21
|
+
@response = MyAbstractResponse.new(@net_http_res, @request)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "fetches the numeric response code" do
|
25
|
+
expect(@net_http_res).to receive(:code).and_return('200')
|
26
|
+
expect(@response.code).to eq 200
|
27
|
+
end
|
28
|
+
|
29
|
+
it "has a nice description" do
|
30
|
+
expect(@net_http_res).to receive(:to_hash).and_return({'Content-Type' => ['application/pdf']})
|
31
|
+
expect(@net_http_res).to receive(:code).and_return('200')
|
32
|
+
expect(@response.description).to eq "200 OK | application/pdf bytes\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '.beautify_headers' do
|
36
|
+
it "beautifies the headers by turning the keys to symbols" do
|
37
|
+
h = RestClient::AbstractResponse.beautify_headers('content-type' => [ 'x' ])
|
38
|
+
expect(h.keys.first).to eq :content_type
|
39
|
+
end
|
40
|
+
|
41
|
+
it "beautifies the headers by turning the values to strings instead of one-element arrays" do
|
42
|
+
h = RestClient::AbstractResponse.beautify_headers('x' => [ 'text/html' ] )
|
43
|
+
expect(h.values.first).to eq 'text/html'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'joins multiple header values by comma' do
|
47
|
+
expect(RestClient::AbstractResponse.beautify_headers(
|
48
|
+
{'My-Header' => ['one', 'two']}
|
49
|
+
)).to eq({:my_header => 'one, two'})
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'leaves set-cookie headers as array' do
|
53
|
+
expect(RestClient::AbstractResponse.beautify_headers(
|
54
|
+
{'Set-Cookie' => ['cookie1=foo', 'cookie2=bar']}
|
55
|
+
)).to eq({:set_cookie => ['cookie1=foo', 'cookie2=bar']})
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "fetches the headers" do
|
60
|
+
expect(@net_http_res).to receive(:to_hash).and_return('content-type' => [ 'text/html' ])
|
61
|
+
expect(@response.headers).to eq({ :content_type => 'text/html' })
|
62
|
+
end
|
63
|
+
|
64
|
+
it "extracts cookies from response headers" do
|
65
|
+
expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
|
66
|
+
expect(@response.cookies).to eq({ 'session_id' => '1' })
|
67
|
+
end
|
68
|
+
|
69
|
+
it "extract strange cookies" do
|
70
|
+
expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/'])
|
71
|
+
expect(@response.headers).to eq({:set_cookie => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/']})
|
72
|
+
expect(@response.cookies).to eq({ 'session_id' => 'ZJ/HQVH6YE+rVkTpn0zvTQ==' })
|
73
|
+
end
|
74
|
+
|
75
|
+
it "doesn't escape cookies" do
|
76
|
+
expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca; path=/'])
|
77
|
+
expect(@response.cookies).to eq({ 'session_id' => 'BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca' })
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '.cookie_jar' do
|
81
|
+
it 'extracts cookies into cookie jar' do
|
82
|
+
expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
|
83
|
+
expect(@response.cookie_jar).to be_a HTTP::CookieJar
|
84
|
+
|
85
|
+
cookie = @response.cookie_jar.cookies.first
|
86
|
+
expect(cookie.domain).to eq 'example.com'
|
87
|
+
expect(cookie.name).to eq 'session_id'
|
88
|
+
expect(cookie.value).to eq '1'
|
89
|
+
expect(cookie.path).to eq '/'
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'handles cookies when URI scheme is implicit' do
|
93
|
+
net_http_res = double('net http response')
|
94
|
+
expect(net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
|
95
|
+
request = double(url: 'example.com', uri: URI.parse('http://example.com'),
|
96
|
+
method: 'get', cookie_jar: HTTP::CookieJar.new)
|
97
|
+
response = MyAbstractResponse.new(net_http_res, request)
|
98
|
+
expect(response.cookie_jar).to be_a HTTP::CookieJar
|
99
|
+
|
100
|
+
cookie = response.cookie_jar.cookies.first
|
101
|
+
expect(cookie.domain).to eq 'example.com'
|
102
|
+
expect(cookie.name).to eq 'session_id'
|
103
|
+
expect(cookie.value).to eq '1'
|
104
|
+
expect(cookie.path).to eq '/'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it "can access the net http result directly" do
|
109
|
+
expect(@response.net_http_res).to eq @net_http_res
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#return!" do
|
113
|
+
it "should return the response itself on 200-codes" do
|
114
|
+
expect(@net_http_res).to receive(:code).and_return('200')
|
115
|
+
expect(@response.return!).to be_equal(@response)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should raise RequestFailed on unknown codes" do
|
119
|
+
expect(@net_http_res).to receive(:code).and_return('1000')
|
120
|
+
expect { @response.return! }.to raise_error RestClient::RequestFailed
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should raise an error on a redirection after non-GET/HEAD requests" do
|
124
|
+
expect(@net_http_res).to receive(:code).and_return('301')
|
125
|
+
expect(@request).to receive(:method).and_return('put')
|
126
|
+
expect(@response).not_to receive(:follow_redirection)
|
127
|
+
expect { @response.return! }.to raise_error RestClient::RequestFailed
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should follow 302 redirect" do
|
131
|
+
expect(@net_http_res).to receive(:code).and_return('302')
|
132
|
+
expect(@response).to receive(:check_max_redirects).and_return('fake-check')
|
133
|
+
expect(@response).to receive(:follow_redirection).and_return('fake-redirection')
|
134
|
+
expect(@response.return!).to eq 'fake-redirection'
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should gracefully handle 302 redirect with no location header" do
|
138
|
+
@net_http_res = response_double(code: 302, location: nil)
|
139
|
+
@request = request_double()
|
140
|
+
@response = MyAbstractResponse.new(@net_http_res, @request)
|
141
|
+
expect(@response).to receive(:check_max_redirects).and_return('fake-check')
|
142
|
+
expect { @response.return! }.to raise_error RestClient::Found
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require_relative '_lib'
|
2
|
+
|
3
|
+
describe RestClient::Exception do
|
4
|
+
it "returns a 'message' equal to the class name if the message is not set, because 'message' should not be nil" do
|
5
|
+
e = RestClient::Exception.new
|
6
|
+
expect(e.message).to eq "RestClient::Exception"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns the 'message' that was set" do
|
10
|
+
e = RestClient::Exception.new
|
11
|
+
message = "An explicitly set message"
|
12
|
+
e.message = message
|
13
|
+
expect(e.message).to eq message
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets the exception message to ErrorMessage" do
|
17
|
+
expect(RestClient::ResourceNotFound.new.message).to eq 'Not Found'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "contains exceptions in RestClient" do
|
21
|
+
expect(RestClient::Unauthorized.new).to be_a_kind_of(RestClient::Exception)
|
22
|
+
expect(RestClient::ServerBrokeConnection.new).to be_a_kind_of(RestClient::Exception)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe RestClient::ServerBrokeConnection do
|
27
|
+
it "should have a default message of 'Server broke connection'" do
|
28
|
+
e = RestClient::ServerBrokeConnection.new
|
29
|
+
expect(e.message).to eq 'Server broke connection'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe RestClient::RequestFailed do
|
34
|
+
before do
|
35
|
+
@response = double('HTTP Response', :code => '502')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "stores the http response on the exception" do
|
39
|
+
response = "response"
|
40
|
+
begin
|
41
|
+
raise RestClient::RequestFailed, response
|
42
|
+
rescue RestClient::RequestFailed => e
|
43
|
+
expect(e.response).to eq response
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "http_code convenience method for fetching the code as an integer" do
|
48
|
+
expect(RestClient::RequestFailed.new(@response).http_code).to eq 502
|
49
|
+
end
|
50
|
+
|
51
|
+
it "http_body convenience method for fetching the body (decoding when necessary)" do
|
52
|
+
expect(RestClient::RequestFailed.new(@response).http_code).to eq 502
|
53
|
+
expect(RestClient::RequestFailed.new(@response).message).to eq 'HTTP status code 502'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "shows the status code in the message" do
|
57
|
+
expect(RestClient::RequestFailed.new(@response).to_s).to match(/502/)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe RestClient::ResourceNotFound do
|
62
|
+
it "also has the http response attached" do
|
63
|
+
response = "response"
|
64
|
+
begin
|
65
|
+
raise RestClient::ResourceNotFound, response
|
66
|
+
rescue RestClient::ResourceNotFound => e
|
67
|
+
expect(e.response).to eq response
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'stores the body on the response of the exception' do
|
72
|
+
body = "body"
|
73
|
+
stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
|
74
|
+
begin
|
75
|
+
RestClient.get "www.example.com"
|
76
|
+
raise
|
77
|
+
rescue RestClient::ResourceNotFound => e
|
78
|
+
expect(e.response.body).to eq body
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "backwards compatibility" do
|
84
|
+
it 'aliases RestClient::NotFound as ResourceNotFound' do
|
85
|
+
expect(RestClient::ResourceNotFound).to eq RestClient::NotFound
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'aliases old names for HTTP 413, 414, 416' do
|
89
|
+
expect(RestClient::RequestEntityTooLarge).to eq RestClient::PayloadTooLarge
|
90
|
+
expect(RestClient::RequestURITooLong).to eq RestClient::URITooLong
|
91
|
+
expect(RestClient::RequestedRangeNotSatisfiable).to eq RestClient::RangeNotSatisfiable
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'subclasses NotFound from RequestFailed, ExceptionWithResponse' do
|
95
|
+
expect(RestClient::NotFound).to be < RestClient::RequestFailed
|
96
|
+
expect(RestClient::NotFound).to be < RestClient::ExceptionWithResponse
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'subclasses timeout from RestClient::RequestTimeout, RequestFailed, EWR' do
|
100
|
+
expect(RestClient::Exceptions::OpenTimeout).to be < RestClient::Exceptions::Timeout
|
101
|
+
expect(RestClient::Exceptions::ReadTimeout).to be < RestClient::Exceptions::Timeout
|
102
|
+
|
103
|
+
expect(RestClient::Exceptions::Timeout).to be < RestClient::RequestTimeout
|
104
|
+
expect(RestClient::Exceptions::Timeout).to be < RestClient::RequestFailed
|
105
|
+
expect(RestClient::Exceptions::Timeout).to be < RestClient::ExceptionWithResponse
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
File without changes
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative '_lib'
|
2
|
+
|
3
|
+
describe RestClient::ParamsArray do
|
4
|
+
|
5
|
+
describe '.new' do
|
6
|
+
it 'accepts various types of containers' do
|
7
|
+
as_array = [[:foo, 123], [:foo, 456], [:bar, 789], [:empty, nil]]
|
8
|
+
[
|
9
|
+
[[:foo, 123], [:foo, 456], [:bar, 789], [:empty, nil]],
|
10
|
+
[{foo: 123}, {foo: 456}, {bar: 789}, {empty: nil}],
|
11
|
+
[{foo: 123}, {foo: 456}, {bar: 789}, {empty: nil}],
|
12
|
+
[{foo: 123}, [:foo, 456], {bar: 789}, {empty: nil}],
|
13
|
+
[{foo: 123}, [:foo, 456], {bar: 789}, [:empty]],
|
14
|
+
].each do |input|
|
15
|
+
expect(RestClient::ParamsArray.new(input).to_a).to eq as_array
|
16
|
+
end
|
17
|
+
|
18
|
+
expect(RestClient::ParamsArray.new([]).to_a).to eq []
|
19
|
+
expect(RestClient::ParamsArray.new([]).empty?).to eq true
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'rejects various invalid input' do
|
23
|
+
expect {
|
24
|
+
RestClient::ParamsArray.new([[]])
|
25
|
+
}.to raise_error(IndexError)
|
26
|
+
|
27
|
+
expect {
|
28
|
+
RestClient::ParamsArray.new([[1,2,3]])
|
29
|
+
}.to raise_error(ArgumentError)
|
30
|
+
|
31
|
+
expect {
|
32
|
+
RestClient::ParamsArray.new([1,2,3])
|
33
|
+
}.to raise_error(NoMethodError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,59 +1,68 @@
|
|
1
1
|
# encoding: binary
|
2
|
-
|
2
|
+
|
3
|
+
require_relative '_lib'
|
3
4
|
|
4
5
|
describe RestClient::Payload do
|
6
|
+
context "Base Payload" do
|
7
|
+
it "should reset stream after to_s" do
|
8
|
+
payload = RestClient::Payload::Base.new('foobar')
|
9
|
+
expect(payload.to_s).to eq 'foobar'
|
10
|
+
expect(payload.to_s).to eq 'foobar'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
context "A regular Payload" do
|
6
15
|
it "should use standard enctype as default content-type" do
|
7
|
-
RestClient::Payload::UrlEncoded.new({}).headers['Content-Type'].
|
8
|
-
|
16
|
+
expect(RestClient::Payload::UrlEncoded.new({}).headers['Content-Type']).
|
17
|
+
to eq 'application/x-www-form-urlencoded'
|
9
18
|
end
|
10
19
|
|
11
20
|
it "should form properly encoded params" do
|
12
|
-
RestClient::Payload::UrlEncoded.new({:foo => 'bar'}).to_s.
|
13
|
-
|
14
|
-
["foo=bar&baz=qux", "baz=qux&foo=bar"].
|
21
|
+
expect(RestClient::Payload::UrlEncoded.new({:foo => 'bar'}).to_s).
|
22
|
+
to eq "foo=bar"
|
23
|
+
expect(["foo=bar&baz=qux", "baz=qux&foo=bar"]).to include(
|
15
24
|
RestClient::Payload::UrlEncoded.new({:foo => 'bar', :baz => 'qux'}).to_s)
|
16
25
|
end
|
17
26
|
|
18
27
|
it "should escape parameters" do
|
19
|
-
RestClient::Payload::UrlEncoded.new({'foo ' => '
|
20
|
-
|
28
|
+
expect(RestClient::Payload::UrlEncoded.new({'foo + bar' => 'baz'}).to_s).
|
29
|
+
to eq "foo+%2B+bar=baz"
|
21
30
|
end
|
22
31
|
|
23
32
|
it "should properly handle hashes as parameter" do
|
24
|
-
RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz'}}).to_s.
|
25
|
-
|
26
|
-
RestClient::Payload::UrlEncoded.new({:foo => {:bar => {:baz => 'qux'}}}).to_s.
|
27
|
-
|
33
|
+
expect(RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz'}}).to_s).
|
34
|
+
to eq "foo[bar]=baz"
|
35
|
+
expect(RestClient::Payload::UrlEncoded.new({:foo => {:bar => {:baz => 'qux'}}}).to_s).
|
36
|
+
to eq "foo[bar][baz]=qux"
|
28
37
|
end
|
29
38
|
|
30
39
|
it "should handle many attributes inside a hash" do
|
31
40
|
parameters = RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz', :baz => 'qux'}}).to_s
|
32
|
-
parameters.
|
41
|
+
expect(parameters).to eq 'foo[bar]=baz&foo[baz]=qux'
|
33
42
|
end
|
34
43
|
|
35
|
-
it "should handle attributes inside
|
44
|
+
it "should handle attributes inside an array inside an hash" do
|
36
45
|
parameters = RestClient::Payload::UrlEncoded.new({"foo" => [{"bar" => 'baz'}, {"bar" => 'qux'}]}).to_s
|
37
|
-
parameters.
|
46
|
+
expect(parameters).to eq 'foo[][bar]=baz&foo[][bar]=qux'
|
38
47
|
end
|
39
48
|
|
40
|
-
it "should handle
|
41
|
-
parameters = RestClient::Payload::UrlEncoded.new({"foo" =>
|
42
|
-
parameters.
|
49
|
+
it "should handle arrays inside a hash inside a hash" do
|
50
|
+
parameters = RestClient::Payload::UrlEncoded.new({"foo" => {'even' => [0, 2], 'odd' => [1, 3]}}).to_s
|
51
|
+
expect(parameters).to eq 'foo[even][]=0&foo[even][]=2&foo[odd][]=1&foo[odd][]=3'
|
43
52
|
end
|
44
53
|
|
45
54
|
it "should form properly use symbols as parameters" do
|
46
|
-
RestClient::Payload::UrlEncoded.new({:foo => :bar}).to_s.
|
47
|
-
|
48
|
-
RestClient::Payload::UrlEncoded.new({:foo => {:bar => :baz}}).to_s.
|
49
|
-
|
55
|
+
expect(RestClient::Payload::UrlEncoded.new({:foo => :bar}).to_s).
|
56
|
+
to eq "foo=bar"
|
57
|
+
expect(RestClient::Payload::UrlEncoded.new({:foo => {:bar => :baz}}).to_s).
|
58
|
+
to eq "foo[bar]=baz"
|
50
59
|
end
|
51
60
|
|
52
61
|
it "should properly handle arrays as repeated parameters" do
|
53
|
-
RestClient::Payload::UrlEncoded.new({:foo => ['bar']}).to_s.
|
54
|
-
|
55
|
-
RestClient::Payload::UrlEncoded.new({:foo => ['bar', 'baz']}).to_s.
|
56
|
-
|
62
|
+
expect(RestClient::Payload::UrlEncoded.new({:foo => ['bar']}).to_s).
|
63
|
+
to eq "foo[]=bar"
|
64
|
+
expect(RestClient::Payload::UrlEncoded.new({:foo => ['bar', 'baz']}).to_s).
|
65
|
+
to eq "foo[]=bar&foo[]=baz"
|
57
66
|
end
|
58
67
|
|
59
68
|
it 'should not close if stream already closed' do
|
@@ -66,8 +75,8 @@ describe RestClient::Payload do
|
|
66
75
|
context "A multipart Payload" do
|
67
76
|
it "should use standard enctype as default content-type" do
|
68
77
|
m = RestClient::Payload::Multipart.new({})
|
69
|
-
m.
|
70
|
-
m.headers['Content-Type'].
|
78
|
+
allow(m).to receive(:boundary).and_return(123)
|
79
|
+
expect(m.headers['Content-Type']).to eq 'multipart/form-data; boundary=123'
|
71
80
|
end
|
72
81
|
|
73
82
|
it 'should not error on close if stream already closed' do
|
@@ -77,7 +86,7 @@ describe RestClient::Payload do
|
|
77
86
|
|
78
87
|
it "should form properly separated multipart data" do
|
79
88
|
m = RestClient::Payload::Multipart.new([[:bar, "baz"], [:foo, "bar"]])
|
80
|
-
m.to_s.
|
89
|
+
expect(m.to_s).to eq <<-EOS
|
81
90
|
--#{m.boundary}\r
|
82
91
|
Content-Disposition: form-data; name="bar"\r
|
83
92
|
\r
|
@@ -92,7 +101,7 @@ bar\r
|
|
92
101
|
|
93
102
|
it "should not escape parameters names" do
|
94
103
|
m = RestClient::Payload::Multipart.new([["bar ", "baz"]])
|
95
|
-
m.to_s.
|
104
|
+
expect(m.to_s).to eq <<-EOS
|
96
105
|
--#{m.boundary}\r
|
97
106
|
Content-Disposition: form-data; name="bar "\r
|
98
107
|
\r
|
@@ -104,7 +113,7 @@ baz\r
|
|
104
113
|
it "should form properly separated multipart data" do
|
105
114
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
106
115
|
m = RestClient::Payload::Multipart.new({:foo => f})
|
107
|
-
m.to_s.
|
116
|
+
expect(m.to_s).to eq <<-EOS
|
108
117
|
--#{m.boundary}\r
|
109
118
|
Content-Disposition: form-data; name="foo"; filename="master_shake.jpg"\r
|
110
119
|
Content-Type: image/jpeg\r
|
@@ -117,7 +126,7 @@ Content-Type: image/jpeg\r
|
|
117
126
|
it "should ignore the name attribute when it's not set" do
|
118
127
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
119
128
|
m = RestClient::Payload::Multipart.new({nil => f})
|
120
|
-
m.to_s.
|
129
|
+
expect(m.to_s).to eq <<-EOS
|
121
130
|
--#{m.boundary}\r
|
122
131
|
Content-Disposition: form-data; filename="master_shake.jpg"\r
|
123
132
|
Content-Type: image/jpeg\r
|
@@ -132,7 +141,7 @@ Content-Type: image/jpeg\r
|
|
132
141
|
f.instance_eval "def content_type; 'text/plain'; end"
|
133
142
|
f.instance_eval "def original_filename; 'foo.txt'; end"
|
134
143
|
m = RestClient::Payload::Multipart.new({:foo => f})
|
135
|
-
m.to_s.
|
144
|
+
expect(m.to_s).to eq <<-EOS
|
136
145
|
--#{m.boundary}\r
|
137
146
|
Content-Disposition: form-data; name="foo"; filename="foo.txt"\r
|
138
147
|
Content-Type: text/plain\r
|
@@ -144,7 +153,7 @@ Content-Type: text/plain\r
|
|
144
153
|
|
145
154
|
it "should handle hash in hash parameters" do
|
146
155
|
m = RestClient::Payload::Multipart.new({:bar => {:baz => "foo"}})
|
147
|
-
m.to_s.
|
156
|
+
expect(m.to_s).to eq <<-EOS
|
148
157
|
--#{m.boundary}\r
|
149
158
|
Content-Disposition: form-data; name="bar[baz]"\r
|
150
159
|
\r
|
@@ -156,7 +165,7 @@ foo\r
|
|
156
165
|
f.instance_eval "def content_type; 'text/plain'; end"
|
157
166
|
f.instance_eval "def original_filename; 'foo.txt'; end"
|
158
167
|
m = RestClient::Payload::Multipart.new({:foo => {:bar => f}})
|
159
|
-
m.to_s.
|
168
|
+
expect(m.to_s).to eq <<-EOS
|
160
169
|
--#{m.boundary}\r
|
161
170
|
Content-Disposition: form-data; name="foo[bar]"; filename="foo.txt"\r
|
162
171
|
Content-Type: text/plain\r
|
@@ -166,29 +175,36 @@ Content-Type: text/plain\r
|
|
166
175
|
EOS
|
167
176
|
end
|
168
177
|
|
178
|
+
it 'should correctly format hex boundary' do
|
179
|
+
allow(SecureRandom).to receive(:base64).with(12).and_return('TGs89+ttw/xna6TV')
|
180
|
+
f = File.new(File.dirname(__FILE__) + '/master_shake.jpg')
|
181
|
+
m = RestClient::Payload::Multipart.new({:foo => f})
|
182
|
+
expect(m.boundary).to eq('-' * 4 + 'RubyFormBoundary' + 'TGs89AttwBxna6TV')
|
183
|
+
end
|
184
|
+
|
169
185
|
end
|
170
186
|
|
171
187
|
context "streamed payloads" do
|
172
188
|
it "should properly determine the size of file payloads" do
|
173
189
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
174
190
|
payload = RestClient::Payload.generate(f)
|
175
|
-
payload.size.
|
176
|
-
payload.length.
|
191
|
+
expect(payload.size).to eq 76_988
|
192
|
+
expect(payload.length).to eq 76_988
|
177
193
|
end
|
178
194
|
|
179
195
|
it "should properly determine the size of other kinds of streaming payloads" do
|
180
196
|
s = StringIO.new 'foo'
|
181
197
|
payload = RestClient::Payload.generate(s)
|
182
|
-
payload.size.
|
183
|
-
payload.length.
|
198
|
+
expect(payload.size).to eq 3
|
199
|
+
expect(payload.length).to eq 3
|
184
200
|
|
185
201
|
begin
|
186
202
|
f = Tempfile.new "rest-client"
|
187
203
|
f.write 'foo bar'
|
188
204
|
|
189
205
|
payload = RestClient::Payload.generate(f)
|
190
|
-
payload.size.
|
191
|
-
payload.length.
|
206
|
+
expect(payload.size).to eq 7
|
207
|
+
expect(payload.length).to eq 7
|
192
208
|
ensure
|
193
209
|
f.close
|
194
210
|
end
|
@@ -197,48 +213,51 @@ Content-Type: text/plain\r
|
|
197
213
|
|
198
214
|
context "Payload generation" do
|
199
215
|
it "should recognize standard urlencoded params" do
|
200
|
-
RestClient::Payload.generate({"foo" => 'bar'}).
|
216
|
+
expect(RestClient::Payload.generate({"foo" => 'bar'})).to be_kind_of(RestClient::Payload::UrlEncoded)
|
201
217
|
end
|
202
218
|
|
203
219
|
it "should recognize multipart params" do
|
204
220
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
205
|
-
RestClient::Payload.generate({"foo" => f}).
|
221
|
+
expect(RestClient::Payload.generate({"foo" => f})).to be_kind_of(RestClient::Payload::Multipart)
|
206
222
|
end
|
207
223
|
|
208
224
|
it "should be multipart if forced" do
|
209
|
-
RestClient::Payload.generate({"foo" => "bar", :multipart => true}).
|
225
|
+
expect(RestClient::Payload.generate({"foo" => "bar", :multipart => true})).to be_kind_of(RestClient::Payload::Multipart)
|
210
226
|
end
|
211
227
|
|
228
|
+
it "should handle deeply nested multipart" do
|
229
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
230
|
+
params = {foo: RestClient::ParamsArray.new({nested: f})}
|
231
|
+
expect(RestClient::Payload.generate(params)).to be_kind_of(RestClient::Payload::Multipart)
|
232
|
+
end
|
233
|
+
|
234
|
+
|
212
235
|
it "should return data if no of the above" do
|
213
|
-
RestClient::Payload.generate("data").
|
236
|
+
expect(RestClient::Payload.generate("data")).to be_kind_of(RestClient::Payload::Base)
|
214
237
|
end
|
215
238
|
|
216
239
|
it "should recognize nested multipart payloads in hashes" do
|
217
240
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
218
|
-
RestClient::Payload.generate({"foo" => {"file" => f}}).
|
241
|
+
expect(RestClient::Payload.generate({"foo" => {"file" => f}})).to be_kind_of(RestClient::Payload::Multipart)
|
219
242
|
end
|
220
243
|
|
221
244
|
it "should recognize nested multipart payloads in arrays" do
|
222
245
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
223
|
-
RestClient::Payload.generate({"foo" => [f]}).
|
246
|
+
expect(RestClient::Payload.generate({"foo" => [f]})).to be_kind_of(RestClient::Payload::Multipart)
|
224
247
|
end
|
225
248
|
|
226
249
|
it "should recognize file payloads that can be streamed" do
|
227
250
|
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
228
|
-
RestClient::Payload.generate(f).
|
251
|
+
expect(RestClient::Payload.generate(f)).to be_kind_of(RestClient::Payload::Streamed)
|
229
252
|
end
|
230
253
|
|
231
254
|
it "should recognize other payloads that can be streamed" do
|
232
|
-
RestClient::Payload.generate(StringIO.new('foo')).
|
255
|
+
expect(RestClient::Payload.generate(StringIO.new('foo'))).to be_kind_of(RestClient::Payload::Streamed)
|
233
256
|
end
|
234
257
|
|
235
258
|
# hashery gem introduces Hash#read convenience method. Existence of #read method used to determine of content is streameable :/
|
236
259
|
it "shouldn't treat hashes as streameable" do
|
237
|
-
RestClient::Payload.generate({"foo" => 'bar'}).
|
260
|
+
expect(RestClient::Payload.generate({"foo" => 'bar'})).to be_kind_of(RestClient::Payload::UrlEncoded)
|
238
261
|
end
|
239
262
|
end
|
240
|
-
|
241
|
-
class HashMapForTesting < Hash
|
242
|
-
alias :read :[]
|
243
|
-
end
|
244
263
|
end
|