class Sentry::RequestInterface

Constants

CONTENT_HEADERS
IP_HEADERS
MAX_BODY_LIMIT

See Sentry server default limits at github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py

REQUEST_ID_HEADERS

Attributes

cookies[RW]
data[RW]
env[RW]
headers[RW]
method[RW]
query_string[RW]
url[RW]

Public Class Methods

build(env:) click to toggle source
# File lib/sentry/interfaces/request.rb, line 20
def self.build(env:)
  env = clean_env(env)
  request = ::Rack::Request.new(env)
  self.new(request: request)
end
clean_env(env) click to toggle source
# File lib/sentry/interfaces/request.rb, line 26
def self.clean_env(env)
  unless Sentry.configuration.send_default_pii
    # need to completely wipe out ip addresses
    RequestInterface::IP_HEADERS.each do |header|
      env.delete(header)
    end
  end

  env
end
new(request:) click to toggle source
# File lib/sentry/interfaces/request.rb, line 37
def initialize(request:)
  env = request.env

  if Sentry.configuration.send_default_pii
    self.data = read_data_from(request)
    self.cookies = request.cookies
    self.query_string = request.query_string
  end

  self.url = request.scheme && request.url.split('?').first
  self.method = request.request_method

  self.headers = filter_and_format_headers(env)
  self.env     = filter_and_format_env(env)
end

Private Instance Methods

encode_to_utf_8(value) click to toggle source
# File lib/sentry/interfaces/request.rb, line 91
def encode_to_utf_8(value)
  if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
    value = value.dup.force_encoding(Encoding::UTF_8)
  end

  if !value.valid_encoding?
    value = value.scrub
  end

  value
end
filter_and_format_env(env) click to toggle source
# File lib/sentry/interfaces/request.rb, line 119
def filter_and_format_env(env)
  return env if Sentry.configuration.rack_env_whitelist.empty?

  env.select do |k, _v|
    Sentry.configuration.rack_env_whitelist.include? k.to_s
  end
end
filter_and_format_headers(env) click to toggle source
# File lib/sentry/interfaces/request.rb, line 68
def filter_and_format_headers(env)
  env.each_with_object({}) do |(key, value), memo|
    begin
      key = key.to_s # rack env can contain symbols
      next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
      next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
      next if is_skippable_header?(key)

      # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
      key = key.sub(/^HTTP_/, "")
      key = key.split('_').map(&:capitalize).join('-')

      memo[key] = encode_to_utf_8(value.to_s)
    rescue StandardError => e
      # Rails adds objects to the Rack env that can sometimes raise exceptions
      # when `to_s` is called.
      # See: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L134
      Sentry.logger.warn(LOGGER_PROGNAME) { "Error raised while formatting headers: #{e.message}" }
      next
    end
  end
end
is_server_protocol?(key, value, protocol_version) click to toggle source

Rack adds in an incorrect HTTP_VERSION key, which causes downstream to think this is a Version header. Instead, this is mapped to env. But we don't want to ignore a valid header if the request has legitimately sent a Version header themselves. See: github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29 NOTE: This will be removed in version 3.0+

# File lib/sentry/interfaces/request.rb, line 115
def is_server_protocol?(key, value, protocol_version)
  key == 'HTTP_VERSION' && value == protocol_version
end
is_skippable_header?(key) click to toggle source
# File lib/sentry/interfaces/request.rb, line 103
def is_skippable_header?(key)
  key.upcase != key || # lower-case envs aren't real http headers
    key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
    !(key.start_with?('HTTP_') || CONTENT_HEADERS.include?(key))
end
read_data_from(request) click to toggle source
# File lib/sentry/interfaces/request.rb, line 55
def read_data_from(request)
  if request.form_data?
    request.POST
  elsif request.body # JSON requests, etc
    data = request.body.read(MAX_BODY_LIMIT)
    data = encode_to_utf_8(data.to_s)
    request.body.rewind
    data
  end
rescue IOError => e
  e.message
end