parse_curl_response: Handle duplicate headers

`Curl#parse_curl_response` only includes the last instance of a given
header in its `:headers` hash (replicating pre-existing behavior).
This is a problem for headers like `Set-Cookie`, which can appear more
than once in a response.

This commit addresses the issue by collecting duplicate headers into
an array instead. Headers that only appear once in the response will
still have a string value but headers that appear more than once will
be an array of strings. Whenever headers from `#parse_curl_response`
are used (directly or indirectly), it's important to conditionally
handle the expected types.
This commit is contained in:
Sam Ford 2022-05-02 15:31:30 -04:00
parent 14ff6be6d0
commit 94449d07c0
No known key found for this signature in database
GPG Key ID: 95209E46C7FFDEFE
2 changed files with 45 additions and 4 deletions

View File

@ -112,6 +112,24 @@ describe "Utils::Curl" do
},
}
response_hash[:duplicate_header] = {
status_code: "200",
status_text: "OK",
headers: {
"cache-control" => "max-age=604800",
"content-type" => "text/html; charset=UTF-8",
"date" => "Wed, 1 Jan 2020 01:23:45 GMT",
"expires" => "Wed, 31 Jan 2020 01:23:45 GMT",
"last-modified" => "Thu, 1 Jan 2019 01:23:45 GMT",
"content-length" => "123",
"set-cookie" => [
"example1=first",
"example2=second; Expires Wed, 31 Jan 2020 01:23:45 GMT",
"example3=third",
],
},
}
response_hash
}
@ -144,6 +162,13 @@ describe "Utils::Curl" do
#{response_text[:ok]}
EOS
response_text[:duplicate_header] = response_text[:ok].sub(
/\r\n\Z/,
"Set-Cookie: #{response_hash[:duplicate_header][:headers]["set-cookie"][0]}\r\n" \
"Set-Cookie: #{response_hash[:duplicate_header][:headers]["set-cookie"][1]}\r\n" \
"Set-Cookie: #{response_hash[:duplicate_header][:headers]["set-cookie"][2]}\r\n\r\n",
)
response_text
}
@ -312,6 +337,7 @@ describe "Utils::Curl" do
it "returns a correct hash when given HTTP response text" do
expect(parse_curl_response(response_text[:ok])).to eq(response_hash[:ok])
expect(parse_curl_response(response_text[:redirection])).to eq(response_hash[:redirection])
expect(parse_curl_response(response_text[:duplicate_header])).to eq(response_hash[:duplicate_header])
end
it "returns an empty hash when given an empty string" do

View File

@ -484,10 +484,25 @@ module Utils
response_text = response_text.sub(%r{^HTTP/.* (\d+).*$\s*}, "")
# Create a hash from the header lines
response[:headers] =
response_text.split("\r\n")
.to_h { |header| header.split(/:\s*/, 2) }
.transform_keys(&:downcase)
response[:headers] = {}
response_text.split("\r\n").each do |line|
header_name, header_value = line.split(/:\s*/, 2)
next if header_name.blank?
header_name = header_name.strip.downcase
header_value&.strip!
case response[:headers][header_name]
when nil
response[:headers][header_name] = header_value
when String
response[:headers][header_name] = [response[:headers][header_name], header_value]
when Array
response[:headers][header_name].push(header_value)
end
response[:headers][header_name]
end
response
end