Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom http connection factories #157

Merged
merged 2 commits into from
Sep 7, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/httparty.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
require 'httparty/cookie_hash'
require 'httparty/net_digest_auth'
require 'httparty/version'
require 'httparty/connection_adapter'

# @see HTTParty::ClassMethods
module HTTParty
Expand Down Expand Up @@ -57,9 +58,11 @@ def self.included(base)
# * :+maintain_method_across_redirects+: see HTTParty::ClassMethods.maintain_method_across_redirects.
# * :+no_follow+: see HTTParty::ClassMethods.no_follow.
# * :+parser+: see HTTParty::ClassMethods.parser.
# * :+connection_adapter+: see HTTParty::ClassMethods.connection_adapter.
# * :+pem+: see HTTParty::ClassMethods.pem.
# * :+query_string_normalizer+: see HTTParty::ClassMethods.query_string_normalizer
# * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
# * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.

module ClassMethods

Expand Down Expand Up @@ -331,6 +334,30 @@ def parser(custom_parser = nil)
end
end

# Allows setting a custom connection_adapter for the http connections
#
# @example
# class Foo
# include HTTParty
# connection_adapter Proc.new {|uri, options| ... }
# end
#
# @example provide optional configuration for your connection_adapter
# class Foo
# include HTTParty
# connection_adapter Proc.new {|uri, options| ... }, {:foo => :bar}
# end
#
# @see HTTParty::ConnectionAdapter
def connection_adapter(custom_adapter = nil, options = nil)
if custom_adapter.nil?
default_options[:connection_adapter]
else
default_options[:connection_adapter] = custom_adapter
default_options[:connection_adapter_options] = options
end
end

# Allows making a get request to a url.
#
# class Foo
Expand Down
111 changes: 111 additions & 0 deletions lib/httparty/connection_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
module HTTParty
# Default connection adapter that returns a new Net::HTTP each time
#
# == Custom Connection Factories
#
# If you like to implement your own connection adapter, subclassing
# HTTPParty::ConnectionAdapter will make it easier. Just override
# the #connection method. The uri and options attributes will have
# all the info you need to construct your http connection. Whatever
# you return from your connection method needs to adhere to the
# Net::HTTP interface as this is what HTTParty expects.
#
# @example log the uri and options
# class LoggingConnectionAdapter < HTTParty::ConnectionAdapter
# def connection
# puts uri
# puts options
# Net::HTTP.new(uri)
# end
# end
#
# @example count number of http calls
# class CountingConnectionAdapter < HTTParty::ConnectionAdapter
# @@count = 0
#
# self.count
# @@count
# end
#
# def connection
# self.count += 1
# super
# end
# end
#
# === Configuration
# There is lots of configuration data available for your connection adapter
# in the #options attribute. It is up to you to interpret them within your
# connection adapter. Take a look at the implementation of
# HTTParty::ConnectionAdapter#connection for examples of how they are used.
# Something are probably interesting are as follows:
# * :+timeout+: timeout in seconds
# * :+debug_output+: see HTTParty::ClassMethods.debug_output.
# * :+pem+: contains pem data. see HTTParty::ClassMethods.pem.
# * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
# * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
# * :+connection_adapter_options+: contains the hash your passed to HTTParty.connection_adapter when you configured your connection adapter
class ConnectionAdapter

def self.call(uri, options)
new(uri, options).connection
end

attr_reader :uri, :options

def initialize(uri, options={})
raise ArgumentError, "uri must be a URI, not a #{uri.class}" unless uri.kind_of? URI

@uri = uri
@options = options
end

def connection
http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])

http.use_ssl = ssl_implied?(uri)

attach_ssl_certificates(http, options)

if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
http.open_timeout = options[:timeout]
http.read_timeout = options[:timeout]
end

if options[:debug_output]
http.set_debug_output(options[:debug_output])
end

return http
end

private
def ssl_implied?(uri)
uri.port == 443 || uri.instance_of?(URI::HTTPS)
end

def attach_ssl_certificates(http, options)
if http.use_ssl?
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

# Client certificate authentication
if options[:pem]
http.cert = OpenSSL::X509::Certificate.new(options[:pem])
http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end

# SSL certificate authority file and/or directory
if options[:ssl_ca_file]
http.ca_file = options[:ssl_ca_file]
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end

if options[:ssl_ca_path]
http.ca_path = options[:ssl_ca_path]
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
end
end
end
end
51 changes: 7 additions & 44 deletions lib/httparty/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def initialize(http_method, path, o={})
:limit => o.delete(:no_follow) ? 1 : 5,
:default_params => {},
:follow_redirects => true,
:parser => Parser
:parser => Parser,
:connection_adapter => ConnectionAdapter
}.merge(o)
end

Expand Down Expand Up @@ -68,6 +69,10 @@ def parser
options[:parser]
end

def connection_adapter
options[:connection_adapter]
end

def perform(&block)
validate
setup_raw_request
Expand All @@ -92,50 +97,8 @@ def perform(&block)

private

def attach_ssl_certificates(http)
if http.use_ssl?
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

# Client certificate authentication
if options[:pem]
http.cert = OpenSSL::X509::Certificate.new(options[:pem])
http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end

# SSL certificate authority file and/or directory
if options[:ssl_ca_file]
http.ca_file = options[:ssl_ca_file]
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end

if options[:ssl_ca_path]
http.ca_path = options[:ssl_ca_path]
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
end
end

def http
http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
http.use_ssl = ssl_implied?

if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
http.open_timeout = options[:timeout]
http.read_timeout = options[:timeout]
end

attach_ssl_certificates(http)

if options[:debug_output]
http.set_debug_output(options[:debug_output])
end

http
end

def ssl_implied?
uri.port == 443 || uri.instance_of?(URI::HTTPS)
connection_adapter.call(uri, options)
end

def body
Expand Down
Loading