diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c5980bb..501c995f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ #### Fixes * [#2767](https://github.com/ruby-grape/grape/pull/2767): Update rubocop to 1.88.0 and rubocop-rspec to 3.10.2 - [@ericproulx](https://github.com/ericproulx). +* [#2770](https://github.com/ruby-grape/grape/pull/2770): Avoid per-entry array allocation in `Request#build_headers` - [@ericproulx](https://github.com/ericproulx). * Your contribution here. ### 3.3.0 (2026-06-20) diff --git a/lib/grape/request.rb b/lib/grape/request.rb index 12a0fca0d..c56237e1d 100644 --- a/lib/grape/request.rb +++ b/lib/grape/request.rb @@ -177,13 +177,19 @@ def make_params raise Grape::Exceptions::RequestError end + # Uses a plain `each_header` block instead of `each_header.with_object`: + # `with_object` can only pass the block one value plus the memo, so the + # `k, v` pair would be boxed into a throwaway Array on every header. A + # two-arg block receives `k`/`v` directly and allocates nothing extra. def build_headers - each_header.with_object(Grape::Util::Header.new) do |(k, v), headers| + headers = Grape::Util::Header.new + each_header do |k, v| next unless k.start_with? 'HTTP_' transformed_header = KNOWN_HEADERS.fetch(k) { -k[5..].tr('_', '-').downcase } headers[transformed_header] = v end + headers end end end