class Stripe::ConnectionManager
Manages connections across multiple hosts which is useful because the library may connect to multiple hosts during a typical session (main API, Connect, Uploads). Ruby doesn't provide an easy way to make this happen easily, so this class is designed to track what we're connected to and manage the lifecycle of those connections.
Note that this class in itself is not thread safe. We expect it to be instantiated once per thread.
Attributes
Timestamp (in seconds procured from the system's monotonic clock) indicating when the connection manager last made a request. This is used by `StripeClient` to determine whether a connection manager should be garbage collected or not.
Public Class Methods
# File lib/stripe/connection_manager.rb, line 20 def initialize(config = Stripe.config) @config = config @active_connections = {} @last_used = Util.monotonic_time # A connection manager may be accessed across threads as one thread makes # requests on it while another is trying to clear it (either because it's # trying to garbage collect the manager or trying to clear it because a # configuration setting has changed). That's probably thread-safe already # because of Ruby's GIL, but just in case the library's running on JRuby # or the like, use a mutex to synchronize access in this connection # manager. @mutex = Mutex.new end
Public Instance Methods
Finishes any active connections by closing their TCP connection and clears them from internal tracking.
# File lib/stripe/connection_manager.rb, line 37 def clear @mutex.synchronize do @active_connections.each do |_, connection| connection.finish end @active_connections = {} end end
Gets a connection for a given URI. This is for internal use only as it's subject to change (we've moved between HTTP client schemes in the past and may do it again).
`uri` is expected to be a string.
# File lib/stripe/connection_manager.rb, line 51 def connection_for(uri) @mutex.synchronize do u = URI.parse(uri) connection = @active_connections[[u.host, u.port]] if connection.nil? connection = create_connection(u) connection.start @active_connections[[u.host, u.port]] = connection end connection end end
Executes an HTTP request to the given URI with the given method. Also allows a request body, headers, and query string to be specified.
# File lib/stripe/connection_manager.rb, line 69 def execute_request(method, uri, body: nil, headers: nil, query: nil, &block) # Perform some basic argument validation because it's easy to get # confused between strings and hashes for things like body and query # parameters. raise ArgumentError, "method should be a symbol" \ unless method.is_a?(Symbol) raise ArgumentError, "uri should be a string" \ unless uri.is_a?(String) raise ArgumentError, "body should be a string" \ if body && !body.is_a?(String) raise ArgumentError, "headers should be a hash" \ if headers && !headers.is_a?(Hash) raise ArgumentError, "query should be a string" \ if query && !query.is_a?(String) @last_used = Util.monotonic_time connection = connection_for(uri) u = URI.parse(uri) path = if query u.path + "?" + query else u.path end method_name = method.to_s.upcase has_response_body = method_name != "HEAD" request = Net::HTTPGenericRequest.new( method_name, (body ? true : false), has_response_body, path, headers ) @mutex.synchronize do # The block parameter is special here. If a block is provided, the block # is invoked with the Net::HTTPResponse. However, the body will not have # been read yet in the block, and can be streamed by calling # HTTPResponse#read_body. connection.request(request, body, &block) end end
Private Instance Methods
`uri` should be a parsed `URI` object.
# File lib/stripe/connection_manager.rb, line 120 def create_connection(uri) # These all come back as `nil` if no proxy is configured. proxy_host, proxy_port, proxy_user, proxy_pass = proxy_parts connection = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port, proxy_user, proxy_pass) # Time in seconds within which Net::HTTP will try to reuse an already # open connection when issuing a new operation. Outside this window, Ruby # will transparently close and re-open the connection without trying to # reuse it. # # Ruby's default of 2 seconds is almost certainly too short. Here I've # reused Go's default for `DefaultTransport`. connection.keep_alive_timeout = 30 connection.open_timeout = config.open_timeout connection.read_timeout = config.read_timeout if connection.respond_to?(:write_timeout=) connection.write_timeout = config.write_timeout end connection.use_ssl = uri.scheme == "https" if config.verify_ssl_certs connection.verify_mode = OpenSSL::SSL::VERIFY_PEER connection.cert_store = config.ca_store else connection.verify_mode = OpenSSL::SSL::VERIFY_NONE warn_ssl_verify_none end connection end
`Net::HTTP` somewhat awkwardly requires each component of a proxy URI (host, port, etc.) rather than the URI itself. This method simply parses out those pieces to make passing them into a new connection a little less ugly.
# File lib/stripe/connection_manager.rb, line 160 def proxy_parts if config.proxy.nil? [nil, nil, nil, nil] else u = URI.parse(config.proxy) [u.host, u.port, u.user, u.password] end end
# File lib/stripe/connection_manager.rb, line 169 def warn_ssl_verify_none return if @verify_ssl_warned @verify_ssl_warned = true warn("WARNING: Running without SSL cert verification. " \ "You should never do this in production. " \ "Execute `Stripe.verify_ssl_certs = true` to enable " \ "verification.") end