httparty 0.8.2 → 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.

data/.gitignore CHANGED
@@ -7,3 +7,4 @@ log/
7
7
  pkg/
8
8
  *.swp
9
9
  /.bundle
10
+ .rvmrc
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - ree
5
+ - 1.9.2
6
+ - 1.9.3
7
+ notifications:
8
+ email: false
9
+ bundler_args: --without development
data/Gemfile CHANGED
@@ -1,8 +1,15 @@
1
1
  source :rubygems
2
2
  gemspec
3
3
 
4
- gem 'rake', '~> 0.8.7'
4
+ gem 'rake'
5
5
  gem 'cucumber', '~> 0.7'
6
6
  gem 'fakeweb', '~> 1.2'
7
7
  gem 'rspec', '~> 1.3'
8
8
  gem 'mongrel', '1.2.0.pre2'
9
+ gem 'multi_json', '~> 1.3'
10
+
11
+ group :development do
12
+ gem 'guard'
13
+ gem 'guard-rspec'
14
+ gem 'guard-bundler'
15
+ end
@@ -0,0 +1,16 @@
1
+ rspec_options = {
2
+ :version => 1,
3
+ :all_after_pass => false,
4
+ :all_on_start => false,
5
+ }
6
+
7
+ guard 'rspec', rspec_options do
8
+ watch(%r{^spec/.+_spec\.rb$})
9
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
10
+ watch('spec/spec_helper.rb') { "spec" }
11
+ end
12
+
13
+ guard 'bundler' do
14
+ watch('Gemfile')
15
+ watch(/^.+\.gemspec/)
16
+ end
data/History CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.8.3 2012-04-21
2
+ * new
3
+ * [lazy parsing of responses](https://github.com/jnunemaker/httparty/commit/9fd5259c8dab00e426082b66af44ede2c9068f45)
4
+ * [add support for PATCH requests](https://github.com/jnunemaker/httparty/commit/7ab6641e37a9e31517e46f6124f38c615395d38a)
5
+ * bug fixes
6
+ * [subclasses no longer override superclass options](https://github.com/jnunemaker/httparty/commit/682af8fbf672e7b3009e650da776c85cdfe78d39)
7
+
1
8
  == 0.8.2 2012-04-12
2
9
  * new
3
10
  * add -r to make CLI return failure code if status >= 400
@@ -2,22 +2,6 @@
2
2
 
3
3
  Makes http fun again!
4
4
 
5
- == Note on Releases
6
-
7
- Releases are tagged on github and also released as gems on github and rubyforge. Master is pushed to whenever I add a patch or a new feature. To build from master, you can clone the code, generate the updated gemspec, build the gem and install.
8
-
9
- * rake gemspec
10
- * gem build httparty.gemspec
11
- * gem install the gem that was built
12
-
13
- == Note on Patches/Pull Requests
14
-
15
- * Fork the project.
16
- * Make your feature addition or bug fix.
17
- * Add tests for it. This is important so I don't break it in a future version unintentionally.
18
- * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
19
- * Send me a pull request. Bonus points for topic branches.
20
-
21
5
  == Features:
22
6
 
23
7
  * Easy get, post requests
@@ -29,6 +13,14 @@ Releases are tagged on github and also released as gems on github and rubyforge.
29
13
 
30
14
  See http://github.com/jnunemaker/httparty/tree/master/examples
31
15
 
16
+ == Note on Patches/Pull Requests
17
+
18
+ * Fork the project.
19
+ * Make your feature addition or bug fix.
20
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
21
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
22
+ * Send me a pull request. Bonus points for topic branches.
23
+
32
24
  == Command Line Interface
33
25
 
34
26
  httparty also includes the executable <tt>httparty</tt> which can be
@@ -1 +1 @@
1
- default: features
1
+ default: features --format progress
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
13
13
  s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
14
14
 
15
- s.add_dependency 'multi_json'
15
+ s.add_dependency 'multi_json', "~> 1.0"
16
16
  s.add_dependency 'multi_xml'
17
17
 
18
18
  s.post_install_message = "When you HTTParty, you must party hard!"
@@ -36,7 +36,7 @@ module HTTParty
36
36
  end
37
37
 
38
38
  # == Common Request Options
39
- # Request methods (get, post, put, delete, head, options) all take a common set of options. These are:
39
+ # Request methods (get, post, patch, put, delete, head, options) all take a common set of options. These are:
40
40
  #
41
41
  # [:+body+:] Body of the request. If passed a Hash, will try to normalize it first, by default passing it to ActiveSupport::to_params. Any other kind of object will get used as-is.
42
42
  # [:+http_proxyaddr+:] Address of proxy server to use.
@@ -363,6 +363,11 @@ module HTTParty
363
363
  perform_request Net::HTTP::Post, path, options, &block
364
364
  end
365
365
 
366
+ # Perform a PATCH request to a path
367
+ def patch(path, options={}, &block)
368
+ perform_request Net::HTTP::Patch, path, options, &block
369
+ end
370
+
366
371
  # Perform a PUT request to a path
367
372
  def put(path, options={}, &block)
368
373
  perform_request Net::HTTP::Put, path, options, &block
@@ -431,6 +436,10 @@ module HTTParty
431
436
  Basement.post(*args, &block)
432
437
  end
433
438
 
439
+ def self.patch(*args, &block)
440
+ Basement.patch(*args, &block)
441
+ end
442
+
434
443
  def self.put(*args, &block)
435
444
  Basement.put(*args, &block)
436
445
  end
@@ -1,7 +1,7 @@
1
1
  class HTTParty::CookieHash < Hash #:nodoc:
2
-
2
+
3
3
  CLIENT_COOKIES = %w{path expires domain path secure HTTPOnly}
4
-
4
+
5
5
  def add_cookies(value)
6
6
  case value
7
7
  when Hash
@@ -6,4 +6,27 @@ module HTTParty
6
6
  instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
7
7
  end
8
8
  end
9
+
10
+ unless defined?(Net::HTTP::Patch)
11
+ class Net::HTTP
12
+ def patch(path, data, initheader = nil, dest = nil, &block) #:nodoc:
13
+ res = nil
14
+ request(Patch.new(path, initheader), data) {|r|
15
+ r.read_body dest, &block
16
+ res = r
17
+ }
18
+ unless @newimpl
19
+ res.value
20
+ return res, res.body
21
+ end
22
+ res
23
+ end
24
+
25
+ class Patch < Net::HTTPRequest
26
+ METHOD = 'PATCH'
27
+ REQUEST_HAS_BODY = true
28
+ RESPONSE_HAS_BODY = true
29
+ end
30
+ end
31
+ end
9
32
  end
@@ -48,4 +48,4 @@ module HTTParty
48
48
  param
49
49
  end
50
50
  end
51
- end
51
+ end
@@ -22,7 +22,7 @@ module HTTParty
22
22
  if instance_variable_get(ivar).respond_to?(:merge)
23
23
  method = <<-EOM
24
24
  def self.#{inheritable_attribute}
25
- #{ivar} = superclass.#{inheritable_attribute}.merge #{ivar}
25
+ #{ivar} = superclass.#{inheritable_attribute}.merge Marshal.load(Marshal.dump(#{ivar}))
26
26
  end
27
27
  EOM
28
28
  subclass.class_eval method
@@ -93,7 +93,6 @@ 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
98
  # @return [nil] when the response body is nil, an empty string, spaces only or "null"
@@ -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,
@@ -70,6 +71,7 @@ module HTTParty
70
71
  def perform(&block)
71
72
  validate
72
73
  setup_raw_request
74
+ chunked_body = nil
73
75
 
74
76
  self.last_response = http.request(@raw_request) do |http_response|
75
77
  if block
@@ -80,12 +82,12 @@ module HTTParty
80
82
  block.call(fragment)
81
83
  end
82
84
 
83
- http_response.body = chunks.join
85
+ chunked_body = chunks.join
84
86
  end
85
87
  end
86
88
 
87
89
  handle_deflation
88
- handle_response
90
+ handle_response(chunked_body)
89
91
  end
90
92
 
91
93
  private
@@ -196,7 +198,7 @@ module HTTParty
196
198
  query_string_parts.size > 0 ? query_string_parts.join('&') : nil
197
199
  end
198
200
 
199
- def handle_response
201
+ def handle_response(body)
200
202
  if response_redirects?
201
203
  options[:limit] -= 1
202
204
  self.path = last_response['location']
@@ -205,7 +207,8 @@ module HTTParty
205
207
  capture_cookies(last_response)
206
208
  perform
207
209
  else
208
- 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)
209
212
  end
210
213
  end
211
214
 
@@ -258,7 +261,7 @@ module HTTParty
258
261
 
259
262
  def validate
260
263
  raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
261
- 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)
262
265
  raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
263
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]
264
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.2"
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
@@ -145,6 +145,12 @@ describe HTTParty::Parser do
145
145
  end
