Skip to content

Commit

Permalink
Optimizes normalize_headers for performance
Browse files Browse the repository at this point in the history
Optimizes the `WebMock::Util::Headers.normalize_headers` method to:

* Create less objects
* Avoid using Regexp where possible
* Hoists constants

More details can be found in the related issue, including performance
benchmarks:

#1027
  • Loading branch information
baweaver committed Jun 6, 2023
1 parent 833291d commit 99924c2
Showing 1 changed file with 28 additions and 8 deletions.
36 changes: 28 additions & 8 deletions lib/webmock/util/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ module Util

class Headers

STANDARD_HEADER_DELIMITER = '-'.freeze
NONSTANDARD_HEADER_DELIMITER = '_'.freeze
JOIN = ', '.freeze

def self.normalize_headers(headers)
return nil unless headers
array = headers.map { |name, value|
[name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"),
case value

headers.each_with_object({}) do |(name, value), new_headers|
new_headers[normalize_name(name)] =
case value
when Regexp then value
when Array then (value.size == 1) ? value.first.to_s : value.map {|v| v.to_s}.sort
when Array then (value.size == 1) ? value.first.to_s : value.map(&:to_s).sort
else value.to_s
end
]
}
Hash[*array.inject([]) {|r,x| r + x}]
end
end
end

def self.sorted_headers_string(headers)
Expand Down Expand Up @@ -57,6 +60,23 @@ def self.basic_auth_header(*credentials)
"Basic #{Base64.strict_encode64(credentials.join(':')).chomp}"
end

def self.normalize_name(name)
name
.to_s
# By using `tr` here we can normalize the headers ahead of time
.tr(NONSTANDARD_HEADER_DELIMITER, STANDARD_HEADER_DELIMITER)
# ...which will be faster because `split` on a `String` is a good deal faster
# than on a `Regexp`.
.split(STANDARD_HEADER_DELIMITER)
# Now since this is state we control here we can mutate it with `map!` to again
# avoid extra object creations.
.map!(&:capitalize)
# Then join it back together. Most of these constants are to avoid recreating
# these strings hundreds of thousands of times in larger codebases. Ours I
# believe was in the millions.
.join(STANDARD_HEADER_DELIMITER)
end

end

end
Expand Down

0 comments on commit 99924c2

Please sign in to comment.