httparty 0.15.6 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/workflows/ci.yml +26 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop_todo.yml +1 -1
  6. data/Changelog.md +105 -0
  7. data/Gemfile +7 -0
  8. data/Guardfile +3 -2
  9. data/README.md +6 -6
  10. data/docs/README.md +90 -5
  11. data/examples/README.md +28 -11
  12. data/examples/aaws.rb +6 -2
  13. data/examples/body_stream.rb +14 -0
  14. data/examples/custom_parsers.rb +4 -0
  15. data/examples/headers_and_user_agents.rb +7 -3
  16. data/examples/idn.rb +10 -0
  17. data/examples/logging.rb +3 -3
  18. data/examples/microsoft_graph.rb +52 -0
  19. data/examples/multipart.rb +22 -0
  20. data/examples/peer_cert.rb +9 -0
  21. data/examples/stream_download.rb +8 -2
  22. data/httparty.gemspec +3 -3
  23. data/lib/httparty/connection_adapter.rb +59 -16
  24. data/lib/httparty/cookie_hash.rb +10 -8
  25. data/lib/httparty/decompressor.rb +102 -0
  26. data/lib/httparty/exceptions.rb +3 -1
  27. data/lib/httparty/hash_conversions.rb +28 -12
  28. data/lib/httparty/headers_processor.rb +32 -0
  29. data/lib/httparty/logger/apache_formatter.rb +31 -6
  30. data/lib/httparty/logger/curl_formatter.rb +9 -7
  31. data/lib/httparty/logger/logger.rb +5 -1
  32. data/lib/httparty/logger/logstash_formatter.rb +61 -0
  33. data/lib/httparty/module_inheritable_attributes.rb +6 -4
  34. data/lib/httparty/net_digest_auth.rb +15 -15
  35. data/lib/httparty/parser.rb +21 -15
  36. data/lib/httparty/request/body.rb +105 -0
  37. data/lib/httparty/request/multipart_boundary.rb +13 -0
  38. data/lib/httparty/request.rb +86 -94
  39. data/lib/httparty/response/headers.rb +4 -2
  40. data/lib/httparty/response.rb +59 -8
  41. data/lib/httparty/response_fragment.rb +21 -0
  42. data/lib/httparty/text_encoder.rb +72 -0
  43. data/lib/httparty/utils.rb +13 -0
  44. data/lib/httparty/version.rb +3 -1
  45. data/lib/httparty.rb +70 -25
  46. data/website/css/common.css +1 -1
  47. metadata +37 -103
  48. data/.simplecov +0 -1
  49. data/.travis.yml +0 -9
  50. data/features/basic_authentication.feature +0 -20
  51. data/features/command_line.feature +0 -95
  52. data/features/deals_with_http_error_codes.feature +0 -26
  53. data/features/digest_authentication.feature +0 -30
  54. data/features/handles_compressed_responses.feature +0 -27
  55. data/features/handles_multiple_formats.feature +0 -57
  56. data/features/steps/env.rb +0 -27
  57. data/features/steps/httparty_response_steps.rb +0 -56
  58. data/features/steps/httparty_steps.rb +0 -43
  59. data/features/steps/mongrel_helper.rb +0 -127
  60. data/features/steps/remote_service_steps.rb +0 -92
  61. data/features/supports_read_timeout_option.feature +0 -13
  62. data/features/supports_redirection.feature +0 -22
  63. data/features/supports_timeout_option.feature +0 -13
  64. data/spec/fixtures/delicious.xml +0 -23
  65. data/spec/fixtures/empty.xml +0 -0
  66. data/spec/fixtures/google.html +0 -3
  67. data/spec/fixtures/ssl/generate.sh +0 -29
  68. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  69. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  70. data/spec/fixtures/ssl/generated/ca.key +0 -15
  71. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  72. data/spec/fixtures/ssl/generated/server.crt +0 -13
  73. data/spec/fixtures/ssl/generated/server.key +0 -15
  74. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  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 -495
  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 -49
  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 -268
  87. data/spec/httparty/parser_spec.rb +0 -190
  88. data/spec/httparty/request_spec.rb +0 -1279
  89. data/spec/httparty/response_spec.rb +0 -347
  90. data/spec/httparty/ssl_spec.rb +0 -74
  91. data/spec/httparty_spec.rb +0 -878
  92. data/spec/spec_helper.rb +0 -51
  93. data/spec/support/ssl_test_helper.rb +0 -47
  94. data/spec/support/ssl_test_server.rb +0 -80
  95. data/spec/support/stub_response.rb +0 -49
