Skip to content

Commit abcbe0c

Browse files
committed
Handle duplicate Content-Length headers per RFC 7230 §3.3.2
When a server sends duplicate Content-Length headers with identical values, collapse them into a single value instead of returning nil. When values conflict, return nil (reject as invalid). Previously, duplicate headers caused either a TypeError or silently returned nil even for valid identical duplicates. Closes #566.
1 parent 54a8415 commit abcbe0c

3 files changed

Lines changed: 35 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
- `Response#content_length` now handles duplicate `Content-Length` headers per
13+
RFC 7230 Section 3.3.2. When all values are identical, they are collapsed into
14+
a single valid value. When values conflict, `nil` is returned instead of
15+
raising `TypeError`. ([#566])
1216
- HTTP 1xx informational responses (e.g. `100 Continue`) are now transparently
1317
skipped, returning the final response. This was a regression introduced when
1418
the parser was migrated from http-parser to llhttp. ([#667])

lib/http/response.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,12 @@ def content_length
220220
# and a Content-Length header field, the Transfer-Encoding overrides the Content-Length.
221221
return nil if @headers.include?(Headers::TRANSFER_ENCODING)
222222

223-
value = @headers[Headers::CONTENT_LENGTH]
224-
return nil unless value
223+
# RFC 7230 Section 3.3.2: If multiple Content-Length values are present,
224+
# they must all be identical; otherwise treat as invalid.
225+
values = @headers.get(Headers::CONTENT_LENGTH).uniq
226+
return nil unless values.one?
225227

226-
Integer(value, exception: false)
228+
Integer(values.first, exception: false)
227229
end
228230

229231
# Parsed Content-Type header

test/http/response_test.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@
120120
end
121121
end
122122

123+
context "with duplicate identical Content-Length" do
124+
let(:headers) do
125+
h = HTTP::Headers.new
126+
h.add("Content-Length", "5")
127+
h.add("Content-Length", "5")
128+
h
129+
end
130+
131+
it "returns the deduplicated value" do
132+
assert_equal 5, response.content_length
133+
end
134+
end
135+
136+
context "with conflicting Content-Length values" do
137+
let(:headers) do
138+
h = HTTP::Headers.new
139+
h.add("Content-Length", "5")
140+
h.add("Content-Length", "10")
141+
h
142+
end
143+
144+
it "returns nil" do
145+
assert_nil response.content_length
146+
end
147+
end
148+
123149
context "with Transfer-Encoding header" do
124150
let(:headers) { { "Transfer-Encoding" => "chunked", "Content-Length" => "5" } }
125151

0 commit comments

Comments
 (0)