Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lib/protocol/http1/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,14 @@ def read_headers
break if line.empty?

if match = line.match(HEADER)
fields << [match[1], match[2] || ""]
# The RFCs require stripping of optional whitespace, but only at the end of the field value:
if value = match[2]
value.rstrip!
else
value = ""
end

fields << [match[1], value]
else
raise BadHeader, "Could not parse header: #{line.inspect}"
end
Expand Down
4 changes: 4 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Releases

## Unreleased

- Strip optional whitespace (OWS) from the end of header field values when parsing headers.

## v0.37.0

- `Protocol::HTTP1::BadRequest` now includes `Protocol::HTTP::BadRequest` for better interoperability and handling of bad request errors across different HTTP protocol implementations.
Expand Down
98 changes: 98 additions & 0 deletions test/protocol/http1/connection/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,103 @@ def validate_headers!(expected_headers = self.headers)
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end

with "a header with leading whitespace (spaces)" do
let(:headers) {[
"x-test: here it is"
]}

it "strips leading spaces" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"x-test" => be == ["here it is"]
)
end
end

with "a header with leading whitespace (tabs)" do
let(:headers) {[
"x-test:\t\there it is"
]}

it "strips leading tabs" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"x-test" => be == ["here it is"]
)
end
end

with "a header with leading whitespace (mixed)" do
let(:headers) {[
"x-test: \t \there it is"
]}

it "strips leading spaces and tabs" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"x-test" => be == ["here it is"]
)
end
end

with "a header with trailing whitespace (spaces)" do
let(:headers) {[
"x-test: here it is "
]}

it "strips trailing spaces" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"x-test" => be == ["here it is"]
)
end
end

with "a header with trailing whitespace (tabs)" do
let(:headers) {[
"x-test: here it is\t\t"
]}

it "strips trailing tabs" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"x-test" => be == ["here it is"]
)
end
end

with "a header with trailing whitespace (mixed)" do
let(:headers) {[
"x-test: here it is \t \t"
]}

it "strips trailing spaces and tabs" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"x-test" => be == ["here it is"]
)
end
end

with "a header with both leading and trailing whitespace" do
let(:headers) {[
"x-test: \t here it is \t "
]}

it "strips both leading and trailing whitespace" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"x-test" => be == ["here it is"]
)
end
end
end
end
Loading