httparty 0.8.0 → 0.8.3

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.

@@ -93,12 +93,11 @@ module HTTParty
93
93
  @body = body
94
94
  @format = format
95
95
  end
96
- private_class_method :new
97
96
 
98
97
  # @return [Object] the parsed body
99
- # @return [nil] when the response body is nil or an empty string
98
+ # @return [nil] when the response body is nil, an empty string, spaces only or "null"
100
99
  def parse
101
- return nil if body.nil? || body.empty?
100
+ return nil if body.nil? || body.strip.empty? || body == "null"
102
101
  if supports_format?
103
102
  parse_supported_format
104
103
  else
@@ -113,7 +112,12 @@ module HTTParty
113
112
  end
114
113
 
115
114
  def json
116
- MultiJson.decode(body)
115
+ # https://github.com/sferik/rails/commit/5e62670131dfa1718eaf21ff8dd3371395a5f1cc
116
+ if MultiJson.respond_to?(:adapter)
117
+ MultiJson.load(body)
118
+ else
119
+ MultiJson.decode(body)
120
+ end
117
121
  end
118
122
 
119
123
  def yaml
@@ -3,6 +3,7 @@ module HTTParty
3
3
  SupportedHTTPMethods = [
4
4
  Net::HTTP::Get,
5
5
  Net::HTTP::Post,
6
+ Net::HTTP::Patch,
6
7
  Net::HTTP::Put,
7
8
  Net::HTTP::Delete,
8
9
  Net::HTTP::Head,
@@ -67,12 +68,26 @@ module HTTParty
67
68
  options[:parser]
68
69
  end
69
70
 
70
- def perform
71
+ def perform(&block)
71
72
  validate
72
73
  setup_raw_request
73
- self.last_response = http.request(@raw_request)
74
+ chunked_body = nil
75
+
76
+ self.last_response = http.request(@raw_request) do |http_response|
77
+ if block
78
+ chunks = []
79
+
80
+ http_response.read_body do |fragment|
81
+ chunks << fragment
82
+ block.call(fragment)
83
+ end
84
+
85
+ chunked_body = chunks.join
86
+ end
87
+ end
88
+
74
89
  handle_deflation
75
- handle_response
90
+ handle_response(chunked_body)
76
91
  end
77
92
 
78
93
  private
@@ -102,7 +117,7 @@ module HTTParty
102
117
  end
103
118
 
104
119
  def http
105
- http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport])
120
+ http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
106
121
  http.use_ssl = ssl_implied?
107
122
 
108
123
  if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
@@ -160,7 +175,10 @@ module HTTParty
160
175
  end
161
176
 
162
177
  def setup_digest_auth
163
- res = http.head(uri.request_uri, options[:headers])
178
+ auth_request = http_method.new(uri.request_uri)
179
+ auth_request.initialize_http_header(options[:headers])
180
+ res = http.request(auth_request)
181
+
164
182
  if res['www-authenticate'] != nil && res['www-authenticate'].length > 0
165
183
  @raw_request.digest_auth(username, password, res)
166
184
  end
@@ -180,8 +198,7 @@ module HTTParty
180
198
  query_string_parts.size > 0 ? query_string_parts.join('&') : nil
181
199
  end
182
200
 
183
- # Raises exception Net::XXX (http error code) if an http error occured
184
- def handle_response
201
+ def handle_response(body)
185
202
  if response_redirects?
186
203
  options[:limit] -= 1
187
204
  self.path = last_response['location']
@@ -190,18 +207,21 @@ module HTTParty
190
207
  capture_cookies(last_response)
191
208
  perform
192
209
  else
193
- Response.new(self, last_response, parse_response(last_response.body))
210
+ body = body || last_response.body
211
+ Response.new(self, last_response, lambda { parse_response(body) }, :body => body)
194
212
  end
195
213
  end
196
214
 
197
215
  # Inspired by Ruby 1.9
198
216
  def handle_deflation
199
217
  case last_response["content-encoding"]
200
- when "gzip"
218
+ when "gzip", "x-gzip"
201
219
  body_io = StringIO.new(last_response.body)
202
220
  last_response.body.replace Zlib::GzipReader.new(body_io).read
221
+ last_response.delete('content-encoding')
203
222
  when "deflate"
204
223
  last_response.body.replace Zlib::Inflate.inflate(last_response.body)
224
+ last_response.delete('content-encoding')
205
225
  end
206
226
  end
207
227
 
@@ -241,7 +261,7 @@ module HTTParty
241
261
 
