module OneApm::Collector::CollectorService::HttpConnection

Constants

OA_API_VERSION
OA_CONNECTION_ERRORS

These include Errno connection errors, and all indicate that the underlying TCP connection may be in a bad state.

Public Instance Methods

cert_file_path() click to toggle source

The path to the certificate file used to verify the SSL connection if verify_peer is enabled

# File lib/one_apm/collector/collector/http_connection.rb, line 215
def cert_file_path
  if path_override = OneApm::Manager.config[:ca_bundle_path]
    OneApm::Manager.logger.warn("Couldn't find CA bundle from configured ca_bundle_path: #{path_override}") unless File.exists? path_override
    path_override
  else
    File.expand_path(File.join(probe.oneapm_root, 'config', 'cert', 'cacert.pem'))
  end
end
check_post_size(post_string) click to toggle source

Raises an UnrecoverableServerException if the post_string is longer than the limit configured in the probe object

# File lib/one_apm/collector/collector/http_connection.rb, line 244
def check_post_size(post_string)
  return if post_string.size < OneApm::Manager.config[:post_size_limit]
  OneApm::Manager.logger.debug "Tried to send too much data: #{post_string.size} bytes"
  raise UnrecoverableServerException.new('413 Request Entity Too Large')
end
close_shared_connection() click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 146
def close_shared_connection
  if @shared_tcp_connection
    OneApm::Manager.logger.debug("Closing shared TCP connection to #{@shared_tcp_connection.address}:#{@shared_tcp_connection.port}")
    @shared_tcp_connection.finish if @shared_tcp_connection.started?
    @shared_tcp_connection = nil
  end
end
compress_request(data) click to toggle source

Compress request data

# File lib/one_apm/collector/collector/http_connection.rb, line 225
def compress_request(data)
  encoding = 'deflate'
  data = OneApm::Support::Encoders::Compressed.encode(data)
  check_post_size(data)
  [data, encoding]
end
create_and_start_http_connection() click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 163
def create_and_start_http_connection
  conn = create_http_connection
  start_connection(conn)
  conn
end
create_http_connection() click to toggle source

Return the Net::HTTP with proxy configuration given the OneApm::Support::Server object.

# File lib/one_apm/collector/collector/http_connection.rb, line 176
def create_http_connection
  # Proxy returns regular HTTP if @proxy_host is nil (the default)
  http_class = Net::HTTP::Proxy(proxy_server.name, proxy_server.port,
                                proxy_server.user, proxy_server.password)

  conn = http_class.new((@collector.ip || @collector.name), @collector.port)
  setup_connection_for_ssl(conn) if OneApm::Manager.config[:ssl]
  setup_connection_timeouts(conn)

  OneApm::Manager.logger.debug("Created net/http handle to #{conn.address}:#{conn.port}")
  conn
end
decompress_response(response) click to toggle source

Decompresses the response from the server, if it is gzip encoded, otherwise returns it verbatim

# File lib/one_apm/collector/collector/http_connection.rb, line 234
def decompress_response(response)
  if response['content-encoding'] == 'gzip'
    Zlib::GzipReader.new(StringIO.new(response.body)).read
  else
    response.body
  end
end
establish_shared_connection() click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 139
def establish_shared_connection
  unless @shared_tcp_connection
    @shared_tcp_connection = create_and_start_http_connection
  end
  @shared_tcp_connection
end
http_connection() click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 131
def http_connection
  if @in_session
    establish_shared_connection
  else
    create_http_connection
  end
end
invoke_remote(method, payload = [], options = {},query_params={}) click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 11
def invoke_remote(method, payload = [], options = {},query_params={})
  start_ts = Time.now

  data = nil
  begin
    data = @marshaller.dump(payload, options)
  rescue StandardError, SystemStackError => e
    handle_serialization_error(method, e)
  end
  serialize_finish_ts = Time.now

  data, encoding = compress_request(data)
  size = data.size

  uri = remote_method_uri(method, @marshaller.format,query_params)
  full_uri = "#{@collector}#{uri}"

  @audit_logger.log_request(full_uri, payload, @marshaller)
  response = send_request(:data      => data,
                          :uri       => uri,
                          :encoding  => encoding,
                          :collector => @collector)
  response =  @marshaller.load(decompress_response(response))
  @audit_logger.log_response(response)
  response
ensure
  record_timing_supportability_metrics(method, start_ts, serialize_finish_ts)
  if size
    record_size_supportability_metrics(method, size, options[:item_count])
  end
end
remote_method_uri(method, format, query_params={}) click to toggle source

The path on the server that we should post our data to