146
146
 
147
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)
148
154
  MultiJson.should_receive(:decode).with('body')
149
155
  subject.send(:json)
150
156
  end
@@ -411,6 +411,16 @@ describe HTTParty::Request do
411
411
  resp.body.should == "<foo><bar>error</bar></foo>"
412
412
  resp['foo']['bar'].should == "error"
413
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
414
424
  end
415
425
  end
416
426
 
@@ -456,6 +466,11 @@ describe HTTParty::Request do
456
466
  @request.perform.should == {"hash" => {"foo" => "bar"}}
457
467
  end
458
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
+
459
474
  it "should be handled by PUT transparently" do
460
475
  @request.http_method = Net::HTTP::Put
461
476
  @request.perform.should == {"hash" => {"foo" => "bar"}}
@@ -9,7 +9,7 @@ describe HTTParty::Response do
9
9
  @response_object.stub(:body => "{foo:'bar'}")
10
10
  @response_object['last-modified'] = @last_modified
11
11
  @response_object['content-length'] = @content_length
12
- @parsed_response = {"foo" => "bar"}
12
+ @parsed_response = lambda { {"foo" => "bar"} }
13
13
  @response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
14
14
  end
15
15
 
@@ -51,17 +51,42 @@ describe HTTParty::Response do
51
51
  end
