httparty 0.16.2 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/dependabot.yml +6 -0
  4. data/.github/workflows/ci.yml +23 -0
  5. data/.gitignore +2 -0
  6. data/.rubocop_todo.yml +1 -1
  7. data/Changelog.md +425 -280
  8. data/Gemfile +7 -0
  9. data/Guardfile +3 -2
  10. data/README.md +5 -5
  11. data/docs/README.md +90 -5
  12. data/examples/README.md +28 -11
  13. data/examples/aaws.rb +6 -2
  14. data/examples/body_stream.rb +14 -0
  15. data/examples/idn.rb +10 -0
  16. data/examples/microsoft_graph.rb +52 -0
  17. data/examples/multipart.rb +22 -0
  18. data/examples/peer_cert.rb +9 -0
  19. data/examples/stream_download.rb +8 -2
  20. data/httparty.gemspec +4 -3
  21. data/lib/httparty/connection_adapter.rb +44 -20
  22. data/lib/httparty/cookie_hash.rb +10 -8
  23. data/lib/httparty/decompressor.rb +102 -0
  24. data/lib/httparty/exceptions.rb +3 -1
  25. data/lib/httparty/hash_conversions.rb +10 -4
  26. data/lib/httparty/headers_processor.rb +32 -0
  27. data/lib/httparty/logger/apache_formatter.rb +31 -6
  28. data/lib/httparty/logger/curl_formatter.rb +9 -7
  29. data/lib/httparty/logger/logger.rb +5 -1
  30. data/lib/httparty/logger/logstash_formatter.rb +62 -0
  31. data/lib/httparty/module_inheritable_attributes.rb +9 -9
  32. data/lib/httparty/net_digest_auth.rb +15 -15
  33. data/lib/httparty/parser.rb +12 -5
  34. data/lib/httparty/request/body.rb +54 -27
  35. data/lib/httparty/request/multipart_boundary.rb +2 -0
  36. data/lib/httparty/request.rb +105 -107
  37. data/lib/httparty/response/headers.rb +4 -2
  38. data/lib/httparty/response.rb +52 -9
  39. data/lib/httparty/response_fragment.rb +21 -0
  40. data/lib/httparty/text_encoder.rb +72 -0
  41. data/lib/httparty/utils.rb +13 -0
  42. data/lib/httparty/version.rb +3 -1
  43. data/lib/httparty.rb +81 -33
  44. data/script/release +4 -4
  45. data/website/css/common.css +1 -1
  46. metadata +50 -107
  47. data/.simplecov +0 -1
  48. data/.travis.yml +0 -10
  49. data/features/basic_authentication.feature +0 -20
  50. data/features/command_line.feature +0 -95
  51. data/features/deals_with_http_error_codes.feature +0 -26
  52. data/features/digest_authentication.feature +0 -30
  53. data/features/handles_compressed_responses.feature +0 -27
  54. data/features/handles_multiple_formats.feature +0 -57
  55. data/features/steps/env.rb +0 -27
  56. data/features/steps/httparty_response_steps.rb +0 -56
  57. data/features/steps/httparty_steps.rb +0 -43
  58. data/features/steps/mongrel_helper.rb +0 -127
  59. data/features/steps/remote_service_steps.rb +0 -92
  60. data/features/supports_read_timeout_option.feature +0 -13
  61. data/features/supports_redirection.feature +0 -22
  62. data/features/supports_timeout_option.feature +0 -13
  63. data/spec/fixtures/delicious.xml +0 -23
  64. data/spec/fixtures/empty.xml +0 -0
  65. data/spec/fixtures/google.html +0 -3
  66. data/spec/fixtures/ssl/generate.sh +0 -29
  67. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  68. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  69. data/spec/fixtures/ssl/generated/ca.key +0 -15
  70. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  71. data/spec/fixtures/ssl/generated/server.crt +0 -13
  72. data/spec/fixtures/ssl/generated/server.key +0 -15
  73. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  74. data/spec/fixtures/tiny.gif +0 -0
  75. data/spec/fixtures/twitter.csv +0 -2
  76. data/spec/fixtures/twitter.json +0 -1
  77. data/spec/fixtures/twitter.xml +0 -403
  78. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  79. data/spec/httparty/connection_adapter_spec.rb +0 -498
  80. data/spec/httparty/cookie_hash_spec.rb +0 -100
  81. data/spec/httparty/exception_spec.rb +0 -45
  82. data/spec/httparty/hash_conversions_spec.rb +0 -56
  83. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  84. data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
  85. data/spec/httparty/logger/logger_spec.rb +0 -38
  86. data/spec/httparty/net_digest_auth_spec.rb +0 -270
  87. data/spec/httparty/parser_spec.rb +0 -190
  88. data/spec/httparty/request/body_spec.rb +0 -60
  89. data/spec/httparty/request_spec.rb +0 -1312
  90. data/spec/httparty/response_spec.rb +0 -347
  91. data/spec/httparty/ssl_spec.rb +0 -74
  92. data/spec/httparty_spec.rb +0 -896
  93. data/spec/spec_helper.rb +0 -51
  94. data/spec/support/ssl_test_helper.rb +0 -47
  95. data/spec/support/ssl_test_server.rb +0 -80
  96. data/spec/support/stub_response.rb +0 -49
