class Stripe::ConnectionManager
instantiated once per thread.
Note that this class in itself is not thread safe. We expect it to be
manage the lifecycle of those connections.
easily, so this class is designed to track what we’re connected to and
Connect, Uploads). Ruby doesn’t provide an easy way to make this happen
library may connect to multiple hosts during a typical session (main API,
Manages connections across multiple hosts which is useful because the
def clear
Finishes any active connections by closing their TCP connection and
def clear @mutex.synchronize do @active_connections.each do |_, connection| connection.finish end @active_connections = {} end end
def connection_for(uri)
and may do it again).
subject to change (we've moved between HTTP client schemes in the past
Gets a connection for a given URI. This is for internal use only as it's
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
def create_connection(uri)
def create_connection(uri) e all come back as `nil` if no proxy is configured. host, proxy_port, proxy_user, proxy_pass = proxy_parts tion = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port, proxy_user, proxy_pass) in seconds within which Net::HTTP will try to reuse an already connection when issuing a new operation. Outside this window, Ruby transparently close and re-open the connection without trying to e it. 's default of 2 seconds is almost certainly too short. Here I've ed Go's default for `DefaultTransport`. tion.keep_alive_timeout = 30 tion.open_timeout = config.open_timeout tion.read_timeout = config.read_timeout nection.respond_to?(:write_timeout=) ection.write_timeout = config.write_timeout tion.use_ssl = uri.scheme == "https" fig.verify_ssl_certs ection.verify_mode = OpenSSL::SSL::VERIFY_PEER ection.cert_store = config.ca_store ection.verify_mode = OpenSSL::SSL::VERIFY_NONE _ssl_verify_none tion
def execute_request(method, uri, body: nil, headers: nil, query: nil,
Executes an HTTP request to the given URI with the given method. Also
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 ) Util.log_debug("ConnectionManager starting request", method_name: method_name, path: path, process_id: Process.pid, thread_object_id: Thread.current.object_id, connection_manager_object_id: object_id, connection_object_id: connection.object_id, log_timestamp: Util.monotonic_time) resp = @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 Util.log_debug("ConnectionManager request complete", method_name: method_name, path: path, process_id: Process.pid, thread_object_id: Thread.current.object_id, connection_manager_object_id: object_id, connection_object_id: connection.object_id, response_object_id: resp.object_id, log_timestamp: Util.monotonic_time) resp end
def initialize(config = Stripe.config)
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
def proxy_parts
out those pieces to make passing them into a new connection a little less
(host, port, etc.) rather than the URI itself. This method simply parses
`Net::HTTP` somewhat awkwardly requires each component of a proxy URI
def proxy_parts fig.proxy.nil? , nil, nil, nil] URI.parse(config.proxy) ost, u.port, u.user, u.password]
def warn_ssl_verify_none
def warn_ssl_verify_none if @verify_ssl_warned y_ssl_warned = true WARNING: Running without SSL cert verification. " \ should never do this in production. " \ cute `Stripe.verify_ssl_certs = true` to enable " \ ification.")