From 93884b8f38511c1c4d3d54618e30bd9bcfa4c4b5 Mon Sep 17 00:00:00 2001 From: Ben Kallus Date: Tue, 9 Sep 2025 16:56:24 -0400 Subject: [PATCH] Properly strip whitespace from the right side of header values --- lib/protocol/http1/connection.rb | 6 +++--- test/protocol/http1/connection/headers.rb | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/protocol/http1/connection.rb b/lib/protocol/http1/connection.rb index 61ce0cd..b217f83 100644 --- a/lib/protocol/http1/connection.rb +++ b/lib/protocol/http1/connection.rb @@ -39,11 +39,11 @@ module HTTP1 FIELD_NAME = TOKEN OWS = /[ \t]*/ # A field value is any string of characters that does not contain a null character, CR, or LF. After reflecting on the RFCs and surveying real implementations, I came to the conclusion that the RFCs are too restrictive. Most servers only check for the presence of null bytes, and obviously CR/LF characters have semantic meaning in the parser. So, I decided to follow this defacto standard, even if I'm not entirely happy with it. - FIELD_VALUE = /[^\0\r\n]+/.freeze - HEADER = /\A(#{FIELD_NAME}):#{OWS}(?:(#{FIELD_VALUE})#{OWS})?\z/.freeze + FIELD_VALUE = /[^\0\r\n]*?/.freeze + HEADER = /\A(#{FIELD_NAME}):#{OWS}(#{FIELD_VALUE})#{OWS}\z/.freeze VALID_FIELD_NAME = /\A#{FIELD_NAME}\z/.freeze - VALID_FIELD_VALUE = /\A#{FIELD_VALUE}?\z/.freeze + VALID_FIELD_VALUE = /\A#{FIELD_VALUE}\z/.freeze DEFAULT_MAXIMUM_LINE_LENGTH = 8192 diff --git a/test/protocol/http1/connection/headers.rb b/test/protocol/http1/connection/headers.rb index b6cd011..93b3a31 100644 --- a/test/protocol/http1/connection/headers.rb +++ b/test/protocol/http1/connection/headers.rb @@ -106,6 +106,19 @@ def validate_headers!(expected_headers = self.headers) end end + with "a header that contains trailing whitespace" do + let(:headers) {[ + "has-trailing-whitespace: here it is \t" + ]} + + it "can parse the header" do + authority, method, target, version, headers, body = server.read_request + expect(headers).to have_keys( + "has-trailing-whitespace" => be == ["here it is"] + ) + end + end + with "a header that contains obsolete folding whitespace" do let(:headers) {[ "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko)\n\tChrome/55.0.2883.95 Safari/537.36"