52
52
 
53
53
  it "should send missing methods to delegate" do
54
- response = HTTParty::Response.new(@request_object, @response_object, {'foo' => 'bar'})
54
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
55
55
  response['foo'].should == 'bar'
56
56
  end
57
-
58
- it "should respond_to? methods it supports" do
59
- response = HTTParty::Response.new(@request_object, @response_object, {'foo' => 'bar'})
57
+
58
+ it "response to request" do
59
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
60
+ response.respond_to?(:request).should be_true
61
+ end
62
+
63
+ it "responds to response" do
64
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
65
+ response.respond_to?(:response).should be_true
66
+ end
67
+
68
+ it "responds to body" do
69
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
70
+ response.respond_to?(:body).should be_true
71
+ end
72
+
73
+ it "responds to headers" do
74
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
75
+ response.respond_to?(:headers).should be_true
76
+ end
77
+
78
+ it "responds to parsed_response" do
79
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
60
80
  response.respond_to?(:parsed_response).should be_true
61
81
  end
62
82
 
83
+ it "responds to anything parsed_response responds to" do
84
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
85
+ response.respond_to?(:[]).should be_true
86
+ end
87
+
63
88
  it "should be able to iterate if it is array" do
64
- response = HTTParty::Response.new(@request_object, @response_object, [{'foo' => 'bar'}, {'foo' => 'baz'}])
89
+ response = HTTParty::Response.new(@request_object, @response_object, lambda { [{'foo' => 'bar'}, {'foo' => 'baz'}] })
65
90
  response.size.should == 2
