class Seahorse::Client::NetHttp::Handler

The default HTTP handler for Seahorse::Client. This is based on the Ruby’s ‘Net::HTTP`.

Constants

DNS_ERROR_MESSAGES

@api private

NETWORK_ERRORS

Public Instance Methods

call(context) click to toggle source

@param [RequestContext] context @return [Response]

# File lib/seahorse/client/net_http/handler.rb, line 44
def call(context)
  span_wrapper(context) do
    transmit(
      context.config,
      context.http_request,
      context.http_response
    )
  end
  Response.new(context: context)
end
pool_for(config) click to toggle source

@param [Configuration] config @return [ConnectionPool]

# File lib/seahorse/client/net_http/handler.rb, line 57
def pool_for(config)
  ConnectionPool.for(pool_options(config))
end

Private Instance Methods

build_net_request(request) click to toggle source

Constructs and returns a Net::HTTP::Request object from a {Http::Request}. @param [Http::Request] request @return [Net::HTTP::Request]

# File lib/seahorse/client/net_http/handler.rb, line 152
def build_net_request(request)
  request_class = net_http_request_class(request)
  req = request_class.new(request.endpoint.request_uri, headers(request))
  # Net::HTTP adds a default Content-Type when a body is present.
  # Set the body stream when it has an unknown size or when it is > 0.
  if !request.body.respond_to?(:size) ||
     (request.body.respond_to?(:size) && request.body.size > 0)
    req.body_stream = request.body
  end
  req
end
complete_response(req, resp, bytes_received) click to toggle source
# File lib/seahorse/client/net_http/handler.rb, line 106
def complete_response(req, resp, bytes_received)
  if should_verify_bytes?(req, resp)
    verify_bytes_received(resp, bytes_received)
  else
    resp.signal_done
  end
end
error_message(req, error) click to toggle source
# File lib/seahorse/client/net_http/handler.rb, line 63
def error_message(req, error)
  if error.is_a?(SocketError) && DNS_ERROR_MESSAGES.include?(error.message)
    host = req.endpoint.host
    "unable to connect to `#{host}`; SocketError: #{error.message}"
  else
    error.message
  end
end
extract_headers(response) click to toggle source

@param [Net::HTTP::Response] response @return [Hash<String, String>]

# File lib/seahorse/client/net_http/handler.rb, line 194
def extract_headers(response)
  response.to_hash.inject({}) do |headers, (k, v)|
    headers[k] = v.first
    headers
  end
end
headers(request) click to toggle source

@param [Http::Request] request @return [Hash] Returns a vanilla hash of headers to send with the

HTTP request.
# File lib/seahorse/client/net_http/handler.rb, line 178
def headers(request)
  # Net::HTTP adds a default header for accept-encoding (2.0.0+).
  # Setting a default empty value defeats this.
  #
  # Removing this is necessary for most services to not break request
  # signatures as well as dynamodb crc32 checks (these fail if the
  # response is gzipped).
  headers = { 'accept-encoding' => '' }
  request.headers.each_pair do |key, value|
    headers[key] = value
  end
  headers
end
net_http_request_class(request) click to toggle source

@param [Http::Request] request @raise [InvalidHttpVerbError] @return Returns a base ‘Net::HTTP::Request` class, e.g.,

`Net::HTTP::Get`, `Net::HTTP::Post`, etc.
# File lib/seahorse/client/net_http/handler.rb, line 168
def net_http_request_class(request)
  Net::HTTP.const_get(request.http_method.capitalize)
rescue NameError
  msg = "`#{request.http_method}` is not a valid http verb"
  raise InvalidHttpVerbError, msg
end
pool_options(config) click to toggle source

Extracts the {ConnectionPool} configuration options. @param [Configuration] config @return [Hash]

# File lib/seahorse/client/net_http/handler.rb, line 141
def pool_options(config)
  ConnectionPool::OPTIONS.keys.inject({}) do |opts,opt|
    opts[opt] = config.send(opt)
    opts
  end
end
session(config, req) { |http| ... } click to toggle source
# File lib/seahorse/client/net_http/handler.rb, line 128
def session(config, req, &block)
  pool_for(config).session_for(req.endpoint) do |http|
    # Ruby 2.5, can disable retries for idempotent operations
    # avoid patching for Ruby 2.5 for disable retry
    http.max_retries = 0 if http.respond_to?(:max_retries)
    http.read_timeout = config.http_read_timeout
    yield(http)
  end
end
should_verify_bytes?(req, resp) click to toggle source
# File lib/seahorse/client/net_http/handler.rb, line 114
def should_verify_bytes?(req, resp)
  req.http_method != 'HEAD' && resp.headers['content-length']
end
span_wrapper(context, &block) click to toggle source
# File lib/seahorse/client/net_http/handler.rb, line 201
def span_wrapper(context, &block)
  context.tracer.in_span(
    'Handler.NetHttp',
    attributes: Aws::Telemetry.http_request_attrs(context)
  ) do |span|
    block.call
    span.add_attributes(
      Aws::Telemetry.http_response_attrs(context)
    )
  end
end
transmit(config, req, resp) click to toggle source

@param [Configuration] config @param [Http::Request] req @param [Http::Response] resp @return [void]

# File lib/seahorse/client/net_http/handler.rb, line 76
def transmit(config, req, resp)
  session(config, req) do |http|
    # Monkey patch default content-type set by Net::HTTP
    Thread.current[:net_http_skip_default_content_type] = true
    http.request(build_net_request(req)) do |net_resp|
      status_code = net_resp.code.to_i
      headers = extract_headers(net_resp)

      bytes_received = 0
      resp.signal_headers(status_code, headers)
      net_resp.read_body do |chunk|
        bytes_received += chunk.bytesize
        resp.signal_data(chunk)
      end
      complete_response(req, resp, bytes_received)

    end
  end
rescue *NETWORK_ERRORS => error
  # these are retryable
  error = NetworkingError.new(error, error_message(req, error))
  resp.signal_error(error)
rescue => error
  # not retryable
  resp.signal_error(error)
ensure
  # ensure we turn off monkey patch in case of error
  Thread.current[:net_http_skip_default_content_type] = nil
end
verify_bytes_received(resp, bytes_received) click to toggle source
# File lib/seahorse/client/net_http/handler.rb, line 118
def verify_bytes_received(resp, bytes_received)
  bytes_expected = resp.headers['content-length'].to_i
  if bytes_expected == bytes_received
    resp.signal_done
  else
    error = TruncatedBodyError.new(bytes_expected, bytes_received)
    resp.signal_error(NetworkingError.new(error, error.message))
  end
end