Skip to content

Commit 57bb1d1

Browse files
committed
Merge branch 'perf/request-interface-optimizations' into perf/reduce-memory-allocations
# Conflicts: # sentry-ruby/lib/sentry/interfaces/request.rb
2 parents 37508ca + 2377baa commit 57bb1d1

1 file changed

Lines changed: 29 additions & 18 deletions

File tree

sentry-ruby/lib/sentry/interfaces/request.rb

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@ class RequestInterface < Interface
1111
"HTTP_X_FORWARDED_FOR"
1212
].freeze
1313

14-
# Regex to detect lowercase chars — match? is allocation-free (no MatchData/String)
15-
LOWERCASE_PATTERN = /[a-z]/.freeze
14+
# Cache for Rack env key → HTTP header name transformations
15+
# e.g. "HTTP_ACCEPT_LANGUAGE" → "Accept-Language", "CONTENT_TYPE" → "Content-Type"
16+
@header_name_cache = {}
17+
18+
class << self
19+
attr_reader :header_name_cache
20+
end
1621

1722
# See Sentry server default limits at
1823
# https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
@@ -45,15 +50,6 @@ class RequestInterface < Interface
4550
# @see Configuration#send_default_pii
4651
# @see Configuration#rack_env_whitelist
4752
def initialize(env:, send_default_pii:, rack_env_whitelist:)
48-
env = env.dup
49-
50-
unless send_default_pii
51-
# need to completely wipe out ip addresses
52-
RequestInterface::IP_HEADERS.each do |header|
53-
env.delete(header)
54-
end
55-
end
56-
5753
request = ::Rack::Request.new(env)
5854

5955
if send_default_pii
@@ -66,7 +62,7 @@ def initialize(env:, send_default_pii:, rack_env_whitelist:)
6662
self.method = request.request_method
6763

6864
self.headers = filter_and_format_headers(env, send_default_pii)
69-
self.env = filter_and_format_env(env, rack_env_whitelist)
65+
self.env = filter_and_format_env(env, rack_env_whitelist, send_default_pii)
7066
end
7167

7268
private
@@ -94,12 +90,22 @@ def filter_and_format_headers(env, send_default_pii)
9490
next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
9591
next if is_skippable_header?(key)
9692
next if key == "HTTP_AUTHORIZATION" && !send_default_pii
93+
# Filter IP headers inline instead of env.dup + delete
94+
next if !send_default_pii && IP_HEADERS.include?(key)
9795

9896
# Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
99-
key = key.delete_prefix("HTTP_")
100-
key = key.split("_").map(&:capitalize).join("-")
101-
102-
memo[key] = Utils::EncodingHelper.encode_to_utf_8(value.to_s)
97+
key = self.class.header_name_cache[key] ||= begin
98+
k = key.delete_prefix("HTTP_")
99+
k.split("_").map(&:capitalize).join("-").freeze
100+
end
101+
102+
# Fast path: ASCII strings are valid UTF-8, skip dup+force_encoding
103+
str = value.to_s
104+
memo[key] = if str.ascii_only?
105+
str
106+
else
107+
Utils::EncodingHelper.encode_to_utf_8(str)
108+
end
103109
rescue StandardError => e
104110
# Rails adds objects to the Rack env that can sometimes raise exceptions
105111
# when `to_s` is called.
@@ -110,6 +116,9 @@ def filter_and_format_headers(env, send_default_pii)
110116
end
111117
end
112118

119+
# Regex to detect lowercase chars — match? is allocation-free (no MatchData/String)
120+
LOWERCASE_PATTERN = /[a-z]/.freeze
121+
113122
def is_skippable_header?(key)
114123
key.match?(LOWERCASE_PATTERN) || # lower-case envs aren't real http headers
115124
key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
@@ -134,11 +143,13 @@ def self.rack_3_or_above?
134143
Gem::Version.new(::Rack.release) >= Gem::Version.new("3.0")
135144
end
136145

137-
def filter_and_format_env(env, rack_env_whitelist)
146+
def filter_and_format_env(env, rack_env_whitelist, send_default_pii)
138147
return env if rack_env_whitelist.empty?
139148

140149
env.select do |k, _v|
141-
rack_env_whitelist.include? k.to_s
150+
key = k.to_s
151+
next false if !send_default_pii && IP_HEADERS.include?(key)
152+
rack_env_whitelist.include?(key)
142153
end
143154
end
144155
end

0 commit comments

Comments
 (0)