66
91
  expect {
67
92
  response.each { |item| }
@@ -89,22 +114,11 @@ describe HTTParty::Response do
89
114
  end
90
115
  end
91
116
 
92
- xit "should allow hashes to be accessed with dot notation" do
93
- response = HTTParty::Response.new(@request_object, {'foo' => 'bar'}, "{foo:'bar'}", 200, 'OK')
94
- response.foo.should == 'bar'
95
- end
96
-
97
- xit "should allow nested hashes to be accessed with dot notation" do
98
- response = HTTParty::Response.new(@request_object, {'foo' => {'bar' => 'baz'}}, "{foo: {bar:'baz'}}", 200, 'OK')
99
- response.foo.should == {'bar' => 'baz'}
100
- response.foo.bar.should == 'baz'
101
- end
102
-
103
117
  describe "semantic methods for response codes" do
104
118
  def response_mock(klass)
105
- r = klass.new('', '', '')
106
- r.stub(:body)
107
- r
119
+ response = klass.new('', '', '')
120
+ response.stub(:body)
121
+ response
108
122
  end
109
123
 
110
124
  context "major codes" do
@@ -3,18 +3,18 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
3
3
  describe HTTParty::Request do
4
4
  context "SSL certificate verification" do
5
5
  before do
6
- FakeWeb.allow_net_connect = true # enable network connections just for this test
6
+ FakeWeb.allow_net_connect = true
7
7
  end
8
8
 
9
9
  after do
10
- FakeWeb.allow_net_connect = false # Restore allow_net_connect value for testing
10
+ FakeWeb.allow_net_connect = false
11
11
  end
12
12
 
13
- it "should work with when no trusted CA list is specified" do
13
+ it "should work when no trusted CA list is specified" do
14
14
  ssl_verify_test(nil, nil, "selfsigned.crt").should == {'success' => true}
15
15
  end
16
16
 
17
- it "should work with when no trusted CA list is specified, even with a bogus hostname" do
17
+ it "should work when no trusted CA list is specified, even with a bogus hostname" do
18
18
  ssl_verify_test(nil, nil, "bogushost.crt").should == {'success' => true}
19
19
  end
20
20
 
@@ -25,8 +25,14 @@ describe HTTParty::Request do
25
25
  it "should work when using ssl_ca_file with a certificate authority" do
26
26
  ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt").should == {'success' => true}
27
27
  end
28
+
28
29
  it "should work when using ssl_ca_path with a certificate authority" do
29
- ssl_verify_test(:ssl_ca_path, ".", "server.crt").should == {'success' => true}
30
+ http = Net::HTTP.new('www.google.com', 443, nil, nil, nil, nil)
31
+ response = stub(Net::HTTPResponse, :[] => '', :body => '', :to_hash => {})
32
+ http.stub(:request).and_return(response)
33
+ Net::HTTP.should_receive(:new).with('www.google.com', 443, nil, nil, nil, nil).and_return(http)
34
+ http.should_receive(:ca_path=).with('/foo/bar')
35
+ HTTParty.get('https://www.google.com', :ssl_ca_path => '/foo/bar')
30
36
  end
31
37
 
32
38
  it "should fail when using ssl_ca_file and the server uses an unrecognized certificate authority" do
@@ -34,6 +40,7 @@ describe HTTParty::Request do
34
40
  ssl_verify_test(:ssl_ca_file, "ca.crt", "selfsigned.crt")
35
41
  end.should raise_error(OpenSSL::SSL::SSLError)
36
42
  end
43
+
37
44
  it "should fail when using ssl_ca_path and the server uses an unrecognized certificate authority" do
38
45
  lambda do
39
46
  ssl_verify_test(:ssl_ca_path, ".", "selfsigned.crt")
@@ -45,6 +52,7 @@ describe HTTParty::Request do
45
52
  ssl_verify_test(:ssl_ca_file, "ca.crt", "bogushost.crt")
46
53
  end.should raise_error(OpenSSL::SSL::SSLError)
47
54
  end
55
+
48
56
  it "should fail when using ssl_ca_path and the server uses a bogus hostname" do
49
57
  lambda do
50
58
  ssl_verify_test(:ssl_ca_path, ".", "bogushost.crt")
@@ -10,6 +10,7 @@ describe HTTParty do
10
10
  before do
11
11
  Kernel.stub(:warn)
12
12
  end
13
+
13
14
  it "warns with a deprecation message" do
14
15
  Kernel.should_receive(:warn).with("Deprecated: Use HTTParty::Parser::SupportedFormats")
15
16
  HTTParty::AllowedFormats
@@ -21,7 +22,6 @@ describe HTTParty do
21
22
  end
22
23
 
23
24
  describe "pem" do
24
-
25
25
  it 'should set the pem content' do
26
26
  @klass.pem 'PEM-CONTENT'
27
27
  @klass.default_options[:pem].should == 'PEM-CONTENT'
@@ -36,7 +36,6 @@ describe HTTParty do
36
36
  @klass.pem 'PEM-CONTENT', 'PASSWORD'
37
37
  @klass.default_options[:pem_password].should == 'PASSWORD'
38
38
  end
39
-
40
39
  end
41
40
 
42
41
  describe 'http_proxy' do
@@ -433,6 +432,12 @@ describe HTTParty do
433
432
  end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
434
433
  end
435
434
 
435
+ it "should fail with redirected PATCH" do
436
+ lambda do
437
+ @klass.patch('/foo', :no_follow => true)
438
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
439
+ end
440
+
436
441
  it "should fail with redirected DELETE" do
437
442
  lambda do
438
443
  @klass.delete('/foo', :no_follow => true)
@@ -521,6 +526,14 @@ describe HTTParty do
521
526
  @parent.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
522
527
  end
523
528
 
529
+ it "doesn't modify hashes in the parent's default options" do
530
+ @parent.headers 'Accept' => 'application/json'
531
+ @child1.headers 'Accept' => 'application/xml'
532
+
533
+ @parent.default_options[:headers].should == {'Accept' => 'application/json'}
534
+ @child1.default_options[:headers].should == {'Accept' => 'application/xml'}
535
+ end
536
+
524
537
  it "inherits default_cookies from the parent class" do
525
538
  @parent.cookies 'type' => 'chocolate_chip'
526
539
  @child1.default_cookies.should == {"type" => "chocolate_chip"}
@@ -1,3 +1,2 @@
1
1
  --colour
2
- --format specdoc
3
2
  --backtrace
@@ -13,9 +13,11 @@ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].e
13
13
  Spec::Runner.configure do |config|
