From 2589aaa14366ae51705ef58bcf02265a0b076fbd Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 1 Jul 2025 13:32:51 +1200 Subject: [PATCH] Allow non-string value in `Headers#[]=`. --- lib/protocol/http/headers.rb | 25 +++++++++++-------------- releases.md | 1 + test/protocol/http/headers.rb | 23 +++++++++++++++-------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/lib/protocol/http/headers.rb b/lib/protocol/http/headers.rb index 4bce8b9..2080353 100644 --- a/lib/protocol/http/headers.rb +++ b/lib/protocol/http/headers.rb @@ -198,9 +198,18 @@ def extract(keys) # @parameter key [String] the header key. # @parameter value [String] the header value to assign. def add(key, value) - self[key] = value + # The value MUST be a string, so we convert it to a string to prevent errors later on. + value = value.to_s + + if @indexed + merge_into(@indexed, key.downcase, value) + end + + @fields << [key, value] end + alias []= add + # Set the specified header key to the specified value, replacing any existing header keys with the same name. # # @parameter key [String] the header key to replace. @@ -214,7 +223,7 @@ def set(key, value) # Merge the headers into this instance. def merge!(headers) headers.each do |key, value| - self[key] = value + self.add(key, value) end return self @@ -225,18 +234,6 @@ def merge(headers) self.dup.merge!(headers) end - # Append the value to the given key. Some values can be appended multiple times, others can only be set once. - # - # @parameter key [String] The header key. - # @parameter value [String] The header value. - def []= key, value - if @indexed - merge_into(@indexed, key.downcase, value) - end - - @fields << [key, value] - end - # The policy for various headers, including how they are merged and normalized. POLICY = { # Headers which may only be specified once: diff --git a/releases.md b/releases.md index 4114ea3..e8cef03 100644 --- a/releases.md +++ b/releases.md @@ -3,6 +3,7 @@ ## Unreleased - `Protocol::HTTP::Headers` now raise a `DuplicateHeaderError` when a duplicate singleton header (e.g. `content-length`) is added. + - `Protocol::HTTP::Headers#add` now coerces the value to a string when adding a header, ensuring consistent behaviour. ## v0.50.0 diff --git a/test/protocol/http/headers.rb b/test/protocol/http/headers.rb index 2b2809b..dbb0955 100644 --- a/test/protocol/http/headers.rb +++ b/test/protocol/http/headers.rb @@ -167,18 +167,25 @@ end with "#[]=" do - it "can add field" do + it "can add field with a String value" do + headers["Content-Length"] = "1" + + expect(headers.fields.last).to be == ["Content-Length", "1"] + expect(headers["content-length"]).to be == "1" + end + + it "can add field with an Integer value" do headers["Content-Length"] = 1 - expect(headers.fields.last).to be == ["Content-Length", 1] - expect(headers["content-length"]).to be == 1 + expect(headers.fields.last).to be == ["Content-Length", "1"] + expect(headers["content-length"]).to be == "1" end it "can add field with indexed hash" do expect(headers.to_h).not.to be(:empty?) - headers["Content-Length"] = 1 - expect(headers["content-length"]).to be == 1 + headers["Content-Length"] = "1" + expect(headers["content-length"]).to be == "1" end end @@ -186,8 +193,8 @@ it "can add field" do headers.add("Content-Length", 1) - expect(headers.fields.last).to be == ["Content-Length", 1] - expect(headers["content-length"]).to be == 1 + expect(headers.fields.last).to be == ["Content-Length", "1"] + expect(headers["content-length"]).to be == "1" end end @@ -241,7 +248,7 @@ it "can merge content-length" do headers.merge!("content-length" => 2) - expect(headers["content-length"]).to be == 2 + expect(headers["content-length"]).to be == "2" end end