httparty 0.8.0 → 0.9.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.

@@ -0,0 +1,31 @@
1
+ module HTTParty
2
+ class Response #:nodoc:
3
+ class Headers
4
+ include ::Net::HTTPHeader
5
+
6
+ def initialize(header = {})
7
+ @header = header
8
+ end
9
+
10
+ def ==(other)
11
+ @header == other
12
+ end
13
+
14
+ def inspect
15
+ @header.inspect
16
+ end
17
+
18
+ def method_missing(name, *args, &block)
19
+ if @header.respond_to?(name)
20
+ @header.send(name, *args, &block)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def respond_to?(method)
27
+ super || @header.respond_to?(method)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  module HTTParty
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -0,0 +1,206 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe HTTParty::ConnectionAdapter do
4
+
5
+ describe "initialization" do
6
+ let(:uri) { URI 'http://www.google.com' }
7
+ it "takes a URI as input" do
8
+ HTTParty::ConnectionAdapter.new(uri)
9
+ end
10
+
11
+ it "raises an ArgumentError if the uri is nil" do
12
+ expect { HTTParty::ConnectionAdapter.new(nil) }.to raise_error ArgumentError
13
+ end
14
+
15
+ it "raises an ArgumentError if the uri is a String" do
16
+ expect { HTTParty::ConnectionAdapter.new('http://www.google.com') }.to raise_error ArgumentError
17
+ end
18
+
19
+ it "sets the uri" do
20
+ adapter = HTTParty::ConnectionAdapter.new(uri)
21
+ adapter.uri.should be uri
22
+ end
23
+
24
+ it "also accepts an optional options hash" do
25
+ HTTParty::ConnectionAdapter.new(uri, {})
26
+ end
27
+
28
+ it "sets the options" do
29
+ options = {:foo => :bar}
30
+ adapter = HTTParty::ConnectionAdapter.new(uri, options)
31
+ adapter.options.should be options
32
+ end
33
+ end
34
+
35
+ describe ".call" do
36
+ it "generates an HTTParty::ConnectionAdapter instance with the given uri and options" do
37
+ HTTParty::ConnectionAdapter.should_receive(:new).with(@uri, @options).and_return(stub(:connection => nil))
38
+ HTTParty::ConnectionAdapter.call(@uri, @options)
39
+ end
40
+
41
+ it "calls #connection on the connection adapter" do
42
+ adapter = mock('Adapter')
43
+ connection = mock('Connection')
44
+ adapter.should_receive(:connection).and_return(connection)
45
+ HTTParty::ConnectionAdapter.stub(:new => adapter)
46
+ HTTParty::ConnectionAdapter.call(@uri, @options).should be connection
47
+ end
48
+ end
49
+
50
+ describe '#connection' do
51
+ let(:uri) { URI 'http://www.google.com' }
52
+ let(:options) { Hash.new }
53
+ let(:adapter) { HTTParty::ConnectionAdapter.new(uri, options) }
54
+
55
+ describe "the resulting connection" do
56
+ subject { adapter.connection }
57
+ it { should be_an_instance_of Net::HTTP }
58
+
59
+ context "using port 80" do
60
+ let(:uri) { URI 'http://foobar.com' }
61
+ it { should_not use_ssl }
62
+ end
63
+
64
+ context "when dealing with ssl" do
65
+ let(:uri) { URI 'https://foobar.com' }
66
+
67
+ context "using port 443 for ssl" do
68
+ let(:uri) { URI 'https://api.foo.com/v1:443' }
69
+ it { should use_ssl }
70
+ end
71
+
72
+ context "https scheme with default port" do
73
+ it { should use_ssl }
74
+ end
75
+
76
+ context "https scheme with non-standard port" do
77
+ let(:uri) { URI 'https://foobar.com:123456' }
78
+ it { should use_ssl }
79
+ end
80
+
81
+ context "when ssl version is set" do
82
+ let(:options) { {:ssl_version => :TLSv1} }
83
+
84
+ it "sets ssl version" do
85
+ subject.ssl_version.should == :TLSv1
86
+ end
87
+ end if RUBY_VERSION > '1.9'
88
+ end
89
+
90
+ context "when timeout is not set" do
91
+ it "doesn't set the timeout" do
92
+ http = mock("http", :null_object => true)
93
+ http.should_not_receive(:open_timeout=)
94
+ http.should_not_receive(:read_timeout=)
95
+ Net::HTTP.stub(:new => http)
96
+
97
+ adapter.connection
98
+ end
99
+ end
100
+
101
+ context "when setting timeout" do
102
+ context "to 5 seconds" do
103
+ let(:options) { {:timeout => 5} }
104
+
105
+ its(:open_timeout) { should == 5 }
106
+ its(:read_timeout) { should == 5 }
107
+ end
108
+
109
+ context "and timeout is a string" do
110
+ let(:options) { {:timeout => "five seconds"} }
111
+
112
+ it "doesn't set the timeout" do
113
+ http = mock("http", :null_object => true)
114
+ http.should_not_receive(:open_timeout=)
115
+ http.should_not_receive(:read_timeout=)
116
+ Net::HTTP.stub(:new => http)
117
+
118
+ adapter.connection
119
+ end
120
+ end
121
+ end
122
+
123
+ context "when debug_output" do
124
+ let(:http) { Net::HTTP.new(uri) }
125
+ before do
126
+ Net::HTTP.stub(:new => http)
127
+ end
128
+
129
+ context "is set to $stderr" do
130
+ let(:options) { {:debug_output => $stderr} }
131
+ it "has debug output set" do
132
+ http.should_receive(:set_debug_output).with($stderr)
133
+ adapter.connection
134
+ end
135
+ end
136
+
137
+ context "is not provided" do
138
+ it "does not set_debug_output" do
139
+ http.should_not_receive(:set_debug_output)
140
+ adapter.connection
141
+ end
142
+ end
143
+ end
144
+
145
+ context 'when providing proxy address and port' do
146
+ let(:options) { {:http_proxyaddr => '1.2.3.4', :http_proxyport => 8080} }
147
+
148
+ it { should be_a_proxy }
149
+ its(:proxy_address) { should == '1.2.3.4' }
150
+ its(:proxy_port) { should == 8080 }
151
+
152
+ context 'as well as proxy user and password' do
153
+ let(:options) do
154
+ {:http_proxyaddr => '1.2.3.4', :http_proxyport => 8080,
155
+ :http_proxyuser => 'user', :http_proxypass => 'pass'}
156
+ end
157
+ its(:proxy_user) { should == 'user' }
158
+ its(:proxy_pass) { should == 'pass' }
159
+ end
160
+ end
161
+
162
+ context "when providing PEM certificates" do
163
+ let(:pem) { :pem_contents }
164
+ let(:options) { {:pem => pem, :pem_password => "password"} }
165
+
166
+ context "when scheme is https" do
167
+ let(:uri) { URI 'https://google.com' }
168
+ let(:cert) { mock("OpenSSL::X509::Certificate") }
169
+ let(:key) { mock("OpenSSL::PKey::RSA") }
170
+
171
+ before do
172
+ OpenSSL::X509::Certificate.should_receive(:new).with(pem).and_return(cert)
173
+ OpenSSL::PKey::RSA.should_receive(:new).with(pem, "password").and_return(key)
174
+ end
175
+
176
+ it "uses the provided PEM certificate " do
177
+ subject.cert.should == cert
178
+ subject.key.should == key
179
+ end
180
+
181
+ it "will verify the certificate" do
182
+ subject.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
183
+ end
184
+ end
185
+
186
+ context "when scheme is not https" do
187
+ let(:uri) { URI 'http://google.com' }
188
+ let(:http) { Net::HTTP.new(uri) }
189
+
190
+ before do
191
+ Net::HTTP.stub(:new => http)
192
+ OpenSSL::X509::Certificate.should_not_receive(:new).with(pem)
193
+ OpenSSL::PKey::RSA.should_not_receive(:new).with(pem, "password")
194
+ http.should_not_receive(:cert=)
195
+ http.should_not_receive(:key=)
196
+ end
197
+
198
+ it "has no PEM certificate " do
199
+ subject.cert.should be_nil
200
+ subject.key.should be_nil
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -6,7 +6,6 @@ describe HTTParty::CookieHash do
6
6
  end
