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,896 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
-
3
- RSpec.describe HTTParty do
4
- before(:each) do
5
- @klass = Class.new
6
- @klass.instance_eval { include HTTParty }
7
- end
8
-
9
- describe "pem" do
10
- it 'should set the pem content' do
11
- @klass.pem 'PEM-CONTENT'
12
- expect(@klass.default_options[:pem]).to eq('PEM-CONTENT')
13
- end
14
-
15
- it "should set the password to nil if it's not provided" do
16
- @klass.pem 'PEM-CONTENT'
17
- expect(@klass.default_options[:pem_password]).to be_nil
18
- end
19
-
20
- it 'should set the password' do
21
- @klass.pem 'PEM-CONTENT', 'PASSWORD'
22
- expect(@klass.default_options[:pem_password]).to eq('PASSWORD')
23
- end
24
- end
25
-
26
- describe "pkcs12" do
27
- it 'should set the p12 content' do
28
- @klass.pkcs12 'P12-CONTENT', 'PASSWORD'
29
- expect(@klass.default_options[:p12]).to eq('P12-CONTENT')
30
- end
31
-
32
- it 'should set the password' do
33
- @klass.pkcs12 'P12-CONTENT', 'PASSWORD'
34
- expect(@klass.default_options[:p12_password]).to eq('PASSWORD')
35
- end
36
- end
37
-
38
- describe 'ssl_version' do
39
- it 'should set the ssl_version content' do
40
- @klass.ssl_version :SSLv3
41
- expect(@klass.default_options[:ssl_version]).to eq(:SSLv3)
42
- end
43
- end
44
-
45
- describe 'ciphers' do
46
- it 'should set the ciphers content' do
47
- expect(@klass.default_options[:ciphers]).to be_nil
48
- @klass.ciphers 'RC4-SHA'
49
- expect(@klass.default_options[:ciphers]).to eq('RC4-SHA')
50
- end
51
- end
52
-
53
- describe 'http_proxy' do
54
- it 'should set the address' do
55
- @klass.http_proxy 'proxy.foo.com', 80
56
- options = @klass.default_options
57
- expect(options[:http_proxyaddr]).to eq('proxy.foo.com')
58
- expect(options[:http_proxyport]).to eq(80)
59
- end
60
-
61
- it 'should set the proxy user and pass when they are provided' do
62
- @klass.http_proxy 'proxy.foo.com', 80, 'user', 'pass'
63
- options = @klass.default_options
64
- expect(options[:http_proxyuser]).to eq('user')
65
- expect(options[:http_proxypass]).to eq('pass')
66
- end
67
- end
68
-
69
- describe "base uri" do
70
- before(:each) do
71
- @klass.base_uri('api.foo.com/v1')
72
- end
73
-
74
- it "should have reader" do
75
- expect(@klass.base_uri).to eq('http://api.foo.com/v1')
76
- end
77
-
78
- it 'should have writer' do
79
- @klass.base_uri('http://api.foobar.com')
80
- expect(@klass.base_uri).to eq('http://api.foobar.com')
81
- end
82
-
83
- it 'should not modify the parameter during assignment' do
84
- uri = 'http://api.foobar.com'
85
- @klass.base_uri(uri)
86
- expect(uri).to eq('http://api.foobar.com')
87
- end
88
- end
89
-
90
- describe ".disable_rails_query_string_format" do
91
- it "sets the query string normalizer to HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER" do
92
- @klass.disable_rails_query_string_format
93
- expect(@klass.default_options[:query_string_normalizer]).to eq(HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER)
94
- end
95
- end
96
-
97
- describe ".normalize_base_uri" do
98
- it "should add http if not present for non ssl requests" do
99
- uri = HTTParty.normalize_base_uri('api.foobar.com')
100
- expect(uri).to eq('http://api.foobar.com')
101
- end
102
-
103
- it "should add https if not present for ssl requests" do
104
- uri = HTTParty.normalize_base_uri('api.foo.com/v1:443')
105
- expect(uri).to eq('https://api.foo.com/v1:443')
106
- end
107
-
108
- it "should not remove https for ssl requests" do
109
- uri = HTTParty.normalize_base_uri('https://api.foo.com/v1:443')
110
- expect(uri).to eq('https://api.foo.com/v1:443')
111
- end
112
-
113
- it 'should not modify the parameter' do
114
- uri = 'http://api.foobar.com'
115
- HTTParty.normalize_base_uri(uri)
116
- expect(uri).to eq('http://api.foobar.com')
117
- end
118
-
119
- it "should not treat uri's with a port of 4430 as ssl" do
120
- uri = HTTParty.normalize_base_uri('http://api.foo.com:4430/v1')
121
- expect(uri).to eq('http://api.foo.com:4430/v1')
122
- end
123
- end
124
-
125
- describe "headers" do
126
- def expect_headers(header = {})
127
- expect(HTTParty::Request).to receive(:new) \
128
- .with(anything, anything, hash_including({ headers: header })) \
129
- .and_return(double("mock response", perform: nil))
130
- end
131
-
132
- it "does not modify default_options when no arguments are passed" do
133
- @klass.headers
134
- expect(@klass.default_options[:headers]).to eq(nil)
135
- end
136
-
137
- it "should default to empty hash" do
138
- expect(@klass.headers).to eq({})
139
- end
140
-
141
- it "should be able to be updated" do
142
- init_headers = {foo: 'bar', baz: 'spax'}
143
- @klass.headers init_headers
144
- expect(@klass.headers).to eq(init_headers)
145
- end
146
-
147
- it "uses the class headers when sending a request" do
148
- expect_headers(foo: 'bar')
149
- @klass.headers(foo: 'bar')
150
- @klass.get('')
151
- end
152
-
153
- it "merges class headers with request headers" do
154
- expect_headers(baz: 'spax', foo: 'bar')
155
- @klass.headers(foo: 'bar')
156
- @klass.get('', headers: {baz: 'spax'})
157
- end
158
-
159
- it 'overrides class headers with request headers' do
160
- expect_headers(baz: 'spax', foo: 'baz')
161
- @klass.headers(foo: 'bar')
162
- @klass.get('', headers: {baz: 'spax', foo: 'baz'})
163
- end
164
-
165
- context "with cookies" do
166
- it 'utilizes the class-level cookies' do
167
- expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
168
- @klass.headers(foo: 'bar')
169
- @klass.cookies(type: 'snickerdoodle')
170
- @klass.get('')
171
- end
172
-
173
- it 'adds cookies to the headers' do
174
- expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
175
- @klass.headers(foo: 'bar')
176
- @klass.get('', cookies: {type: 'snickerdoodle'})
177
- end
178
-
179
- it 'doesnt modify default headers' do
180
- expect(@klass.headers).to eq({})
181
- expect_headers('cookie' => 'type=snickerdoodle')
182
- @klass.get('', cookies: {type: 'snickerdoodle'})
183
- expect(@klass.headers).to eq({})
184
- end
185
-
186
- it 'adds optional cookies to the optional headers' do
187
- expect_headers(baz: 'spax', 'cookie' => 'type=snickerdoodle')
188
- @klass.get('', cookies: {type: 'snickerdoodle'}, headers: {baz: 'spax'})
189
- end
190
- end
191
-
192
- context 'when posting file' do
193
- let(:boundary) { '------------------------c772861a5109d5ef' }
194
- let(:headers) do
195
- { 'Content-Type'=>"multipart/form-data; boundary=#{boundary}" }
196
- end
197
-
198
- before do
199
- expect(HTTParty::Request::MultipartBoundary).to receive(:generate).and_return(boundary)
200
- end
201
-
202
- it 'changes content-type headers to multipart/form-data' do
203
- stub_request(:post, "http://example.com/").with(headers: headers)
204
-
205
- @klass.post('http://example.com', body: { file: File.open('spec/fixtures/tiny.gif')})
206
- end
207
- end
208
- end
209
-
210
- describe "cookies" do
211
- def expect_cookie_header(s)
212
- expect(HTTParty::Request).to receive(:new) \
213
- .with(anything, anything, hash_including({ headers: { "cookie" => s } })) \
214
- .and_return(double("mock response", perform: nil))
215
- end
216
-
217
- it "should not be in the headers by default" do
218
- allow(HTTParty::Request).to receive(:new).and_return(double(nil, perform: nil))
219
- @klass.get("")
220
- expect(@klass.headers.keys).not_to include("cookie")
221
- end
222
-
223
- it "should raise an ArgumentError if passed a non-Hash" do
224
- expect do
225
- @klass.cookies("nonsense")
226
- end.to raise_error(ArgumentError)
227
- end
228
-
229
- it "should allow a cookie to be specified with a one-off request" do
230
- expect_cookie_header "type=snickerdoodle"
231
- @klass.get("", cookies: { type: "snickerdoodle" })
232
- end
233
-
234
- describe "when a cookie is set at the class level" do
235
- before(:each) do
236
- @klass.cookies({ type: "snickerdoodle" })
237
- end
238
-
239
- it "should include that cookie in the request" do
240
- expect_cookie_header "type=snickerdoodle"
241
- @klass.get("")
242
- end
243
-
244
- it "should pass the proper cookies when requested multiple times" do
245
- 2.times do
246
- expect_cookie_header "type=snickerdoodle"
247
- @klass.get("")
248
- end
249
- end
250
-
251
- it "should allow the class defaults to be overridden" do
252
- expect_cookie_header "type=chocolate_chip"
253
-
254
- @klass.get("", cookies: { type: "chocolate_chip" })
255
- end
256
- end
257
-
258
- describe "in a class with multiple methods that use different cookies" do
259
- before(:each) do
260
- @klass.instance_eval do
261
- def first_method
262
- get("first_method", cookies: { first_method_cookie: "foo" })
263
- end
264
-
265
- def second_method
266
- get("second_method", cookies: { second_method_cookie: "foo" })
267
- end
268
- end
269
- end
270
-
271
- it "should not allow cookies used in one method to carry over into other methods" do
272
- expect_cookie_header "first_method_cookie=foo"
273
- @klass.first_method
274
-
275
- expect_cookie_header "second_method_cookie=foo"
276
- @klass.second_method
277
- end
278
- end
279
- end
280
-
281
- describe "default params" do
282
- it "should default to empty hash" do
283
- expect(@klass.default_params).to eq({})
284
- end
285
-
286
- it "should be able to be updated" do
287
- new_defaults = {foo: 'bar', baz: 'spax'}
288
- @klass.default_params new_defaults
289
- expect(@klass.default_params).to eq(new_defaults)
290
- end
291
- end
292
-
293
- describe "default timeout" do
294
- it "should default to nil" do
295
- expect(@klass.default_options[:timeout]).to eq(nil)
296
- end
297
-
298
- it "should support updating" do
299
- @klass.default_timeout 10
300
- expect(@klass.default_options[:timeout]).to eq(10)
301
- end
302
-
303
- it "should support floats" do
304
- @klass.default_timeout 0.5
305
- expect(@klass.default_options[:timeout]).to eq(0.5)
306
- end
307
- end
308
-
309
- describe "debug_output" do
310
- it "stores the given stream as a default_option" do
311
- @klass.debug_output $stdout
312
- expect(@klass.default_options[:debug_output]).to eq($stdout)
313
- end
314
-
315
- it "stores the $stderr stream by default" do
316
- @klass.debug_output
317
- expect(@klass.default_options[:debug_output]).to eq($stderr)
318
- end
319
- end
320
-
321
- describe "basic http authentication" do
322
- it "should work" do
323
- @klass.basic_auth 'foobar', 'secret'
324
- expect(@klass.default_options[:basic_auth]).to eq({username: 'foobar', password: 'secret'})
325
- end
326
- end
327
-
328
- describe "digest http authentication" do
329
- it "should work" do
330
- @klass.digest_auth 'foobar', 'secret'
331
- expect(@klass.default_options[:digest_auth]).to eq({username: 'foobar', password: 'secret'})
332
- end
333
- end
334
-
335
- describe "parser" do
336
- class CustomParser
337
- def self.parse(body)
338
- {sexy: true}
339
- end
340
- end
341
-
342
- let(:parser) do
343
- proc { |data, format| CustomParser.parse(data) }
344
- end
345
-
346
- it "should set parser options" do
347
- @klass.parser parser
348
- expect(@klass.default_options[:parser]).to eq(parser)
349
- end
350
-
351
- it "should be able parse response with custom parser" do
352
- @klass.parser parser
353
- stub_request(:get, 'http://twitter.com/statuses/public_timeline.xml')
354
- .to_return(body: 'tweets')
355
- custom_parsed_response = @klass.get('http://twitter.com/statuses/public_timeline.xml')
356
- expect(custom_parsed_response[:sexy]).to eq(true)
357
- end
358
-
359
- it "raises UnsupportedFormat when the parser cannot handle the format" do
360
- @klass.format :json
361
-
362
- parser_class = Class.new(HTTParty::Parser)
363
- parser_class::SupportedFormats = {}
364
-
365
- expect do
366
- @klass.parser parser_class
367
- end.to raise_error(HTTParty::UnsupportedFormat)
368
- end
369
-
370
- it 'does not validate format whe custom parser is a proc' do
371
- expect do
372
- @klass.format :json
373
- @klass.parser lambda {|body, format|}
374
- end.to_not raise_error
375
- end
376
- end
377
-
378
- describe "uri_adapter" do
379
-
380
- require 'forwardable'
381
- class CustomURIAdaptor
382
- extend Forwardable
383
- def_delegators :@uri, :userinfo, :relative?, :query, :query=, :scheme, :path, :host, :port
384
-
385
- def initialize uri
386
- @uri = uri
387
- end
388
-
389
- def self.parse uri
390
- new URI.parse uri
391
- end
392
- end
393
-
394
- let(:uri_adapter) { CustomURIAdaptor }
395
-
396
- it "should set the uri_adapter" do
397
- @klass.uri_adapter uri_adapter
398
- expect(@klass.default_options[:uri_adapter]).to be uri_adapter
399
- end
400
-
401
- it "should raise an ArgumentError if uri_adapter doesn't implement parse method" do
402
- expect do
403
- @klass.uri_adapter double()
404
- end.to raise_error(ArgumentError)
405
- end
406
-
407
-
408
- it "should process a request with a uri instance parsed from the uri_adapter" do
409
- uri = 'http://foo.com/bar'
410
- stub_request(:get, uri).to_return(body: 'stuff')
411
- @klass.uri_adapter uri_adapter
412
- expect(@klass.get(uri).parsed_response).to eq('stuff')
413
- end
414
-
415
- end
416
-
417
- describe "connection_adapter" do
418
- let(:uri) { 'http://google.com/api.json' }
419
- let(:connection_adapter) { double('CustomConnectionAdapter') }
420
-
421
- it "should set the connection_adapter" do
422
- @klass.connection_adapter connection_adapter
423
- expect(@klass.default_options[:connection_adapter]).to be connection_adapter
424
- end
425
-
426
- it "should set the connection_adapter_options when provided" do
427
- options = {foo: :bar}
428
- @klass.connection_adapter connection_adapter, options
429
- expect(@klass.default_options[:connection_adapter_options]).to be options
430
- end
431
-
432
- it "should not set the connection_adapter_options when not provided" do
433
- @klass.connection_adapter connection_adapter
434
- expect(@klass.default_options[:connection_adapter_options]).to be_nil
435
- end
436
-
437
- it "should process a request with a connection from the adapter" do
438
- connection_adapter_options = {foo: :bar}
439
- expect(connection_adapter).to receive(:call) { |u, o|
440
- expect(o[:connection_adapter_options]).to eq(connection_adapter_options)
441
- HTTParty::ConnectionAdapter.call(u, o)
442
- }.with(URI.parse(uri), kind_of(Hash))
443
- stub_request(:get, uri).to_return(body: 'stuff')
444
- @klass.connection_adapter connection_adapter, connection_adapter_options
445
- expect(@klass.get(uri).parsed_response).to eq('stuff')
446
- end
447
- end
448
-
449
- describe "format" do
450
- it "should allow xml" do
451
- @klass.format :xml
452
- expect(@klass.default_options[:format]).to eq(:xml)
453
- end
454
-
455
- it "should allow csv" do
456
- @klass.format :csv
457
- expect(@klass.default_options[:format]).to eq(:csv)
458
- end
459
-
460
- it "should allow json" do
461
- @klass.format :json
462
- expect(@klass.default_options[:format]).to eq(:json)
463
- end
464
-
465
- it "should allow plain" do
466
- @klass.format :plain
467
- expect(@klass.default_options[:format]).to eq(:plain)
468
- end
469
-
470
- it 'should not allow funky format' do
471
- expect do
472
- @klass.format :foobar
473
- end.to raise_error(HTTParty::UnsupportedFormat)
474
- end
475
-
476
- it 'should only print each format once with an exception' do
477
- expect do
478
- @klass.format :foobar
479
- end.to raise_error(HTTParty::UnsupportedFormat, "':foobar' Must be one of: csv, html, json, plain, xml")
480
- end
481
-
482
- it 'sets the default parser' do
483
- expect(@klass.default_options[:parser]).to be_nil
484
- @klass.format :json
485
- expect(@klass.default_options[:parser]).to eq(HTTParty::Parser)
486
- end
487
-
488
- it 'does not reset parser to the default parser' do
489
- my_parser = lambda {}
490
- @klass.parser my_parser
491
- @klass.format :json
492
- expect(@klass.parser).to eq(my_parser)
493
- end
494
- end
495
-
496
- describe "#no_follow" do
497
- it "sets no_follow to false by default" do
498
- @klass.no_follow
499
- expect(@klass.default_options[:no_follow]).to be_falsey
500
- end
501
-
502
- it "sets the no_follow option to true" do
503
- @klass.no_follow true
504
- expect(@klass.default_options[:no_follow]).to be_truthy
505
- end
506
- end
507
-
508
- describe "#maintain_method_across_redirects" do
509
- it "sets maintain_method_across_redirects to true by default" do
510
- @klass.maintain_method_across_redirects
511
- expect(@klass.default_options[:maintain_method_across_redirects]).to be_truthy
512
- end
513
-
514
- it "sets the maintain_method_across_redirects option to false" do
515
- @klass.maintain_method_across_redirects false
516
- expect(@klass.default_options[:maintain_method_across_redirects]).to be_falsey
517
- end
518
- end
519
-
520
- describe "#resend_on_redirect" do
521
- it "sets resend_on_redirect to true by default" do
522
- @klass.resend_on_redirect
523
- expect(@klass.default_options[:resend_on_redirect]).to be_truthy
524
- end
525
-
526
- it "sets resend_on_redirect option to false" do
527
- @klass.resend_on_redirect false
528
- expect(@klass.default_options[:resend_on_redirect]).to be_falsey
529
- end
530
- end
531
-
532
- describe ".follow_redirects" do
533
- it "sets follow redirects to true by default" do
534
- @klass.follow_redirects
535
- expect(@klass.default_options[:follow_redirects]).to be_truthy
536
- end
537
-
538
- it "sets the follow_redirects option to false" do
539
- @klass.follow_redirects false
540
- expect(@klass.default_options[:follow_redirects]).to be_falsey
541
- end
542
- end
543
-
544
- describe ".query_string_normalizer" do
545
- it "sets the query_string_normalizer option" do
546
- normalizer = proc {}
547
- @klass.query_string_normalizer normalizer
548
- expect(@klass.default_options[:query_string_normalizer]).to eq(normalizer)
549
- end
550
- end
551
-
552
- describe ".raise_on" do
553
- context 'when parameters is an array' do
554
- it 'sets raise_on option' do
555
- @klass.raise_on [500, 404]
556
- expect(@klass.default_options[:raise_on]).to contain_exactly(404, 500)
557
- end
558
- end
559
-
560
- context 'when parameters is a fixnum' do
561
- it 'sets raise_on option' do
562
- @klass.raise_on 404
563
- expect(@klass.default_options[:raise_on]).to contain_exactly(404)
564
- end
565
- end
566
- end
567
-
568
- describe "with explicit override of automatic redirect handling" do
569
- before do
570
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml, no_follow: true)
571
- @redirect = stub_response 'first redirect', 302
572
- @redirect['location'] = 'http://foo.com/bar'
573
- allow(HTTParty::Request).to receive_messages(new: @request)
574
- end
575
-
576
- it "should fail with redirected GET" do
577
- expect do
578
- @error = @klass.get('/foo', no_follow: true)
579
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
580
- end
581
-
582
- it "should fail with redirected POST" do
583
- expect do
584
- @klass.post('/foo', no_follow: true)
585
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
586
- end
587
-
588
- it "should fail with redirected PATCH" do
589
- expect do
590
- @klass.patch('/foo', no_follow: true)
591
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
592
- end
593
-
594
- it "should fail with redirected DELETE" do
595
- expect do
596
- @klass.delete('/foo', no_follow: true)
597
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
598
- end
599
-
600
- it "should fail with redirected MOVE" do
601
- expect do
602
- @klass.move('/foo', no_follow: true)
603
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
604
- end
605
-
606
- it "should fail with redirected COPY" do
607
- expect do
608
- @klass.copy('/foo', no_follow: true)
609
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
610
- end
611
-
612
- it "should fail with redirected PUT" do
613
- expect do
614
- @klass.put('/foo', no_follow: true)
615
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
616
- end
617
-
618
- it "should fail with redirected HEAD" do
619
- expect do
620
- @klass.head('/foo', no_follow: true)
621
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
622
- end
623
-
624
- it "should fail with redirected OPTIONS" do
625
- expect do
626
- @klass.options('/foo', no_follow: true)
627
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
628
- end
629
-
630
- it "should fail with redirected MKCOL" do
631
- expect do
632
- @klass.mkcol('/foo', no_follow: true)
633
- end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')}
634
- end
635
- end
636
-
637
- describe "head requests should follow redirects requesting HEAD only" do
638
- before do
639
- allow(HTTParty::Request).to receive(:new).
640
- and_return(double("mock response", perform: nil))
641
- end
642
-
643
- it "should remain HEAD request across redirects, unless specified otherwise" do
644
- expect(@klass).to receive(:ensure_method_maintained_across_redirects).with({})
645
- @klass.head('/foo')
646
- end
647
-
648
- end
649
-
650
- describe "#ensure_method_maintained_across_redirects" do
651
- it "should set maintain_method_across_redirects option if unspecified" do
652
- options = {}
653
- @klass.send(:ensure_method_maintained_across_redirects, options)
654
- expect(options[:maintain_method_across_redirects]).to be_truthy
655
- end
656
-
657
- it "should not set maintain_method_across_redirects option if value is present" do
658
- options = { maintain_method_across_redirects: false }
659
- @klass.send(:ensure_method_maintained_across_redirects, options)
660
- expect(options[:maintain_method_across_redirects]).to be_falsey
661
- end
662
- end
663
-
664
- describe "with multiple class definitions" do
665
- before(:each) do
666
- @klass.instance_eval do
667
- base_uri "http://first.com"
668
- default_params one: 1
669
- end
670
-
671
- @additional_klass = Class.new
672
- @additional_klass.instance_eval do
673
- include HTTParty
674
- base_uri "http://second.com"
675
- default_params two: 2
676
- end
677
- end
678
-
679
- it "should not run over each others options" do
680
- expect(@klass.default_options).to eq({ base_uri: 'http://first.com', default_params: { one: 1 } })
681
- expect(@additional_klass.default_options).to eq({ base_uri: 'http://second.com', default_params: { two: 2 } })
682
- end
683
- end
684
-
685
- describe "two child classes inheriting from one parent" do
686
- before(:each) do
687
- @parent = Class.new do
688
- include HTTParty
689
- def self.name
690
- "Parent"
691
- end
692
- end
693
-
694
- @child1 = Class.new(@parent)
695
- @child2 = Class.new(@parent)
696
- end
697
-
698
- it "does not modify each others inherited attributes" do
699
- @child1.default_params joe: "alive"
700
- @child2.default_params joe: "dead"
701
-
702
- expect(@child1.default_options).to eq({ default_params: {joe: "alive"} })
703
- expect(@child2.default_options).to eq({ default_params: {joe: "dead"} })
704
-
705
- expect(@parent.default_options).to eq({ })
706
- end
707
-
708
- it "inherits default_options from the superclass" do
709
- @parent.basic_auth 'user', 'password'
710
- expect(@child1.default_options).to eq({basic_auth: {username: 'user', password: 'password'}})
711
- @child1.basic_auth 'u', 'p' # modifying child1 has no effect on child2
712
- expect(@child2.default_options).to eq({basic_auth: {username: 'user', password: 'password'}})
713
- end
714
-
715
- it "doesn't modify the parent's default options" do
716
- @parent.basic_auth 'user', 'password'
717
-
718
- @child1.basic_auth 'u', 'p'
719
- expect(@child1.default_options).to eq({basic_auth: {username: 'u', password: 'p'}})
720
-
721
- @child1.basic_auth 'email', 'token'
722
- expect(@child1.default_options).to eq({basic_auth: {username: 'email', password: 'token'}})
723
-
724
- expect(@parent.default_options).to eq({basic_auth: {username: 'user', password: 'password'}})
725
- end
726
-
727
- it "doesn't modify hashes in the parent's default options" do
728
- @parent.headers 'Accept' => 'application/json'
729
- @child1.headers 'Accept' => 'application/xml'
730
-
731
- expect(@parent.default_options[:headers]).to eq({'Accept' => 'application/json'})
732
- expect(@child1.default_options[:headers]).to eq({'Accept' => 'application/xml'})
733
- end
734
-
735
- it "works with lambda values" do
736
- @child1.default_options[:imaginary_option] = lambda { "This is a new lambda "}
737
- expect(@child1.default_options[:imaginary_option]).to be_a Proc
738
- end
739
-
740
- it 'should dup the proc on the child class' do
741
- imaginary_option = lambda { 2 * 3.14 }
742
- @parent.default_options[:imaginary_option] = imaginary_option
743
- expect(@parent.default_options[:imaginary_option].call).to eq(imaginary_option.call)
744
- @child1.default_options[:imaginary_option]
745
- expect(@child1.default_options[:imaginary_option].call).to eq(imaginary_option.call)
746
- expect(@child1.default_options[:imaginary_option]).not_to be_equal imaginary_option
747
- end
748
-
749
- it "inherits default_cookies from the parent class" do
750
- @parent.cookies 'type' => 'chocolate_chip'
751
- expect(@child1.default_cookies).to eq({"type" => "chocolate_chip"})
752
- @child1.cookies 'type' => 'snickerdoodle'
753
- expect(@child1.default_cookies).to eq({"type" => "snickerdoodle"})
754
- expect(@child2.default_cookies).to eq({"type" => "chocolate_chip"})
755
- end
756
-
757
- it "doesn't modify the parent's default cookies" do
758
- @parent.cookies 'type' => 'chocolate_chip'
759
-
760
- @child1.cookies 'type' => 'snickerdoodle'
761
- expect(@child1.default_cookies).to eq({"type" => "snickerdoodle"})
762
-
763
- expect(@parent.default_cookies).to eq({"type" => "chocolate_chip"})
764
- end
765
- end
766
-
767
- describe "grand parent with inherited callback" do
768
- before do
769
- @grand_parent = Class.new do
770
- def self.inherited(subclass)
771
- subclass.instance_variable_set(:@grand_parent, true)
772
- end
773
- end
774
- @parent = Class.new(@grand_parent) do
775
- include HTTParty
776
- end
777
- end
778
- it "continues running the #inherited on the parent" do
779
- child = Class.new(@parent)
780
- expect(child.instance_variable_get(:@grand_parent)).to be_truthy
781
- end
782
- end
783
-
784
- describe "#get" do
785
- it "should be able to get html" do
786
- stub_http_response_with('google.html')
787
- expect(HTTParty.get('http://www.google.com').parsed_response).to eq(file_fixture('google.html'))
788
- end
789
-
790
- it "should be able to get chunked html" do
791
- chunks = %w(Chunk1 Chunk2 Chunk3 Chunk4)
792
- stub_chunked_http_response_with(chunks)
793
-
794
- expect(
795
- HTTParty.get('http://www.google.com') do |fragment|
796
- expect(chunks).to include(fragment)
797
- end.parsed_response
798
- ).to eq(chunks.join)
799
- end
800
-
801
- it "should return an empty body if stream_body option is turned on" do
802
- chunks = %w(Chunk1 Chunk2 Chunk3 Chunk4)
803
- options = {stream_body: true, format: 'html'}
804
- stub_chunked_http_response_with(chunks, options)
805
-
806
- expect(
807
- HTTParty.get('http://www.google.com', options) do |fragment|
808
- expect(chunks).to include(fragment)
809
- end.parsed_response
810
- ).to eq(nil)
811
- end
812
-
813
- it "should be able parse response type json automatically" do
814
- stub_http_response_with('twitter.json')
815
- tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
816
- expect(tweets.size).to eq(20)
817
- expect(tweets.first['user']).to eq({
818
- "name" => "Pyk",
819
- "url" => nil,
820
- "id" => "7694602",
821
- "description" => nil,
822
- "protected" => false,
823
- "screen_name" => "Pyk",
824
- "followers_count" => 1,
825
- "location" => "Opera Plaza, California",
826
- "profile_image_url" => "http://static.twitter.com/images/default_profile_normal.png"
827
- })
828
- end
829
-
830
- it "should be able parse response type xml automatically" do
831
- stub_http_response_with('twitter.xml')
832
- tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.xml')
833
- expect(tweets['statuses'].size).to eq(20)
834
- expect(tweets['statuses'].first['user']).to eq({
835
- "name" => "Magic 8 Bot",
836
- "url" => nil,
837
- "id" => "17656026",
838
- "description" => "ask me a question",
839
- "protected" => "false",
840
- "screen_name" => "magic8bot",
841
- "followers_count" => "90",
842
- "profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",
843
- "location" => nil
844
- })
845
- end
846
-
847
- it "should be able parse response type csv automatically" do
848
- stub_http_response_with('twitter.csv')
849
- profile = HTTParty.get('http://twitter.com/statuses/profile.csv')
850
- expect(profile.size).to eq(2)
851
- expect(profile[0]).to eq(%w(name url id description protected screen_name followers_count profile_image_url location))
852
- expect(profile[1]).to eq(["Magic 8 Bot", nil, "17656026", "ask me a question", "false", "magic8bot", "90", "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg", nil])
853
- end
854
-
855
- it "should not get undefined method add_node for nil class for the following xml" do
856
- stub_http_response_with('undefined_method_add_node_for_nil.xml')
857
- result = HTTParty.get('http://foobar.com')
858
- expect(result.parsed_response).to eq({"Entities" => {"href" => "https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results" => "0", "total" => "0", "page_size" => "25", "page" => "1"}})
859
- end
860
-
861
- it "should parse empty response fine" do
862
- stub_http_response_with('empty.xml')
863
- result = HTTParty.get('http://foobar.com')
864
- expect(result).to be_nil
865
- end
866
-
867
- it "should accept http URIs" do
868
- stub_http_response_with('google.html')
869
- expect do
870
- HTTParty.get('http://google.com')
871
- end.not_to raise_error
872
- end
873
-
874
- it "should accept https URIs" do
875
- stub_http_response_with('google.html')
876
- expect do
877
- HTTParty.get('https://google.com')
878
- end.not_to raise_error
879
- end
880
-
881
- it "should accept webcal URIs" do
882
- uri = 'http://google.com/'
883
- stub_request(:get, uri).to_return(body: 'stuff')
884
- uri = 'webcal://google.com/'
885
- expect do
886
- HTTParty.get(uri)
887
- end.not_to raise_error
888
- end
889
-
890
- it "should raise an InvalidURIError on URIs that can't be parsed at all" do
891
- expect do
892
- HTTParty.get("It's the one that says 'Bad URI'")
893
- end.to raise_error(URI::InvalidURIError)
894
- end
895
- end
896
- end