Class: Contrast::Agent::Request
- Extended by:
- Forwardable
- Includes:
- Components::Logger::InstanceMethods, Components::Scope::InstanceMethods, Utils::RequestUtils
- Defined in:
- lib/contrast/agent/request/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
- EMPTY_PATH =
'/'
Constants included from Utils::RequestUtils
Utils::RequestUtils::END_PATTERN, Utils::RequestUtils::HASH_PATTERN, Utils::RequestUtils::ID_, Utils::RequestUtils::MEDIA_TYPE_MARKERS, Utils::RequestUtils::NUM_, Utils::RequestUtils::NUM_PATTERN, Utils::RequestUtils::STATIC_SUFFIXES, Utils::RequestUtils::UUID_PATTERN, Utils::RequestUtils::WIN_PATTERN
Instance Attribute Summary collapse
- #discovered_route ⇒ Contrast::Agent::Reporting::RouteDiscovery
-
#observed_route ⇒ Contrast::Agent::Reporting::ObservedRoute
The route, used for coverage, of this request.
-
#rack_request ⇒ Rack::Request
readonly
The passed to the Agent RackRequest to be wrapped.
Instance Method Summary collapse
-
#body ⇒ Object
Try and read the body and return memorized object of the body.
-
#document_type ⇒ Object
Returns the request file type.
-
#file_names ⇒ Object
returns multipart filenames.
-
#hash_id ⇒ Object
returns or generates the hash checksum for the request.
-
#headers ⇒ Object
Header keys upcased and any underscores replaced with dashes.
-
#initialize(rack_request) ⇒ Request
constructor
Initialize new Contrast Request.
-
#normalized_uri ⇒ Object
Returns a normalized form of the URI.
-
#parameters ⇒ Object
flattened hash of request params.
-
#static? ⇒ Boolean
Utility method for checking if a request is for a static resource.
-
#version ⇒ String
The HTTP version of this request.
Methods included from Components::Scope::InstanceMethods
#contrast_enter_method_scopes!, #contrast_exit_method_scopes!, #with_app_scope, #with_contrast_scope, #with_deserialization_scope, #with_split_scope
Methods included from Components::Logger::InstanceMethods
Methods included from Utils::RequestUtils
#normalize_params, #read_body, #traverse_parsed_multipart
Constructor Details
#initialize(rack_request) ⇒ Request
Initialize new Contrast Request
44 45 46 |
# File 'lib/contrast/agent/request/request.rb', line 44 def initialize rack_request @rack_request = rack_request end |
Instance Attribute Details
#discovered_route ⇒ Contrast::Agent::Reporting::RouteDiscovery
35 36 37 |
# File 'lib/contrast/agent/request/request.rb', line 35 def discovered_route @discovered_route end |
#observed_route ⇒ Contrast::Agent::Reporting::ObservedRoute
Returns the route, used for coverage, of this request.
33 34 35 |
# File 'lib/contrast/agent/request/request.rb', line 33 def observed_route @observed_route end |
#rack_request ⇒ Rack::Request (readonly)
Returns The passed to the Agent RackRequest to be wrapped.
31 32 33 |
# File 'lib/contrast/agent/request/request.rb', line 31 def rack_request @rack_request end |
Instance Method Details
#body ⇒ Object
Try and read the body and return memorized object of the body. If body contains file, do not parse it, otherwiseBody might be nil
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/contrast/agent/request/request.rb', line 137 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', file_name: body.original_filename, content_type: body.content_type) @_body = nil else logger.trace('parsing body from request', body_type: 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_type ⇒ Object
Returns the request file type
105 106 107 108 109 110 111 112 113 |
# File 'lib/contrast/agent/request/request.rb', line 105 def document_type @_document_type ||= if /xml/i.match?(media_type) || body&.start_with?('<?xml') :XML elsif /json/i.match?(media_type) || body&.match?(/\s*[{\[]/) :JSON else :NORMAL end end |
#file_names ⇒ Object
returns multipart filenames
172 173 174 175 176 177 178 179 180 181 |
# File 'lib/contrast/agent/request/request.rb', line 172 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_id ⇒ Object
returns or generates the hash checksum for the request
186 187 188 |
# File 'lib/contrast/agent/request/request.rb', line 186 def hash_id @_hash_id ||= Contrast::Utils::HashDigest.generate_request_hash(self) end |
#headers ⇒ Object
Header keys upcased and any underscores replaced with dashes
118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/contrast/agent/request/request.rb', line 118 def headers @_headers ||= 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 |
#normalized_uri ⇒ Object
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.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/contrast/agent/request/request.rb', line 71 def normalized_uri @_normalized_uri ||= begin path = rack_request.path_info || rack_request.path.to_s path = EMPTY_PATH if Contrast::Utils::DuckUtils.empty_duck?(path) # /foo/bar;jsessionid=123 => /foo/bar uri = path.split(Contrast::Utils::ObjectShare::SEMICOLON)[0] # /foo/bar?query_string=123 => /foo/bar uri = uri.split(Contrast::Utils::ObjectShare::QUESTION_MARK)[0] # Replace with tokens: # NUM_ => '/{n}/' # ID_ => '{ID}' # # replace UUIDs: /123e4567-e89b-42d3-a456-556642440000/ => /{ID}/ uri.gsub!(UUID_PATTERN, ID_) # replace hash patterns: /6f1ed002ab5595859014ebf0951522d9/ => /{ID}/ uri.gsub!(HASH_PATTERN, ID_) # replace windows SID: /S-1-5-21-1843332746-572796286-2118856591-1000/ => /{ID}/ uri.gsub!(WIN_PATTERN, ID_) # replace interior number tokens: /123/ => /{n}/ uri.gsub!(NUM_PATTERN, NUM_) # replace last number tokens: /123 => /{n} uri.gsub!(END_PATTERN, NUM_[0..-2]) uri rescue StandardError => e logger.error('error normalizing uri', error: e, backtrace: e.backtrace) EMPTY_PATH end end |
#parameters ⇒ Object
flattened hash of request params
165 166 167 |
# File 'lib/contrast/agent/request/request.rb', line 165 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.
193 194 195 196 197 198 199 200 201 |
# File 'lib/contrast/agent/request/request.rb', line 193 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 |
#version ⇒ String
The HTTP version of this request
50 51 52 53 54 55 56 57 58 |
# File 'lib/contrast/agent/request/request.rb', line 50 def version env = rack_request.env return '1.1' unless env version = env['HTTP_VERSION'] return '1.1' unless version version.split('/')[-1] end |