7
7
 
8
8
  describe "#add_cookies" do
9
-
10
9
  describe "with a hash" do
11
10
  it "should add new key/value pairs to the hash" do
12
11
  @cookie_hash.add_cookies(:foo => "bar")
@@ -29,7 +28,7 @@ describe HTTParty::CookieHash do
29
28
  @cookie_hash[:second].should == 'two'
30
29
  @cookie_hash[:third].should == nil
31
30
  end
32
-
31
+
33
32
  it "should overwrite any existing key" do
34
33
  @cookie_hash[:foo] = 'bar'
35
34
  @cookie_hash.add_cookies("foo=tar")
@@ -37,7 +36,7 @@ describe HTTParty::CookieHash do
37
36
  @cookie_hash[:foo].should eql("tar")
38
37
  end
39
38
  end
40
-
39
+
41
40
  describe 'with other class' do
42
41
  it "should error" do
43
42
  lambda {
@@ -61,7 +60,7 @@ describe HTTParty::CookieHash do
61
60
  @s.should match(/rofl=copter/)
62
61
  @s.should match(/^\w+=\w+; \w+=\w+$/)
63
62
  end
64
-
63
+
65
64
  it "should not include client side only cookies" do
66
65
  @cookie_hash.add_cookies(:path => "/")
67
66
  @s = @cookie_hash.to_cookie_string
@@ -14,10 +14,35 @@ describe Net::HTTPHeader::DigestAuthenticator do
14
14
  end
15
15
 
16
16
 
17
+ context "with an opaque value in the response header" do
18
+ before do
19
+ @digest = setup_digest({
20
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", opaque="solid"'
21
+ })
22
+ end
23
+
24
+ it "should set opaque" do
25
+ authorization_header.should include(%Q(opaque="solid"))
26
+ end
27
+ end
28
+
29
+ context "without an opaque valid in the response header" do
30
+ before do
31
+ @digest = setup_digest({
32
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
33
+ })
34
+ end
35
+
36
+ it "should not set opaque" do
37
+ authorization_header.should_not include(%Q(opaque=))
38
+ end
39
+ end
40
+
17
41
  context "with specified quality of protection (qop)" do
18
42
  before do
19
- @digest = setup_digest({'www-authenticate' =>
20
- 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'})
43
+ @digest = setup_digest({
44
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"',
45
+ })
21
46
  end
22
47
 
23
48
  it "should set prefix" do
@@ -41,13 +66,11 @@ describe Net::HTTPHeader::DigestAuthenticator do
41
66
  end
42
67
 
43
68
  it "should set nonce-count" do
44
- authorization_header.should include(%Q(nc="0"))
69
+ authorization_header.should include(%Q(nc="00000001"))
45
70
  end
46
71
 
47
72
  it "should set response" do
48
- request_digest =
49
- "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life)" +
50
- ":NONCE:0:md5(deadbeef):auth:md5(GET:/dir/index.html))"
73
+ request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
51
74
  authorization_header.should include(%Q(response="#{request_digest}"))
52
75
  end
53
76
  end
@@ -55,8 +78,9 @@ describe Net::HTTPHeader::DigestAuthenticator do
55
78
 
56
79
  context "with unspecified quality of protection (qop)" do
57
80
  before do
58
- @digest = setup_digest({'www-authenticate' =>
59
- 'Digest realm="myhost@testrealm.com", nonce="NONCE"'})
81
+ @digest = setup_digest({
82
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"',
83
+ })
60
84
  end
61
85
 
62
86
  it "should set prefix" do
@@ -84,9 +108,7 @@ describe Net::HTTPHeader::DigestAuthenticator do
84
108
  end
85
109
 
86
110
  it "should set response" do
87
- request_digest =
88
- "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life)" +
89
- ":NONCE:md5(GET:/dir/index.html))"
111
+ request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))"
90
112
  authorization_header.should include(%Q(response="#{request_digest}"))
91
113
  end
92
114
  end
@@ -87,6 +87,16 @@ describe HTTParty::Parser do
87
87
  @parser.stub(:body => nil)
88
88
  @parser.parse.should be_nil
89
89
  end
90
+
91
+ it "returns nil for a 'null' body" do
92
+ @parser.stub(:body => "null")
93
+ @parser.parse.should be_nil
94
+ end
95
+
96
+ it "returns nil for a body with spaces only" do
97
+ @parser.stub(:body => " ")
98
+ @parser.parse.should be_nil
99
+ end
90
100
  end
91
101
 
92
102
  describe "#supports_format?" do
@@ -135,6 +145,12 @@ describe HTTParty::Parser do
135
145
  end
136
146
 
137
147
  it "parses json with MultiJson" do
148
+ MultiJson.should_receive(:load).with('body')
149
+ subject.send(:json)
150
+ end
151
+
152
+ it "uses MultiJson.decode if MultiJson does not respond to adapter" do
153
+ MultiJson.should_receive(:respond_to?).with(:adapter).and_return(false)
138
154
  MultiJson.should_receive(:decode).with('body')
139
155
  subject.send(:json)
140
156
  end
@@ -45,6 +45,17 @@ describe HTTParty::Request do
45
45
  request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', :parser => my_parser)
46
46
  request.parser.should == my_parser
47
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
48
59
  end
49
60
 
50
61
  describe "#format" do
@@ -86,7 +97,7 @@ describe HTTParty::Request do
86
97
  end
87
98
 
88
99
  it "should use digest auth when configured" do
89
- FakeWeb.register_uri(:head, "http://api.foo.com/v1",
100
+ FakeWeb.register_uri(:get, "http://api.foo.com/v1",
90
101
  :www_authenticate => 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
91
102
 
92
103
  @request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
@@ -95,6 +106,17 @@ describe HTTParty::Request do
95
106
  raw_request = @request.instance_variable_get(:@raw_request)
96
107
  raw_request.instance_variable_get(:@header)['Authorization'].should_not be_nil
97
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
98
120
  end
99
121
 
100
122
  describe "#uri" do
@@ -134,106 +156,12 @@ describe HTTParty::Request do
134
156
  end
135
157
 
136
158
  describe 'http' do
137
- it "should use ssl for port 443" do
138
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443')
139
- request.send(:http).use_ssl?.should == true
140
- end
141
-
142
- it 'should not use ssl for port 80' do
143
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://foobar.com')
144
- request.send(:http).use_ssl?.should == false
145
- end
146
-
147
- it "uses ssl for https scheme with default port" do
148
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com')
149
- request.send(:http).use_ssl?.should == true
150
- end
151
-
152
- it "uses ssl for https scheme regardless of port" do
153
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com:123456')
154
- request.send(:http).use_ssl?.should == true
155
- end
156
-
157
- context "PEM certificates" do
158
- before do
159
- OpenSSL::X509::Certificate.stub(:new)
160
- OpenSSL::PKey::RSA.stub(:new)
161
- end
162
-
163
- context "when scheme is https" do
164
- before do
165
- @request.stub!(:uri).and_return(URI.parse("https://google.com"))
166
- pem = :pem_contents
167
- @cert = mock("OpenSSL::X509::Certificate")
168
- @key = mock("OpenSSL::PKey::RSA")
169
- OpenSSL::X509::Certificate.should_receive(:new).with(pem).and_return(@cert)
170
- OpenSSL::PKey::RSA.should_receive(:new).with(pem, "password").and_return(@key)
171
-
172
- @request.options[:pem] = pem
173
- @request.options[:pem_password] = "password"
174
- @pem_http = @request.send(:http)
175
- end
176
-
177
- it "should use a PEM certificate when provided" do
178
- @pem_http.cert.should == @cert
179
- @pem_http.key.should == @key
180
- end
181
-
182
- it "should verify the certificate when provided" do
183
- @pem_http = @request.send(:http)
184
- @pem_http.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
185
- end
186
- end
187
-
188
- context "when scheme is not https" do
189
- it "does not assign a PEM" do
190
- http = Net::HTTP.new('google.com')
191
- http.should_not_receive(:cert=)
192
- http.should_not_receive(:key=)
193
- Net::HTTP.stub(:new => http)
194
-
195
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
196
- request.options[:pem] = :pem_contents
197
- request.send(:http)
198
- end
199
- end
200
-
201
- context "debugging" do
202
- before do
203
- @http = Net::HTTP.new('google.com')
204
- Net::HTTP.stub(:new => @http)
205
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
206
- end
207
-
208
- it "calls #set_debug_output when the option is provided" do
209
- @request.options[:debug_output] = $stderr
210
- @http.should_receive(:set_debug_output).with($stderr)
211
- @request.send(:http)
212
- end
213
-
214
- it "does not set_debug_output when the option is not provided" do
215
- @http.should_not_receive(:set_debug_output)
216
- @request.send(:http)
217
- end
218
- end
219
- end
220
-
221
- context "when setting timeout" do
222
- it "does nothing if the timeout option is a string" do
223
- http = mock("http", :null_object => true)
224
- http.should_not_receive(:open_timeout=)
225
- http.should_not_receive(:read_timeout=)
226
- Net::HTTP.stub(:new => http)
227
-
228
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com', {:timeout => "five seconds"})
229
- request.send(:http)
230
- end
231
-
232
- it "sets the timeout to 5 seconds" do
233
- @request.options[:timeout] = 5
234
- @request.send(:http).open_timeout.should == 5
235
- @request.send(:http).read_timeout.should == 5
236
- end
159
+ it "should get a connection from the connection_adapter" do
160
+ 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
237
165
  end
238
166
  end
239
167
 
@@ -381,6 +309,16 @@ describe HTTParty::Request do
381
309
  resp.body.should == "<foo><bar>error</bar></foo>"
382
310
  resp['foo']['bar'].should == "error"
383
311
  end
312
+
313
+ it "parses response lazily so codes can be checked prior" do
314
+ stub_response 'not xml', 500
315
+ @request.options[:format] = :xml
316
+ lambda {
317
+ response = @request.perform
318
+ response.code.should == 500
319
+ response.body.should == 'not xml'
320
+ }.should_not raise_error
321
+ end
384
322
  end
385
323
  end
386
324
 
@@ -426,6 +364,11 @@ describe HTTParty::Request do
426
364
  @request.perform.should == {"hash" => {"foo" => "bar"}}
427
365
  end
428
366
 
367
+ it "should be handled by PATCH transparently" do
368
+ @request.http_method = Net::HTTP::Patch
369
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
370
+ end
371
+
429
372
  it "should be handled by PUT transparently" do
430
373
  @request.http_method = Net::HTTP::Put
431
374
  @request.perform.should == {"hash" => {"foo" => "bar"}}
@@ -487,6 +430,40 @@ describe HTTParty::Request do
487
430
  end
488
431
  end
489
432
 
433
+ describe "#handle_deflation" do
434
+ context "context-encoding" do
435
+ before do
436
+ @request.options[:format] = :html
437
+ @last_response = mock()
438
+ @last_response.stub!(:body).and_return('')
439
+ end
440
+
441
+ it "should inflate the gzipped body with content-encoding: gzip" do
442
+ @last_response.stub!(:[]).with("content-encoding").and_return("gzip")
443
+ @request.stub!(:last_response).and_return(@last_response)
444
+ Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
445
+ @request.last_response.should_receive(:delete).with('content-encoding')
446
+ @request.send(:handle_deflation)
447
+ end
448
+
449
+ it "should inflate the gzipped body with content-encoding: x-gzip" do
450
+ @last_response.stub!(:[]).with("content-encoding").and_return("x-gzip")
451
+ @request.stub!(:last_response).and_return(@last_response)
452
+ Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
453
+ @request.last_response.should_receive(:delete).with('content-encoding')
454
+ @request.send(:handle_deflation)
455
+ end
456
+
457
+ it "should inflate the deflated body" do
458
+ @last_response.stub!(:[]).with("content-encoding").and_return("deflate")
459
+ @request.stub!(:last_response).and_return(@last_response)
460
+ Zlib::Inflate.should_receive(:inflate).and_return('')
461
+ @request.last_response.should_receive(:delete).with('content-encoding')
462
+ @request.send(:handle_deflation)
463
+ end
464
+ end
465
+ end
466
+
490
467
  context "with POST http method" do
491
468
  it "should raise argument error if query is not a hash" do
492
469
  lambda {