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

@@ -2,7 +2,6 @@ language: ruby
2
2
  rvm:
3
3
  - 1.8.7
4
4
  - ree
5
- - 1.9.2
6
5
  - 1.9.3
7
6
  notifications:
8
7
  email: false
data/History CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.9.0 2012-09-07
2
+ * new
3
+ * [support for connection adapters](https://github.com/jnunemaker/httparty/pull/157)
4
+ * [allow ssl_version on ruby 1.9](https://github.com/jnunemaker/httparty/pull/159)
5
+
6
+ * bug fixes
7
+ * [don't treat port 4430 as ssl](https://github.com/jnunemaker/httparty/commit/a296b1c97f83d7dcc6ef85720a43664c265685ac)
8
+ * [deep clone default options](https://github.com/jnunemaker/httparty/commit/f74227d30f9389b4b23a888c9af49fb9b8248e1f)
9
+ * a few net digest auth fixes
10
+
1
11
  == 0.8.3 2012-04-21
2
12
  * new
3
13
  * [lazy parsing of responses](https://github.com/jnunemaker/httparty/commit/9fd5259c8dab00e426082b66af44ede2c9068f45)
@@ -0,0 +1,79 @@
1
+ # httparty
2
+
3
+ Makes http fun again!
4
+
5
+ ## Install
6
+
7
+ ```
8
+ gem install httparty
9
+ ```
10
+
11
+ ## Requirements
12
+
13
+ * multi_json and multi_xml
14
+ * You like to party!
15
+
16
+ ## Examples
17
+
18
+ ```ruby
19
+ # Use the class methods to get down to business quickly
20
+ response = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
21
+ puts response.body, response.code, response.message, response.headers.inspect
22
+
23
+ response.each do |item|
24
+ puts item['user']['screen_name']
25
+ end
26
+
27
+ # Or wrap things up in your own class
28
+ class Twitter
29
+ include HTTParty
30
+ base_uri 'twitter.com'
31
+
32
+ def initialize(u, p)
33
+ @auth = {:username => u, :password => p}
34
+ end
35
+
36
+ # which can be :friends, :user or :public
37
+ # options[:query] can be things like since, since_id, count, etc.
38
+ def timeline(which=:friends, options={})
39
+ options.merge!({:basic_auth => @auth})
40
+ self.class.get("/statuses/#{which}_timeline.json", options)
41
+ end
42
+
43
+ def post(text)
44
+ options = { :body => {:status => text}, :basic_auth => @auth }
45
+ self.class.post('/statuses/update.json', options)
46
+ end
47
+ end
48
+
49
+ twitter = Twitter.new(config['email'], config['password'])
50
+ pp twitter.timeline
51
+ ```
52
+
53
+ See the [examples directory](http://github.com/jnunemaker/httparty/tree/master/examples) for even more goodies.
54
+
55
+ ## Command Line Interface
56
+
57
+ httparty also includes the executable `httparty` which can be
58
+ used to query web services and examine the resulting output. By default
59
+ it will output the response as a pretty-printed Ruby object (useful for
60
+ grokking the structure of output). This can also be overridden to output
61
+ formatted XML or JSON. Execute `httparty --help` for all the
62
+ options. Below is an example of how easy it is.
63
+
64
+ ```
65
+ httparty "http://twitter.com/statuses/public_timeline.json"
66
+ ```
67
+
68
+ ## Help and Docs
69
+
70
+ * https://groups.google.com/forum/#!forum/httparty-gem
71
+ * http://rdoc.info/projects/jnunemaker/httparty
72
+
73
+ ## Contributing
74
+
75
+ * Fork the project.
76
+ * Make your feature addition or bug fix.
77
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
78
+ * 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)
79
+ * Send me a pull request. Bonus points for topic branches.
@@ -38,7 +38,7 @@ OptionParser.new do |o|
38
38
  end
39
39
  end
40
40
 
41
- o.on("-H", "--header [NAME=VALUE]", "Additional HTTP headers in NAME=VALUE form") do |h|
41
+ o.on("-H", "--header [NAME:VALUE]", "Additional HTTP headers in NAME:VALUE form") do |h|
42
42
  abort "Invalid header specification, should be Name:Value" unless h =~ /.+:.+/
43
43
  name, value = h.split(':')
44
44
  opts[:headers][name.strip] = value.strip
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["John Nunemaker", "Sandro Turriate"]
10
10
  s.email = ["nunemaker@gmail.com"]
11
- s.homepage = "http://httparty.rubyforge.org/"
11
+ s.homepage = "http://jnunemaker.github.com/httparty"
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
 
@@ -10,6 +10,7 @@ require 'httparty/module_inheritable_attributes'
10
10
  require 'httparty/cookie_hash'
11
11
  require 'httparty/net_digest_auth'
12
12
  require 'httparty/version'
13
+ require 'httparty/connection_adapter'
13
14
 
14
15
  # @see HTTParty::ClassMethods
15
16
  module HTTParty
@@ -57,9 +58,11 @@ module HTTParty
57
58
  # * :+maintain_method_across_redirects+: see HTTParty::ClassMethods.maintain_method_across_redirects.
58
59
  # * :+no_follow+: see HTTParty::ClassMethods.no_follow.
59
60
  # * :+parser+: see HTTParty::ClassMethods.parser.
61
+ # * :+connection_adapter+: see HTTParty::ClassMethods.connection_adapter.
60
62
  # * :+pem+: see HTTParty::ClassMethods.pem.
61
63
  # * :+query_string_normalizer+: see HTTParty::ClassMethods.query_string_normalizer
62
64
  # * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
65
+ # * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
63
66
 
64
67
  module ClassMethods
65
68
 
@@ -296,6 +299,17 @@ module HTTParty
296
299
  default_options[:query_string_normalizer] = normalizer
297
300
  end
298
301
 
302
+ # Allows setting of SSL version to use. This only works in Ruby 1.9.
303
+ # You can get a list of valid versions from OpenSSL::SSL::SSLContext::METHODS.
304
+ #
305
+ # class Foo
306
+ # include HTTParty
307
+ # ssl_version :SSLv3
308
+ # end
309
+ def ssl_version(version)
310
+ default_options[:ssl_version] = version
311
+ end
312
+
299
313
  # Allows setting an OpenSSL certificate authority file
300
314
  #
301
315
  # class Foo
@@ -331,6 +345,30 @@ module HTTParty
331
345
  end
332
346
  end
333
347
 
348
+ # Allows setting a custom connection_adapter for the http connections
349
+ #
350
+ # @example
351
+ # class Foo
352
+ # include HTTParty
353
+ # connection_adapter Proc.new {|uri, options| ... }
354
+ # end
355
+ #
356
+ # @example provide optional configuration for your connection_adapter
357
+ # class Foo
358
+ # include HTTParty
359
+ # connection_adapter Proc.new {|uri, options| ... }, {:foo => :bar}
360
+ # end
361
+ #
362
+ # @see HTTParty::ConnectionAdapter
363
+ def connection_adapter(custom_adapter = nil, options = nil)
364
+ if custom_adapter.nil?
365
+ default_options[:connection_adapter]
366
+ else
367
+ default_options[:connection_adapter] = custom_adapter
368
+ default_options[:connection_adapter_options] = options
369
+ end
370
+ end
371
+
334
372
  # Allows making a get request to a url.
335
373
  #
336
374
  # class Foo
@@ -415,7 +453,7 @@ module HTTParty
415
453
 
416
454
  def self.normalize_base_uri(url) #:nodoc:
417
455
  normalized_url = url.dup
418
- use_ssl = (normalized_url =~ /^https/) || normalized_url.include?(':443')
456
+ use_ssl = (normalized_url =~ /^https/) || (normalized_url =~ /:443\b/)
419
457
  ends_with_slash = normalized_url =~ /\/$/
420
458
 
421
459
  normalized_url.chop! if ends_with_slash
@@ -0,0 +1,116 @@
1
+ module HTTParty
2
+ # Default connection adapter that returns a new Net::HTTP each time
3
+ #
4
+ # == Custom Connection Factories
5
+ #
6
+ # If you like to implement your own connection adapter, subclassing
7
+ # HTTPParty::ConnectionAdapter will make it easier. Just override
8
+ # the #connection method. The uri and options attributes will have
9
+ # all the info you need to construct your http connection. Whatever
10
+ # you return from your connection method needs to adhere to the
11
+ # Net::HTTP interface as this is what HTTParty expects.
12
+ #
13
+ # @example log the uri and options
14
+ # class LoggingConnectionAdapter < HTTParty::ConnectionAdapter
15
+ # def connection
16
+ # puts uri
17
+ # puts options
18
+ # Net::HTTP.new(uri)
19
+ # end
20
+ # end
21
+ #
22
+ # @example count number of http calls
23
+ # class CountingConnectionAdapter < HTTParty::ConnectionAdapter
24
+ # @@count = 0
25
+ #
26
+ # self.count
27
+ # @@count
28
+ # end
29
+ #
30
+ # def connection
31
+ # self.count += 1
32
+ # super
33
+ # end
34
+ # end
35
+ #
36
+ # === Configuration
37
+ # There is lots of configuration data available for your connection adapter
38
+ # in the #options attribute. It is up to you to interpret them within your
39
+ # connection adapter. Take a look at the implementation of
40
+ # HTTParty::ConnectionAdapter#connection for examples of how they are used.
41
+ # Something are probably interesting are as follows:
42
+ # * :+timeout+: timeout in seconds
43
+ # * :+debug_output+: see HTTParty::ClassMethods.debug_output.
44
+ # * :+pem+: contains pem data. see HTTParty::ClassMethods.pem.
45
+ # * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
46
+ # * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
47
+ # * :+connection_adapter_options+: contains the hash your passed to HTTParty.connection_adapter when you configured your connection adapter
48
+ class ConnectionAdapter
49
+
50
+ def self.call(uri, options)
51
+ new(uri, options).connection
52
+ end
53
+
54
+ attr_reader :uri, :options
55
+
56
+ def initialize(uri, options={})
57
+ raise ArgumentError, "uri must be a URI, not a #{uri.class}" unless uri.kind_of? URI
58
+
59
+ @uri = uri
60
+ @options = options
61
+ end
62
+
63
+ def connection
64
+ http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
65
+
66
+ http.use_ssl = ssl_implied?(uri)
67
+
68
+ attach_ssl_certificates(http, options)
69
+
70
+ if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
71
+ http.open_timeout = options[:timeout]
72
+ http.read_timeout = options[:timeout]
73
+ end
74
+
75
+ if options[:debug_output]
76
+ http.set_debug_output(options[:debug_output])
77
+ end
78
+
79
+ return http
80
+ end
81
+
82
+ private
83
+ def ssl_implied?(uri)
84
+ uri.port == 443 || uri.instance_of?(URI::HTTPS)
85
+ end
86
+
87
+ def attach_ssl_certificates(http, options)
88
+ if http.use_ssl?
89
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
90
+
91
+ # Client certificate authentication
92
+ if options[:pem]
93
+ http.cert = OpenSSL::X509::Certificate.new(options[:pem])
94
+ http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
95
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
96
+ end
97
+
98
+ # SSL certificate authority file and/or directory
99
+ if options[:ssl_ca_file]
100
+ http.ca_file = options[:ssl_ca_file]
101
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
102
+ end
103
+
104
+ if options[:ssl_ca_path]
105
+ http.ca_path = options[:ssl_ca_path]
106
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
107
+ end
108
+
109
+ # This is only Ruby 1.9+
110
+ if options[:ssl_version] && http.respond_to?(:ssl_version=)
111
+ http.ssl_version = options[:ssl_version]
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -4,6 +4,16 @@ module HTTParty
4
4
  base.extend(ClassMethods)
5
5
  end
6
6
 
7
+ # borrowed from Rails 3.2 ActiveSupport
8
+ def self.hash_deep_dup(h)
9
+ duplicate = h.dup
10
+ duplicate.each_pair do |k,v|
11
+ tv = duplicate[k]
12
+ duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? hash_deep_dup(tv) : v
13
+ end
14
+ duplicate
15
+ end
16
+
7
17
  module ClassMethods #:nodoc:
8
18
  def mattr_inheritable(*args)
9
19
  @mattr_inheritable_attrs ||= [:mattr_inheritable_attrs]
@@ -22,7 +32,7 @@ module HTTParty
22
32
  if instance_variable_get(ivar).respond_to?(:merge)
23
33
  method = <<-EOM
24
34
  def self.#{inheritable_attribute}
25
- #{ivar} = superclass.#{inheritable_attribute}.merge Marshal.load(Marshal.dump(#{ivar}))
35
+ #{ivar} = superclass.#{inheritable_attribute}.merge ModuleInheritableAttributes.hash_deep_dup(#{ivar})
26
36
  end
27
37
  EOM
28
38
  subclass.class_eval method
@@ -20,15 +20,24 @@ module Net
20
20
 
21
21
  def authorization_header
22
22
  @cnonce = md5(random)
23
- header = [%Q(Digest username="#{@username}"),
23
+ header = [
24
+ %Q(Digest username="#{@username}"),
24
25
  %Q(realm="#{@response['realm']}"),
25
26
  %Q(nonce="#{@response['nonce']}"),
26
27
  %Q(uri="#{@path}"),
27
- %Q(response="#{request_digest}")]
28
- [%Q(cnonce="#{@cnonce}"),
29
- %Q(opaque="#{@response['opaque']}"),
30
- %Q(qop="#{@response['qop']}"),
31
- %Q(nc="0")].each { |field| header << field } if qop_present?
28
+ %Q(response="#{request_digest}"),
29
+ ]
30
+
31
+ if qop_present?
32
+ fields = [
33
+ %Q(cnonce="#{@cnonce}"),
34
+ %Q(qop="#{@response['qop']}"),
35
+ %Q(nc="00000001")
36
+ ]
37
+ fields.each { |field| header << field }
38
+ end
39
+
40
+ header << %Q(opaque="#{@response['opaque']}") if opaque_present?
32
41
  header
33
42
  end
34
43
 
@@ -41,6 +50,10 @@ module Net
41
50
  params
42
51
  end
43
52
 
53
+ def opaque_present?
54
+ @response.has_key?('opaque') and not @response['opaque'].empty?
55
+ end
56
+
44
57
  def qop_present?
45
58
  @response.has_key?('qop') and not @response['qop'].empty?
46
59
  end
@@ -51,7 +64,7 @@ module Net
51
64
 
52
65
  def request_digest
53
66
  a = [md5(a1), @response['nonce'], md5(a2)]
54
- a.insert(2, "0", @cnonce, @response['qop']) if qop_present?
67
+ a.insert(2, "00000001", @cnonce, @response['qop']) if qop_present?
55
68
  md5(a.join(":"))
56
69
  end
57
70
 
@@ -138,8 +138,8 @@ module HTTParty
138
138
 
139
139
  def parse_supported_format
140
140
  send(format)
141
- rescue NoMethodError
142
- raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format."
141
+ rescue NoMethodError => e
142
+ raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format.", e.backtrace
143
143
  end
144
144
  end
145
145
  end
@@ -33,7 +33,8 @@ module HTTParty
33
33
  :limit => o.delete(:no_follow) ? 1 : 5,
34
34
  :default_params => {},
35
35
  :follow_redirects => true,
36
- :parser => Parser
36
+ :parser => Parser,
37
+ :connection_adapter => ConnectionAdapter
37
38
  }.merge(o)
38
39
  end
39
40
 
@@ -68,6 +69,10 @@ module HTTParty
68
69
  options[:parser]
69
70
  end
70
71
 
72
+ def connection_adapter
73
+ options[:connection_adapter]
74
+ end
75
+
71
76
  def perform(&block)
72
77
  validate
73
78
  setup_raw_request
@@ -92,50 +97,8 @@ module HTTParty
92
97
 
93
98
  private
94
99
 
95
- def attach_ssl_certificates(http)
96
- if http.use_ssl?
97
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
98
-
99
- # Client certificate authentication
100
- if options[:pem]
101
- http.cert = OpenSSL::X509::Certificate.new(options[:pem])
102
- http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
103
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
104
- end
105
-
106
- # SSL certificate authority file and/or directory
107
- if options[:ssl_ca_file]
108
- http.ca_file = options[:ssl_ca_file]
109
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
110
- end
111
-
112
- if options[:ssl_ca_path]
113
- http.ca_path = options[:ssl_ca_path]
114
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
115
- end
116
- end
117
- end
118
-
119
100
  def http
120
- http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
121
- http.use_ssl = ssl_implied?
122
-
123
- if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
124
- http.open_timeout = options[:timeout]
125
- http.read_timeout = options[:timeout]
126
- end
127
-
128
- attach_ssl_certificates(http)
129
-
130
- if options[:debug_output]
131
- http.set_debug_output(options[:debug_output])
132
- end
133
-
134
- http
135
- end
136
-
137
- def ssl_implied?
138
- uri.port == 443 || uri.instance_of?(URI::HTTPS)
101
+ connection_adapter.call(uri, options)
139
102
  end
140
103
 
141
104
  def body
@@ -1,3 +1,3 @@
1
1
  module HTTParty
2
- VERSION = "0.8.3"
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
@@ -13,6 +13,31 @@ describe Net::HTTPHeader::DigestAuthenticator do
13
13
  @digest.authorization_header.join(", ")
14
14
  end
15
15
 
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
+
16
41
  context "with specified quality of protection (qop)" do
17
42
  before do
18
43
  @digest = setup_digest({
@@ -41,11 +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 = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):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))"
49
74
  authorization_header.should include(%Q(response="#{request_digest}"))
50
75
  end
51
76
  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
@@ -145,125 +156,12 @@ describe HTTParty::Request do
145
156
  end
146
157
 
147
158
  describe 'http' do
148
- it "should use ssl for port 443" do
149
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443')
150
- request.send(:http).use_ssl?.should == true
151
- end
152
-
153
- it 'should not use ssl for port 80' do
154
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://foobar.com')
155
- request.send(:http).use_ssl?.should == false
156
- end
157
-
158
- it "uses ssl for https scheme with default port" do
159
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com')
160
- request.send(:http).use_ssl?.should == true
161
- end
162
-
163
- it "uses ssl for https scheme regardless of port" do
164
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com:123456')
165
- request.send(:http).use_ssl?.should == true
166
- end
167
-
168
- context "PEM certificates" do
169
- before do
170
- OpenSSL::X509::Certificate.stub(:new)
171
- OpenSSL::PKey::RSA.stub(:new)
172
- end
173
-
174
- context "when scheme is https" do
175
- before do
176
- @request.stub!(:uri).and_return(URI.parse("https://google.com"))
177
- pem = :pem_contents
178
- @cert = mock("OpenSSL::X509::Certificate")
179
- @key = mock("OpenSSL::PKey::RSA")
180
- OpenSSL::X509::Certificate.should_receive(:new).with(pem).and_return(@cert)
181
- OpenSSL::PKey::RSA.should_receive(:new).with(pem, "password").and_return(@key)
182
-
183
- @request.options[:pem] = pem
184
- @request.options[:pem_password] = "password"
185
- @pem_http = @request.send(:http)
186
- end
187
-
188
- it "should use a PEM certificate when provided" do
189
- @pem_http.cert.should == @cert
190
- @pem_http.key.should == @key
191
- end
192
-
193
- it "should verify the certificate when provided" do
194
- @pem_http = @request.send(:http)
195
- @pem_http.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
196
- end
197
- end
198
-
199
- context "when scheme is not https" do
200
- it "does not assign a PEM" do
201
- http = Net::HTTP.new('google.com')
202
- http.should_not_receive(:cert=)
203
- http.should_not_receive(:key=)
204
- Net::HTTP.stub(:new => http)
205
-
206
- request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
207
- request.options[:pem] = :pem_contents
208
- request.send(:http)
209
- end
210
- end
211
-
212
- context "debugging" do
213
- before do
214
- @http = Net::HTTP.new('google.com')
215
- Net::HTTP.stub(:new => @http)
216
- @request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
217
- end
218
-
219
- it "calls #set_debug_output when the option is provided" do
220
- @request.options[:debug_output] = $stderr
221
- @http.should_receive(:set_debug_output).with($stderr)
222
- @request.send(:http)
223
- end
224
-
225
- it "does not set_debug_output when the option is not provided" do
226
- @http.should_not_receive(:set_debug_output)
227
- @request.send(:http)
228
- end
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
249
- end
250
-
251
- context "when setting timeout" do
252
- it "does nothing if the timeout option is a string" do
253
- http = mock("http", :null_object => true)
254
- http.should_not_receive(:open_timeout=)
255
- http.should_not_receive(:read_timeout=)
256
- Net::HTTP.stub(:new => http)
257
-
258
- request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com', {:timeout => "five seconds"})
259
- request.send(:http)
260
- end
261
-
262
- it "sets the timeout to 5 seconds" do
263
- @request.options[:timeout] = 5
264
- @request.send(:http).open_timeout.should == 5
265
- @request.send(:http).read_timeout.should == 5
266
- 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
267
165
  end
268
166
  end
269
167
 
@@ -38,6 +38,13 @@ describe HTTParty do
38
38
  end
39
39
  end
40
40
 
41
+ describe 'ssl_version' do
42
+ it 'should set the ssl_version content' do
43
+ @klass.ssl_version :SSLv3
44
+ @klass.default_options[:ssl_version].should == :SSLv3
45
+ end
46
+ end
47
+
41
48
  describe 'http_proxy' do
42
49
  it 'should set the address' do
43
50
  @klass.http_proxy 'proxy.foo.com', 80
@@ -103,6 +110,11 @@ describe HTTParty do
103
110
  HTTParty.normalize_base_uri(uri)
104
111
  uri.should == 'http://api.foobar.com'
105
112
  end
113
+
114
+ it "should not treat uri's with a port of 4430 as ssl" do
115
+ uri = HTTParty.normalize_base_uri('http://api.foo.com:4430/v1')
116
+ uri.should == 'http://api.foo.com:4430/v1'
117
+ end
106
118
  end
107
119
 
108
120
  describe "headers" do
@@ -321,6 +333,38 @@ describe HTTParty do
321
333
  end
322
334
  end
323
335
 
336
+ describe "connection_adapter" do
337
+ let(:uri) { 'http://google.com/api.json' }
338
+ let(:connection_adapter) { mock('CustomConnectionAdapter') }
339
+
340
+ it "should set the connection_adapter" do
341
+ @klass.connection_adapter connection_adapter
342
+ @klass.default_options[:connection_adapter].should be connection_adapter
343
+ end
344
+
345
+ it "should set the connection_adapter_options when provided" do
346
+ options = {:foo => :bar}
347
+ @klass.connection_adapter connection_adapter, options
348
+ @klass.default_options[:connection_adapter_options].should be options
349
+ end
350
+
351
+ it "should not set the connection_adapter_options when not provided" do
352
+ @klass.connection_adapter connection_adapter
353
+ @klass.default_options[:connection_adapter_options].should be_nil
354
+ end
355
+
356
+ it "should process a request with a connection from the adapter" do
357
+ connection_adapter_options = {:foo => :bar}
358
+ connection_adapter.should_receive(:call) do |u,o|
359
+ o[:connection_adapter_options].should == connection_adapter_options
360
+ HTTParty::ConnectionAdapter.call(u,o)
361
+ end.with(URI.parse(uri), kind_of(Hash))
362
+ FakeWeb.register_uri(:get, uri, :body => 'stuff')
363
+ @klass.connection_adapter connection_adapter, connection_adapter_options
364
+ @klass.get(uri).should == 'stuff'
365
+ end
366
+ end
367
+
324
368
  describe "format" do
325
369
  it "should allow xml" do
326
370
  @klass.format :xml
@@ -22,3 +22,9 @@ Spec::Runner.configure do |config|
22
22
  FakeWeb.allow_net_connect = true
23
23
  end
24
24
  end
25
+
26
+ Spec::Matchers.define :use_ssl do
27
+ match do |connection|
28
+ connection.use_ssl?
29
+ end
30
+ end
metadata CHANGED
@@ -1,69 +1,54 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: httparty
3
- version: !ruby/object:Gem::Version
4
- hash: 57
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 8
9
- - 3
10
- version: 0.8.3
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - John Nunemaker
14
9
  - Sandro Turriate
15
10
  autorequire:
16
11
  bindir: bin
17
12
  cert_chain: []
18
-
19
- date: 2012-04-22 00:00:00 Z
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
22
- type: :runtime
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
13
+ date: 2012-09-07 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: multi_json
17
+ requirement: &70139029121860 !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
19
+ requirements:
27
20
  - - ~>
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
21
+ - !ruby/object:Gem::Version
22
+ version: '1.0'
37
23
  type: :runtime
38
24
  prerelease: false
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
25
+ version_requirements: *70139029121860
26
+ - !ruby/object:Gem::Dependency
49
27
  name: multi_xml
28
+ requirement: &70139029121440 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70139029121440
50
37
  description: Makes http fun! Also, makes consuming restful web services dead easy.
51
- email:
38
+ email:
52
39
  - nunemaker@gmail.com
53
- executables:
40
+ executables:
54
41
  - httparty
55
42
  extensions: []
56
-
57
43
  extra_rdoc_files: []
58
-
59
- files:
44
+ files:
60
45
  - .gitignore
61
46
  - .travis.yml
62
47
  - Gemfile
63
48
  - Guardfile
64
49
  - History
65
50
  - MIT-LICENSE
66
- - README.rdoc
51
+ - README.md
67
52
  - Rakefile
68
53
  - bin/httparty
69
54
  - cucumber.yml
@@ -94,6 +79,7 @@ files:
94
79
  - features/supports_timeout_option.feature
95
80
  - httparty.gemspec
96
81
  - lib/httparty.rb
82
+ - lib/httparty/connection_adapter.rb
97
83
  - lib/httparty/cookie_hash.rb
98
84
  - lib/httparty/core_extensions.rb
99
85
  - lib/httparty/exceptions.rb
@@ -120,6 +106,7 @@ files:
120
106
  - spec/fixtures/twitter.json
121
107
  - spec/fixtures/twitter.xml
122
108
  - spec/fixtures/undefined_method_add_node_for_nil.xml
109
+ - spec/httparty/connection_adapter_spec.rb
123
110
  - spec/httparty/cookie_hash_spec.rb
124
111
  - spec/httparty/net_digest_auth_spec.rb
125
112
  - spec/httparty/parser_spec.rb
@@ -134,40 +121,37 @@ files:
134
121
  - spec/support/stub_response.rb
135
122
  - website/css/common.css
136
123
  - website/index.html
137
- homepage: http://httparty.rubyforge.org/
124
+ homepage: http://jnunemaker.github.com/httparty
138
125
  licenses: []
139
-
140
126
  post_install_message: When you HTTParty, you must party hard!
141
127
  rdoc_options: []
142
-
143
- require_paths:
128
+ require_paths:
144
129
  - lib
145
- required_ruby_version: !ruby/object:Gem::Requirement
130
+ required_ruby_version: !ruby/object:Gem::Requirement
146
131
  none: false
147
- requirements:
148
- - - ">="
149
- - !ruby/object:Gem::Version
150
- hash: 3
151
- segments:
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ segments:
152
137
  - 0
153
- version: "0"
154
- required_rubygems_version: !ruby/object:Gem::Requirement
138
+ hash: -1568840353759951881
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
140
  none: false
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- hash: 3
160
- segments:
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ segments:
161
146
  - 0
162
- version: "0"
147
+ hash: -1568840353759951881
163
148
  requirements: []
164
-
165
149
  rubyforge_project:
166
150
  rubygems_version: 1.8.10
167
151
  signing_key:
168
152
  specification_version: 3
169
153
  summary: Makes http fun! Also, makes consuming restful web services dead easy.
170
- test_files:
154
+ test_files:
171
155
  - features/basic_authentication.feature
172
156
  - features/command_line.feature
173
157
  - features/deals_with_http_error_codes.feature
@@ -196,6 +180,7 @@ test_files:
196
180
  - spec/fixtures/twitter.json
197
181
  - spec/fixtures/twitter.xml
198
182
  - spec/fixtures/undefined_method_add_node_for_nil.xml
183
+ - spec/httparty/connection_adapter_spec.rb
199
184
  - spec/httparty/cookie_hash_spec.rb
200
185
  - spec/httparty/net_digest_auth_spec.rb
201
186
  - spec/httparty/parser_spec.rb
@@ -1,47 +0,0 @@
1
- = httparty
2
-
3
- Makes http fun again!
4
-
5
- == Features:
6
-
7
- * Easy get, post requests
8
- * Basic http authentication
9
- * Default request query string parameters (ie: for api keys that are needed on each request)
10
- * Automatic parsing of JSON and XML into ruby hashes based on response content-type
11
-
12
- == Examples
13
-
14
- See http://github.com/jnunemaker/httparty/tree/master/examples
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
-
24
- == Command Line Interface
25
-
26
- httparty also includes the executable <tt>httparty</tt> which can be
27
- used to query web services and examine the resulting output. By default
28
- it will output the response as a pretty-printed Ruby object (useful for
29
- grokking the structure of output). This can also be overridden to output
30
- formatted XML or JSON. Execute <tt>httparty --help</tt> for all the
31
- options. Below is an example of how easy it is.
32
-
33
- httparty "http://twitter.com/statuses/public_timeline.json"
34
-
35
- == Requirements
36
-
37
- * multijson and multixml
38
- * You like to party!
39
-
40
- == Install
41
-
42
- * sudo gem install httparty
43
-
44
- == Help and Docs
45
-
46
- * https://groups.google.com/forum/#!forum/httparty-gem
47
- * http://rdoc.info/projects/jnunemaker/httparty