242
262
  def validate
243
263
  raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
244
- raise ArgumentError, 'only get, post, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
264
+ raise ArgumentError, 'only get, post, patch, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
245
265
  raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
246
266
  raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth]
247
267
  raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].is_a?(Hash)
@@ -1,46 +1,21 @@
1
1
  module HTTParty
2
2
  class Response < HTTParty::BasicObject #: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
-
31
-
32
3
  def self.underscore(string)
33
4
  string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
34
5
  end
35
6
 
36
- attr_reader :request, :response, :parsed_response, :body, :headers
7
+ attr_reader :request, :response, :body, :headers
37
8
 
38
- def initialize(request, response, parsed_response)
39
- @request = request
40
- @response = response
41
- @body = response.body
42
- @parsed_response = parsed_response
43
- @headers = Headers.new(response.to_hash)
9
+ def initialize(request, response, parsed_block, options={})
10
+ @request = request
11
+ @response = response
12
+ @body = response.body || options[:body]
13
+ @parsed_block = parsed_block
14
+ @headers = Headers.new(response.to_hash)
15
+ end
16
+
17
+ def parsed_response
18
+ @parsed_response ||= @parsed_block.call
44
19
  end
45
20
 
46
21
  def class
@@ -53,7 +28,7 @@ module HTTParty
53
28
 
54
29
  def inspect
55
30
  inspect_id = "%x" % (object_id * 2)
56
- %(#<#{self.class}:0x#{inspect_id} @parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
31
+ %(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
57
32
  end
58
33
 
59
34
  CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
@@ -64,10 +39,10 @@ module HTTParty
64
39
  klass === response
65
40
  end
66
41
  end
67
-
42
+
68
43
  def respond_to?(name)
69
- return true if [:request,:response,:parsed_response,:body,:headers].include?(name)
70
- parsed_response.respond_to?(name) or response.respond_to?(name)
44
+ return true if [:request, :response, :parsed_response, :body, :headers].include?(name)
45
+ parsed_response.respond_to?(name) || response.respond_to?(name)
71
46
  end
72
47
 
73
48
  protected
@@ -83,3 +58,5 @@ module HTTParty
83
58
  end
84
59
  end
85
60
  end
61
+
62
+ require 'httparty/response/headers'
@@ -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.8.3"
3
3
  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
@@ -13,11 +13,11 @@ describe Net::HTTPHeader::DigestAuthenticator do
13
13
  @digest.authorization_header.join(", ")
14
14
  end
15
15
 
16
-
17
16
  context "with specified quality of protection (qop)" do
18
17
  before do
19
- @digest = setup_digest({'www-authenticate' =>
20
- 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'})
18
+ @digest = setup_digest({
19
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"',
20
+ })
21
21
  end
22
22
 
23
23
  it "should set prefix" do
@@ -45,9 +45,7 @@ describe Net::HTTPHeader::DigestAuthenticator do
45
45
  end
46
46
 
47
47
  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))"
48
+ request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:0:md5(deadbeef):auth:md5(GET:/dir/index.html))"
51
49
  authorization_header.should include(%Q(response="#{request_digest}"))
52
50
  end
53
51
  end
@@ -55,8 +53,9 @@ describe Net::HTTPHeader::DigestAuthenticator do
55
53
 
56
54
  context "with unspecified quality of protection (qop)" do
57
55
  before do
58
- @digest = setup_digest({'www-authenticate' =>
59
- 'Digest realm="myhost@testrealm.com", nonce="NONCE"'})
56
+ @digest = setup_digest({
57
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"',
58
+ })
60
59
  end
61
60
 
62
61
  it "should set prefix" do
@@ -84,9 +83,7 @@ describe Net::HTTPHeader::DigestAuthenticator do
84
83
  end
85
84
 
86
85
  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))"
86
+ request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))"
90
87
  authorization_header.should include(%Q(response="#{request_digest}"))
91
88
  end
92
89
  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
@@ -86,7 +86,7 @@ describe HTTParty::Request do
86
86
  end
87
87
 
88
88
  it "should use digest auth when configured" do
