rest-man 1.0.0

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.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/multi-matrix-test.yml +35 -0
  3. data/.github/workflows/single-matrix-test.yml +27 -0
  4. data/.gitignore +13 -0
  5. data/.mailmap +10 -0
  6. data/.rspec +2 -0
  7. data/.rubocop +2 -0
  8. data/.rubocop-disables.yml +386 -0
  9. data/.rubocop.yml +8 -0
  10. data/AUTHORS +106 -0
  11. data/CHANGELOG.md +7 -0
  12. data/Gemfile +11 -0
  13. data/LICENSE +21 -0
  14. data/README.md +843 -0
  15. data/Rakefile +140 -0
  16. data/exe/restman +92 -0
  17. data/lib/rest-man.rb +2 -0
  18. data/lib/rest_man.rb +2 -0
  19. data/lib/restman/abstract_response.rb +252 -0
  20. data/lib/restman/exceptions.rb +238 -0
  21. data/lib/restman/params_array.rb +72 -0
  22. data/lib/restman/payload.rb +234 -0
  23. data/lib/restman/platform.rb +49 -0
  24. data/lib/restman/raw_response.rb +49 -0
  25. data/lib/restman/request.rb +859 -0
  26. data/lib/restman/resource.rb +178 -0
  27. data/lib/restman/response.rb +90 -0
  28. data/lib/restman/utils.rb +274 -0
  29. data/lib/restman/version.rb +8 -0
  30. data/lib/restman/windows/root_certs.rb +105 -0
  31. data/lib/restman/windows.rb +8 -0
  32. data/lib/restman.rb +183 -0
  33. data/matrixeval.yml +73 -0
  34. data/rest-man.gemspec +41 -0
  35. data/spec/ISS.jpg +0 -0
  36. data/spec/cassettes/request_httpbin_with_basic_auth.yml +83 -0
  37. data/spec/cassettes/request_httpbin_with_cookies.yml +49 -0
  38. data/spec/cassettes/request_httpbin_with_cookies_2.yml +94 -0
  39. data/spec/cassettes/request_httpbin_with_cookies_3.yml +49 -0
  40. data/spec/cassettes/request_httpbin_with_encoding_deflate.yml +45 -0
  41. data/spec/cassettes/request_httpbin_with_encoding_deflate_and_accept_headers.yml +44 -0
  42. data/spec/cassettes/request_httpbin_with_encoding_gzip.yml +45 -0
  43. data/spec/cassettes/request_httpbin_with_encoding_gzip_and_accept_headers.yml +44 -0
  44. data/spec/cassettes/request_httpbin_with_user_agent.yml +44 -0
  45. data/spec/cassettes/request_mozilla_org.yml +151 -0
  46. data/spec/cassettes/request_mozilla_org_callback_returns_true.yml +178 -0
  47. data/spec/cassettes/request_mozilla_org_with_system_cert.yml +152 -0
  48. data/spec/cassettes/request_mozilla_org_with_system_cert_and_callback.yml +151 -0
  49. data/spec/helpers.rb +54 -0
  50. data/spec/integration/_lib.rb +1 -0
  51. data/spec/integration/capath_digicert/README +8 -0
  52. data/spec/integration/capath_digicert/ce5e74ef.0 +1 -0
  53. data/spec/integration/capath_digicert/digicert.crt +20 -0
  54. data/spec/integration/capath_digicert/update +1 -0
  55. data/spec/integration/capath_verisign/415660c1.0 +14 -0
  56. data/spec/integration/capath_verisign/7651b327.0 +14 -0
  57. data/spec/integration/capath_verisign/README +8 -0
  58. data/spec/integration/capath_verisign/verisign.crt +14 -0
  59. data/spec/integration/certs/digicert.crt +20 -0
  60. data/spec/integration/certs/verisign.crt +14 -0
  61. data/spec/integration/httpbin_spec.rb +137 -0
  62. data/spec/integration/integration_spec.rb +118 -0
  63. data/spec/integration/request_spec.rb +134 -0
  64. data/spec/spec_helper.rb +40 -0
  65. data/spec/unit/_lib.rb +1 -0
  66. data/spec/unit/abstract_response_spec.rb +145 -0
  67. data/spec/unit/exceptions_spec.rb +108 -0
  68. data/spec/unit/params_array_spec.rb +36 -0
  69. data/spec/unit/payload_spec.rb +295 -0
  70. data/spec/unit/raw_response_spec.rb +22 -0
  71. data/spec/unit/request2_spec.rb +54 -0
  72. data/spec/unit/request_spec.rb +1205 -0
  73. data/spec/unit/resource_spec.rb +134 -0
  74. data/spec/unit/response_spec.rb +252 -0
  75. data/spec/unit/restclient_spec.rb +80 -0
  76. data/spec/unit/utils_spec.rb +147 -0
  77. data/spec/unit/windows/root_certs_spec.rb +22 -0
  78. metadata +336 -0
