Class: Contrast::Agent::Request

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Components::Interface
Defined in:
lib/contrast/agent/request.rb

Overview

This class is the Contrast representation of the Rack::Request object. It provides access to the original Rack::Request object as well as extracts data in a format that the Agent expects, caching those transformations in order to avoid repeatedly creating Strings & thrashing GC.

Constant Summary collapse

INNER_REST_TOKEN =
%r{/\d+/}.cs__freeze
LAST_REST_TOKEN =
%r{/\d+$}.cs__freeze
INNER_NUMBER_MARKER =
'/{n}/'
LAST_NUMBER_MARKER =
'/{n}'
STATIC_SUFFIXES =
/\.(?:js|css|jpeg|jpg|gif|png|ico|woff|svg|pdf|eot|ttf|jar)$/i.cs__freeze
MEDIA_TYPE_MARKERS =
%w[image/ text/css text/javascript].cs__freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Components::Interface

included

Constructor Details

#initialize(rack_request) ⇒ Request

Returns a new instance of Request.



34
35
36
# File 'lib/contrast/agent/request.rb', line 34

def initialize rack_request
  @rack_request = rack_request
end

Instance Attribute Details

#rack_requestObject (readonly)

Returns the value of attribute rack_request.



29
30
31
# File 'lib/contrast/agent/request.rb', line 29

def rack_request
  @rack_request
end

Instance Method Details

#bodyObject



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/contrast/agent/request.rb', line 87

def body
  # Memoize a flag indicating whether we've tried to read the body or not
  # (can't use body because it might be nil)
  @_body_read ||= begin
    body = rack_request.body
    if defined?(Rack::Multipart) && defined?(Rack::Multipart::UploadedFile) && body.is_a?(Rack::Multipart::UploadedFile)
      logger.trace("not parsing uploaded file body :: #{ body.original_filename }::#{ body.content_type }")
      @_body = nil
    else
      logger.trace("parsing body from request :: #{ body.cs__class.cs__name }")
      @_body = Contrast::Utils::StringUtils.force_utf8(read_body(body))
    end

    true
  end

  # Return memoized body (which might be nil)
  @_body
end

#document_typeObject



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/contrast/agent/request.rb', line 57

def document_type
  @_document_type ||= begin
    if /xml/i.match?(content_type) || body&.start_with?('<?xml')
      :XML
    elsif /json/i.match?(content_type) || body&.match?(/\s*[{\[]/)
      :JSON
    else
      :NORMAL
    end
  end
end

#dtmContrast::Api::Dtm::HttpRequest

Unlike most of our translation, which is called where needed for each message and forgotten, we’ll leave this method to call the build as we don’t want to pay to reconstruct the DTM for this Request multiple times.

Returns:



114
115
116
# File 'lib/contrast/agent/request.rb', line 114

def dtm
  @_dtm ||= Contrast::Api::Dtm::HttpRequest.build(self)
end

#file_namesObject



122
123
124
125
126
127
128
129
130
131
# File 'lib/contrast/agent/request.rb', line 122

def file_names
  @_file_names ||= begin
    names = {}
    parsed_data = Rack::Multipart.parse_multipart(rack_request.env)
    traverse_parsed_multipart(parsed_data, names)
  rescue StandardError => _e
    logger.warn('Unable to parse multipart request!')
    {}
  end
end

#hash_idObject



133
134
135
# File 'lib/contrast/agent/request.rb', line 133

def hash_id
  @_hash_id ||= Contrast::Utils::HashDigest.generate_request_hash(self)
end

#headersObject

Header keys upcased and any underscores replaced with dashes



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/contrast/agent/request.rb', line 70

def headers
  @_headers ||= begin
    with_contrast_scope do
      hash = {}
      env.each do |key, value|
        next unless key

        name = key.to_s
        next unless name.start_with?(Contrast::Utils::ObjectShare::HTTP_SCORE)

        hash[Contrast::Utils::StringUtils.normalized_key(name)] = value
      end
      hash
    end
  end
end

#normalized_uriObject

Returns a normalized form of the URI. In “normal” URIs this will return an unchanged String, but in REST-y URIs this will normalize the digit path tokens, e.g.:

/accounts/5/view …becomes: /accounts/n/view

Should also handle the ;jsessionid.



47
48
49
50
51
52
53
54
55
# File 'lib/contrast/agent/request.rb', line 47

def normalized_uri
  @_normalized_uri ||= begin
    path = rack_request.path
    uri = path.split(Contrast::Utils::ObjectShare::SEMICOLON)[0]    # remove ;jsessionid
    uri = uri.split(Contrast::Utils::ObjectShare::QUESTION_MARK)[0] # remove ?query_string=
    uri.gsub(INNER_REST_TOKEN, INNER_NUMBER_MARKER)                 # replace interior tokens
    uri.gsub(LAST_REST_TOKEN, LAST_NUMBER_MARKER)                   # replace last token
  end
end

#parametersObject



118
119
120
# File 'lib/contrast/agent/request.rb', line 118

def parameters
  @_parameters ||= with_contrast_scope { normalize_params(rack_request.params) }
end

#static?Boolean

Utility method for checking if a request is for a static resource.

Returns:

  • (Boolean)

    true, if the request is for a well-known static type as determined by the request suffix or the accept header.



142
143
144
145
146
147
148
149
150
# File 'lib/contrast/agent/request.rb', line 142

def static?
  return true if normalized_uri&.match?(STATIC_SUFFIXES)

  accepts = Array(headers['ACCEPT'])&.first&.to_s
  return false unless accepts
  return false if accepts.start_with?('*/*')

  accepts.start_with?(*MEDIA_TYPE_MARKERS)
end