rest-man 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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