@@ -0,0 +1,1205 @@
1
+ require_relative './_lib'
2
+
3
+ describe RestMan::Request, :include_helpers do
4
+ before do
5
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
6
+
7
+ @uri = double("uri")
8
+ allow(@uri).to receive(:request_uri).and_return('/resource')
9
+ allow(@uri).to receive(:hostname).and_return('some')
10
+ allow(@uri).to receive(:port).and_return(80)
11
+
12
+ @net = double("net::http base")
13
+ @http = double("net::http connection")
14
+
15
+ allow(Net::HTTP).to receive(:new).and_return(@net)
16
+
17
+ allow(@net).to receive(:start).and_yield(@http)
18
+ allow(@net).to receive(:use_ssl=)
19
+ allow(@net).to receive(:verify_mode=)
20
+ allow(@net).to receive(:verify_callback=)
21
+ allow(@net).to receive(:ciphers=)
22
+ allow(@net).to receive(:cert_store=)
23
+ RestMan.log = nil
24
+ end
25
+
26
+ it "accept */* mimetype" do
27
+ expect(@request.default_headers[:accept]).to eq '*/*'
28
+ end
29
+
30
+ it "processes a successful result" do
31
+ res = res_double
32
+ allow(res).to receive(:code).and_return("200")
33
+ allow(res).to receive(:body).and_return('body')
34
+ allow(res).to receive(:[]).with('content-encoding').and_return(nil)
35
+ expect(@request.send(:process_result, res, Time.now).body).to eq 'body'
36
+ expect(@request.send(:process_result, res, Time.now).to_s).to eq 'body'
37
+ end
38
+
39
+ it "doesn't classify successful requests as failed" do
40
+ 203.upto(207) do |code|
41
+ res = res_double
42
+ allow(res).to receive(:code).and_return(code.to_s)
43
+ allow(res).to receive(:body).and_return("")
44
+ allow(res).to receive(:[]).with('content-encoding').and_return(nil)
45
+ expect(@request.send(:process_result, res, Time.now)).to be_empty
46
+ end
47
+ end
48
+
49
+ describe '.normalize_url' do
50
+ it "adds http:// to the front of resources specified in the syntax example.com/resource" do
51
+ expect(@request.normalize_url('example.com/resource')).to eq 'http://example.com/resource'
52
+ end
53
+
54
+ it 'adds http:// to resources containing a colon' do
55
+ expect(@request.normalize_url('example.com:1234')).to eq 'http://example.com:1234'
56
+ end
57
+
58
+ it 'does not add http:// to the front of https resources' do
59
+ expect(@request.normalize_url('https://example.com/resource')).to eq 'https://example.com/resource'
60
+ end
61
+
62
+ it 'does not add http:// to the front of capital HTTP resources' do
63
+ expect(@request.normalize_url('HTTP://example.com/resource')).to eq 'HTTP://example.com/resource'
64
+ end
65
+
66
+ it 'does not add http:// to the front of capital HTTPS resources' do
67
+ expect(@request.normalize_url('HTTPS://example.com/resource')).to eq 'HTTPS://example.com/resource'
68
+ end
69
+
70
+ it 'raises with invalid URI' do
71
+ expect {
72
+ RestMan::Request.new(method: :get, url: 'http://a@b:c')
73
+ }.to raise_error(URI::InvalidURIError)
74
+ expect {
75
+ RestMan::Request.new(method: :get, url: 'http://::')
76
+ }.to raise_error(URI::InvalidURIError)
77
+ end
78
+ end
79
+
80
+ describe "user - password" do
81
+ it "extracts the username and password when parsing http://user:password@example.com/" do
82
+ @request.send(:parse_url_with_auth!, 'http://joe:pass1@example.com/resource')
83
+ expect(@request.user).to eq 'joe'
84
+ expect(@request.password).to eq 'pass1'
85
+ end
86
+
87
+ it "extracts with escaping the username and password when parsing http://user:password@example.com/" do
88
+ @request.send(:parse_url_with_auth!, 'http://joe%20:pass1@example.com/resource')
89
+ expect(@request.user).to eq 'joe '
90
+ expect(@request.password).to eq 'pass1'
91
+ end
92
+
93
+ 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
94
+ request = RestMan::Request.new(method: :get, url: 'http://example.com/resource', user: 'beth', password: 'pass2')
95
+ expect(request.user).to eq 'beth'
96
+ expect(request.password).to eq 'pass2'
97
+ end
98
+
99
+ it 'uses the username and password from the URL' do
100
+ request = RestMan::Request.new(method: :get, url: 'http://person:secret@example.com/resource')
101
+ expect(request.user).to eq 'person'
102
+ expect(request.password).to eq 'secret'
103
+ end
104
+
105
+ it 'overrides URL user/pass with explicit options' do
106
+ request = RestMan::Request.new(method: :get, url: 'http://person:secret@example.com/resource', user: 'beth', password: 'pass2')
107
+ expect(request.user).to eq 'beth'
108
+ expect(request.password).to eq 'pass2'
109
+ end
110
+ end
111
+
112
+ it "correctly formats cookies provided to the constructor" do
113
+ cookies_arr = [
114
+ HTTP::Cookie.new('session_id', '1', domain: 'example.com', path: '/'),
115
+ HTTP::Cookie.new('user_id', 'someone', domain: 'example.com', path: '/'),
116
+ ]
117
+
118
+ jar = HTTP::CookieJar.new
119
+ cookies_arr.each {|c| jar << c }
120
+
121
+ # test Hash, HTTP::CookieJar, and Array<HTTP::Cookie> modes
122
+ [
123
+ {session_id: '1', user_id: 'someone'},
124
+ jar,
125
+ cookies_arr
126
+ ].each do |cookies|
127
+ [true, false].each do |in_headers|
128
+ if in_headers
129
+ opts = {headers: {cookies: cookies}}
130
+ else
131
+ opts = {cookies: cookies}
132
+ end
133
+
134
+ request = RestMan::Request.new(method: :get, url: 'example.com', **opts)
135
+ expect(request).to receive(:default_headers).and_return({'Foo' => 'bar'})
136
+ expect(request.make_headers({})).to eq({'Foo' => 'bar', 'Cookie' => 'session_id=1; user_id=someone'})
137
+ expect(request.make_cookie_header).to eq 'session_id=1; user_id=someone'
138
+ expect(request.cookies).to eq({'session_id' => '1', 'user_id' => 'someone'})
139
+ expect(request.cookie_jar.cookies.length).to eq 2
140
+ expect(request.cookie_jar.object_id).not_to eq jar.object_id # make sure we dup it
141
+ end
142
+ end
143
+
144
+ # test with no cookies
145
+ request = RestMan::Request.new(method: :get, url: 'example.com')
146
+ expect(request).to receive(:default_headers).and_return({'Foo' => 'bar'})
147
+ expect(request.make_headers({})).to eq({'Foo' => 'bar'})
148
+ expect(request.make_cookie_header).to be_nil
149
+ expect(request.cookies).to eq({})
150
+ expect(request.cookie_jar.cookies.length).to eq 0
151
+ end
152
+
153
+ it 'strips out cookies set for a different domain name' do
154
+ jar = HTTP::CookieJar.new
155
+ jar << HTTP::Cookie.new('session_id', '1', domain: 'other.example.com', path: '/')
156
+ jar << HTTP::Cookie.new('user_id', 'someone', domain: 'other.example.com', path: '/')
157
+
158
+ request = RestMan::Request.new(method: :get, url: 'www.example.com', cookies: jar)
159
+ expect(request).to receive(:default_headers).and_return({'Foo' => 'bar'})
160
+ expect(request.make_headers({})).to eq({'Foo' => 'bar'})
161
+ expect(request.make_cookie_header).to eq nil
162
+ expect(request.cookies).to eq({})
163
+ expect(request.cookie_jar.cookies.length).to eq 2
164
+ end
165
+
166
+ it 'assumes default domain and path for cookies set by hash' do
167
+ request = RestMan::Request.new(method: :get, url: 'www.example.com', cookies: {'session_id' => '1'})
168
+ expect(request.cookie_jar.cookies.length).to eq 1
169
+
170
+ cookie = request.cookie_jar.cookies.first
171
+ expect(cookie).to be_a(HTTP::Cookie)
172
+ expect(cookie.domain).to eq('www.example.com')
173
+ expect(cookie.for_domain?).to be_truthy
174
+ expect(cookie.path).to eq('/')
175
+ end
176
+
177
+ it 'rejects or warns with contradictory cookie options' do
178
+ # same opt in two different places
179
+ expect {
180
+ RestMan::Request.new(method: :get, url: 'example.com',
181
+ cookies: {bar: '456'},
182
+ headers: {cookies: {foo: '123'}})
183
+ }.to raise_error(ArgumentError, /Cannot pass :cookies in Request.*headers/)
184
+
185
+ # :cookies opt and Cookie header
186
+ [
187
+ {cookies: {foo: '123'}, headers: {cookie: 'foo'}},
188
+ {cookies: {foo: '123'}, headers: {'Cookie' => 'foo'}},
189
+ {headers: {cookies: {foo: '123'}, cookie: 'foo'}},
190
+ {headers: {cookies: {foo: '123'}, 'Cookie' => 'foo'}},
191
+ ].each do |opts|
192
+ expect(fake_stderr {
193
+ RestMan::Request.new(method: :get, url: 'example.com', **opts)
194
+ }).to match(/warning: overriding "Cookie" header with :cookies option/)
195
+ end
196
+ end
197
+
198
+ it "does not escape or unescape cookies" do
199
+ cookie = 'Foo%20:Bar%0A~'
200
+ @request = RestMan::Request.new(:method => 'get', :url => 'example.com',
201
+ :cookies => {:test => cookie})
202
+ expect(@request).to receive(:default_headers).and_return({'Foo' => 'bar'})
203
+ expect(@request.make_headers({})).to eq({
204
+ 'Foo' => 'bar',
205
+ 'Cookie' => "test=#{cookie}"
206
+ })
207
+ end
208
+
209
+ it "rejects cookie names containing invalid characters" do
210
+ # Cookie validity is something of a mess, but we should reject the worst of
211
+ # the RFC 6265 (4.1.1) prohibited characters such as control characters.
212
+
213
+ ['foo=bar', 'foo;bar', "foo\nbar"].each do |cookie_name|
214
+ expect {
215
+ RestMan::Request.new(:method => 'get', :url => 'example.com',
216
+ :cookies => {cookie_name => 'value'})
217
+ }.to raise_error(ArgumentError, /\AInvalid cookie name/i)
218
+ end
219
+
220
+ cookie_name = ''
221
+ expect {
222
+ RestMan::Request.new(:method => 'get', :url => 'example.com',
223
+ :cookies => {cookie_name => 'value'})
224
+ }.to raise_error(ArgumentError, /cookie name cannot be empty/i)
225
+ end
226
+
227
+ it "rejects cookie values containing invalid characters" do
228
+ # Cookie validity is something of a mess, but we should reject the worst of
229
+ # the RFC 6265 (4.1.1) prohibited characters such as control characters.
230
+
231
+ ["foo\tbar", "foo\nbar"].each do |cookie_value|
232
+ expect {
233
+ RestMan::Request.new(:method => 'get', :url => 'example.com',
234
+ :cookies => {'test' => cookie_value})
235
+ }.to raise_error(ArgumentError, /\AInvalid cookie value/i)
236
+ end
237
+ end
238
+
239
+ it 'warns when overriding existing headers via payload' do
240
+ expect(fake_stderr {
241
+ RestMan::Request.new(method: :post, url: 'example.com',
242
+ payload: {'foo' => 1}, headers: {content_type: :json})
243
+ }).to match(/warning: Overriding "Content-Type" header/i)
244
+ expect(fake_stderr {
245
+ RestMan::Request.new(method: :post, url: 'example.com',
246
+ payload: {'foo' => 1}, headers: {'Content-Type' => 'application/json'})
247
+ }).to match(/warning: Overriding "Content-Type" header/i)
248
+
249
+ expect(fake_stderr {
250
+ RestMan::Request.new(method: :post, url: 'example.com',
251
+ payload: '123456', headers: {content_length: '20'})
252
+ }).to match(/warning: Overriding "Content-Length" header/i)
253
+ expect(fake_stderr {
254
+ RestMan::Request.new(method: :post, url: 'example.com',
255
+ payload: '123456', headers: {'Content-Length' => '20'})
256
+ }).to match(/warning: Overriding "Content-Length" header/i)
257
+ end
258
+
259
+ it "does not warn when overriding user header with header derived from payload if those header values were identical" do
260
+ expect(fake_stderr {
261
+ RestMan::Request.new(method: :post, url: 'example.com',
262
+ payload: {'foo' => '123456'}, headers: { 'Content-Type' => 'application/x-www-form-urlencoded' })
263
+ }).not_to match(/warning: Overriding "Content-Type" header/i)
264
+ end
265
+
266
+ it 'does not warn for a normal looking payload' do
267
+ expect(fake_stderr {
268
+ RestMan::Request.new(method: :post, url: 'example.com', payload: 'payload')
269
+ RestMan::Request.new(method: :post, url: 'example.com', payload: 'payload', headers: {content_type: :json})
270
+ RestMan::Request.new(method: :post, url: 'example.com', payload: {'foo' => 'bar'})
271
+ }).to eq ''
272
+ end
273
+
274
+ it "uses netrc credentials" do
275
+ expect(Netrc).to receive(:read).and_return('example.com' => ['a', 'b'])
276
+ request = RestMan::Request.new(:method => :put, :url => 'http://example.com/', :payload => 'payload')
277
+ expect(request.user).to eq 'a'
278
+ expect(request.password).to eq 'b'
279
+ end
280
+
281
+ it "uses credentials in the url in preference to netrc" do
282
+ allow(Netrc).to receive(:read).and_return('example.com' => ['a', 'b'])
283
+ request = RestMan::Request.new(:method => :put, :url => 'http://joe%20:pass1@example.com/', :payload => 'payload')
284
+ expect(request.user).to eq 'joe '
285
+ expect(request.password).to eq 'pass1'
286
+ end
287
+
288
+ it "determines the Net::HTTP class to instantiate by the method name" do
289
+ expect(@request.net_http_request_class(:put)).to eq Net::HTTP::Put
290
+ end
291
+
292
+ describe "user headers" do
293
+ it "merges user headers with the default headers" do
294
+ expect(@request).to receive(:default_headers).and_return({:accept => '*/*'})
295
+ headers = @request.make_headers("Accept" => "application/json", :accept_encoding => 'gzip')
296
+ expect(headers).to have_key "Accept-Encoding"
297
+ expect(headers["Accept-Encoding"]).to eq "gzip"
298
+ expect(headers).to have_key "Accept"
299
+ expect(headers["Accept"]).to eq "application/json"
300
+ end
301
+
302
+ it "prefers the user header when the same header exists in the defaults" do
303
+ expect(@request).to receive(:default_headers).and_return({ '1' => '2' })
304
+ headers = @request.make_headers('1' => '3')
305
+ expect(headers).to have_key('1')
306
+ expect(headers['1']).to eq '3'
307
+ end
308
+
309
+ it "converts user headers to string before calling CGI::unescape which fails on non string values" do
310
+ expect(@request).to receive(:default_headers).and_return({ '1' => '2' })
311
+ headers = @request.make_headers('1' => 3)
312
+ expect(headers).to have_key('1')
313
+ expect(headers['1']).to eq '3'
314
+ end
315
+ end
316
+
317
+ describe "header symbols" do
318
+
319
+ it "converts header symbols from :content_type to 'Content-Type'" do
320
+ expect(@request).to receive(:default_headers).and_return({})
321
+ headers = @request.make_headers(:content_type => 'abc')
322
+ expect(headers).to have_key('Content-Type')
323
+ expect(headers['Content-Type']).to eq 'abc'
324
+ end
325
+
326
+ it "converts content-type from extension to real content-type" do
327
+ expect(@request).to receive(:default_headers).and_return({})
328
+ headers = @request.make_headers(:content_type => 'json')
329
+ expect(headers).to have_key('Content-Type')
330
+ expect(headers['Content-Type']).to eq 'application/json'
331
+ end
332
+
333
+ it "converts accept from extension(s) to real content-type(s)" do
334
+ expect(@request).to receive(:default_headers).and_return({})
335
+ headers = @request.make_headers(:accept => 'json, mp3')
336
+ expect(headers).to have_key('Accept')
337
+ expect(headers['Accept']).to eq 'application/json, audio/mpeg'
338
+
339
+ expect(@request).to receive(:default_headers).and_return({})
340
+ headers = @request.make_headers(:accept => :json)
341
+ expect(headers).to have_key('Accept')
342
+ expect(headers['Accept']).to eq 'application/json'
343
+ end
344
+
345
+ it "only convert symbols in header" do
346
+ expect(@request).to receive(:default_headers).and_return({})
347
+ headers = @request.make_headers({:foo_bar => 'value', "bar_bar" => 'value'})
348
+ expect(headers['Foo-Bar']).to eq 'value'
349
+ expect(headers['bar_bar']).to eq 'value'
350
+ end
351
+
352
+ it "converts header values to strings" do
353
+ expect(@request.make_headers('A' => 1)['A']).to eq '1'
354
+ end
355
+ end
356
+
357
+ it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
358
+ klass = double("net:http class")
359
+ expect(@request).to receive(:net_http_request_class).with('put').and_return(klass)
360
+ expect(klass).to receive(:new).and_return('result')
361
+ expect(@request).to receive(:transmit).with(@request.uri, 'result', kind_of(RestMan::Payload::Base))
362
+ @request.execute
363
+ end
364
+
365
+ it "IPv6: executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
366
+ @request = RestMan::Request.new(:method => :put, :url => 'http://[::1]/some/resource', :payload => 'payload')
367
+ klass = double("net:http class")
368
+ expect(@request).to receive(:net_http_request_class).with('put').and_return(klass)
369
+
370
+ if RUBY_VERSION >= "2.0.0"
371
+ expect(klass).to receive(:new).with(kind_of(URI), kind_of(Hash)).and_return('result')
372
+ else
373
+ expect(klass).to receive(:new).with(kind_of(String), kind_of(Hash)).and_return('result')
374
+ end
375
+
376
+ expect(@request).to receive(:transmit)
377
+ @request.execute
378
+ end
379
+
380
+ # TODO: almost none of these tests should actually call transmit, which is
381
+ # part of the private API
382
+
383
+ it "transmits the request with Net::HTTP" do
384
+ expect(@http).to receive(:request).with('req', 'payload')
385
+ expect(@request).to receive(:process_result)
386
+ @request.send(:transmit, @uri, 'req', 'payload')
387
+ end
388
+
389
+ # TODO: most of these payload tests are historical relics that actually
390
+ # belong in payload_spec.rb. Or we need new tests that actually cover the way
391
+ # that Request#initialize or Request#execute uses the payload.
392
+ describe "payload" do
393
+ it "sends nil payloads" do
394
+ expect(@http).to receive(:request).with('req', nil)
395
+ expect(@request).to receive(:process_result)
396
+ @request.send(:transmit, @uri, 'req', nil)
397
+ end
398
+
399
+ it "passes non-hash payloads straight through" do
400
+ expect(RestMan::Payload.generate("x").to_s).to eq "x"
401
+ end
402
+
403
+ it "converts a hash payload to urlencoded data" do
404
+ expect(RestMan::Payload.generate(:a => 'b c+d').to_s).to eq "a=b+c%2Bd"
405
+ end
406
+
407
+ it "accepts nested hashes in payload" do
408
+ payload = RestMan::Payload.generate(:user => { :name => 'joe', :location => { :country => 'USA', :state => 'CA' }}).to_s
409
+ expect(payload).to include('user[name]=joe')
410
+ expect(payload).to include('user[location][country]=USA')
411
+ expect(payload).to include('user[location][state]=CA')
412
+ end
413
+ end
414
+
415
+ it "set urlencoded content_type header on hash payloads" do
416
+ req = RestMan::Request.new(method: :post, url: 'http://some/resource', payload: {a: 1})
417
+ expect(req.processed_headers.fetch('Content-Type')).to eq 'application/x-www-form-urlencoded'
418
+ end
419
+
420
+ describe "credentials" do
421
+ it "sets up the credentials prior to the request" do
422
+ allow(@http).to receive(:request)
423
+
424
+ allow(@request).to receive(:process_result)
425
+
426
+ allow(@request).to receive(:user).and_return('joe')
427
+ allow(@request).to receive(:password).and_return('mypass')
428
+ expect(@request).to receive(:setup_credentials).with('req')
429
+
430
+ @request.send(:transmit, @uri, 'req', nil)
431
+ end
432
+
433
+ it "does not attempt to send any credentials if user is nil" do
434
+ allow(@request).to receive(:user).and_return(nil)
435
+ req = double("request")
436
+ expect(req).not_to receive(:basic_auth)
437
+ @request.send(:setup_credentials, req)
438
+ end
439
+
440
+ it "setup credentials when there's a user" do
441
+ allow(@request).to receive(:user).and_return('joe')
442
+ allow(@request).to receive(:password).and_return('mypass')
443
+ req = double("request")
444
+ expect(req).to receive(:basic_auth).with('joe', 'mypass')
445
+ @request.send(:setup_credentials, req)
446
+ end
447
+
448
+ it "does not attempt to send credentials if Authorization header is set" do
449
+ ['Authorization', 'authorization', 'auTHORization', :authorization].each do |authorization|
450
+ headers = {authorization => 'Token abc123'}
451
+ request = RestMan::Request.new(method: :get, url: 'http://some/resource', headers: headers, user: 'joe', password: 'mypass')
452
+ req = double("net::http request")
453
+ expect(req).not_to receive(:basic_auth)
454
+ request.send(:setup_credentials, req)
455
+ end
456
+ end
457
+ end
458
+
459
+ it "catches EOFError and shows the more informative ServerBrokeConnection" do
460
+ allow(@http).to receive(:request).and_raise(EOFError)
461
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(RestMan::ServerBrokeConnection)
462
+ end
463
+
464
+ it "catches OpenSSL::SSL::SSLError and raise it back without more informative message" do
465
+ allow(@http).to receive(:request).and_raise(OpenSSL::SSL::SSLError)
466
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(OpenSSL::SSL::SSLError)
467
+ end
468
+
469
+ it "catches Timeout::Error and raise the more informative ReadTimeout" do
470
+ allow(@http).to receive(:request).and_raise(Timeout::Error)
471
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(RestMan::Exceptions::ReadTimeout)
472
+ end
473
+
474
+ it "catches Errno::ETIMEDOUT and raise the more informative ReadTimeout" do
475
+ allow(@http).to receive(:request).and_raise(Errno::ETIMEDOUT)
476
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(RestMan::Exceptions::ReadTimeout)
477
+ end
478
+
479
+ it "catches Net::ReadTimeout and raises RestMan's ReadTimeout",
480
+ :if => defined?(Net::ReadTimeout) do
481
+ allow(@http).to receive(:request).and_raise(Net::ReadTimeout)
482
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(RestMan::Exceptions::ReadTimeout)
483
+ end
484
+
485
+ it "catches Net::OpenTimeout and raises RestMan's OpenTimeout",
486
+ :if => defined?(Net::OpenTimeout) do
487
+ allow(@http).to receive(:request).and_raise(Net::OpenTimeout)
488
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(RestMan::Exceptions::OpenTimeout)
489
+ end
490
+
491
+ it "uses correct error message for ReadTimeout",
492
+ :if => defined?(Net::ReadTimeout) do
493
+ allow(@http).to receive(:request).and_raise(Net::ReadTimeout)
494
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(RestMan::Exceptions::ReadTimeout, 'Timed out reading data from server')
495
+ end
496
+
497
+ it "uses correct error message for OpenTimeout",
498
+ :if => defined?(Net::OpenTimeout) do
499
+ allow(@http).to receive(:request).and_raise(Net::OpenTimeout)
500
+ expect { @request.send(:transmit, @uri, 'req', nil) }.to raise_error(RestMan::Exceptions::OpenTimeout, 'Timed out connecting to server')
501
+ end
502
+
503
+
504
+ it "class method execute wraps constructor" do
505
+ req = double("rest request")
506
+ expect(RestMan::Request).to receive(:new).with(1 => 2).and_return(req)
507
+ expect(req).to receive(:execute)
508
+ RestMan::Request.execute(1 => 2)
509
+ end
510
+
511
+ describe "exception" do
512
+ it "raises Unauthorized when the response is 401" do
513
+ res = res_double(:code => '401', :[] => ['content-encoding' => ''], :body => '' )
514
+ expect { @request.send(:process_result, res, Time.now) }.to raise_error(RestMan::Unauthorized)
515
+ end
516
+
517
+ it "raises ResourceNotFound when the response is 404" do
518
+ res = res_double(:code => '404', :[] => ['content-encoding' => ''], :body => '' )
519
+ expect { @request.send(:process_result, res, Time.now) }.to raise_error(RestMan::ResourceNotFound)
520
+ end
521
+
522
+ it "raises RequestFailed otherwise" do
523
+ res = res_double(:code => '500', :[] => ['content-encoding' => ''], :body => '' )
524
+ expect { @request.send(:process_result, res, Time.now) }.to raise_error(RestMan::InternalServerError)
525
+ end
526
+ end
527
+
528
+ describe "block usage" do
529
+ it "returns what asked to" do
530
+ res = res_double(:code => '401', :[] => ['content-encoding' => ''], :body => '' )
531
+ expect(@request.send(:process_result, res, Time.now){|response, request| "foo"}).to eq "foo"
532
+ end
533
+ end
534
+
535
+ describe "proxy" do
536
+ before do
537
+ # unstub Net::HTTP creation since we need to test it
538
+ allow(Net::HTTP).to receive(:new).and_call_original
539
+
540
+ @proxy_req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
541
+ end
542
+
543
+ it "creates a proxy class if a proxy url is given" do
544
+ allow(RestMan).to receive(:proxy).and_return("http://example.com/")
545
+ allow(RestMan).to receive(:proxy_set?).and_return(true)
546
+ expect(@proxy_req.net_http_object('host', 80).proxy?).to be true
547
+ end
548
+
549
+ it "creates a proxy class with the correct address if a IPv6 proxy url is given" do
550
+ allow(RestMan).to receive(:proxy).and_return("http://[::1]/")
551
+ allow(RestMan).to receive(:proxy_set?).and_return(true)
552
+ expect(@proxy_req.net_http_object('host', 80).proxy?).to be true
553
+ expect(@proxy_req.net_http_object('host', 80).proxy_address).to eq('::1')
554
+ end
555
+
556
+ it "creates a non-proxy class if a proxy url is not given" do
557
+ expect(@proxy_req.net_http_object('host', 80).proxy?).to be_falsey
558
+ end
559
+
560
+ it "disables proxy on a per-request basis" do
561
+ allow(RestMan).to receive(:proxy).and_return('http://example.com')
562
+ allow(RestMan).to receive(:proxy_set?).and_return(true)
563
+ expect(@proxy_req.net_http_object('host', 80).proxy?).to be true
564
+
565
+ disabled_req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :proxy => nil)
566
+ expect(disabled_req.net_http_object('host', 80).proxy?).to be_falsey
567
+ end
568
+
569
+ it "sets proxy on a per-request basis" do
570
+ expect(@proxy_req.net_http_object('some', 80).proxy?).to be_falsey
571
+
572
+ req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :proxy => 'http://example.com')
573
+ expect(req.net_http_object('host', 80).proxy?).to be true
574
+ end
575
+
576
+ it "overrides proxy from environment", if: RUBY_VERSION >= '2.0' do
577
+ allow(ENV).to receive(:[]).with("http_proxy").and_return("http://127.0.0.1")
578
+ allow(ENV).to receive(:[]).with("no_proxy").and_return(nil)
579
+ allow(ENV).to receive(:[]).with("NO_PROXY").and_return(nil)
580
+ allow(Netrc).to receive(:read).and_return({})
581
+
582
+ req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
583
+ obj = req.net_http_object('host', 80)
584
+ expect(obj.proxy?).to be true
585
+ expect(obj.proxy_address).to eq '127.0.0.1'
586
+
587
+ # test original method .proxy?
588
+ req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :proxy => nil)
589
+ obj = req.net_http_object('host', 80)
590
+ expect(obj.proxy?).to be_falsey
591
+
592
+ # stub RestMan.proxy_set? to peek into implementation
593
+ allow(RestMan).to receive(:proxy_set?).and_return(true)
594
+ req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
595
+ obj = req.net_http_object('host', 80)
596
+ expect(obj.proxy?).to be_falsey
597
+
598
+ # test stubbed Net::HTTP.new
599
+ req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :proxy => nil)
600
+ expect(Net::HTTP).to receive(:new).with('host', 80, nil, nil, nil, nil)
601
+ req.net_http_object('host', 80)
602
+ end
603
+
604
+ it "overrides global proxy with per-request proxy" do
605
+ allow(RestMan).to receive(:proxy).and_return('http://example.com')
606
+ allow(RestMan).to receive(:proxy_set?).and_return(true)
607
+ obj = @proxy_req.net_http_object('host', 80)
608
+ expect(obj.proxy?).to be true
609
+ expect(obj.proxy_address).to eq 'example.com'
610
+
611
+ req = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :proxy => 'http://127.0.0.1/')
612
+ expect(req.net_http_object('host', 80).proxy?).to be true
613
+ expect(req.net_http_object('host', 80).proxy_address).to eq('127.0.0.1')
614
+ end
615
+ end
616
+
617
+
618
+ describe "logging" do
619
+ it "logs a get request" do
620
+ log = RestMan.log = []
621
+ RestMan::Request.new(:method => :get, :url => 'http://url', :headers => {:user_agent => 'rest-man'}).log_request
622
+ expect(log[0]).to eq %Q{RestMan.get "http://url", "Accept"=>"*/*", "User-Agent"=>"rest-man"\n}
623
+ end
624
+
625
+ it "logs a post request with a small payload" do
626
+ log = RestMan.log = []
627
+ RestMan::Request.new(:method => :post, :url => 'http://url', :payload => 'foo', :headers => {:user_agent => 'rest-man'}).log_request
628
+ expect(log[0]).to eq %Q{RestMan.post "http://url", "foo", "Accept"=>"*/*", "Content-Length"=>"3", "User-Agent"=>"rest-man"\n}
629
+ end
630
+
631
+ it "logs a post request with a large payload" do
632
+ log = RestMan.log = []
633
+ RestMan::Request.new(:method => :post, :url => 'http://url', :payload => ('x' * 1000), :headers => {:user_agent => 'rest-man'}).log_request
634
+ expect(log[0]).to eq %Q{RestMan.post "http://url", 1000 byte(s) length, "Accept"=>"*/*", "Content-Length"=>"1000", "User-Agent"=>"rest-man"\n}
635
+ end
636
+
637
+ it "logs input headers as a hash" do
638
+ log = RestMan.log = []
639
+ RestMan::Request.new(:method => :get, :url => 'http://url', :headers => { :accept => 'text/plain', :user_agent => 'rest-man' }).log_request
640
+ expect(log[0]).to eq %Q{RestMan.get "http://url", "Accept"=>"text/plain", "User-Agent"=>"rest-man"\n}
641
+ end
642
+
643
+ it "logs a response including the status code, content type, and result body size in bytes" do
644
+ log = RestMan.log = []
645
+ res = res_double(code: '200', class: Net::HTTPOK, body: 'abcd')
646
+ allow(res).to receive(:[]).with('Content-type').and_return('text/html')
647
+ response = response_from_res_double(res, @request)
648
+ response.log_response
649
+ expect(log).to eq ["# => 200 OK | text/html 4 bytes, 1.00s\n"]
650
+ end
651
+
652
+ it "logs a response with a nil Content-type" do
653
+ log = RestMan.log = []
654
+ res = res_double(code: '200', class: Net::HTTPOK, body: 'abcd')
655
+ allow(res).to receive(:[]).with('Content-type').and_return(nil)
656
+ response = response_from_res_double(res, @request)
657
+ response.log_response
658
+ expect(log).to eq ["# => 200 OK | 4 bytes, 1.00s\n"]
659
+ end
660
+
661
+ it "logs a response with a nil body" do
662
+ log = RestMan.log = []
663
+ res = res_double(code: '200', class: Net::HTTPOK, body: nil)
664
+ allow(res).to receive(:[]).with('Content-type').and_return('text/html; charset=utf-8')
665
+ response = response_from_res_double(res, @request)
666
+ response.log_response
667
+ expect(log).to eq ["# => 200 OK | text/html 0 bytes, 1.00s\n"]
668
+ end
669
+
670
+ it 'does not log request password' do
671
+ log = RestMan.log = []
672
+ RestMan::Request.new(:method => :get, :url => 'http://user:password@url', :headers => {:user_agent => 'rest-man'}).log_request
673
+ expect(log[0]).to eq %Q{RestMan.get "http://user:REDACTED@url", "Accept"=>"*/*", "User-Agent"=>"rest-man"\n}
674
+ end
675
+
676
+ it 'logs to a passed logger, if provided' do
677
+ logger = double('logger', '<<' => true)
678
+ expect(logger).to receive(:<<)
679
+ RestMan::Request.new(:method => :get, :url => 'http://user:password@url', log: logger).log_request
680
+ end
681
+ end
682
+
683
+ it "strips the charset from the response content type" do
684
+ log = RestMan.log = []
685
+ res = res_double(code: '200', class: Net::HTTPOK, body: 'abcd')
686
+ allow(res).to receive(:[]).with('Content-type').and_return('text/html; charset=utf-8')
687
+ response = response_from_res_double(res, @request)
688
+ response.log_response
689
+ expect(log).to eq ["# => 200 OK | text/html 4 bytes, 1.00s\n"]
690
+ end
691
+
692
+ describe "timeout" do
693
+ it "does not set timeouts if not specified" do
694
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
695
+ allow(@http).to receive(:request)
696
+ allow(@request).to receive(:process_result)
697
+
698
+ expect(@net).not_to receive(:read_timeout=)
699
+ expect(@net).not_to receive(:open_timeout=)
700
+
701
+ @request.send(:transmit, @uri, 'req', nil)
702
+ end
703
+
704
+ it 'sets read_timeout' do
705
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :read_timeout => 123)
706
+ allow(@http).to receive(:request)
707
+ allow(@request).to receive(:process_result)
708
+
709
+ expect(@net).to receive(:read_timeout=).with(123)
710
+
711
+ @request.send(:transmit, @uri, 'req', nil)
712
+ end
713
+
714
+ it "sets open_timeout" do
715
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123)
716
+ allow(@http).to receive(:request)
717
+ allow(@request).to receive(:process_result)
718
+
719
+ expect(@net).to receive(:open_timeout=).with(123)
720
+
721
+ @request.send(:transmit, @uri, 'req', nil)
722
+ end
723
+
724
+ it 'sets both timeouts with :timeout' do
725
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123)
726
+ allow(@http).to receive(:request)
727
+ allow(@request).to receive(:process_result)
728
+
729
+ expect(@net).to receive(:open_timeout=).with(123)
730
+ expect(@net).to receive(:read_timeout=).with(123)
731
+
732
+ @request.send(:transmit, @uri, 'req', nil)
733
+ end
734
+
735
+ it 'supersedes :timeout with open/read_timeout' do
736
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123, :open_timeout => 34, :read_timeout => 56)
737
+ allow(@http).to receive(:request)
738
+ allow(@request).to receive(:process_result)
739
+
740
+ expect(@net).to receive(:open_timeout=).with(34)
741
+ expect(@net).to receive(:read_timeout=).with(56)
742
+
743
+ @request.send(:transmit, @uri, 'req', nil)
744
+ end
745
+
746
+
747
+ it "disable timeout by setting it to nil" do
748
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :read_timeout => nil, :open_timeout => nil)
749
+ allow(@http).to receive(:request)
750
+ allow(@request).to receive(:process_result)
751
+
752
+ expect(@net).to receive(:read_timeout=).with(nil)
753
+ expect(@net).to receive(:open_timeout=).with(nil)
754
+
755
+ @request.send(:transmit, @uri, 'req', nil)
756
+ end
757
+
758
+ it 'deprecated: warns when disabling timeout by setting it to -1' do
759
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :read_timeout => -1)
760
+ allow(@http).to receive(:request)
761
+ allow(@request).to receive(:process_result)
762
+
763
+ expect(@net).to receive(:read_timeout=).with(nil)
764
+
765
+ expect(fake_stderr {
766
+ @request.send(:transmit, @uri, 'req', nil)
767
+ }).to match(/^Deprecated: .*timeout.* nil instead of -1$/)
768
+ end
769
+
770
+ it "deprecated: disable timeout by setting it to -1" do
771
+ @request = RestMan::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :read_timeout => -1, :open_timeout => -1)
772
+ allow(@http).to receive(:request)
773
+ allow(@request).to receive(:process_result)
774
+
775
+ expect(@request).to receive(:warn)
776
+ expect(@net).to receive(:read_timeout=).with(nil)
777
+
778
+ expect(@request).to receive(:warn)
779
+ expect(@net).to receive(:open_timeout=).with(nil)
780
+
781
+ @request.send(:transmit, @uri, 'req', nil)
782
+ end
783
+ end
784
+
785
+ describe "ssl" do
786
+ it "uses SSL when the URI refers to a https address" do
787
+ allow(@uri).to receive(:is_a?).with(URI::HTTPS).and_return(true)
788
+ expect(@net).to receive(:use_ssl=).with(true)
789
+ allow(@http).to receive(:request)
790
+ allow(@request).to receive(:process_result)
791
+ @request.send(:transmit, @uri, 'req', 'payload')
792
+ end
793
+
794
+ it "should default to verifying ssl certificates" do
795
+ expect(@request.verify_ssl).to eq OpenSSL::SSL::VERIFY_PEER
796
+ end
797
+
798
+ it "should have expected values for VERIFY_PEER and VERIFY_NONE" do
799
+ expect(OpenSSL::SSL::VERIFY_NONE).to eq(0)
800
+ expect(OpenSSL::SSL::VERIFY_PEER).to eq(1)
801
+ end
802
+
803
+ it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
804
+ @request = RestMan::Request.new(:method => :put, :verify_ssl => false, :url => 'http://some/resource', :payload => 'payload')
805
+ expect(@net).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
806
+ allow(@http).to receive(:request)
807
+ allow(@request).to receive(:process_result)
808
+ @request.send(:transmit, @uri, 'req', 'payload')
809
+ end
810
+
811
+ it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do
812
+ @request = RestMan::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
813
+ expect(@net).not_to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
814
+ allow(@http).to receive(:request)
815
+ allow(@request).to receive(:process_result)
816
+ @request.send(:transmit, @uri, 'req', 'payload')
817
+ end
818
+
819
+ it "should set net.verify_mode to OpenSSL::SSL::VERIFY_PEER if verify_ssl is true" do
820
+ @request = RestMan::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
821
+ expect(@net).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
822
+ allow(@http).to receive(:request)
823
+ allow(@request).to receive(:process_result)
824
+ @request.send(:transmit, @uri, 'req', 'payload')
825
+ end
826
+
827
+ it "should set net.verify_mode to OpenSSL::SSL::VERIFY_PEER if verify_ssl is not given" do
828
+ @request = RestMan::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload')
829
+ expect(@net).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
830
+ allow(@http).to receive(:request)
831
+ allow(@request).to receive(:process_result)
832
+ @request.send(:transmit, @uri, 'req', 'payload')
833
+ end
834
+
835
+ it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
836
+ mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
837
+ @request = RestMan::Request.new( :method => :put,
838
+ :url => 'https://some/resource',
839
+ :payload => 'payload',
840
+ :verify_ssl => mode )
841
+ expect(@net).to receive(:verify_mode=).with(mode)
842
+ allow(@http).to receive(:request)
843
+ allow(@request).to receive(:process_result)
844
+ @request.send(:transmit, @uri, 'req', 'payload')
845
+ end
846
+
847
+ it "should default to not having an ssl_client_cert" do
848
+ expect(@request.ssl_client_cert).to be(nil)
849
+ end
850
+
851
+ it "should set the ssl_version if provided" do
852
+ @request = RestMan::Request.new(
853
+ :method => :put,
854
+ :url => 'https://some/resource',
855
+ :payload => 'payload',
856
+ :ssl_version => "TLSv1"
857
+ )
858
+ expect(@net).to receive(:ssl_version=).with("TLSv1")
859
+ allow(@http).to receive(:request)
860
+ allow(@request).to receive(:process_result)
861
+ @request.send(:transmit, @uri, 'req', 'payload')
862
+ end
863
+
864
+ it "should not set the ssl_version if not provided" do
865
+ @request = RestMan::Request.new(
866
+ :method => :put,
867
+ :url => 'https://some/resource',
868
+ :payload => 'payload'
869
+ )
870
+ expect(@net).not_to receive(:ssl_version=).with("TLSv1")
871
+ allow(@http).to receive(:request)
872
+ allow(@request).to receive(:process_result)
873
+ @request.send(:transmit, @uri, 'req', 'payload')
874
+ end
875
+
876
+ it "should set the ssl_ciphers if provided" do
877
+ ciphers = 'AESGCM:HIGH:!aNULL:!eNULL:RC4+RSA'
878
+ @request = RestMan::Request.new(
879
+ :method => :put,
880
+ :url => 'https://some/resource',
881
+ :payload => 'payload',
882
+ :ssl_ciphers => ciphers
883
+ )
884
+ expect(@net).to receive(:ciphers=).with(ciphers)
885
+ allow(@http).to receive(:request)
886
+ allow(@request).to receive(:process_result)
887
+ @request.send(:transmit, @uri, 'req', 'payload')
888
+ end
889
+
890
+ it "should not set the ssl_ciphers if set to nil" do
891
+ @request = RestMan::Request.new(
892
+ :method => :put,
893
+ :url => 'https://some/resource',
894
+ :payload => 'payload',
895
+ :ssl_ciphers => nil,
896
+ )
897
+ expect(@net).not_to receive(:ciphers=)
898
+ allow(@http).to receive(:request)
899
+ allow(@request).to receive(:process_result)
900
+ @request.send(:transmit, @uri, 'req', 'payload')
901
+ end
902
+
903
+ it "should set the ssl_client_cert if provided" do
904
+ @request = RestMan::Request.new(
905
+ :method => :put,
906
+ :url => 'https://some/resource',
907
+ :payload => 'payload',
908
+ :ssl_client_cert => "whatsupdoc!"
909
+ )
910
+ expect(@net).to receive(:cert=).with("whatsupdoc!")
911
+ allow(@http).to receive(:request)
912
+ allow(@request).to receive(:process_result)
913
+ @request.send(:transmit, @uri, 'req', 'payload')
914
+ end
915
+
916
+ it "should not set the ssl_client_cert if it is not provided" do
917
+ @request = RestMan::Request.new(
918
+ :method => :put,
919
+ :url => 'https://some/resource',
920
+ :payload => 'payload'
921
+ )
922
+ expect(@net).not_to receive(:cert=)
923
+ allow(@http).to receive(:request)
924
+ allow(@request).to receive(:process_result)
925
+ @request.send(:transmit, @uri, 'req', 'payload')
926
+ end
927
+
928
+ it "should default to not having an ssl_client_key" do
929
+ expect(@request.ssl_client_key).to be(nil)
930
+ end
931
+
932
+ it "should set the ssl_client_key if provided" do
933
+ @request = RestMan::Request.new(
934
+ :method => :put,
935
+ :url => 'https://some/resource',
936
+ :payload => 'payload',
937
+ :ssl_client_key => "whatsupdoc!"
938
+ )
939
+ expect(@net).to receive(:key=).with("whatsupdoc!")
940
+ allow(@http).to receive(:request)
941
+ allow(@request).to receive(:process_result)
942
+ @request.send(:transmit, @uri, 'req', 'payload')
943
+ end
944
+
945
+ it "should not set the ssl_client_key if it is not provided" do
946
+ @request = RestMan::Request.new(
947
+ :method => :put,
948
+ :url => 'https://some/resource',
949
+ :payload => 'payload'
950
+ )
951
+ expect(@net).not_to receive(:key=)
952
+ allow(@http).to receive(:request)
953
+ allow(@request).to receive(:process_result)
954
+ @request.send(:transmit, @uri, 'req', 'payload')
955
+ end
956
+
957
+ it "should default to not having an ssl_ca_file" do
958
+ expect(@request.ssl_ca_file).to be(nil)
959
+ end
960
+
961
+ it "should set the ssl_ca_file if provided" do
962
+ @request = RestMan::Request.new(
963
+ :method => :put,
964
+ :url => 'https://some/resource',
965
+ :payload => 'payload',
966
+ :ssl_ca_file => "Certificate Authority File"
967
+ )
968
+ expect(@net).to receive(:ca_file=).with("Certificate Authority File")
969
+ expect(@net).not_to receive(:cert_store=)
970
+ allow(@http).to receive(:request)
971
+ allow(@request).to receive(:process_result)
972
+ @request.send(:transmit, @uri, 'req', 'payload')
973
+ end
974
+
975
+ it "should not set the ssl_ca_file if it is not provided" do
976
+ @request = RestMan::Request.new(
977
+ :method => :put,
978
+ :url => 'https://some/resource',
979
+ :payload => 'payload'
980
+ )
981
+ expect(@net).not_to receive(:ca_file=)
982
+ allow(@http).to receive(:request)
983
+ allow(@request).to receive(:process_result)
984
+ @request.send(:transmit, @uri, 'req', 'payload')
985
+ end
986
+
987
+ it "should default to not having an ssl_ca_path" do
988
+ expect(@request.ssl_ca_path).to be(nil)
989
+ end
990
+
991
+ it "should set the ssl_ca_path if provided" do
992
+ @request = RestMan::Request.new(
993
+ :method => :put,
994
+ :url => 'https://some/resource',
995
+ :payload => 'payload',
996
+ :ssl_ca_path => "Certificate Authority Path"
997
+ )
998
+ expect(@net).to receive(:ca_path=).with("Certificate Authority Path")
999
+ expect(@net).not_to receive(:cert_store=)
1000
+ allow(@http).to receive(:request)
1001
+ allow(@request).to receive(:process_result)
1002
+ @request.send(:transmit, @uri, 'req', 'payload')
1003
+ end
1004
+
1005
+ it "should not set the ssl_ca_path if it is not provided" do
1006
+ @request = RestMan::Request.new(
1007
+ :method => :put,
1008
+ :url => 'https://some/resource',
1009
+ :payload => 'payload'
1010
+ )
1011
+ expect(@net).not_to receive(:ca_path=)
1012
+ allow(@http).to receive(:request)
1013
+ allow(@request).to receive(:process_result)
1014
+ @request.send(:transmit, @uri, 'req', 'payload')
1015
+ end
1016
+
1017
+ it "should set the ssl_cert_store if provided" do
1018
+ store = OpenSSL::X509::Store.new
1019
+ store.set_default_paths
1020
+
1021
+ @request = RestMan::Request.new(
1022
+ :method => :put,
1023
+ :url => 'https://some/resource',
1024
+ :payload => 'payload',
1025
+ :ssl_cert_store => store
1026
+ )
1027
+ expect(@net).to receive(:cert_store=).with(store)
1028
+ expect(@net).not_to receive(:ca_path=)
1029
+ expect(@net).not_to receive(:ca_file=)
1030
+ allow(@http).to receive(:request)
1031
+ allow(@request).to receive(:process_result)
1032
+ @request.send(:transmit, @uri, 'req', 'payload')
1033
+ end
1034
+
1035
+ it "should by default set the ssl_cert_store if no CA info is provided" do
1036
+ @request = RestMan::Request.new(
1037
+ :method => :put,
1038
+ :url => 'https://some/resource',
1039
+ :payload => 'payload'
1040
+ )
1041
+ expect(@net).to receive(:cert_store=)
1042
+ expect(@net).not_to receive(:ca_path=)
1043
+ expect(@net).not_to receive(:ca_file=)
1044
+ allow(@http).to receive(:request)
1045
+ allow(@request).to receive(:process_result)
1046
+ @request.send(:transmit, @uri, 'req', 'payload')
1047
+ end
1048
+
1049
+ it "should not set the ssl_cert_store if it is set falsy" do
1050
+ @request = RestMan::Request.new(
1051
+ :method => :put,
1052
+ :url => 'https://some/resource',
1053
+ :payload => 'payload',
1054
+ :ssl_cert_store => nil,
1055
+ )
1056
+ expect(@net).not_to receive(:cert_store=)
1057
+ allow(@http).to receive(:request)
1058
+ allow(@request).to receive(:process_result)
1059
+ @request.send(:transmit, @uri, 'req', 'payload')
1060
+ end
1061
+
1062
+ it "should not set the ssl_verify_callback by default" do
1063
+ @request = RestMan::Request.new(
1064
+ :method => :put,
1065
+ :url => 'https://some/resource',
1066
+ :payload => 'payload',
1067
+ )
1068
+ expect(@net).not_to receive(:verify_callback=)
1069
+ allow(@http).to receive(:request)
1070
+ allow(@request).to receive(:process_result)
1071
+ @request.send(:transmit, @uri, 'req', 'payload')
1072
+ end
1073
+
1074
+ it "should set the ssl_verify_callback if passed" do
1075
+ callback = lambda {}
1076
+ @request = RestMan::Request.new(
1077
+ :method => :put,
1078
+ :url => 'https://some/resource',
1079
+ :payload => 'payload',
1080
+ :ssl_verify_callback => callback,
1081
+ )
1082
+ expect(@net).to receive(:verify_callback=).with(callback)
1083
+
1084
+ # we'll read cert_store on jruby
1085
+ # https://github.com/jruby/jruby/issues/597
1086
+ if RestMan::Platform.jruby?
1087
+ allow(@net).to receive(:cert_store)
1088
+ end
1089
+
1090
+ allow(@http).to receive(:request)
1091
+ allow(@request).to receive(:process_result)
1092
+ @request.send(:transmit, @uri, 'req', 'payload')
1093
+ end
1094
+
1095
+ # </ssl>
1096
+ end
1097
+
1098
+ it "should still return a response object for 204 No Content responses" do
1099
+ @request = RestMan::Request.new(
1100
+ :method => :put,
1101
+ :url => 'https://some/resource',
1102
+ :payload => 'payload'
1103
+ )
1104
+ net_http_res = Net::HTTPNoContent.new("", "204", "No Content")
1105
+ allow(net_http_res).to receive(:read_body).and_return(nil)
1106
+ expect(@http).to receive(:request).and_return(net_http_res)
1107
+ response = @request.send(:transmit, @uri, 'req', 'payload')
1108
+ expect(response).not_to be_nil
1109
+ expect(response.code).to eq 204
1110
+ end
1111
+
1112
+ describe "raw response" do
1113
+ it "should read the response into a binary-mode tempfile" do
1114
+ @request = RestMan::Request.new(:method => "get", :url => "example.com", :raw_response => true)
1115
+
1116
+ tempfile = double("tempfile")
1117
+ expect(tempfile).to receive(:binmode)
1118
+ allow(tempfile).to receive(:open)
1119
+ allow(tempfile).to receive(:close)
1120
+ expect(Tempfile).to receive(:new).with("rest-man.").and_return(tempfile)
1121
+
1122
+ net_http_res = Net::HTTPOK.new(nil, "200", "body")
1123
+ allow(net_http_res).to receive(:read_body).and_return("body")
1124
+ received_tempfile = @request.send(:fetch_body_to_tempfile, net_http_res)
1125
+ expect(received_tempfile).to eq tempfile
1126
+ end
1127
+ end
1128
+
1129
+ describe 'payloads' do
1130
+ it 'should accept string payloads' do
1131
+ payload = 'Foo'
1132
+ @request = RestMan::Request.new(method: :get, url: 'example.com', :payload => payload)
1133
+ expect(@request).to receive(:process_result)
1134
+ expect(@http).to receive(:request).with('req', payload)
1135
+ @request.send(:transmit, @uri, 'req', payload)
1136
+ end
1137
+
1138
+ it 'should accept streaming IO payloads' do
1139
+ payload = StringIO.new('streamed')
1140
+
1141
+ @request = RestMan::Request.new(method: :get, url: 'example.com', :payload => payload)
1142
+ expect(@request).to receive(:process_result)
1143
+
1144
+ @get = double('net::http::get')
1145
+ expect(@get).to receive(:body_stream=).with(instance_of(RestMan::Payload::Streamed))
1146
+
1147
+ allow(@request.net_http_request_class(:GET)).to receive(:new).and_return(@get)
1148
+ expect(@http).to receive(:request).with(@get, nil)
1149
+ @request.execute
1150
+ end
1151
+ end
1152
+
1153
+ describe 'constructor' do
1154
+ it 'should reject valid URIs with no hostname' do
1155
+ expect(URI.parse('http:///').hostname).to be_nil
1156
+
1157
+ expect {
1158
+ RestMan::Request.new(method: :get, url: 'http:///')
1159
+ }.to raise_error(URI::InvalidURIError, /\Abad URI/)
1160
+ end
1161
+
1162
+ it 'should reject invalid URIs' do
1163
+ expect {
1164
+ RestMan::Request.new(method: :get, url: 'http://::')
1165
+ }.to raise_error(URI::InvalidURIError)
1166
+ end
1167
+ end
1168
+
1169
+ describe 'process_url_params' do
1170
+ it 'should handle basic URL params' do
1171
+ expect(@request.process_url_params('https://example.com/foo', params: {key1: 123, key2: 'abc'})).
1172
+ to eq 'https://example.com/foo?key1=123&key2=abc'
1173
+
1174
+ expect(@request.process_url_params('https://example.com/foo', params: {'key1' => 123})).
1175
+ to eq 'https://example.com/foo?key1=123'
1176
+
1177
+ expect(@request.process_url_params('https://example.com/path',
1178
+ params: {foo: 'one two', bar: 'three + four == seven'})).
1179
+ to eq 'https://example.com/path?foo=one+two&bar=three+%2B+four+%3D%3D+seven'
1180
+ end
1181
+
1182
+ it 'should combine with & when URL params already exist' do
1183
+ expect(@request.process_url_params('https://example.com/path?foo=1', params: {bar: 2})).
1184
+ to eq 'https://example.com/path?foo=1&bar=2'
1185
+ end
1186
+
1187
+ it 'should handle complex nested URL params per Rack / Rails conventions' do
1188
+ expect(@request.process_url_params('https://example.com/', params: {
1189
+ foo: [1,2,3],
1190
+ null: nil,
1191
+ falsy: false,
1192
+ math: '2+2=4',
1193
+ nested: {'key + escaped' => 'value + escaped', other: [], arr: [1,2]},
1194
+ })).to eq 'https://example.com/?foo[]=1&foo[]=2&foo[]=3&null&falsy=false&math=2%2B2%3D4' \
1195
+ '&nested[key+%2B+escaped]=value+%2B+escaped&nested[other]' \
1196
+ '&nested[arr][]=1&nested[arr][]=2'
1197
+ end
1198
+
1199
+ it 'should handle ParamsArray objects' do
1200
+ expect(@request.process_url_params('https://example.com/',
1201
+ params: RestMan::ParamsArray.new([[:foo, 1], [:foo, 2]])
1202
+ )).to eq 'https://example.com/?foo=1&foo=2'
1203
+ end
1204
+ end
1205
+ end