httparty 0.10.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of httparty might be problematic. Click here for more details.

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