httsoiree 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +7 -0
- data/Gemfile +14 -0
- data/Guardfile +16 -0
- data/History +303 -0
- data/MIT-LICENSE +20 -0
- data/README.md +77 -0
- data/Rakefile +12 -0
- data/bin/httparty +117 -0
- data/cucumber.yml +1 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +28 -0
- data/examples/crack.rb +19 -0
- data/examples/custom_parsers.rb +67 -0
- data/examples/delicious.rb +37 -0
- data/examples/google.rb +16 -0
- data/examples/headers_and_user_agents.rb +6 -0
- data/examples/logging.rb +38 -0
- data/examples/nokogiri_html_parser.rb +22 -0
- data/examples/rubyurl.rb +14 -0
- data/examples/stackexchange.rb +24 -0
- data/examples/tripit_sign_in.rb +33 -0
- data/examples/twitter.rb +31 -0
- data/examples/whoismyrep.rb +10 -0
- data/features/basic_authentication.feature +20 -0
- data/features/command_line.feature +7 -0
- data/features/deals_with_http_error_codes.feature +26 -0
- data/features/digest_authentication.feature +20 -0
- data/features/handles_compressed_responses.feature +27 -0
- data/features/handles_multiple_formats.feature +57 -0
- data/features/steps/env.rb +22 -0
- data/features/steps/httparty_response_steps.rb +52 -0
- data/features/steps/httparty_steps.rb +43 -0
- data/features/steps/mongrel_helper.rb +94 -0
- data/features/steps/remote_service_steps.rb +74 -0
- data/features/supports_read_timeout_option.feature +13 -0
- data/features/supports_redirection.feature +22 -0
- data/features/supports_timeout_option.feature +13 -0
- data/httparty.gemspec +25 -0
- data/lib/httparty/connection_adapter.rb +188 -0
- data/lib/httparty/cookie_hash.rb +22 -0
- data/lib/httparty/core_extensions.rb +32 -0
- data/lib/httparty/exceptions.rb +29 -0
- data/lib/httparty/hash_conversions.rb +51 -0
- data/lib/httparty/logger/apache_logger.rb +22 -0
- data/lib/httparty/logger/curl_logger.rb +48 -0
- data/lib/httparty/logger/logger.rb +18 -0
- data/lib/httparty/module_inheritable_attributes.rb +56 -0
- data/lib/httparty/net_digest_auth.rb +84 -0
- data/lib/httparty/parser.rb +141 -0
- data/lib/httparty/request.rb +339 -0
- data/lib/httparty/response/headers.rb +31 -0
- data/lib/httparty/response.rb +72 -0
- data/lib/httparty/version.rb +3 -0
- data/lib/httparty.rb +618 -0
- data/lib/httsoiree.rb +3 -0
- data/script/release +42 -0
- data/spec/fixtures/delicious.xml +23 -0
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +3 -0
- data/spec/fixtures/ssl/generate.sh +29 -0
- data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
- data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
- data/spec/fixtures/ssl/generated/ca.crt +16 -0
- data/spec/fixtures/ssl/generated/ca.key +15 -0
- data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
- data/spec/fixtures/ssl/generated/server.crt +13 -0
- data/spec/fixtures/ssl/generated/server.key +15 -0
- data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
- data/spec/fixtures/twitter.csv +2 -0
- data/spec/fixtures/twitter.json +1 -0
- data/spec/fixtures/twitter.xml +403 -0
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
- data/spec/httparty/connection_adapter_spec.rb +370 -0
- data/spec/httparty/cookie_hash_spec.rb +83 -0
- data/spec/httparty/exception_spec.rb +23 -0
- data/spec/httparty/logger/apache_logger_spec.rb +41 -0
- data/spec/httparty/logger/curl_logger_spec.rb +18 -0
- data/spec/httparty/logger/logger_spec.rb +22 -0
- data/spec/httparty/net_digest_auth_spec.rb +152 -0
- data/spec/httparty/parser_spec.rb +165 -0
- data/spec/httparty/request_spec.rb +774 -0
- data/spec/httparty/response_spec.rb +221 -0
- data/spec/httparty/ssl_spec.rb +74 -0
- data/spec/httparty_spec.rb +783 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/ssl_test_helper.rb +47 -0
- data/spec/support/ssl_test_server.rb +80 -0
- data/spec/support/stub_response.rb +43 -0
- data/website/css/common.css +47 -0
- data/website/index.html +73 -0
- metadata +215 -0
@@ -0,0 +1,774 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe HTTParty::Request do
|
4
|
+
before do
|
5
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "::NON_RAILS_QUERY_STRING_NORMALIZER" do
|
9
|
+
let(:normalizer) { HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER }
|
10
|
+
|
11
|
+
it "doesn't modify strings" do
|
12
|
+
query_string = normalizer["foo=bar&foo=baz"]
|
13
|
+
URI.unescape(query_string).should == "foo=bar&foo=baz"
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when the query is an array" do
|
17
|
+
|
18
|
+
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"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "URI encodes array values" do
|
24
|
+
query_string = normalizer[{people: ["Otis Redding", "Bob Marley", "Tim & Jon"], page: 1, xyzzy: 3}]
|
25
|
+
query_string.should == "page=1&people=Otis%20Redding&people=Bob%20Marley&people=Tim%20%26%20Jon&xyzzy=3"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when the query is a hash" do
|
30
|
+
it "correctly handles nil values" do
|
31
|
+
query_string = normalizer[{page: 1, per_page: nil}]
|
32
|
+
query_string.should == "page=1&per_page"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "initialization" do
|
38
|
+
it "sets parser to HTTParty::Parser" do
|
39
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
|
40
|
+
request.parser.should == HTTParty::Parser
|
41
|
+
end
|
42
|
+
|
43
|
+
it "sets parser to the optional parser" do
|
44
|
+
my_parser = lambda {}
|
45
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', parser: my_parser)
|
46
|
+
request.parser.should == my_parser
|
47
|
+
end
|
48
|
+
|
49
|
+
it "sets connection_adapter to HTTPParty::ConnectionAdapter" do
|
50
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
|
51
|
+
request.connection_adapter.should == HTTParty::ConnectionAdapter
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sets connection_adapter to the optional connection_adapter" do
|
55
|
+
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
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#format" do
|
62
|
+
context "request yet to be made" do
|
63
|
+
it "returns format option" do
|
64
|
+
request = HTTParty::Request.new 'get', '/', format: :xml
|
65
|
+
request.format.should == :xml
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns nil format" do
|
69
|
+
request = HTTParty::Request.new 'get', '/'
|
70
|
+
request.format.should be_nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "request has been made" do
|
75
|
+
it "returns format option" do
|
76
|
+
request = HTTParty::Request.new 'get', '/', format: :xml
|
77
|
+
request.last_response = stub
|
78
|
+
request.format.should == :xml
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns the content-type from the last response when the option is not set" do
|
82
|
+
request = HTTParty::Request.new 'get', '/'
|
83
|
+
response = stub
|
84
|
+
response.should_receive(:[]).with('content-type').and_return('text/json')
|
85
|
+
request.last_response = response
|
86
|
+
request.format.should == :json
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
context "options" do
|
93
|
+
it "should use basic auth when configured" do
|
94
|
+
@request.options[:basic_auth] = {username: 'foobar', password: 'secret'}
|
95
|
+
@request.send(:setup_raw_request)
|
96
|
+
@request.instance_variable_get(:@raw_request)['authorization'].should_not be_nil
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should use digest auth when configured" do
|
100
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v1",
|
101
|
+
www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
|
102
|
+
|
103
|
+
@request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
|
104
|
+
@request.send(:setup_raw_request)
|
105
|
+
|
106
|
+
raw_request = @request.instance_variable_get(:@raw_request)
|
107
|
+
raw_request.instance_variable_get(:@header)['Authorization'].should_not be_nil
|
108
|
+
end
|
109
|
+
|
110
|
+
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)
|
112
|
+
FakeWeb.register_uri(:post, "http://api.foo.com/v1", {})
|
113
|
+
|
114
|
+
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'}
|
118
|
+
@post_request.send(:setup_raw_request)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should use body_stream when configured' do
|
122
|
+
stream = StringIO.new('foo')
|
123
|
+
request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', body_stream: stream)
|
124
|
+
request.send(:setup_raw_request)
|
125
|
+
request.instance_variable_get(:@raw_request).body_stream.should == stream
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#uri" do
|
130
|
+
context "query strings" do
|
131
|
+
it "does not add an empty query string when default_params are blank" do
|
132
|
+
@request.options[:default_params] = {}
|
133
|
+
@request.uri.query.should be_nil
|
134
|
+
end
|
135
|
+
|
136
|
+
it "respects the query string normalization proc" do
|
137
|
+
empty_proc = lambda {|qs| ""}
|
138
|
+
@request.options[:query_string_normalizer] = empty_proc
|
139
|
+
@request.options[:query] = {foo: :bar}
|
140
|
+
URI.unescape(@request.uri.query).should == ""
|
141
|
+
end
|
142
|
+
|
143
|
+
it "does not append an ampersand when queries are embedded in paths" do
|
144
|
+
@request.path = "/path?a=1"
|
145
|
+
@request.options[:query] = {}
|
146
|
+
@request.uri.query.should == "a=1"
|
147
|
+
end
|
148
|
+
|
149
|
+
it "does not duplicate query string parameters when uri is called twice" do
|
150
|
+
@request.options[:query] = {foo: :bar}
|
151
|
+
@request.uri
|
152
|
+
@request.uri.query.should == "foo=bar"
|
153
|
+
end
|
154
|
+
|
155
|
+
context "when representing an array" do
|
156
|
+
it "returns a Rails style query string" do
|
157
|
+
@request.options[:query] = {foo: %w(bar baz)}
|
158
|
+
URI.unescape(@request.uri.query).should == "foo[]=bar&foo[]=baz"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#setup_raw_request" do
|
166
|
+
context "when query_string_normalizer is set" do
|
167
|
+
it "sets the body to the return value of the proc" do
|
168
|
+
@request.options[:query_string_normalizer] = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
|
169
|
+
@request.options[:body] = {page: 1, foo: %w(bar baz)}
|
170
|
+
@request.send(:setup_raw_request)
|
171
|
+
body = @request.instance_variable_get(:@raw_request).body
|
172
|
+
URI.unescape(body).should == "foo=bar&foo=baz&page=1"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe 'http' do
|
178
|
+
it "should get a connection from the connection_adapter" do
|
179
|
+
http = Net::HTTP.new('google.com')
|
180
|
+
adapter = mock('adapter')
|
181
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443', connection_adapter: adapter)
|
182
|
+
adapter.should_receive(:call).with(request.uri, request.options).and_return(http)
|
183
|
+
request.send(:http).should be http
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe '#format_from_mimetype' do
|
188
|
+
it 'should handle text/xml' do
|
189
|
+
["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
|
190
|
+
@request.send(:format_from_mimetype, ct).should == :xml
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'should handle application/xml' do
|
195
|
+
["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
|
196
|
+
@request.send(:format_from_mimetype, ct).should == :xml
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should handle text/json' do
|
201
|
+
["text/json", "text/json; charset=iso8859-1"].each do |ct|
|
202
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should handle application/json' do
|
207
|
+
["application/json", "application/json; charset=iso8859-1"].each do |ct|
|
208
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should handle text/csv' do
|
213
|
+
["text/csv", "text/csv; charset=iso8859-1"].each do |ct|
|
214
|
+
@request.send(:format_from_mimetype, ct).should == :csv
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should handle application/csv' do
|
219
|
+
["application/csv", "application/csv; charset=iso8859-1"].each do |ct|
|
220
|
+
@request.send(:format_from_mimetype, ct).should == :csv
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should handle text/comma-separated-values' do
|
225
|
+
["text/comma-separated-values", "text/comma-separated-values; charset=iso8859-1"].each do |ct|
|
226
|
+
@request.send(:format_from_mimetype, ct).should == :csv
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should handle text/javascript' do
|
231
|
+
["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
|
232
|
+
@request.send(:format_from_mimetype, ct).should == :plain
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should handle application/javascript' do
|
237
|
+
["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
|
238
|
+
@request.send(:format_from_mimetype, ct).should == :plain
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
it "returns nil for an unrecognized mimetype" do
|
243
|
+
@request.send(:format_from_mimetype, "application/atom+xml").should be_nil
|
244
|
+
end
|
245
|
+
|
246
|
+
it "returns nil when using a default parser" do
|
247
|
+
@request.options[:parser] = lambda {}
|
248
|
+
@request.send(:format_from_mimetype, "text/json").should be_nil
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe 'parsing responses' do
|
253
|
+
it 'should handle xml automatically' do
|
254
|
+
xml = %q[<books><book><id>1234</id><name>Foo Bar!</name></book></books>]
|
255
|
+
@request.options[:format] = :xml
|
256
|
+
@request.send(:parse_response, xml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'should handle csv automatically' do
|
260
|
+
csv=[%q["id","Name"],%q["1234","Foo Bar!"]].join("\n")
|
261
|
+
@request.options[:format] = :csv
|
262
|
+
@request.send(:parse_response, csv).should == [["id","Name"],["1234","Foo Bar!"]]
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'should handle json automatically' do
|
266
|
+
json = %q[{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}]
|
267
|
+
@request.options[:format] = :json
|
268
|
+
@request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should include any HTTP headers in the returned response" do
|
272
|
+
@request.options[:format] = :html
|
273
|
+
response = stub_response "Content"
|
274
|
+
response.initialize_http_header("key" => "value")
|
275
|
+
|
276
|
+
@request.perform.headers.should == { "key" => ["value"] }
|
277
|
+
end
|
278
|
+
|
279
|
+
if "".respond_to?(:encoding)
|
280
|
+
|
281
|
+
it "should process charset in content type properly" do
|
282
|
+
response = stub_response "Content"
|
283
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-8")
|
284
|
+
resp = @request.perform
|
285
|
+
resp.body.encoding.should == Encoding.find("UTF-8")
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should process charset in content type properly if it has a different case" do
|
289
|
+
response = stub_response "Content"
|
290
|
+
response.initialize_http_header("Content-Type" => "text/plain;CHARSET = utf-8")
|
291
|
+
resp = @request.perform
|
292
|
+
resp.body.encoding.should == Encoding.find("UTF-8")
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should process quoted charset in content type properly" do
|
296
|
+
response = stub_response "Content"
|
297
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = \"utf-8\"")
|
298
|
+
resp = @request.perform
|
299
|
+
resp.body.encoding.should == Encoding.find("UTF-8")
|
300
|
+
end
|
301
|
+
|
302
|
+
it "should process utf-16 charset with little endian bom correctly" do
|
303
|
+
@request.options[:assume_utf16_is_big_endian] = true
|
304
|
+
|
305
|
+
response = stub_response "\xFF\xFEC\x00o\x00n\x00t\x00e\x00n\x00t\x00"
|
306
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
|
307
|
+
resp = @request.perform
|
308
|
+
resp.body.encoding.should == Encoding.find("UTF-16LE")
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should process utf-16 charset with big endian bom correctly" do
|
312
|
+
@request.options[:assume_utf16_is_big_endian] = false
|
313
|
+
|
314
|
+
response = stub_response "\xFE\xFF\x00C\x00o\x00n\x00t\x00e\x00n\x00t"
|
315
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
|
316
|
+
resp = @request.perform
|
317
|
+
resp.body.encoding.should == Encoding.find("UTF-16BE")
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should assume utf-16 little endian if options has been chosen" do
|
321
|
+
@request.options[:assume_utf16_is_big_endian] = false
|
322
|
+
|
323
|
+
response = stub_response "C\x00o\x00n\x00t\x00e\x00n\x00t\x00"
|
324
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
|
325
|
+
resp = @request.perform
|
326
|
+
resp.body.encoding.should == Encoding.find("UTF-16LE")
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
it "should perform no encoding if the charset is not available" do
|
331
|
+
|
332
|
+
response = stub_response "Content"
|
333
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-lols")
|
334
|
+
resp = @request.perform
|
335
|
+
resp.body.should == "Content"
|
336
|
+
resp.body.encoding.should == "Content".encoding
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should perform no encoding if the content type is specified but no charset is specified" do
|
340
|
+
|
341
|
+
response = stub_response "Content"
|
342
|
+
response.initialize_http_header("Content-Type" => "text/plain")
|
343
|
+
resp = @request.perform
|
344
|
+
resp.body.should == "Content"
|
345
|
+
resp.body.encoding.should == "Content".encoding
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
describe 'with non-200 responses' do
|
351
|
+
context "3xx responses" do
|
352
|
+
it 'returns a valid object for 304 not modified' do
|
353
|
+
stub_response '', 304
|
354
|
+
resp = @request.perform
|
355
|
+
resp.code.should == 304
|
356
|
+
resp.body.should == ''
|
357
|
+
resp.should be_nil
|
358
|
+
end
|
359
|
+
|
360
|
+
it "redirects if a 300 contains a location header" do
|
361
|
+
redirect = stub_response '', 300
|
362
|
+
redirect['location'] = 'http://foo.com/foo'
|
363
|
+
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
364
|
+
@http.stub!(:request).and_return(redirect, ok)
|
365
|
+
response = @request.perform
|
366
|
+
response.request.base_uri.to_s.should == "http://foo.com"
|
367
|
+
response.request.path.to_s.should == "http://foo.com/foo"
|
368
|
+
response.request.uri.request_uri.should == "/foo"
|
369
|
+
response.request.uri.to_s.should == "http://foo.com/foo"
|
370
|
+
response.should == {"hash" => {"foo" => "bar"}}
|
371
|
+
end
|
372
|
+
|
373
|
+
it "calls block given to perform with each redirect" do
|
374
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
|
375
|
+
FakeWeb.register_uri(:get, "http://test.com/redirect", status: [300, "REDIRECT"], location: "http://api.foo.com/v2")
|
376
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v2", body: "<hash><foo>bar</foo></hash>")
|
377
|
+
body = ""
|
378
|
+
response = @request.perform { |chunk| body += chunk }
|
379
|
+
body.length.should == 27
|
380
|
+
end
|
381
|
+
|
382
|
+
it "redirects if a 300 contains a relative location header" do
|
383
|
+
redirect = stub_response '', 300
|
384
|
+
redirect['location'] = '/foo/bar'
|
385
|
+
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
386
|
+
@http.stub!(:request).and_return(redirect, ok)
|
387
|
+
response = @request.perform
|
388
|
+
response.request.base_uri.to_s.should == "http://api.foo.com"
|
389
|
+
response.request.path.to_s.should == "/foo/bar"
|
390
|
+
response.request.uri.request_uri.should == "/foo/bar"
|
391
|
+
response.request.uri.to_s.should == "http://api.foo.com/foo/bar"
|
392
|
+
response.should == {"hash" => {"foo" => "bar"}}
|
393
|
+
end
|
394
|
+
|
395
|
+
it "handles multiple redirects and relative location headers on different hosts" do
|
396
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
|
397
|
+
FakeWeb.register_uri(:get, "http://test.com/redirect", status: [300, "REDIRECT"], location: "http://api.foo.com/v2")
|
398
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v2", status: [300, "REDIRECT"], location: "/v3")
|
399
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v3", body: "<hash><foo>bar</foo></hash>")
|
400
|
+
response = @request.perform
|
401
|
+
response.request.base_uri.to_s.should == "http://api.foo.com"
|
402
|
+
response.request.path.to_s.should == "/v3"
|
403
|
+
response.request.uri.request_uri.should == "/v3"
|
404
|
+
response.request.uri.to_s.should == "http://api.foo.com/v3"
|
405
|
+
response.should == {"hash" => {"foo" => "bar"}}
|
406
|
+
end
|
407
|
+
|
408
|
+
it "returns the HTTParty::Response when the 300 does not contain a location header" do
|
409
|
+
stub_response '', 300
|
410
|
+
HTTParty::Response.should === @request.perform
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'should return a valid object for 4xx response' do
|
415
|
+
stub_response '<foo><bar>yes</bar></foo>', 401
|
416
|
+
resp = @request.perform
|
417
|
+
resp.code.should == 401
|
418
|
+
resp.body.should == "<foo><bar>yes</bar></foo>"
|
419
|
+
resp['foo']['bar'].should == "yes"
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'should return a valid object for 5xx response' do
|
423
|
+
stub_response '<foo><bar>error</bar></foo>', 500
|
424
|
+
resp = @request.perform
|
425
|
+
resp.code.should == 500
|
426
|
+
resp.body.should == "<foo><bar>error</bar></foo>"
|
427
|
+
resp['foo']['bar'].should == "error"
|
428
|
+
end
|
429
|
+
|
430
|
+
it "parses response lazily so codes can be checked prior" do
|
431
|
+
stub_response 'not xml', 500
|
432
|
+
@request.options[:format] = :xml
|
433
|
+
lambda {
|
434
|
+
response = @request.perform
|
435
|
+
response.code.should == 500
|
436
|
+
response.body.should == 'not xml'
|
437
|
+
}.should_not raise_error
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
it "should not attempt to parse empty responses" do
|
443
|
+
[204, 304].each do |code|
|
444
|
+
stub_response "", code
|
445
|
+
|
446
|
+
@request.options[:format] = :xml
|
447
|
+
@request.perform.should be_nil
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
it "should not fail for missing mime type" do
|
452
|
+
stub_response "Content for you"
|
453
|
+
@request.options[:format] = :html
|
454
|
+
@request.perform.should == 'Content for you'
|
455
|
+
end
|
456
|
+
|
457
|
+
describe "a request that 302 redirects" do
|
458
|
+
before(:each) do
|
459
|
+
@redirect = stub_response("", 302)
|
460
|
+
@redirect['location'] = '/foo'
|
461
|
+
|
462
|
+
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
463
|
+
end
|
464
|
+
|
465
|
+
describe "once" do
|
466
|
+
before(:each) do
|
467
|
+
@http.stub!(:request).and_return(@redirect, @ok)
|
468
|
+
end
|
469
|
+
|
470
|
+
it "should be handled by GET transparently" do
|
471
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
472
|
+
end
|
473
|
+
|
474
|
+
it "should be handled by POST transparently" do
|
475
|
+
@request.http_method = Net::HTTP::Post
|
476
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
477
|
+
end
|
478
|
+
|
479
|
+
it "should be handled by DELETE transparently" do
|
480
|
+
@request.http_method = Net::HTTP::Delete
|
481
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
482
|
+
end
|
483
|
+
|
484
|
+
it "should be handled by MOVE transparently" do
|
485
|
+
@request.http_method = Net::HTTP::Move
|
486
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
487
|
+
end
|
488
|
+
|
489
|
+
it "should be handled by COPY transparently" do
|
490
|
+
@request.http_method = Net::HTTP::Copy
|
491
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
492
|
+
end
|
493
|
+
|
494
|
+
it "should be handled by PATCH transparently" do
|
495
|
+
@request.http_method = Net::HTTP::Patch
|
496
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
497
|
+
end
|
498
|
+
|
499
|
+
it "should be handled by PUT transparently" do
|
500
|
+
@request.http_method = Net::HTTP::Put
|
501
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
502
|
+
end
|
503
|
+
|
504
|
+
it "should be handled by HEAD transparently" do
|
505
|
+
@request.http_method = Net::HTTP::Head
|
506
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
507
|
+
end
|
508
|
+
|
509
|
+
it "should be handled by OPTIONS transparently" do
|
510
|
+
@request.http_method = Net::HTTP::Options
|
511
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
512
|
+
end
|
513
|
+
|
514
|
+
it "should keep track of cookies between redirects" do
|
515
|
+
@redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
|
516
|
+
@request.perform
|
517
|
+
@request.options[:headers]['Cookie'].should match(/foo=bar/)
|
518
|
+
@request.options[:headers]['Cookie'].should match(/name=value/)
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'should update cookies with rediects' do
|
522
|
+
@request.options[:headers] = {'Cookie'=> 'foo=bar;'}
|
523
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
524
|
+
@request.perform
|
525
|
+
@request.options[:headers]['Cookie'].should match(/foo=tar/)
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'should keep cookies between rediects' do
|
529
|
+
@request.options[:headers] = {'Cookie'=> 'keep=me'}
|
530
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
531
|
+
@request.perform
|
532
|
+
@request.options[:headers]['Cookie'].should match(/keep=me/)
|
533
|
+
end
|
534
|
+
|
535
|
+
it "should handle multiple Set-Cookie headers between redirects" do
|
536
|
+
@redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
|
537
|
+
@redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
|
538
|
+
@request.perform
|
539
|
+
@request.options[:headers]['Cookie'].should match(/foo=bar/)
|
540
|
+
@request.options[:headers]['Cookie'].should match(/name=value/)
|
541
|
+
@request.options[:headers]['Cookie'].should match(/one=1/)
|
542
|
+
@request.options[:headers]['Cookie'].should match(/two=2/)
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'should make resulting request a get request if it not already' do
|
546
|
+
@request.http_method = Net::HTTP::Delete
|
547
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
548
|
+
@request.http_method.should == Net::HTTP::Get
|
549
|
+
end
|
550
|
+
|
551
|
+
it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
|
552
|
+
@request.options[:maintain_method_across_redirects] = true
|
553
|
+
@request.http_method = Net::HTTP::Delete
|
554
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
555
|
+
@request.http_method.should == Net::HTTP::Delete
|
556
|
+
end
|
557
|
+
|
558
|
+
it 'should log the redirection' do
|
559
|
+
logger_double = double
|
560
|
+
logger_double.should_receive(:info).twice
|
561
|
+
@request.options[:logger] = logger_double
|
562
|
+
@request.perform
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
describe "infinitely" do
|
567
|
+
before(:each) do
|
568
|
+
@http.stub!(:request).and_return(@redirect)
|
569
|
+
end
|
570
|
+
|
571
|
+
it "should raise an exception" do
|
572
|
+
lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
describe "a request that 303 redirects" do
|
578
|
+
before(:each) do
|
579
|
+
@redirect = stub_response("", 303)
|
580
|
+
@redirect['location'] = '/foo'
|
581
|
+
|
582
|
+
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
583
|
+
end
|
584
|
+
|
585
|
+
describe "once" do
|
586
|
+
before(:each) do
|
587
|
+
@http.stub!(:request).and_return(@redirect, @ok)
|
588
|
+
end
|
589
|
+
|
590
|
+
it "should be handled by GET transparently" do
|
591
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
592
|
+
end
|
593
|
+
|
594
|
+
it "should be handled by POST transparently" do
|
595
|
+
@request.http_method = Net::HTTP::Post
|
596
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
597
|
+
end
|
598
|
+
|
599
|
+
it "should be handled by DELETE transparently" do
|
600
|
+
@request.http_method = Net::HTTP::Delete
|
601
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
602
|
+
end
|
603
|
+
|
604
|
+
it "should be handled by MOVE transparently" do
|
605
|
+
@request.http_method = Net::HTTP::Move
|
606
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
607
|
+
end
|
608
|
+
|
609
|
+
it "should be handled by COPY transparently" do
|
610
|
+
@request.http_method = Net::HTTP::Copy
|
611
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
612
|
+
end
|
613
|
+
|
614
|
+
it "should be handled by PATCH transparently" do
|
615
|
+
@request.http_method = Net::HTTP::Patch
|
616
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
617
|
+
end
|
618
|
+
|
619
|
+
it "should be handled by PUT transparently" do
|
620
|
+
@request.http_method = Net::HTTP::Put
|
621
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
622
|
+
end
|
623
|
+
|
624
|
+
it "should be handled by HEAD transparently" do
|
625
|
+
@request.http_method = Net::HTTP::Head
|
626
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
627
|
+
end
|
628
|
+
|
629
|
+
it "should be handled by OPTIONS transparently" do
|
630
|
+
@request.http_method = Net::HTTP::Options
|
631
|
+
@request.perform.should == {"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
|
+
@request.options[:headers]['Cookie'].should match(/foo=bar/)
|
638
|
+
@request.options[:headers]['Cookie'].should match(/name=value/)
|
639
|
+
end
|
640
|
+
|
641
|
+
it 'should update cookies with rediects' do
|
642
|
+
@request.options[:headers] = {'Cookie'=> 'foo=bar;'}
|
643
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
644
|
+
@request.perform
|
645
|
+
@request.options[:headers]['Cookie'].should match(/foo=tar/)
|
646
|
+
end
|
647
|
+
|
648
|
+
it 'should keep cookies between rediects' do
|
649
|
+
@request.options[:headers] = {'Cookie'=> 'keep=me'}
|
650
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
651
|
+
@request.perform
|
652
|
+
@request.options[:headers]['Cookie'].should 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
|
+
@request.options[:headers]['Cookie'].should match(/foo=bar/)
|
660
|
+
@request.options[:headers]['Cookie'].should match(/name=value/)
|
661
|
+
@request.options[:headers]['Cookie'].should match(/one=1/)
|
662
|
+
@request.options[:headers]['Cookie'].should 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
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
668
|
+
@request.http_method.should == Net::HTTP::Get
|
669
|
+
end
|
670
|
+
|
671
|
+
it 'should make resulting request a get request if options[:maintain_method_across_redirects] is false' do
|
672
|
+
@request.options[:maintain_method_across_redirects] = false
|
673
|
+
@request.http_method = Net::HTTP::Delete
|
674
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
675
|
+
@request.http_method.should == Net::HTTP::Get
|
676
|
+
end
|
677
|
+
|
678
|
+
it 'should make resulting request a get request if options[:maintain_method_across_redirects] is true but options[:resend_on_redirect] is false' do
|
679
|
+
@request.options[:maintain_method_across_redirects] = true
|
680
|
+
@request.options[:resend_on_redirect] = false
|
681
|
+
@request.http_method = Net::HTTP::Delete
|
682
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
683
|
+
@request.http_method.should == Net::HTTP::Get
|
684
|
+
end
|
685
|
+
|
686
|
+
it 'should not make resulting request a get request if options[:maintain_method_across_redirects] and options[:resend_on_redirect] is true' do
|
687
|
+
@request.options[:maintain_method_across_redirects] = true
|
688
|
+
@request.options[:resend_on_redirect] = true
|
689
|
+
@request.http_method = Net::HTTP::Delete
|
690
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
691
|
+
@request.http_method.should == Net::HTTP::Delete
|
692
|
+
end
|
693
|
+
|
694
|
+
it 'should log the redirection' do
|
695
|
+
logger_double = double
|
696
|
+
logger_double.should_receive(:info).twice
|
697
|
+
@request.options[:logger] = logger_double
|
698
|
+
@request.perform
|
699
|
+
end
|
700
|
+
end
|
701
|
+
|
702
|
+
describe "infinitely" do
|
703
|
+
before(:each) do
|
704
|
+
@http.stub!(:request).and_return(@redirect)
|
705
|
+
end
|
706
|
+
|
707
|
+
it "should raise an exception" do
|
708
|
+
lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
|
709
|
+
end
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
describe "#handle_deflation" do
|
714
|
+
context "context-encoding" do
|
715
|
+
before do
|
716
|
+
@request.options[:format] = :html
|
717
|
+
@last_response = mock()
|
718
|
+
@last_response.stub!(:body).and_return('')
|
719
|
+
end
|
720
|
+
|
721
|
+
it "should inflate the gzipped body with content-encoding: gzip" do
|
722
|
+
@last_response.stub!(:[]).with("content-encoding").and_return("gzip")
|
723
|
+
@request.stub!(:last_response).and_return(@last_response)
|
724
|
+
Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
|
725
|
+
@request.last_response.should_receive(:delete).with('content-encoding')
|
726
|
+
@request.send(:handle_deflation)
|
727
|
+
end
|
728
|
+
|
729
|
+
it "should inflate the gzipped body with content-encoding: x-gzip" do
|
730
|
+
@last_response.stub!(:[]).with("content-encoding").and_return("x-gzip")
|
731
|
+
@request.stub!(:last_response).and_return(@last_response)
|
732
|
+
Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
|
733
|
+
@request.last_response.should_receive(:delete).with('content-encoding')
|
734
|
+
@request.send(:handle_deflation)
|
735
|
+
end
|
736
|
+
|
737
|
+
it "should inflate the deflated body" do
|
738
|
+
@last_response.stub!(:[]).with("content-encoding").and_return("deflate")
|
739
|
+
@request.stub!(:last_response).and_return(@last_response)
|
740
|
+
Zlib::Inflate.should_receive(:inflate).and_return('')
|
741
|
+
@request.last_response.should_receive(:delete).with('content-encoding')
|
742
|
+
@request.send(:handle_deflation)
|
743
|
+
end
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
context "with POST http method" do
|
748
|
+
it "should raise argument error if query is not a hash" do
|
749
|
+
lambda {
|
750
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', format: :xml, query: 'astring').perform
|
751
|
+
}.should raise_error(ArgumentError)
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
describe "argument validation" do
|
756
|
+
it "should raise argument error if basic_auth and digest_auth are both present" do
|
757
|
+
lambda {
|
758
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: {}, digest_auth: {}).perform
|
759
|
+
}.should raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
|
760
|
+
end
|
761
|
+
|
762
|
+
it "should raise argument error if basic_auth is not a hash" do
|
763
|
+
lambda {
|
764
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: ["foo", "bar"]).perform
|
765
|
+
}.should raise_error(ArgumentError, ":basic_auth must be a hash")
|
766
|
+
end
|
767
|
+
|
768
|
+
it "should raise argument error if digest_auth is not a hash" do
|
769
|
+
lambda {
|
770
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', digest_auth: ["foo", "bar"]).perform
|
771
|
+
}.should raise_error(ArgumentError, ":digest_auth must be a hash")
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|