14
14
  config.include HTTParty::StubResponse
15
15
  config.include HTTParty::SSLTestHelper
16
+
16
17
  config.before(:suite) do
17
18
  FakeWeb.allow_net_connect = false
18
19
  end
20
+
19
21
  config.after(:suite) do
20
22
  FakeWeb.allow_net_connect = true
21
23
  end
@@ -1,25 +1,47 @@
1
+ require 'pathname'
2
+
1
3
  module HTTParty
2
4
  module SSLTestHelper
3
5
  def ssl_verify_test(mode, ca_basename, server_cert_filename)
4
- test_server = nil
6
+ options = {
7
+ :format => :json,
8
+ :timeout => 30,
9
+ }
10
+
11
+ if mode
12
+ ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__)
13
+ raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path)
14
+ options[mode] = ca_path
15
+ end
16
+
5
17
  begin
6
- # Start an HTTPS server
7
18
  test_server = SSLTestServer.new(
8
19
  :rsa_key => File.read(File.expand_path("../../fixtures/ssl/generated/server.key", __FILE__)),
9
- :cert => File.read(File.expand_path("../../fixtures/ssl/generated/#{server_cert_filename}", __FILE__)))
20
+ :cert => File.read(File.expand_path("../../fixtures/ssl/generated/#{server_cert_filename}", __FILE__)))
21
+
10
22
  test_server.start
