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

clears them from internal tracking.
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)

`uri` is expected to be a string.

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)

`uri` should be a parsed `URI` object.
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,

allows a request body, headers, and query string to be specified.
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

ugly.
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.")