89
- FakeWeb.register_uri(:head, "http://api.foo.com/v1",
89
+ FakeWeb.register_uri(:get, "http://api.foo.com/v1",
90
90
  :www_authenticate => 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
91
91
 
92
92
  @request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
@@ -95,6 +95,17 @@ describe HTTParty::Request do
95
95
  raw_request = @request.instance_variable_get(:@raw_request)
96
96
  raw_request.instance_variable_get(:@header)['Authorization'].should_not be_nil
97
97
  end
98
+
99
+ it "should use the right http method for digest authentication" do
100
+ @post_request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml)
101
+ FakeWeb.register_uri(:post, "http://api.foo.com/v1", {})
102
+
103
+ http = @post_request.send(:http)
104
+ @post_request.should_receive(:http).and_return(http)
105
+ http.should_not_receive(:head).and_return({'www-authenticate' => nil})
106
+ @post_request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
107
+ @post_request.send(:setup_raw_request)
108
+ end
98
109
  end
99
110
 
100
111
  describe "#uri" do
@@ -216,6 +227,25 @@ describe HTTParty::Request do
216
227
  @request.send(:http)
217
228
  end
218
229
  end
230
+
231
+ context 'with a proxy' do
232
+ it 'should use a proxy address and port' do
233
+ request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com',
234
+ :http_proxyaddr => '1.2.3.4', :http_proxyport => 8080)
235
+ http = request.send(:http)
236
+ http.proxy_address.should == '1.2.3.4'
237
+ http.proxy_port.should == 8080
238
+ end
239
+
240
+ it 'should use a proxy user and password when provided' do
241
+ request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com',
242
+ :http_proxyaddr => '1.2.3.4', :http_proxyport => 8080,
243
+ :http_proxyuser => 'user', :http_proxypass => 'pass')
244
+ http = request.send(:http)
245
+ http.proxy_user.should == 'user'
246
+ http.proxy_pass.should == 'pass'
247
+ end
248
+ end
219
249
  end
220
250
 
221
251
  context "when setting timeout" do
@@ -381,6 +411,16 @@ describe HTTParty::Request do
381
411
  resp.body.should == "<foo><bar>error</bar></foo>"
382
412
  resp['foo']['bar'].should == "error"
383
413
  end
414
+
415
+ it "parses response lazily so codes can be checked prior" do
416
+ stub_response 'not xml', 500
417
+ @request.options[:format] = :xml
418
+ lambda {
419
+ response = @request.perform
420
+ response.code.should == 500
421
+ response.body.should == 'not xml'
422
+ }.should_not raise_error
423
+ end
384
424
  end
385
425
  end
386
426
 
@@ -426,6 +466,11 @@ describe HTTParty::Request do
426
466
  @request.perform.should == {"hash" => {"foo" => "bar"}}
427
467
  end
428
468
 
469
+ it "should be handled by PATCH transparently" do
470
+ @request.http_method = Net::HTTP::Patch
471
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
472
+ end
473
+
429
474
  it "should be handled by PUT transparently" do
430
475
  @request.http_method = Net::HTTP::Put
431
476
  @request.perform.should == {"hash" => {"foo" => "bar"}}
@@ -487,6 +532,40 @@ describe HTTParty::Request do
487
532
  end
488
533
  end
489
534
 
535
+ describe "#handle_deflation" do
536
+ context "context-encoding" do
537
+ before do
538
+ @request.options[:format] = :html
539
+ @last_response = mock()
540
+ @last_response.stub!(:body).and_return('')
541
+ end
542
+
543
+ it "should inflate the gzipped body with content-encoding: gzip" do
544
+ @last_response.stub!(:[]).with("content-encoding").and_return("gzip")
545
+ @request.stub!(:last_response).and_return(@last_response)
546
+ Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
547
+ @request.last_response.should_receive(:delete).with('content-encoding')
548
+ @request.send(:handle_deflation)
549
+ end
550
+
551
+ it "should inflate the gzipped body with content-encoding: x-gzip" do
552
+ @last_response.stub!(:[]).with("content-encoding").and_return("x-gzip")
553
+ @request.stub!(:last_response).and_return(@last_response)
554
+ Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
555
+ @request.last_response.should_receive(:delete).with('content-encoding')
556
+ @request.send(:handle_deflation)
557
+ end
558
+
559
+ it "should inflate the deflated body" do
560
+ @last_response.stub!(:[]).with("content-encoding").and_return("deflate")
561
+ @request.stub!(:last_response).and_return(@last_response)
562
+ Zlib::Inflate.should_receive(:inflate).and_return('')
563
+ @request.last_response.should_receive(:delete).with('content-encoding')
564
+ @request.send(:handle_deflation)
565
+ end
566
+ end
567
+ end
568
+
490
569
  context "with POST http method" do
491
570
  it "should raise argument error if query is not a hash" do
492
571
  lambda {