11
23
 
12
- # Build a request
13
24
  if mode
14
25
  ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__)
15
26
  raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path)
16
- return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout=>30, mode => ca_path)
27
+ return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout => 30, mode => ca_path)
17
28
  else
18
- return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout=>30)
29
+ return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout => 30)
19
30
  end
20
31
  ensure
21
32
  test_server.stop if test_server
22
33
  end
34
+
35
+ test_server = SSLTestServer.new({
36
+ :rsa_key => path.join('server.key').read,
37
+ :cert => path.join(server_cert_filename).read,
38
+ })
39
+
40
+ test_server.start
41
+
42
+ HTTParty.get("https://localhost:#{test_server.port}/", options)
43
+ ensure
44
+ test_server.stop if test_server
23
45
  end
24
46
  end
25
47
  end
@@ -10,26 +10,30 @@ class SSLTestServer
10
10
  attr_reader :port
11
11
 
12
12
  def initialize(options={})
13
- @ctx = OpenSSL::SSL::SSLContext.new
14
- @ctx.cert = OpenSSL::X509::Certificate.new(options[:cert])
15
- @ctx.key = OpenSSL::PKey::RSA.new(options[:rsa_key])
13
+ @ctx = OpenSSL::SSL::SSLContext.new
14
+ @ctx.cert = OpenSSL::X509::Certificate.new(options[:cert])
15
+ @ctx.key = OpenSSL::PKey::RSA.new(options[:rsa_key])
16
16
  @ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Don't verify client certificate
17
- @port = options[:port] || 0
18
- @thread = nil
19
- @stopping_mutex = Mutex.new
20
- @stopping = false
17
+ @port = options[:port] || 0
18
+ @thread = nil
19
+ @stopping_mutex = Mutex.new
20
+ @stopping = false
21
21
  end
22
22
 
23
23
  def start
24
24
  @raw_server = TCPServer.new(@port)
25
+
25
26
  if @port == 0
26
27
  @port = Socket::getnameinfo(@raw_server.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)[1].to_i
27
28
  end
29
+
28
30
  @ssl_server = OpenSSL::SSL::SSLServer.new(@raw_server, @ctx)
31
+
29
32
  @stopping_mutex.synchronize{
30
33
  return if @stopping
31
34
  @thread = Thread.new{ thread_main }
32
35
  }
36
+
33
37
  nil
34
38
  end
35
39
 
@@ -46,24 +50,31 @@ class SSLTestServer
46
50
  def thread_main
47
51
  until @stopping_mutex.synchronize{ @stopping }
48
52
  (rr,ww,ee) = select([@ssl_server.to_io], nil, nil, 0.1)
53
+
49
54
  next unless rr && rr.include?(@ssl_server.to_io)
55
+
50
56
  socket = @ssl_server.accept
57
+
51
58
  Thread.new{
52
59
  header = []
60
+
53
61
  until (line = socket.readline).rstrip.empty?
54
62
  header << line
55
63
  end
56
64
 
57
- socket.write <<'EOF'.gsub(/\r\n/n, "\n").gsub(/\n/n, "\r\n")
65
+ response =<<EOF
58
66
  HTTP/1.1 200 OK
59
67
  Connection: close
60
68
  Content-Type: application/json; charset=UTF-8
61
69
 
62
70
  {"success":true}
63
71
  EOF
72
+
73
+ socket.write(response.gsub(/\r\n/n, "\n").gsub(/\n/n, "\r\n"))
64
74
  socket.close
65
75
  }
66
76
  end
77
+
67
78
  @ssl_server.close
