class Bugsnag::Middleware::RackRequest

Extracts and attaches rack data to an error report

Constants

SPOOF

Public Class Methods

new(bugsnag) click to toggle source
# File lib/bugsnag/middleware/rack_request.rb, line 8
def initialize(bugsnag)
  @bugsnag = bugsnag
end

Public Instance Methods

call(report) click to toggle source
# File lib/bugsnag/middleware/rack_request.rb, line 12
def call(report)
  if report.request_data[:rack_env]
    env = report.request_data[:rack_env]

    request = ::Rack::Request.new(env)

    params =
      # if the request body isn't rewindable then we can't read request.POST
      # which is used internally by request.params
      if request.body.respond_to?(:rewind)
        request.params rescue {}
      else
        request.GET rescue {}
      end

    client_ip = request.ip.to_s rescue SPOOF
    session = env["rack.session"]

    # Set the automatic context
    report.automatic_context = "#{request.request_method} #{request.path}"

    # Set a sensible default for user_id
    report.user["id"] = request.ip

    # Build the clean url (hide the port if it is obvious)
    url = "#{request.scheme}://#{request.host}"
    url << ":#{request.port}" unless [80, 443].include?(request.port)

    # If app is passed a bad URL, this code will crash attempting to clean it
    begin
      url << Bugsnag.cleaner.clean_url(request.fullpath)
    rescue StandardError => stde
      Bugsnag.configuration.warn "RackRequest - Rescued error while cleaning request.fullpath: #{stde}"
    end

    referer = nil
    begin
      referer = Bugsnag.cleaner.clean_url(request.referer) if request.referer
    rescue StandardError => stde
      Bugsnag.configuration.warn "RackRequest - Rescued error while cleaning request.referer: #{stde}"
    end

    # Add a request tab
    report.add_tab(:request, {
      :url => url,
      :httpMethod => request.request_method,
      :params => params.to_hash,
      :referer => referer,
      :clientIp => client_ip,
      :headers => format_headers(env, referer)
    })

    # add the HTTP version if present
    if env["SERVER_PROTOCOL"]
      report.add_metadata(:request, :httpVersion, env["SERVER_PROTOCOL"])
    end

    add_request_body(report, request, env)
    add_cookies(report, request)

    # Add an environment tab
    if report.configuration.send_environment
      report.add_tab(:environment, env)
    end

    # Add a session tab
    if session
      if session.is_a?(Hash)
        # Rails 3
        report.add_tab(:session, session)
      elsif session.respond_to?(:to_hash)
        # Rails 4
        report.add_tab(:session, session.to_hash)
      end
    end
  end

  @bugsnag.call(report)
end

Private Instance Methods

add_cookies(report, request) click to toggle source
# File lib/bugsnag/middleware/rack_request.rb, line 158
def add_cookies(report, request)
  return unless record_cookies?

  cookies = request.cookies rescue nil

  return unless cookies.is_a?(Hash) && !cookies.empty?

  report.add_metadata(:request, :cookies, cookies)
end
add_request_body(report, request, env) click to toggle source
# File lib/bugsnag/middleware/rack_request.rb, line 114
def add_request_body(report, request, env)
  begin
    body = parsed_request_body(request, env)
  rescue StandardError
    return nil
  end

  # this request may not have a body
  return unless body.is_a?(Hash) && !body.empty?

  report.add_metadata(:request, :body, body)
end
format_headers(env, referer) click to toggle source
# File lib/bugsnag/middleware/rack_request.rb, line 94
def format_headers(env, referer)
  headers = {}

  env.each_pair do |key, value|
    if key.to_s.start_with?("HTTP_")
      header_key = key[5..-1]
    elsif ["CONTENT_TYPE", "CONTENT_LENGTH"].include?(key)
      header_key = key
    else
      next
    end

    headers[header_key.split("_").map {|s| s.capitalize}.join("-")] = value
  end

  headers["Referer"] = referer if headers["Referer"]

  headers
end
parsed_request_body(request, env) click to toggle source
# File lib/bugsnag/middleware/rack_request.rb, line 127
def parsed_request_body(request, env)
  # if the request is not rewindable then either:
  # - it's been read already and so is impossible to read
  # - it hasn't been read yet and us reading it will prevent the user from
  #   reading it themselves
  # in either case we should avoid attempting to
  return nil unless request.body.respond_to?(:rewind)

  if request.form_data?
    begin
      return request.POST
    ensure
      request.body.rewind
    end
  end

  content_type = env["CONTENT_TYPE"]

  return nil if content_type.nil?
  return nil unless content_type.include?('/json') || content_type.include?('+json')

  begin
    body = request.body

    JSON.parse(body.read)
  ensure
    # the body must be rewound so other things can read it after we do
    body.rewind
  end
end
record_cookies?() click to toggle source
# File lib/bugsnag/middleware/rack_request.rb, line 168
def record_cookies?
  # only record cookies in the request if none of the filters match "Cookie"
  # the "Cookie" header will be filtered as normal
  !Bugsnag.cleaner.filters_match?(COOKIE_HEADER)
end