@@ -1,1279 +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
- end
346
-
347
- describe 'http' do
348
- it "should get a connection from the connection_adapter" do
349
- http = Net::HTTP.new('google.com')
350
- adapter = double('adapter')
351
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443', connection_adapter: adapter)
352
- expect(adapter).to receive(:call).with(request.uri, request.options).and_return(http)
353
- expect(request.send(:http)).to be http
354
- end
355
- end
356
-
357
- describe '#format_from_mimetype' do
358
- it 'should handle text/xml' do
359
- ["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
360
- expect(@request.send(:format_from_mimetype, ct)).to eq(:xml)
361
- end
362
- end
363
-
364
- it 'should handle application/xml' do
365
- ["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
366
- expect(@request.send(:format_from_mimetype, ct)).to eq(:xml)
367
- end
368
- end
369
-
370
- it 'should handle text/json' do
371
- ["text/json", "text/json; charset=iso8859-1"].each do |ct|
372
- expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
373
- end
374
- end
375
-
376
- it 'should handle application/json' do
377
- ["application/json", "application/json; charset=iso8859-1"].each do |ct|
378
- expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
379
- end
380
- end
381
-
382
- it 'should handle text/csv' do
383
- ["text/csv", "text/csv; charset=iso8859-1"].each do |ct|
384
- expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
385
- end
386
- end
387
-
388
- it 'should handle application/csv' do
389
- ["application/csv", "application/csv; charset=iso8859-1"].each do |ct|
390
- expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
391
- end
392
- end
393
-
394
- it 'should handle text/comma-separated-values' do
395
- ["text/comma-separated-values", "text/comma-separated-values; charset=iso8859-1"].each do |ct|
396
- expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
397
- end
398
- end
399
-
400
- it 'should handle text/javascript' do
401
- ["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
402
- expect(@request.send(:format_from_mimetype, ct)).to eq(:plain)
403
- end
404
- end
405
-
406
- it 'should handle application/javascript' do
407
- ["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
408
- expect(@request.send(:format_from_mimetype, ct)).to eq(:plain)
409
- end
410
- end
411
-
412
- it "returns nil for an unrecognized mimetype" do
413
- expect(@request.send(:format_from_mimetype, "application/atom+xml")).to be_nil
414
- end
415
-
416
- it "returns nil when using a default parser" do
417
- @request.options[:parser] = lambda {}
418
- expect(@request.send(:format_from_mimetype, "text/json")).to be_nil
419
- end
420
- end
421
-
422
- describe 'parsing responses' do
423
- it 'should handle xml automatically' do
424
- xml = '<books><book><id>1234</id><name>Foo Bar!</name></book></books>'
425
- @request.options[:format] = :xml
426
- expect(@request.send(:parse_response, xml)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
427
- end
428
-
429
- it 'should handle utf-8 bom in xml' do
430
- xml = "\xEF\xBB\xBF<books><book><id>1234</id><name>Foo Bar!</name></book></books>"
431
- @request.options[:format] = :xml
432
- expect(@request.send(:parse_response, xml)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
433
- end
434
-
435
- it 'should handle csv automatically' do
436
- csv = ['"id","Name"', '"1234","Foo Bar!"'].join("\n")
437
- @request.options[:format] = :csv
438
- expect(@request.send(:parse_response, csv)).to eq([%w(id Name), ["1234", "Foo Bar!"]])
439
- end
440
-
441
- it 'should handle json automatically' do
442
- json = '{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}'
443
- @request.options[:format] = :json
444
- expect(@request.send(:parse_response, json)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
445
- end
446
-
447
- it 'should handle utf-8 bom in json' do
448
- json = "\xEF\xBB\xBF{\"books\": {\"book\": {\"name\": \"Foo Bar!\", \"id\": \"1234\"}}}"
449
- @request.options[:format] = :json
450
- expect(@request.send(:parse_response, json)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
451
- end
452
-
453
- it "should include any HTTP headers in the returned response" do
454
- @request.options[:format] = :html
455
- response = stub_response "Content"
456
- response.initialize_http_header("key" => "value")
457
-
458
- expect(@request.perform.headers).to eq({ "key" => ["value"] })
459
- end
460
-
461
- if "".respond_to?(:encoding)
462
-
463
- let(:response_charset) {
464
- @request.send(:get_charset)
465
- }
466
-
467
- it "should process charset in content type properly" do
468
- response = stub_response "Content".force_encoding('ascii-8bit')
469
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-8")
470
- resp = @request.perform
471
- expect(response_charset).to_not be_empty
472
- expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
473
- end
474
-
475
- it "should process charset in content type properly if it has a different case" do
476
- response = stub_response "Content".force_encoding('ascii-8bit')
477
- response.initialize_http_header("Content-Type" => "text/plain;CHARSET = utf-8")
478
- resp = @request.perform
479
- expect(response_charset).to_not be_empty
480
- expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
481
- end
482
-
483
- it "should process quoted charset in content type properly" do
484
- response = stub_response "Content".force_encoding('ascii-8bit')
485
- response.initialize_http_header("Content-Type" => "text/plain;charset = \"utf-8\"")
486
- resp = @request.perform
487
- expect(response_charset).to_not be_empty
488
- expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
489
- end
490
-
491
- it "should process response with a nil body" do
492
- response = stub_response nil
493
- response.initialize_http_header("Content-Type" => "text/html;charset=UTF-8")
494
- resp = @request.perform
495
- expect(resp.body).to be_nil
496
- end
497
-
498
- it "should process utf-16 charset with little endian bom correctly" do
499
- @request.options[:assume_utf16_is_big_endian] = true
500
-
501
- response = stub_response "\xFF\xFEC\x00o\x00n\x00t\x00e\x00n\x00t\x00"
502
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
503
- resp = @request.perform
504
- expect(response_charset).to_not be_empty
505
- expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE"))
506
- end
507
-
508
- it "should process utf-16 charset with big endian bom correctly" do
509
- @request.options[:assume_utf16_is_big_endian] = false
510
-
511
- response = stub_response "\xFE\xFF\x00C\x00o\x00n\x00t\x00e\x00n\x00t"
512
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
513
- resp = @request.perform
514
- expect(response_charset).to_not be_empty
515
- expect(resp.body.encoding).to eq(Encoding.find("UTF-16BE"))
516
- end
517
-
518
- it "should assume utf-16 little endian if options has been chosen" do
519
- @request.options[:assume_utf16_is_big_endian] = false
520
-
521
- response = stub_response "C\x00o\x00n\x00t\x00e\x00n\x00t\x00"
522
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
523
- resp = @request.perform
524
- expect(response_charset).to_not be_empty
525
- expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE"))
526
- end
527
-
528
- it "should perform no encoding if the charset is not available" do
529
- response = stub_response "Content"
530
- response.initialize_http_header("Content-Type" => "text/plain;charset = utf-lols")
531
- resp = @request.perform
532
- expect(response_charset).to_not be_empty
533
- # This encoding does not exist, thus the string should not be encodd with it
534
- expect(resp.body.encoding).to_not eq(response_charset)
535
- expect(resp.body).to eq("Content")
536
- expect(resp.body.encoding).to eq("Content".encoding)
537
- end
538
-
539
- it "should perform no encoding if the content type is specified but no charset is specified" do
540
- response = stub_response "Content"
541
- response.initialize_http_header("Content-Type" => "text/plain")
542
- resp = @request.perform
543
- expect(response_charset).to be_nil
544
- expect(resp.body).to eq("Content")
545
- expect(resp.body.encoding).to eq("Content".encoding)
546
- end
547
- end
548
-
549
- describe 'with non-200 responses' do
550
- context "3xx responses" do
551
- it 'returns a valid object for 304 not modified' do
552
- stub_response '', 304
553
- resp = @request.perform
554
- expect(resp.code).to eq(304)
555
- expect(resp.body).to eq('')
556
- expect(resp).to be_nil
557
- end
558
-
559
- it "redirects if a 300 contains a location header" do
560
- redirect = stub_response '', 300
561
- redirect['location'] = 'http://foo.com/foo'
562
- ok = stub_response('<hash><foo>bar</foo></hash>', 200)
563
- allow(@http).to receive(:request).and_return(redirect, ok)
564
- response = @request.perform
565
- expect(response.request.base_uri.to_s).to eq("http://foo.com")
566
- expect(response.request.path.to_s).to eq("http://foo.com/foo")
567
- expect(response.request.uri.request_uri).to eq("/foo")
568
- expect(response.request.uri.to_s).to eq("http://foo.com/foo")
569
- expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
570
- end
571
-
572
- it "calls block given to perform with each redirect" do
573
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
574
- stub_request(:get, 'http://test.com/redirect')
575
- .to_return(
576
- status: [300, 'REDIRECT'],
577
- headers: { location: 'http://api.foo.com/v2' }
578
- )
579
- stub_request(:get, 'http://api.foo.com/v2')
580
- .to_return(body: '<hash><foo>bar</foo></hash>')
581
- body = ""
582
- response = @request.perform { |chunk| body += chunk }
583
- expect(body.length).to eq(27)
584
- end
585
-
586
- it "redirects if a 300 contains a relative location header" do
587
- redirect = stub_response '', 300
588
- redirect['location'] = '/foo/bar'
589
- ok = stub_response('<hash><foo>bar</foo></hash>', 200)
590
- allow(@http).to receive(:request).and_return(redirect, ok)
591
- response = @request.perform
592
- expect(response.request.base_uri.to_s).to eq("http://api.foo.com")
593
- expect(response.request.path.to_s).to eq("/foo/bar")
594
- expect(response.request.uri.request_uri).to eq("/foo/bar")
595
- expect(response.request.uri.to_s).to eq("http://api.foo.com/foo/bar")
596
- expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
597
- end
598
-
599
- it "handles multiple redirects and relative location headers on different hosts" do
600
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
601
- stub_request(:get, 'http://test.com/redirect')
602
- .to_return(
603
- status: [300, 'REDIRECT'],
604
- headers: { location: "http://api.foo.com/v2" }
605
- )
606
- stub_request(:get, 'http://api.foo.com/v2')
607
- .to_return(
608
- status: [300, 'REDIRECT'],
609
- headers: { location: '/v3' }
610
- )
611
- stub_request(:get, 'http://api.foo.com/v3')
612
- .to_return(body: '<hash><foo>bar</foo></hash>')
613
- response = @request.perform
614
- expect(response.request.base_uri.to_s).to eq("http://api.foo.com")
615
- expect(response.request.path.to_s).to eq("/v3")
616
- expect(response.request.uri.request_uri).to eq("/v3")
617
- expect(response.request.uri.to_s).to eq("http://api.foo.com/v3")
618
- expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
619
- end
620
-
621
- it "raises an error if redirect has duplicate location header" do
622
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
623
- stub_request(:get, 'http://test.com/redirect')
624
- .to_return(
625
- status: [300, 'REDIRECT'],
626
- headers: {
627
- location: ['http://api.foo.com/v2', 'http://api.foo.com/v2']
628
- }
629
- )
630
- expect {@request.perform}.to raise_error(HTTParty::DuplicateLocationHeader)
631
- end
632
-
633
- it "returns the HTTParty::Response when the 300 does not contain a location header" do
634
- stub_response '', 300
635
- expect(HTTParty::Response).to be === @request.perform
636
- end
637
-
638
- it "redirects including port" do
639
- stub_request(:get, 'http://withport.com:3000/v1')
640
- .to_return(
641
- status: [301, 'Moved Permanently'],
642
- headers: { location: 'http://withport.com:3000/v2' }
643
- )
644
- stub_request(:get, 'http://withport.com:3000/v2')
645
- .to_return(status: 200)
646
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://withport.com:3000/v1')
647
- response = request.perform
648
- expect(response.request.base_uri.to_s).to eq("http://withport.com:3000")
649
- end
650
- end
651
-
652
- it 'should return a valid object for 4xx response' do
653
- stub_response '<foo><bar>yes</bar></foo>', 401
654
- resp = @request.perform
655
- expect(resp.code).to eq(401)
656
- expect(resp.body).to eq("<foo><bar>yes</bar></foo>")
657
- expect(resp['foo']['bar']).to eq("yes")
658
- end
659
-
660
- it 'should return a valid object for 5xx response' do
661
- stub_response '<foo><bar>error</bar></foo>', 500
662
- resp = @request.perform
663
- expect(resp.code).to eq(500)
664
- expect(resp.body).to eq("<foo><bar>error</bar></foo>")
665
- expect(resp['foo']['bar']).to eq("error")
666
- end
667
-
668
- it "parses response lazily so codes can be checked prior" do
669
- stub_response 'not xml', 500
670
- @request.options[:format] = :xml
671
- expect {
672
- response = @request.perform
673
- expect(response.code).to eq(500)
674
- expect(response.body).to eq('not xml')
675
- }.not_to raise_error
676
- end
677
- end
678
- end
679
-
680
- it "should not attempt to parse empty responses" do
681
- [204, 304].each do |code|
682
- stub_response "", code
683
-
684
- @request.options[:format] = :xml
685
- expect(@request.perform).to be_nil
686
- end
687
- end
688
-
689
- it "should not fail for missing mime type" do
690
- stub_response "Content for you"
691
- @request.options[:format] = :html
692
- expect(@request.perform.parsed_response).to eq('Content for you')
693
- end
694
-
695
- [300, 301, 302, 305].each do |code|
696
- describe "a request that #{code} redirects" do
697
- before(:each) do
698
- @redirect = stub_response("", code)
699
- @redirect['location'] = '/foo'
700
-
701
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
702
- end
703
-
704
- describe "once" do
705
- before(:each) do
706
- allow(@http).to receive(:request).and_return(@redirect, @ok)
707
- end
708
-
709
- it "should be handled by GET transparently" do
710
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
711
- end
712
-
713
- it "should be handled by POST transparently" do
714
- @request.http_method = Net::HTTP::Post
715
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
716
- end
717
-
718
- it "should be handled by DELETE transparently" do
719
- @request.http_method = Net::HTTP::Delete
720
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
721
- end
722
-
723
- it "should be handled by MOVE transparently" do
724
- @request.http_method = Net::HTTP::Move
725
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
726
- end
727
-
728
- it "should be handled by COPY transparently" do
729
- @request.http_method = Net::HTTP::Copy
730
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
731
- end
732
-
733
- it "should be handled by PATCH transparently" do
734
- @request.http_method = Net::HTTP::Patch
735
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
736
- end
737
-
738
- it "should be handled by PUT transparently" do
739
- @request.http_method = Net::HTTP::Put
740
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
741
- end
742
-
743
- it "should be handled by HEAD transparently" do
744
- @request.http_method = Net::HTTP::Head
745
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
746
- end
747
-
748
- it "should be handled by OPTIONS transparently" do
749
- @request.http_method = Net::HTTP::Options
750
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
751
- end
752
-
753
- it "should be handled by MKCOL transparently" do
754
- @request.http_method = Net::HTTP::Mkcol
755
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
756
- end
757
-
758
- it "should keep track of cookies between redirects" do
759
- @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
760
- @request.perform
761
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
762
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
763
- end
764
-
765
- it 'should update cookies with redirects' do
766
- @request.options[:headers] = {'Cookie' => 'foo=bar;'}
767
- @redirect['Set-Cookie'] = 'foo=tar;'
768
- @request.perform
769
- expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
770
- end
771
-
772
- it 'should keep cookies between redirects' do
773
- @request.options[:headers] = {'Cookie' => 'keep=me'}
774
- @redirect['Set-Cookie'] = 'foo=tar;'
775
- @request.perform
776
- expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
777
- end
778
-
779
- it "should handle multiple Set-Cookie headers between redirects" do
780
- @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
781
- @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
782
- @request.perform
783
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
784
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
785
- expect(@request.options[:headers]['Cookie']).to match(/one=1/)
786
- expect(@request.options[:headers]['Cookie']).to match(/two=2/)
787
- end
788
-
789
- it 'should make resulting request a get request if it not already' do
790
- @request.http_method = Net::HTTP::Delete
791
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
792
- expect(@request.http_method).to eq(Net::HTTP::Get)
793
- end
794
-
795
- it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
796
- @request.options[:maintain_method_across_redirects] = true
797
- @request.http_method = Net::HTTP::Delete
798
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
799
- expect(@request.http_method).to eq(Net::HTTP::Delete)
800
- end
801
-
802
- it 'should log the redirection' do
803
- logger_double = double
804
- expect(logger_double).to receive(:info).twice
805
- @request.options[:logger] = logger_double
806
- @request.perform
807
- end
808
- end
809
-
810
- describe "infinitely" do
811
- before(:each) do
812
- allow(@http).to receive(:request).and_return(@redirect)
813
- end
814
-
815
- it "should raise an exception" do
816
- expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
817
- end
818
- end
819
- end
820
- end
821
-
822
- describe "a request that 303 redirects" do
823
- before(:each) do
824
- @redirect = stub_response("", 303)
825
- @redirect['location'] = '/foo'
826
-
827
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
828
- end
829
-
830
- describe "once" do
831
- before(:each) do
832
- allow(@http).to receive(:request).and_return(@redirect, @ok)
833
- end
834
-
835
- it "should be handled by GET transparently" do
836
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
837
- end
838
-
839
- it "should be handled by POST transparently" do
840
- @request.http_method = Net::HTTP::Post
841
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
842
- end
843
-
844
- it "should be handled by DELETE transparently" do
845
- @request.http_method = Net::HTTP::Delete
846
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
847
- end
848
-
849
- it "should be handled by MOVE transparently" do
850
- @request.http_method = Net::HTTP::Move
851
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
852
- end
853
-
854
- it "should be handled by COPY transparently" do
855
- @request.http_method = Net::HTTP::Copy
856
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
857
- end
858
-
859
- it "should be handled by PATCH transparently" do
860
- @request.http_method = Net::HTTP::Patch
861
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
862
- end
863
-
864
- it "should be handled by PUT transparently" do
865
- @request.http_method = Net::HTTP::Put
866
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
867
- end
868
-
869
- it "should be handled by HEAD transparently" do
870
- @request.http_method = Net::HTTP::Head
871
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
872
- end
873
-
874
- it "should be handled by OPTIONS transparently" do
875
- @request.http_method = Net::HTTP::Options
876
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
877
- end
878
-
879
- it "should be handled by MKCOL transparently" do
880
- @request.http_method = Net::HTTP::Mkcol
881
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
882
- end
883
-
884
- it "should keep track of cookies between redirects" do
885
- @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
886
- @request.perform
887
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
888
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
889
- end
890
-
891
- it 'should update cookies with redirects' do
892
- @request.options[:headers] = {'Cookie' => 'foo=bar;'}
893
- @redirect['Set-Cookie'] = 'foo=tar;'
894
- @request.perform
895
- expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
896
- end
897
-
898
- it 'should keep cookies between redirects' do
899
- @request.options[:headers] = {'Cookie' => 'keep=me'}
900
- @redirect['Set-Cookie'] = 'foo=tar;'
901
- @request.perform
902
- expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
903
- end
904
-
905
- it "should handle multiple Set-Cookie headers between redirects" do
906
- @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
907
- @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
908
- @request.perform
909
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
910
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
911
- expect(@request.options[:headers]['Cookie']).to match(/one=1/)
912
- expect(@request.options[:headers]['Cookie']).to match(/two=2/)
913
- end
914
-
915
- it 'should make resulting request a get request if it not already' do
916
- @request.http_method = Net::HTTP::Delete
917
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
918
- expect(@request.http_method).to eq(Net::HTTP::Get)
919
- end
920
-
921
- it 'should make resulting request a get request if options[:maintain_method_across_redirects] is false' do
922
- @request.options[:maintain_method_across_redirects] = false
923
- @request.http_method = Net::HTTP::Delete
924
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
925
- expect(@request.http_method).to eq(Net::HTTP::Get)
926
- end
927
-
928
- it 'should make resulting request a get request if options[:maintain_method_across_redirects] is true but options[:resend_on_redirect] is false' do
929
- @request.options[:maintain_method_across_redirects] = true
930
- @request.options[:resend_on_redirect] = false
931
- @request.http_method = Net::HTTP::Delete
932
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
933
- expect(@request.http_method).to eq(Net::HTTP::Get)
934
- end
935
-
936
- it 'should not make resulting request a get request if options[:maintain_method_across_redirects] and options[:resend_on_redirect] is true' do
937
- @request.options[:maintain_method_across_redirects] = true
938
- @request.options[:resend_on_redirect] = true
939
- @request.http_method = Net::HTTP::Delete
940
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
941
- expect(@request.http_method).to eq(Net::HTTP::Delete)
942
- end
943
-
944
- it 'should log the redirection' do
945
- logger_double = double
946
- expect(logger_double).to receive(:info).twice
947
- @request.options[:logger] = logger_double
948
- @request.perform
949
- end
950
- end
951
-
952
- describe "infinitely" do
953
- before(:each) do
954
- allow(@http).to receive(:request).and_return(@redirect)
955
- end
956
-
957
- it "should raise an exception" do
958
- expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
959
- end
960
- end
961
- end
962
-
963
- describe "a request that returns 304" do
964
- before(:each) do
965
- @redirect = stub_response("", 304)
966
- @redirect['location'] = '/foo'
967
- end
968
-
969
- before(:each) do
970
- allow(@http).to receive(:request).and_return(@redirect)
971
- end
972
-
973
- it "should report 304 with a GET request" do
974
- expect(@request.perform.code).to eq(304)
975
- end
976
-
977
- it "should report 304 with a POST request" do
978
- @request.http_method = Net::HTTP::Post
979
- expect(@request.perform.code).to eq(304)
980
- end
981
-
982
- it "should report 304 with a DELETE request" do
983
- @request.http_method = Net::HTTP::Delete
984
- expect(@request.perform.code).to eq(304)
985
- end
986
-
987
- it "should report 304 with a MOVE request" do
988
- @request.http_method = Net::HTTP::Move
989
- expect(@request.perform.code).to eq(304)
990
- end
991
-
992
- it "should report 304 with a COPY request" do
993
- @request.http_method = Net::HTTP::Copy
994
- expect(@request.perform.code).to eq(304)
995
- end
996
-
997
- it "should report 304 with a PATCH request" do
998
- @request.http_method = Net::HTTP::Patch
999
- expect(@request.perform.code).to eq(304)
1000
- end
1001
-
1002
- it "should report 304 with a PUT request" do
1003
- @request.http_method = Net::HTTP::Put
1004
- expect(@request.perform.code).to eq(304)
1005
- end
1006
-
1007
- it "should report 304 with a HEAD request" do
1008
- @request.http_method = Net::HTTP::Head
1009
- expect(@request.perform.code).to eq(304)
1010
- end
1011
-
1012
- it "should report 304 with a OPTIONS request" do
1013
- @request.http_method = Net::HTTP::Options
1014
- expect(@request.perform.code).to eq(304)
1015
- end
1016
-
1017
- it "should report 304 with a MKCOL request" do
1018
- @request.http_method = Net::HTTP::Mkcol
1019
- expect(@request.perform.code).to eq(304)
1020
- end
1021
-
1022
- it 'should not log the redirection' do
1023
- logger_double = double
1024
- expect(logger_double).to receive(:info).once
1025
- @request.options[:logger] = logger_double
1026
- @request.perform
1027
- end
1028
- end
1029
-
1030
- [307, 308].each do |code|
1031
- describe "a request that #{code} redirects" do
1032
- before(:each) do
1033
- @redirect = stub_response("", code)
1034
- @redirect['location'] = '/foo'
1035
-
1036
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
1037
- end
1038
-
1039
- describe "once" do
1040
- before(:each) do
1041
- allow(@http).to receive(:request).and_return(@redirect, @ok)
1042
- end
1043
-
1044
- it "should be handled by GET transparently" do
1045
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1046
- end
1047
-
1048
- it "should be handled by POST transparently" do
1049
- @request.http_method = Net::HTTP::Post
1050
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1051
- end
1052
-
1053
- it "should be handled by DELETE transparently" do
1054
- @request.http_method = Net::HTTP::Delete
1055
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1056
- end
1057
-
1058
- it "should be handled by MOVE transparently" do
1059
- @request.http_method = Net::HTTP::Move
1060
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1061
- end
1062
-
1063
- it "should be handled by COPY transparently" do
1064
- @request.http_method = Net::HTTP::Copy
1065
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1066
- end
1067
-
1068
- it "should be handled by PATCH transparently" do
1069
- @request.http_method = Net::HTTP::Patch
1070
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1071
- end
1072
-
1073
- it "should be handled by PUT transparently" do
1074
- @request.http_method = Net::HTTP::Put
1075
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1076
- end
1077
-
1078
- it "should be handled by HEAD transparently" do
1079
- @request.http_method = Net::HTTP::Head
1080
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1081
- end
1082
-
1083
- it "should be handled by OPTIONS transparently" do
1084
- @request.http_method = Net::HTTP::Options
1085
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1086
- end
1087
-
1088
- it "should be handled by MKCOL transparently" do
1089
- @request.http_method = Net::HTTP::Mkcol
1090
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1091
- end
1092
-
1093
- it "should keep track of cookies between redirects" do
1094
- @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
1095
- @request.perform
1096
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
1097
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
1098
- end
1099
-
1100
- it 'should update cookies with redirects' do
1101
- @request.options[:headers] = {'Cookie' => 'foo=bar;'}
1102
- @redirect['Set-Cookie'] = 'foo=tar;'
1103
- @request.perform
1104
- expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
1105
- end
1106
-
1107
- it 'should keep cookies between redirects' do
1108
- @request.options[:headers] = {'Cookie' => 'keep=me'}
1109
- @redirect['Set-Cookie'] = 'foo=tar;'
1110
- @request.perform
1111
- expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
1112
- end
1113
-
1114
- it "should handle multiple Set-Cookie headers between redirects" do
1115
- @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
1116
- @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
1117
- @request.perform
1118
- expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
1119
- expect(@request.options[:headers]['Cookie']).to match(/name=value/)
1120
- expect(@request.options[:headers]['Cookie']).to match(/one=1/)
1121
- expect(@request.options[:headers]['Cookie']).to match(/two=2/)
1122
- end
1123
-
1124
- it 'should maintain method in resulting request' do
1125
- @request.http_method = Net::HTTP::Delete
1126
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1127
- expect(@request.http_method).to eq(Net::HTTP::Delete)
1128
- end
1129
-
1130
- it 'should maintain method in resulting request if options[:maintain_method_across_redirects] is false' do
1131
- @request.options[:maintain_method_across_redirects] = false
1132
- @request.http_method = Net::HTTP::Delete
1133
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1134
- expect(@request.http_method).to eq(Net::HTTP::Delete)
1135
- end
1136
-
1137
- it 'should maintain method in resulting request if options[:maintain_method_across_redirects] is true' do
1138
- @request.options[:maintain_method_across_redirects] = true
1139
- @request.http_method = Net::HTTP::Delete
1140
- expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
1141
- expect(@request.http_method).to eq(Net::HTTP::Delete)
1142
- end
1143
-
1144
- it 'should log the redirection' do
1145
- logger_double = double
1146
- expect(logger_double).to receive(:info).twice
1147
- @request.options[:logger] = logger_double
1148
- @request.perform
1149
- end
1150
- end
1151
-
1152
- describe "infinitely" do
1153
- before(:each) do
1154
- allow(@http).to receive(:request).and_return(@redirect)
1155
- end
1156
-
1157
- it "should raise an exception" do
1158
- expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
1159
- end
1160
- end
1161
- end
1162
- end
1163
-
1164
- describe "#send_authorization_header?" do
1165
- context "basic_auth" do
1166
- before do
1167
- @credentials = { username: "username", password: "password" }
1168
- @authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
1169
- @request.options[:basic_auth] = @credentials
1170
- @redirect = stub_response("", 302)
1171
- @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
1172
- end
1173
-
1174
- before(:each) do
1175
- allow(@http).to receive(:request).and_return(@redirect, @ok)
1176
- end
1177
-
1178
- it "should not send Authorization header when redirecting to a different host" do
1179
- @redirect['location'] = 'http://example.com/'
1180
- @request.perform
1181
- @request.send(:setup_raw_request)
1182
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to be_nil
1183
- end
1184
-
1185
- it "should send Authorization header when redirecting to a relative path" do
1186
- @redirect['location'] = '/v3'
1187
- @request.perform
1188
- @request.send(:setup_raw_request)
1189
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization)
1190
- end
1191
-
1192
- it "should send Authorization header when redirecting to the same host" do
1193
- @redirect['location'] = 'http://api.foo.com/v2'
1194
- @request.perform
1195
- @request.send(:setup_raw_request)
1196
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization)
1197
- end
1198
-
1199
- it "should send Authorization header when redirecting to a different port on the same host" do
1200
- @redirect['location'] = 'http://api.foo.com:3000/v3'
1201
- @request.perform
1202
- @request.send(:setup_raw_request)
1203
- expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization)
1204
- end
1205
- end
1206
- end
1207
-
1208
- context "with POST http method" do
1209
- it "should raise argument error if query is not a hash" do
1210
- expect {
1211
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', format: :xml, query: 'astring').perform
1212
- }.to raise_error(ArgumentError)
1213
- end
1214
- end
1215
-
1216
- describe "argument validation" do
1217
- it "should raise argument error if basic_auth and digest_auth are both present" do
1218
- expect {
1219
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: {}, digest_auth: {}).perform
1220
- }.to raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
1221
- end
1222
-
1223
- it "should raise argument error if basic_auth is not a hash" do
1224
- expect {
1225
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: %w(foo bar)).perform
1226
- }.to raise_error(ArgumentError, ":basic_auth must be a hash")
1227
- end
1228
-
1229
- it "should raise argument error if digest_auth is not a hash" do
1230
- expect {
1231
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', digest_auth: %w(foo bar)).perform
1232
- }.to raise_error(ArgumentError, ":digest_auth must be a hash")
1233
- end
1234
-
1235
- it "should raise argument error if headers is not a hash" do
1236
- expect {
1237
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', headers: %w(foo bar)).perform
1238
- }.to raise_error(ArgumentError, ":headers must be a hash")
1239
- end
1240
-
1241
- it "should raise argument error if options method is not http accepted method" do
1242
- expect {
1243
- HTTParty::Request.new('SuperPost', 'http://api.foo.com/v1').perform
1244
- }.to raise_error(ArgumentError, "only get, post, patch, put, delete, head, and options methods are supported")
1245
- end
1246
-
1247
- it "should raise argument error if http method is post and query is not hash" do
1248
- expect {
1249
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', query: "message: hello").perform
1250
- }.to raise_error(ArgumentError, ":query must be hash if using HTTP Post")
1251
- end
1252
-
1253
- it "should raise RedirectionTooDeep error if limit is negative" do
1254
- expect {
1255
- HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', limit: -1).perform
1256
- }.to raise_error(HTTParty::RedirectionTooDeep, 'HTTP redirects too deep')
1257
- end
1258
- end
1259
-
1260
- context 'with Accept-Encoding header' do
1261
- it 'should disable content decoding if present' do
1262
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', headers:{'Accept-Encoding' => 'custom'})
1263
- request.send(:setup_raw_request)
1264
- expect(request.instance_variable_get(:@raw_request).decode_content).to eq(false)
1265
- end
1266
-
1267
- it 'should disable content decoding if present and lowercase' do
1268
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', headers:{'accept-encoding' => 'custom'})
1269
- request.send(:setup_raw_request)
1270
- expect(request.instance_variable_get(:@raw_request).decode_content).to eq(false)
1271
- end
1272
-
1273
- it 'should disable content decoding if present' do
1274
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1')
1275
- request.send(:setup_raw_request)
1276
- expect(request.instance_variable_get(:@raw_request).decode_content).to eq(true)
1277
- end
1278
- end
1279
- end