# File lib/one_apm/collector/collector/http_connection.rb, line 44
def remote_method_uri(method, format, query_params={})
  params = {'run_id' => @agent_id, 'marshal_format' => format}.merge(query_params)
  uri = "/tpm/agent.do?PROTOCOL_VERSION=#{OA_PROTOCOL_VERSION}&license_key=#{@license_key}&method=#{method}"
  uri << '&' + params.map do |k,v|
    next unless v
    "#{k}=#{v}"
  end.compact.join('&')
  uri
end
send_request(opts) click to toggle source

Posts to the specified server

Options:

- :uri => the path to request on the server (a misnomer of
            course)
- :encoding => the encoding to pass to the server
- :collector => a URI object that responds to the 'name' method
                  and returns the name of the collector to
                  contact
- :data => the data to send as the body of the request
# File lib/one_apm/collector/collector/http_connection.rb, line 64
def send_request(opts)
  request = Net::HTTP::Post.new(opts[:uri],
                                'CONTENT-ENCODING' => opts[:encoding],
                                'HOST'             => opts[:collector].name,
                                'API-VERSION'      => OA_API_VERSION)
  request['user-agent'] = user_agent
  request['_SKIP_CAT_'] = true
  request.body = opts[:data]
  #see http://jira.oneapm.me/browse/AI-3227
  #old content_type is  "multipart/form-data; boundary=-oneapm-#{request.object_id}-"
  request.content_type = 'application/json'
  response     = nil
  attempts     = 0
  max_attempts = 2

  begin
    attempts += 1
    conn = http_connection
    OneApm::Manager.logger.debug "Sending request to #{opts[:collector]}#{opts[:uri]}"
    OneApm::TimerLib.timeout(@request_timeout) do
      response = conn.request(request)
    end
  rescue *OA_CONNECTION_ERRORS => e
    close_shared_connection
    if attempts < max_attempts
      OneApm::Manager.logger.debug("Retrying request to #{opts[:collector]}#{opts[:uri]} after #{e}")
      retry
    else
      raise ServerConnectionException, "Recoverable error talking to #{@collector} after #{attempts} attempts: #{e}"
    end
  end
  log_and_return_response response
end
session(&block) click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 98
def session(&block)
  raise ArgumentError, "#{self.class}#shared_connection must be passed a block" unless block_given?

  begin
    t0 = Time.now
    @in_session = true
    if OneApm::Manager.config[:aggressive_keepalive]
      session_with_keepalive(&block)
    else
      session_without_keepalive(&block)
    end
  rescue *OA_CONNECTION_ERRORS => e
    elapsed = Time.now - t0
    raise OneApm::ServerConnectionException, "Recoverable error connecting to #{@collector} after #{elapsed} seconds: #{e}"
  ensure
    @in_session = false
  end
end
session_with_keepalive(&block) click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 117
def session_with_keepalive(&block)
  establish_shared_connection
  block.call
end
session_without_keepalive(&block) click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 122
def session_without_keepalive(&block)
  begin
    establish_shared_connection
    block.call
  ensure
    close_shared_connection
  end
end
setup_connection_for_ssl(conn) click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 189
def setup_connection_for_ssl(conn)
  # Jruby 1.6.8 requires a gem for full ssl support and will throw
  # an error when use_ssl=(true) is called and jruby-openssl isn't
  # installed
  conn.use_ssl     = true
  conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
  conn.cert_store  = ssl_cert_store
rescue StandardError, LoadError
  msg = "Agent is configured to use SSL, but SSL is not available in the environment. "
  msg << "Either disable SSL in the agent configuration, or install SSL support."
  raise UnrecoverableAgentException.new(msg)
end
setup_connection_timeouts(conn) click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 154
def setup_connection_timeouts(conn)
  # We use Timeout explicitly instead of this
  conn.read_timeout = nil

  if conn.respond_to?(:keep_alive_timeout) && OneApm::Manager.config[:aggressive_keepalive]
    conn.keep_alive_timeout = OneApm::Manager.config[:keep_alive_timeout]
  end
end
ssl_cert_store() click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 202
def ssl_cert_store
  path = cert_file_path
  if !@ssl_cert_store || path != @cached_cert_store_path
    OneApm::Manager.logger.debug("Creating SSL certificate store from file at #{path}")
    @ssl_cert_store = OpenSSL::X509::Store.new
    @ssl_cert_store.add_file(path)
    @cached_cert_store_path = path
  end
  @ssl_cert_store
end
start_connection(conn) click to toggle source
# File lib/one_apm/collector/collector/http_connection.rb, line 169
def start_connection(conn)
  OneApm::Manager.logger.debug("Opening TCP connection to #{conn.address}:#{conn.port}")
  OneApm::TimerLib.timeout(@request_timeout) { conn.start }
  conn
end