@@ -1,1312 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
-
3
- RSpec.describe HTTParty::Request do
4
- before do
5
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml)
6
- end
7
-
8
- describe "::NON_RAILS_QUERY_STRING_NORMALIZER" do
9
- let(:normalizer) { HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER }
10
-
11
- it "doesn't modify strings" do
12
- query_string = normalizer["foo=bar&foo=baz"]
13
- expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz")
14
- end
15
-
16
- context "when the query is an array" do
17
- it "doesn't include brackets" do
18
- query_string = normalizer[{page: 1, foo: %w(bar baz)}]
19
- expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz&page=1")
20
- end
21
-
22
- it "URI encodes array values" do
23
- query_string = normalizer[{people: ["Otis Redding", "Bob Marley", "Tim & Jon"], page: 1, xyzzy: 3}]
24
- expect(query_string).to eq("page=1&people=Otis%20Redding&people=Bob%20Marley&people=Tim%20%26%20Jon&xyzzy=3")
25
- end
26
- end
27
-
28
- context "when the query is a hash" do
29
- it "correctly handles nil values" do
30
- query_string = normalizer[{page: 1, per_page: nil}]
31
- expect(query_string).to eq("page=1&per_page")
32
- end
33
- end
34
- end
35
-
36
- describe "::JSON_API_QUERY_STRING_NORMALIZER" do
37
- let(:normalizer) { HTTParty::Request::JSON_API_QUERY_STRING_NORMALIZER }
38
-
39
- it "doesn't modify strings" do
40
- query_string = normalizer["foo=bar&foo=baz"]
41
- expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz")
42
- end
43
-
44
- context "when the query is an array" do
45
- it "doesn't include brackets" do
46
- query_string = normalizer[{page: 1, foo: %w(bar baz)}]
47
- expect(CGI.unescape(query_string)).to eq("foo=bar,baz&page=1")
48
- end
49
-
50
- it "URI encodes array values" do
51
- query_string = normalizer[{people: ["Otis Redding", "Bob Marley", "Tim & Jon"], page: 1, xyzzy: 3}]
52
- expect(query_string).to eq("page=1&people=Otis%20Redding,Bob%20Marley,Tim%20%26%20Jon&xyzzy=3")
53
- end
54
- end
55
-
56
- context "when the query is a hash" do
57
- it "correctly handles nil values" do
58
- query_string = normalizer[{page: 1, per_page: nil}]
59
- expect(query_string).to eq('page=1&per_page')
60
- end
61
- end
62
- end
63
-
64
- describe "initialization" do
65
- it "sets parser to HTTParty::Parser" do
66
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
67
- expect(request.parser).to eq(HTTParty::Parser)
68
- end
69
-
70
- it "sets parser to the optional parser" do
71
- my_parser = lambda {}
72
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', parser: my_parser)
73
- expect(request.parser).to eq(my_parser)
74
- end
75
-
76
- it "sets connection_adapter to HTTPParty::ConnectionAdapter" do
77
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
78
- expect(request.connection_adapter).to eq(HTTParty::ConnectionAdapter)
79
- end
80
-
81
- it "sets connection_adapter to the optional connection_adapter" do
82
- my_adapter = lambda {}
83
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', connection_adapter: my_adapter)
84
- expect(request.connection_adapter).to eq(my_adapter)
85
- end
86
-
87
- context "when using a query string" do
88
- context "and it has an empty array" do
89
- it "sets correct query string" do
90
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', query: { fake_array: [] })
91
-
92
- expect(request.uri).to eq(URI.parse("http://google.com/?fake_array[]="))
93
- end
94
- end
95
-
96
- context "when sending an array with only one element" do
97
- it "sets correct query" do
98
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', query: { fake_array: [1] })
99
-
100
- expect(request.uri).to eq(URI.parse("http://google.com/?fake_array[]=1"))
101
- end
102
- end
103
- end
104
-
105
- context "when basic authentication credentials provided in uri" do
106
- context "when basic auth options wasn't set explicitly" do
107
- it "sets basic auth from uri" do
108
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://user1:pass1@example.com')
109
- expect(request.options[:basic_auth]).to eq({username: 'user1', password: 'pass1'})
110
- end
111
- end
112
-
113
- context "when basic auth options was set explicitly" do
114
- it "uses basic auth from url anyway" do
115
- basic_auth = {username: 'user2', password: 'pass2'}
116
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://user1:pass1@example.com', basic_auth: basic_auth)
117
- expect(request.options[:basic_auth]).to eq({username: 'user1', password: 'pass1'})
118
- end
119
- end
120
- end
121
- end
122
-
123
- describe "#format" do
124
- context "request yet to be made" do
125
- it "returns format option" do
126
- request = HTTParty::Request.new 'get', '/', format: :xml
127
- expect(request.format).to eq(:xml)
128
- end
129
-
130
- it "returns nil format" do
131
- request = HTTParty::Request.new 'get', '/'
132
- expect(request.format).to be_nil
133
- end
134
- end
135
-
136
- context "request has been made" do
137
- it "returns format option" do
138
- request = HTTParty::Request.new 'get', '/', format: :xml
139
- request.last_response = double
140
- expect(request.format).to eq(:xml)
141
- end
142
-
143
- it "returns the content-type from the last response when the option is not set" do
144
- request = HTTParty::Request.new 'get', '/'
145
- response = double
146
- expect(response).to receive(:[]).with('content-type').and_return('text/json')
147
- request.last_response = response
148
- expect(request.format).to eq(:json)
149
- end
150
- end
151
- end
152
-
153
- context "options" do
154
- it "should use basic auth when configured" do
155
- @request.options[:basic_auth] = {username: 'foobar', password: 'secret'}
156
- @request.send(:setup_raw_request)
157
- expect(@request.instance_variable_get(:@raw_request)['authorization']).not_to be_nil
158
- end
159
-
160
- context 'digest_auth' do
161
- before do
162
- response_sequence = [
163
- {
164
- status: ['401', 'Unauthorized' ], headers: {
165
- www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false',
166
- set_cookie: 'custom-cookie=1234567'
167
- }
168
- },
169
- { status: ['200', 'OK'] }
170
- ]
171
- stub_request(:get, 'http://api.foo.com/v1').to_return(response_sequence)
172
- end
173
-
174
- it 'should not send credentials more than once' do
175
- response_sequence = [
176
- {
177
- status: ['401', 'Unauthorized' ], headers: {
178
- www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false',
179
- set_cookie: 'custom-cookie=1234567'
180
- }
181
- },
182
- {
183
- status: ['401', 'Unauthorized' ], headers: {
184
- www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false',
185
- set_cookie: 'custom-cookie=1234567'
186
- }
187
- },
188
- { status: ['404', 'Not found'] }
189
- ]
190
- stub_request(:get, 'http://api.foo.com/v1').to_return(response_sequence)
191
-
192
- @request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
193
- response = @request.perform { |v| }
194
- expect(response.code).to eq(401)
195
-
196
- raw_request = @request.instance_variable_get(:@raw_request)
197
- expect(raw_request['Authorization']).not_to be_nil
198
- end
199
-
200
- it 'should not be used when configured and the response is 200' do
201
- stub_request(:get, 'http://api.foo.com/v1').to_return(status: 200)
202
- @request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
203
- response = @request.perform { |v| }
204
- expect(response.code).to eq(200)
205
-
206
-
207
- raw_request = @request.instance_variable_get(:@raw_request)
208
- expect(raw_request['Authorization']).to be_nil
209
- end
210
-
211
- it "should be used when configured and the response is 401" do
212
- @request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
213
- response = @request.perform { |v| }
214
- expect(response.code).to eq(200)
215
-
216
- raw_request = @request.instance_variable_get(:@raw_request)
217
- expect(raw_request['Authorization']).not_to be_nil
218
- end
219
-
220
- it 'should maintain cookies returned from a 401 response' do
221
- @request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
222
- response = @request.perform {|v|}
223
- expect(response.code).to eq(200)
224
-
225
- raw_request = @request.instance_variable_get(:@raw_request)
226
- expect(raw_request.get_fields('cookie')).to eql ["custom-cookie=1234567"]
227
- end
228
-
229
- it 'should merge cookies from request and a 401 response' do
230
-
231
- @request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
232
- @request.options[:headers] = {'cookie' => 'request-cookie=test'}
233
- response = @request.perform {|v|}
234
- expect(response.code).to eq(200)
235
-
236
- raw_request = @request.instance_variable_get(:@raw_request)
237
- expect(raw_request.get_fields('cookie')).to eql ['request-cookie=test', 'custom-cookie=1234567']
238
- end
239
- end
240
-
241
- it 'should use body_stream when configured' do
242
- stream = StringIO.new('foo')
243
- request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', body_stream: stream)
244
- request.send(:setup_raw_request)
245
- expect(request.instance_variable_get(:@raw_request).body_stream).to eq(stream)
246
- end
247
-
248
- it 'should normalize base uri when specified as request option' do
249
- stub_request(:get, 'http://foo.com/resource').to_return(body: 'Bar')
250
- response = HTTParty.get('/resource', {
251
- base_uri: 'foo.com'
252
- })
253
- expect(response.code).to eq(200)
254
- end
255
- end
256
-
257
- describe "#uri" do
258
- context "redirects" do
259
- it "returns correct path when the server sets the location header to a filename" do
260
- @request.last_uri = URI.parse("http://example.com/foo/bar")
261
- @request.path = URI.parse("bar?foo=bar")
262
- @request.redirect = true
263
-
264
- expect(@request.uri).to eq(URI.parse("http://example.com/foo/bar?foo=bar"))
265
- end
266
-
267
- context "location header is an absolute path" do
268
- it "returns correct path when location has leading slash" do
269
- @request.last_uri = URI.parse("http://example.com/foo/bar")
270
- @request.path = URI.parse("/bar?foo=bar")
271
- @request.redirect = true
272
-
273
- expect(@request.uri).to eq(URI.parse("http://example.com/bar?foo=bar"))
274
- end
275
-
276
- it "returns the correct path when location has no leading slash" do
277
- @request.last_uri = URI.parse("http://example.com")
278
- @request.path = URI.parse("bar/")
279
- @request.redirect = true
280
-
281
- expect(@request.uri).to eq(URI.parse("http://example.com/bar/"))
282
- end
283
- end
284
- it "returns correct path when the server sets the location header to a full uri" do
285
- @request.last_uri = URI.parse("http://example.com/foo/bar")
286
- @request.path = URI.parse("http://example.com/bar?foo=bar")
287
- @request.redirect = true
288
-
289
- expect(@request.uri).to eq(URI.parse("http://example.com/bar?foo=bar"))
290
- end
291
-
292
- it "returns correct path when the server sets the location header to a network-path reference" do
293
- @request.last_uri = URI.parse("https://example.com")
294
- @request.path = URI.parse("//www.example.com")
295
- @request.redirect = true
296
-
297
- expect(@request.uri).to eq(URI.parse("https://www.example.com"))
298
- end
299
- end
300
-
301
- context "query strings" do
302
- it "does not add an empty query string when default_params are blank" do
303
- @request.options[:default_params] = {}
304
- expect(@request.uri.query).to be_nil
305
- end
306
-
307
- it "respects the query string normalization proc" do
308
- empty_proc = lambda {|qs| "I"}
309
- @request.options[:query_string_normalizer] = empty_proc
310
- @request.options[:query] = {foo: :bar}
311
- expect(CGI.unescape(@request.uri.query)).to eq("I")
312
- end
313
-
314
- it "does not append an ampersand when queries are embedded in paths" do
315
- @request.path = "/path?a=1"
316
- @request.options[:query] = {}
317
- expect(@request.uri.query).to eq("a=1")
318
- end
319
-
320
- it "does not duplicate query string parameters when uri is called twice" do
321
- @request.options[:query] = {foo: :bar}
322
- @request.uri
323
- expect(@request.uri.query).to eq("foo=bar")
324
- end
325
-
326
- context "when representing an array" do
327
- it "returns a Rails style query string" do
328
- @request.options[:query] = {foo: %w(bar baz)}
329
- expect(CGI.unescape(@request.uri.query)).to eq("foo[]=bar&foo[]=baz")
330
- end
331
- end
332
- end
333
- end
334
-
335
- describe "#setup_raw_request" do
336
- context "when query_string_normalizer is set" do
337
- it "sets the body to the return value of the proc" do
338
- @request.options[:query_string_normalizer] = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
339
- @request.options[:body] = {page: 1, foo: %w(bar baz)}
340
- @request.send(:setup_raw_request)
341
- body = @request.instance_variable_get(:@raw_request).body
342
- expect(CGI.unescape(body)).to eq("foo=bar&foo=baz&page=1")
343
- end
344
- end
345
-
346
- context "when body is multipart" do
347
- it "sets header Content-Type: multipart/form-data; boundary=" do
348
- @request.options[:body] = {file: File.open(File::NULL, 'r')}
349
- @request.send(:setup_raw_request)
350
- headers = @request.instance_variable_get(:@raw_request).each_header.to_a
351
- headers = Hash[*headers.flatten] # Ruby 2.0 doesn't have Array#to_h
352
- expect(headers['content-type']).to match(%r{^multipart/form-data; boundary=---})
353
- end
354
-
355
- context "and header Content-Type is provided" do
356
- it "overwrites the header to: multipart/form-data; boundary=" do
357
- @request.options[:body] = {file: File.open(File::NULL, 'r')}
358
- @request.options[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
359
- @request.send(:setup_raw_request)
360
- headers = @request.instance_variable_get(:@raw_request).each_header.to_a
361
- headers = Hash[*headers.flatten] # Ruby 2.0 doesn't have Array#to_h
362
- expect(headers['content-type']).to match(%r{^multipart/form-data; boundary=---})
363
- end
364
- end
365
- end
366
- end
367
-
368
- describe 'http' do
369
- it "should get a connection from the connection_adapter" do
370
- http = Net::HTTP.new('google.com')
371
- adapter = double('adapter')
372
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443', connection_adapter: adapter)
373
- expect(adapter).to receive(:call).with(request.uri, request.options).and_return(http)
374
- expect(request.send(:http)).to be http
375
- end
376
- end
377
-
378
- describe '#format_from_mimetype' do
379
- it 'should handle text/xml' do
380
- ["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
381
- expect(@request.send(:format_from_mimetype, ct)).to eq(:xml)
382
- end
383
- end
384
-
385
- it 'should handle application/xml' do
386
- ["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
387
- expect(@request.send(:format_from_mimetype, ct)).to eq(:xml)
388
- end
389
- end
390
-
391
- it 'should handle text/json' do
392
- ["text/json", "text/json; charset=iso8859-1"].each do |ct|
393
- expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
394
- end
395
- end
396
-
397
- it 'should handle application/vnd.api+json' do
398
- ["application/vnd.api+json", "application/vnd.api+json; charset=iso8859-1"].each do |ct|
399
- expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
400
- end
401
- end
402
-
403
- it 'should handle application/hal+json' do
404
- ["application/hal+json", "application/hal+json; charset=iso8859-1"].each do |ct|
405
- expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
406
- end
407
- end
408
-
409
- it 'should handle application/json' do
410
- ["application/json", "application/json; charset=iso8859-1"].each do |ct|
411
- expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
412
- end
413
- end
414
-
415
- it 'should handle text/csv' do
416
- ["text/csv", "text/csv; charset=iso8859-1"].each do |ct|
417
- expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
418
- end
419
- end
420
-
421
- it 'should handle application/csv' do
422
- ["application/csv", "application/csv; charset=iso8859-1"].each do |ct|
423
- expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
424
- end
425
- end
426
-
427
- it 'should handle text/comma-separated-values' do
428
- ["text/comma-separated-values", "text/comma-separated-values; charset=iso8859-1"].each do |ct|
429
- expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
430
- end
431
- end
432
-
433
- it 'should handle text/javascript' do
434
- ["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
435
- expect(@request.send(:format_from_mimetype, ct)).to eq(:plain)
436
- end
437
- end
438
-
439
- it 'should handle application/javascript' do
440
- ["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
441
- expect(@request.send(:format_from_mimetype, ct)).to eq(:plain)
442
- end
443
- end
444
-
445
- it "returns nil for an unrecognized mimetype" do
446
- expect(@request.send(:format_from_mimetype, "application/atom+xml")).to be_nil
447
- end
448
-
449
- it "returns nil when using a default parser" do
450
- @request.options[:parser] = lambda {}
451
- expect(@request.send(:format_from_mimetype, "text/json")).to be_nil
452
- end
453
- end
454
-
455
- describe 'parsing responses' do
456
- it 'should handle xml automatically' do
457
- xml = '<books><book><id>1234</id><name>Foo Bar!</name></book></books>'
458
- @request.options[:format] = :xml
459
- expect(@request.send(:parse_response, xml)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
460
- end
461
-
462
- it 'should handle utf-8 bom in xml' do
463
- xml = "\xEF\xBB\xBF<books><book><id>1234</id><name>Foo Bar!</name></book></books>"
464
- @request.options[:format] = :xml
465
- expect(@request.send(:parse_response, xml)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
466
- end
467
-
468
- it 'should handle csv automatically' do
469
- csv = ['"id","Name"', '"1234","Foo Bar!"'].join("\n")
470
- @request.options[:format] = :csv
471
- expect(@request.send(:parse_response, csv)).to eq([%w(id Name), ["1234", "Foo Bar!"]])
472
- end
473
-
474
- it 'should handle json automatically' do
475
- json = '{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}'
476
- @request.options[:format] = :json
477
- expect(@request.send(:parse_response, json)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
478
- end
479
-
480
- it 'should handle utf-8 bom in json' do
481
- json = "\xEF\xBB\xBF{\"books\": {\"book\": {\"name\": \"Foo Bar!\", \"id\": \"1234\"}}}"
482
- @request.options[:format] = :json
483
- expect(@request.send(:parse_response, json)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
484
- end
485
-
486
- it "should include any HTTP headers in the returned response" do
487
- @request.options[:format] = :html
488
- response = stub_response "Content"
489
- response.initialize_http_header("key" => "value")
490
-
491
- expect(@request.perform.headers).to eq({ "key" => ["value"] })
492
- end
493
-
494
- if "".respond_to?(:encoding)
495
-
496
- let(:response_charset) {
497
- @request.send(:get_charset)
498
- }
499
-
500
- it "should process charset in content type properly" do
501
- response = stub_response "Content".force_encoding('ascii-8bit')
502
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-8")
503
- resp = @request.perform
504
- expect(response_charset).to_not be_empty
505
- expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
506
- end
507
-
508
- it "should process charset in content type properly if it has a different case" do
509
- response = stub_response "Content".force_encoding('ascii-8bit')
510
- response.initialize_http_header("Content-Type" => "text/plain;CHARSET = utf-8")
511
- resp = @request.perform
512
- expect(response_charset).to_not be_empty
513
- expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
514
- end
515
-
516
- it "should process quoted charset in content type properly" do
517
- response = stub_response "Content".force_encoding('ascii-8bit')
518
- response.initialize_http_header("Content-Type" => "text/plain;charset = \"utf-8\"")
519
- resp = @request.perform
520
- expect(response_charset).to_not be_empty
521
- expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
522
- end
523
-
524
- it "should process response with a nil body" do
525
- response = stub_response nil
526
- response.initialize_http_header("Content-Type" => "text/html;charset=UTF-8")
527
- resp = @request.perform
528
- expect(resp.body).to be_nil
529
- end
530
-
531
- it "should process utf-16 charset with little endian bom correctly" do
532
- @request.options[:assume_utf16_is_big_endian] = true
533
-
534
- response = stub_response "\xFF\xFEC\x00o\x00n\x00t\x00e\x00n\x00t\x00"
535
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
536
- resp = @request.perform
537
- expect(response_charset).to_not be_empty
538
- expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE"))
539
- end
540
-
541
- it "should process utf-16 charset with big endian bom correctly" do
542
- @request.options[:assume_utf16_is_big_endian] = false
543
-
544
- response = stub_response "\xFE\xFF\x00C\x00o\x00n\x00t\x00e\x00n\x00t"
545
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
546
- resp = @request.perform
547
- expect(response_charset).to_not be_empty
548
- expect(resp.body.encoding).to eq(Encoding.find("UTF-16BE"))
549
- end
550
-
551
- it "should assume utf-16 little endian if options has been chosen" do
552
- @request.options[:assume_utf16_is_big_endian] = false
553
-
554
- response = stub_response "C\x00o\x00n\x00t\x00e\x00n\x00t\x00"
555
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
556
- resp = @request.perform
557
- expect(response_charset).to_not be_empty
558
- expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE"))
559
- end
560
-
561
- it "should perform no encoding if the charset is not available" do
562
- response = stub_response "Content"
563
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-lols")
564
- resp = @request.perform
565
- expect(response_charset).to_not be_empty
566
- # This encoding does not exist, thus the string should not be encodd with it
567
- expect(resp.body.encoding).to_not eq(response_charset)
568
- expect(resp.body).to eq("Content")
569
- expect(resp.body.encoding).to eq("Content".encoding)
570
- end
571
-
572
- it "should perform no encoding if the content type is specified but no charset is specified" do
573
- response = stub_response "Content"
574
- response.initialize_http_header("Content-Type" => "text/plain")
575
- resp = @request.perform
576
- expect(response_charset).to be_nil
577
- expect(resp.body).to eq("Content")
578
- expect(resp.body.encoding).to eq("Content".encoding)
579
- end
580
- end
581
-
582
- describe 'with non-200 responses' do
583
- context "3xx responses" do
584
- it 'returns a valid object for 304 not modified' do
585
- stub_response '', 304
586
- resp = @request.perform
587
- expect(resp.code).to eq(304)
588
- expect(resp.body).to eq('')
589
- expect(resp).to be_nil
590
- end
591
-
592
- it "redirects if a 300 contains a location header" do
593
- redirect = stub_response '', 300
594
- redirect['location'] = 'http://foo.com/foo'
595
- ok = stub_response('<hash><foo>bar</foo></hash>', 200)
596
- allow(@http).to receive(:request).and_return(redirect, ok)
597
- response = @request.perform
598
- expect(response.request.base_uri.to_s).to eq("http://foo.com")
599
- expect(response.request.path.to_s).to eq("http://foo.com/foo")
600
- expect(response.request.uri.request_uri).to eq("/foo")
601
- expect(response.request.uri.to_s).to eq("http://foo.com/foo")
602
- expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
603
- end
604
-
605
- it "calls block given to perform with each redirect" do
606
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
607
- stub_request(:get, 'http://test.com/redirect')
608
- .to_return(
609
- status: [300, 'REDIRECT'],
610
- headers: { location: 'http://api.foo.com/v2' }
611
- )
612
- stub_request(:get, 'http://api.foo.com/v2')
613
- .to_return(body: '<hash><foo>bar</foo></hash>')
614
- body = ""
615
- @request.perform { |chunk| body += chunk }
616
- expect(body.length).to eq(27)
617
- end
618
-
619
- it "redirects if a 300 contains a relative location header" do
620
- redirect = stub_response '', 300
621
- redirect['location'] = '/foo/bar'
622
- ok = stub_response('<hash><foo>bar</foo></hash>', 200)
623
- allow(@http).to receive(:request).and_return(redirect, ok)
624
- response = @request.perform
625
- expect(response.request.base_uri.to_s).to eq("http://api.foo.com")
626
- expect(response.request.path.to_s).to eq("/foo/bar")
627
- expect(response.request.uri.request_uri).to eq("/foo/bar")
628
- expect(response.request.uri.to_s).to eq("http://api.foo.com/foo/bar")
629
- expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
630
- end
631
-
632
- it "handles multiple redirects and relative location headers on different hosts" do
633
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
634
- stub_request(:get, 'http://test.com/redirect')
635
- .to_return(
636
- status: [300, 'REDIRECT'],
637
- headers: { location: "http://api.foo.com/v2" }
638
- )
639
- stub_request(:get, 'http://api.foo.com/v2')
640
- .to_return(
641
- status: [300, 'REDIRECT'],
642
- headers: { location: '/v3' }
643
- )
644
- stub_request(:get, 'http://api.foo.com/v3')
645
- .to_return(body: '<hash><foo>bar</foo></hash>')
646
- response = @request.perform
647
- expect(response.request.base_uri.to_s).to eq("http://api.foo.com")
648
- expect(response.request.path.to_s).to eq("/v3")
649
- expect(response.request.uri.request_uri).to eq("/v3")
650
- expect(response.request.uri.to_s).to eq("http://api.foo.com/v3")
651
- expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
652
- end
653
-
654
- it "raises an error if redirect has duplicate location header" do
655
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
656
- stub_request(:get, 'http://test.com/redirect')
657
- .to_return(
658
- status: [300, 'REDIRECT'],
659
- headers: {
660
- location: ['http://api.foo.com/v2', 'http://api.foo.com/v2']
661
- }
662
- )
663
- expect {@request.perform}.to raise_error(HTTParty::DuplicateLocationHeader)
664
- end
665
-
666
- it "returns the HTTParty::Response when the 300 does not contain a location header" do
667
- stub_response '', 300
668
- expect(HTTParty::Response).to be === @request.perform
669
- end
670
-
671
- it "redirects including port" do
672
- stub_request(:get, 'http://withport.com:3000/v1')
673
- .to_return(
674
- status: [301, 'Moved Permanently'],
675
- headers: { location: 'http://withport.com:3000/v2' }
676
- )
677
- stub_request(:get, 'http://withport.com:3000/v2')
678
- .to_return(status: 200)
679
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://withport.com:3000/v1')
680
- response = request.perform
681
- expect(response.request.base_uri.to_s).to eq("http://withport.com:3000")
682
- end
683
- end
684
-
685
- it 'should return a valid object for 4xx response' do
686
- stub_response '<foo><bar>yes</bar></foo>', 401
687
- resp = @request.perform
688
- expect(resp.code).to eq(401)
689
- expect(resp.body).to eq("<foo><bar>yes</bar></foo>")
690
- expect(resp['foo']['bar']).to eq("yes")
691
- end
692
-
693
- it 'should return a valid object for 5xx response' do
694
- stub_response '<foo><bar>error</bar></foo>', 500
695
- resp = @request.perform
696
- expect(resp.code).to eq(500)
697
- expect(resp.body).to eq("<foo><bar>error</bar></foo>")
698
- expect(resp['foo']['bar']).to eq("error")
699
- end
700
-
701
- it "parses response lazily so codes can be checked prior" do
702
- stub_response 'not xml', 500
703
- @request.options[:format] = :xml
704
- expect {
705
- response = @request.perform
706
- expect(response.code).to eq(500)
707
- expect(response.body).to eq('not xml')
708
- }.not_to raise_error
709
- end
710
- end
711
- end
712
-
713
- it "should not attempt to parse empty responses" do
714
- [204, 304].each do |code|
715
- stub_response "", code
716
-
717
- @request.options[:format] = :xml
718
- expect(@request.perform).to be_nil
719
- end
720
- end
721
-
722
- it "should not fail for missing mime type" do
723
- stub_response "Content for you"
724
- @request.options[:format] = :html
725
- expect(@request.perform.parsed_response).to eq('Content for you')
726
- end
727
-
728
- [300, 301, 302, 305].each do |code|
729
- describe "a request that #{code} redirects" do
730
- before(:each) do
731
- @redirect = stub_response("", code)
732
- @redirect['location'] = '/foo'
733
-
734
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
735
- end
736
-
737
- describe "once" do
738
- before(:each) do
739
- allow(@http).to receive(:request).and_return(@redirect, @ok)
740
- end
741
-
742
- it "should be handled by GET transparently" do
743
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
744
- end
745
-
746
- it "should be handled by POST transparently" do
747
- @request.http_method = Net::HTTP::Post
748
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
749
- end
750
-
751
- it "should be handled by DELETE transparently" do
752
- @request.http_method = Net::HTTP::Delete
753
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
754
- end
755
-
756
- it "should be handled by MOVE transparently" do
757
- @request.http_method = Net::HTTP::Move
758
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
759
- end
760
-
761
- it "should be handled by COPY transparently" do
762
- @request.http_method = Net::HTTP::Copy
763
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
764
- end
765
-
766
- it "should be handled by PATCH transparently" do
767
- @request.http_method = Net::HTTP::Patch
768
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
769
- end
770
-
771
- it "should be handled by PUT transparently" do
772
- @request.http_method = Net::HTTP::Put
773
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
774
- end
775
-
776
- it "should be handled by HEAD transparently" do
777
- @request.http_method = Net::HTTP::Head
778
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
779
- end
780
-
781
- it "should be handled by OPTIONS transparently" do
782
- @request.http_method = Net::HTTP::Options
783
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
784
- end
785
-
786
- it "should be handled by MKCOL transparently" do
787
- @request.http_method = Net::HTTP::Mkcol
788
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
789
- end
790
-
791
- it "should keep track of cookies between redirects" do
792
- @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
793
- @request.perform
794
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
795
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
796
- end
797
-
798
- it 'should update cookies with redirects' do
799
- @request.options[:headers] = {'Cookie' => 'foo=bar;'}
800
- @redirect['Set-Cookie'] = 'foo=tar;'
801
- @request.perform
802
- expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
803
- end
804
-
805
- it 'should keep cookies between redirects' do
806
- @request.options[:headers] = {'Cookie' => 'keep=me'}
807
- @redirect['Set-Cookie'] = 'foo=tar;'
808
- @request.perform
809
- expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
810
- end
811
-
812
- it "should handle multiple Set-Cookie headers between redirects" do
813
- @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
814
- @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
815
- @request.perform
816
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
817
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
818
- expect(@request.options[:headers]['Cookie']).to match(/one=1/)
819
- expect(@request.options[:headers]['Cookie']).to match(/two=2/)
820
- end
821
-
822
- it 'should make resulting request a get request if it not already' do
823
- @request.http_method = Net::HTTP::Delete
824
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
825
- expect(@request.http_method).to eq(Net::HTTP::Get)
826
- end
827
-
828
- it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
829
- @request.options[:maintain_method_across_redirects] = true
830
- @request.http_method = Net::HTTP::Delete
831
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
832
- expect(@request.http_method).to eq(Net::HTTP::Delete)
833
- end
834
-
835
- it 'should log the redirection' do
836
- logger_double = double
837
- expect(logger_double).to receive(:info).twice
838
- @request.options[:logger] = logger_double
839
- @request.perform
840
- end
841
- end
842
-
843
- describe "infinitely" do
844
- before(:each) do
845
- allow(@http).to receive(:request).and_return(@redirect)
846
- end
847
-
848
- it "should raise an exception" do
849
- expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
850
- end
851
- end
852
- end
853
- end
854
-
855
- describe "a request that 303 redirects" do
856
- before(:each) do
857
- @redirect = stub_response("", 303)
858
- @redirect['location'] = '/foo'
859
-
860
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
861
- end
862
-
863
- describe "once" do
864
- before(:each) do
865
- allow(@http).to receive(:request).and_return(@redirect, @ok)
866
- end
867
-
868
- it "should be handled by GET transparently" do
869
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
870
- end
871
-
872
- it "should be handled by POST transparently" do
873
- @request.http_method = Net::HTTP::Post
874
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
875
- end
876
-
877
- it "should be handled by DELETE transparently" do
878
- @request.http_method = Net::HTTP::Delete
879
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
880
- end
881
-
882
- it "should be handled by MOVE transparently" do
883
- @request.http_method = Net::HTTP::Move
884
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
885
- end
886
-
887
- it "should be handled by COPY transparently" do
888
- @request.http_method = Net::HTTP::Copy
889
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
890
- end
891
-
892
- it "should be handled by PATCH transparently" do
893
- @request.http_method = Net::HTTP::Patch
894
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
895
- end
896
-
897
- it "should be handled by PUT transparently" do
898
- @request.http_method = Net::HTTP::Put
899
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
900
- end
901
-
902
- it "should be handled by HEAD transparently" do
903
- @request.http_method = Net::HTTP::Head
904
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
905
- end
906
-
907
- it "should be handled by OPTIONS transparently" do
908
- @request.http_method = Net::HTTP::Options
909
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
910
- end
911
-
912
- it "should be handled by MKCOL transparently" do
913
- @request.http_method = Net::HTTP::Mkcol
914
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
915
- end
916
-
917
- it "should keep track of cookies between redirects" do
918
- @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
919
- @request.perform
920
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
921
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
922
- end
923
-
924
- it 'should update cookies with redirects' do
925
- @request.options[:headers] = {'Cookie' => 'foo=bar;'}
926
- @redirect['Set-Cookie'] = 'foo=tar;'
927
- @request.perform
928
- expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
929
- end
930
-
931
- it 'should keep cookies between redirects' do
932
- @request.options[:headers] = {'Cookie' => 'keep=me'}
933
- @redirect['Set-Cookie'] = 'foo=tar;'
934
- @request.perform
935
- expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
936
- end
937
-
938
- it "should handle multiple Set-Cookie headers between redirects" do
939
- @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
940
- @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
941
- @request.perform
942
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
943
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
944
- expect(@request.options[:headers]['Cookie']).to match(/one=1/)
945
- expect(@request.options[:headers]['Cookie']).to match(/two=2/)
946
- end
947
-
948
- it 'should make resulting request a get request if it not already' do
949
- @request.http_method = Net::HTTP::Delete
950
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
951
- expect(@request.http_method).to eq(Net::HTTP::Get)
952
- end
953
-
954
- it 'should make resulting request a get request if options[:maintain_method_across_redirects] is false' do
955
- @request.options[:maintain_method_across_redirects] = false
956
- @request.http_method = Net::HTTP::Delete
957
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
958
- expect(@request.http_method).to eq(Net::HTTP::Get)
959
- end
960
-
961
- it 'should make resulting request a get request if options[:maintain_method_across_redirects] is true but options[:resend_on_redirect] is false' do
962
- @request.options[:maintain_method_across_redirects] = true
963
- @request.options[:resend_on_redirect] = false
964
- @request.http_method = Net::HTTP::Delete
965
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
966
- expect(@request.http_method).to eq(Net::HTTP::Get)
967
- end
968
-
969
- it 'should not make resulting request a get request if options[:maintain_method_across_redirects] and options[:resend_on_redirect] is true' do
970
- @request.options[:maintain_method_across_redirects] = true
971
- @request.options[:resend_on_redirect] = true
972
- @request.http_method = Net::HTTP::Delete
973
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
974
- expect(@request.http_method).to eq(Net::HTTP::Delete)
975
- end
976
-
977
- it 'should log the redirection' do
978
- logger_double = double
979
- expect(logger_double).to receive(:info).twice
980
- @request.options[:logger] = logger_double
981
- @request.perform
982
- end
983
- end
984
-
985
- describe "infinitely" do
986
- before(:each) do
987
- allow(@http).to receive(:request).and_return(@redirect)
988
- end
989
-
990
- it "should raise an exception" do
991
- expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
992
- end
993
- end
994
- end
995
-
996
- describe "a request that returns 304" do
997
- before(:each) do
998
- @redirect = stub_response("", 304)
999
- @redirect['location'] = '/foo'
1000
- end
1001
-
1002
- before(:each) do
1003
- allow(@http).to receive(:request).and_return(@redirect)
1004
- end
1005
-
1006
- it "should report 304 with a GET request" do
1007
- expect(@request.perform.code).to eq(304)
1008
- end
1009
-
1010
- it "should report 304 with a POST request" do
1011
- @request.http_method = Net::HTTP::Post
1012
- expect(@request.perform.code).to eq(304)
1013
- end
1014
-
1015
- it "should report 304 with a DELETE request" do
1016
- @request.http_method = Net::HTTP::Delete
1017
- expect(@request.perform.code).to eq(304)
1018
- end
1019
-
1020
- it "should report 304 with a MOVE request" do
1021
- @request.http_method = Net::HTTP::Move
1022
- expect(@request.perform.code).to eq(304)
1023
- end
1024
-
1025
- it "should report 304 with a COPY request" do
1026
- @request.http_method = Net::HTTP::Copy
1027
- expect(@request.perform.code).to eq(304)
1028
- end
1029
-
1030
- it "should report 304 with a PATCH request" do
1031
- @request.http_method = Net::HTTP::Patch
1032
- expect(@request.perform.code).to eq(304)
1033
- end
1034
-
1035
- it "should report 304 with a PUT request" do
1036
- @request.http_method = Net::HTTP::Put
1037
- expect(@request.perform.code).to eq(304)
1038
- end
1039
-
1040
- it "should report 304 with a HEAD request" do
1041
- @request.http_method = Net::HTTP::Head
1042
- expect(@request.perform.code).to eq(304)
1043
- end
1044
-
1045
- it "should report 304 with a OPTIONS request" do
1046
- @request.http_method = Net::HTTP::Options
1047
- expect(@request.perform.code).to eq(304)
1048
- end
1049
-
1050
- it "should report 304 with a MKCOL request" do
1051
- @request.http_method = Net::HTTP::Mkcol
1052
- expect(@request.perform.code).to eq(304)
1053
- end
1054
-
1055
- it 'should not log the redirection' do
1056
- logger_double = double
1057
- expect(logger_double).to receive(:info).once
1058
- @request.options[:logger] = logger_double
1059
- @request.perform
1060
- end
1061
- end
1062
-
1063
- [307, 308].each do |code|
1064
- describe "a request that #{code} redirects" do
1065
- before(:each) do
1066
- @redirect = stub_response("", code)
1067
- @redirect['location'] = '/foo'
1068
-
1069
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
1070
- end
1071
-
1072
- describe "once" do
1073
- before(:each) do
1074
- allow(@http).to receive(:request).and_return(@redirect, @ok)
1075
- end
1076
-
1077
- it "should be handled by GET transparently" do
1078
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1079
- end
1080
-
1081
- it "should be handled by POST transparently" do
1082
- @request.http_method = Net::HTTP::Post
1083
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1084
- end
1085
-
1086
- it "should be handled by DELETE transparently" do
1087
- @request.http_method = Net::HTTP::Delete
1088
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1089
- end
1090
-
1091
- it "should be handled by MOVE transparently" do
1092
- @request.http_method = Net::HTTP::Move
1093
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1094
- end
1095
-
1096
- it "should be handled by COPY transparently" do
1097
- @request.http_method = Net::HTTP::Copy
1098
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1099
- end
1100
-
1101
- it "should be handled by PATCH transparently" do
1102
- @request.http_method = Net::HTTP::Patch
1103
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1104
- end
1105
-
1106
- it "should be handled by PUT transparently" do
1107
- @request.http_method = Net::HTTP::Put
1108
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1109
- end
1110
-
1111
- it "should be handled by HEAD transparently" do
1112
- @request.http_method = Net::HTTP::Head
1113
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1114
- end
1115
-
1116
- it "should be handled by OPTIONS transparently" do
1117
- @request.http_method = Net::HTTP::Options
1118
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1119
- end
1120
-
1121
- it "should be handled by MKCOL transparently" do
1122
- @request.http_method = Net::HTTP::Mkcol
1123
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1124
- end
1125
-
1126
- it "should keep track of cookies between redirects" do
1127
- @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
1128
- @request.perform
1129
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
1130
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
1131
- end
1132
-
1133
- it 'should update cookies with redirects' do
1134
- @request.options[:headers] = {'Cookie' => 'foo=bar;'}
1135
- @redirect['Set-Cookie'] = 'foo=tar;'
1136
- @request.perform
1137
- expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
1138
- end
1139
-
1140
- it 'should keep cookies between redirects' do
1141
- @request.options[:headers] = {'Cookie' => 'keep=me'}
1142
- @redirect['Set-Cookie'] = 'foo=tar;'
1143
- @request.perform
1144
- expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
1145
- end
1146
-
1147
- it "should handle multiple Set-Cookie headers between redirects" do
1148
- @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
1149
- @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
1150
- @request.perform
1151
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
1152
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
1153
- expect(@request.options[:headers]['Cookie']).to match(/one=1/)
1154
- expect(@request.options[:headers]['Cookie']).to match(/two=2/)
1155
- end
1156
-
1157
- it 'should maintain method in resulting request' do
1158
- @request.http_method = Net::HTTP::Delete
1159
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1160
- expect(@request.http_method).to eq(Net::HTTP::Delete)
1161
- end
1162
-
1163
- it 'should maintain method in resulting request if options[:maintain_method_across_redirects] is false' do
1164
- @request.options[:maintain_method_across_redirects] = false
1165
- @request.http_method = Net::HTTP::Delete
1166
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1167
- expect(@request.http_method).to eq(Net::HTTP::Delete)
1168
- end
1169
-
1170
- it 'should maintain method in resulting request if options[:maintain_method_across_redirects] is true' do
1171
- @request.options[:maintain_method_across_redirects] = true
1172
- @request.http_method = Net::HTTP::Delete
1173
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1174
- expect(@request.http_method).to eq(Net::HTTP::Delete)
1175
- end
1176
-
1177
- it 'should log the redirection' do
1178
- logger_double = double
1179
- expect(logger_double).to receive(:info).twice
1180
- @request.options[:logger] = logger_double
1181
- @request.perform
1182
- end
1183
- end
1184
-
1185
- describe "infinitely" do
1186
- before(:each) do
1187
- allow(@http).to receive(:request).and_return(@redirect)
1188
- end
1189
-
1190
- it "should raise an exception" do
1191
- expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
1192
- end
1193
- end
1194
- end
1195
- end
1196
-
1197
- describe "#send_authorization_header?" do
1198
- context "basic_auth" do
1199
- before do
1200
- @credentials = { username: "username", password: "password" }
1201
- @authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
1202
- @request.options[:basic_auth] = @credentials
1203
- @redirect = stub_response("", 302)
1204
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
1205
- end
1206
-
1207
- before(:each) do
1208
- allow(@http).to receive(:request).and_return(@redirect, @ok)
1209
- end
1210
-
1211
- it "should not send Authorization header when redirecting to a different host" do
1212
- @redirect['location'] = 'http://example.com/'
1213
- @request.perform
1214
- @request.send(:setup_raw_request)
1215
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to be_nil
1216
- end
1217
-
1218
- it "should send Authorization header when redirecting to a relative path" do
1219
- @redirect['location'] = '/v3'
1220
- @request.perform
1221
- @request.send(:setup_raw_request)
1222
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization)
1223
- end
1224
-
1225
- it "should send Authorization header when redirecting to the same host" do
1226
- @redirect['location'] = 'http://api.foo.com/v2'
1227
- @request.perform
1228
- @request.send(:setup_raw_request)
1229
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization)
1230
- end
1231
-
1232
- it "should send Authorization header when redirecting to a different port on the same host" do
1233
- @redirect['location'] = 'http://api.foo.com:3000/v3'
1234
- @request.perform
1235
- @request.send(:setup_raw_request)
1236
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization)
1237
- end
1238
- end
1239
- end
1240
-
1241
- context "with POST http method" do
1242
- it "should raise argument error if query is not a hash" do
1243
- expect {
1244
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', format: :xml, query: 'astring').perform
1245
- }.to raise_error(ArgumentError)
1246
- end
1247
- end
1248
-
1249
- describe "argument validation" do
1250
- it "should raise argument error if basic_auth and digest_auth are both present" do
1251
- expect {
1252
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: {}, digest_auth: {}).perform
1253
- }.to raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
1254
- end
1255
-
1256
- it "should raise argument error if basic_auth is not a hash" do
1257
- expect {
1258
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: %w(foo bar)).perform
1259
- }.to raise_error(ArgumentError, ":basic_auth must be a hash")
1260
- end
1261
-
1262
- it "should raise argument error if digest_auth is not a hash" do
1263
- expect {
1264
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', digest_auth: %w(foo bar)).perform
1265
- }.to raise_error(ArgumentError, ":digest_auth must be a hash")
1266
- end
1267
-
1268
- it "should raise argument error if headers is not a hash" do
1269
- expect {
1270
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', headers: %w(foo bar)).perform
1271
- }.to raise_error(ArgumentError, ":headers must be a hash")
1272
- end
1273
-
1274
- it "should raise argument error if options method is not http accepted method" do
1275
- expect {
1276
- HTTParty::Request.new('SuperPost', 'http://api.foo.com/v1').perform
1277
- }.to raise_error(ArgumentError, "only get, post, patch, put, delete, head, and options methods are supported")
1278
- end
1279
-
1280
- it "should raise argument error if http method is post and query is not hash" do
1281
- expect {
1282
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', query: "message: hello").perform
1283
- }.to raise_error(ArgumentError, ":query must be hash if using HTTP Post")
1284
- end
1285
-
1286
- it "should raise RedirectionTooDeep error if limit is negative" do
1287
- expect {
1288
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', limit: -1).perform
1289
- }.to raise_error(HTTParty::RedirectionTooDeep, 'HTTP redirects too deep')
1290
- end
1291
- end
1292
-
1293
- context 'with Accept-Encoding header' do
1294
- it 'should disable content decoding if present' do
1295
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', headers:{'Accept-Encoding' => 'custom'})
1296
- request.send(:setup_raw_request)
1297
- expect(request.instance_variable_get(:@raw_request).decode_content).to eq(false)
1298
- end
1299
-
1300
- it 'should disable content decoding if present and lowercase' do
1301
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', headers:{'accept-encoding' => 'custom'})
1302
- request.send(:setup_raw_request)
1303
- expect(request.instance_variable_get(:@raw_request).decode_content).to eq(false)
1304
- end
1305
-
1306
- it 'should disable content decoding if present' do
1307
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1')
1308
- request.send(:setup_raw_request)
1309
- expect(request.instance_variable_get(:@raw_request).decode_content).to eq(true)
1310
- end
1311
- end
1312
- end