68
79
  end
69
80
  end
metadata CHANGED
@@ -1,49 +1,66 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: httparty
3
- version: !ruby/object:Gem::Version
4
- version: 0.8.2
3
+ version: !ruby/object:Gem::Version
4
+ hash: 57
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 8
9
+ - 3
10
+ version: 0.8.3
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - John Nunemaker
9
14
  - Sandro Turriate
10
15
  autorequire:
11
16
  bindir: bin
12
17
  cert_chain: []
13
- date: 2012-04-12 00:00:00.000000000Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: multi_json
17
- requirement: &70278647636880 !ruby/object:Gem::Requirement
18
- none: false
19
- requirements:
20
- - - ! '>='
21
- - !ruby/object:Gem::Version
22
- version: '0'
18
+
19
+ date: 2012-04-22 00:00:00 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *70278647636880
26
- - !ruby/object:Gem::Dependency
27
- name: multi_xml
28
- requirement: &70278647636320 !ruby/object:Gem::Requirement
24
+ requirement: &id001 !ruby/object:Gem::Requirement
29
25
  none: false
30
- requirements:
31
- - - ! '>='
32
- - !ruby/object:Gem::Version
33
- version: '0'
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 1
32
+ - 0
33
+ version: "1.0"
34
+ version_requirements: *id001
35
+ name: multi_json
36
+ - !ruby/object:Gem::Dependency
34
37
  type: :runtime
35
38
  prerelease: false
36
- version_requirements: *70278647636320
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ version_requirements: *id002
49
+ name: multi_xml
37
50
  description: Makes http fun! Also, makes consuming restful web services dead easy.
38
- email:
51
+ email:
39
52
  - nunemaker@gmail.com
40
- executables:
53
+ executables:
41
54
  - httparty
42
55
  extensions: []
56
+
43
57
  extra_rdoc_files: []
44
- files:
58
+
59
+ files:
45
60
  - .gitignore
61
+ - .travis.yml
46
62
  - Gemfile
63
+ - Guardfile
47
64
  - History
48
65
  - MIT-LICENSE
49
66
  - README.rdoc
@@ -86,6 +103,7 @@ files:
86
103
  - lib/httparty/parser.rb
87
104
  - lib/httparty/request.rb
88
105
  - lib/httparty/response.rb
106
+ - lib/httparty/response/headers.rb
89
107
  - lib/httparty/version.rb
90
108
  - spec/fixtures/delicious.xml
91
109
  - spec/fixtures/empty.xml
@@ -118,29 +136,38 @@ files:
118
136
  - website/index.html
119
137
  homepage: http://httparty.rubyforge.org/
120
138
  licenses: []
139
+
121
140
  post_install_message: When you HTTParty, you must party hard!
122
141
  rdoc_options: []
123
- require_paths:
142
+
143
+ require_paths:
124
144
  - lib
125
- required_ruby_version: !ruby/object:Gem::Requirement
145
+ required_ruby_version: !ruby/object:Gem::Requirement
126
146
  none: false
127
- requirements:
128
- - - ! '>='
129
- - !ruby/object:Gem::Version
130
- version: '0'
131
- required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ hash: 3
151
+ segments:
152
+ - 0
153
+ version: "0"
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
155
  none: false
133
- requirements:
134
- - - ! '>='
135
- - !ruby/object:Gem::Version
136
- version: '0'
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ hash: 3
160
+ segments:
161
+ - 0
162
+ version: "0"
137
163
  requirements: []
164
+
138
165
  rubyforge_project:
139
166
  rubygems_version: 1.8.10
140
167
  signing_key:
141
168
  specification_version: 3
142
169
  summary: Makes http fun! Also, makes consuming restful web services dead easy.
143
- test_files:
170
+ test_files:
144
171
  - features/basic_authentication.feature
145
172
  - features/command_line.feature
146
173
  - features/deals_with_http_error_codes.feature