Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced Trailing Whitespace Handling in HTTP Headers #3429

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
30 changes: 24 additions & 6 deletions tornado/httputil.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,31 @@ def add(self, name: str, value: str) -> None:
"""Adds a new value for the given key."""
norm_name = _normalize_header(name)
self._last_key = norm_name
if norm_name in self:
self._dict[norm_name] = (
native_str(self[norm_name]) + "," + native_str(value)
)
self._as_list[norm_name].append(value)

# Handle Content-Length specifically
Nirab123456 marked this conversation as resolved.
Show resolved Hide resolved
if norm_name == 'Content-Length':
if not value.isdigit():
raise HTTPInputError("Invalid Content-Length header, must be a non-negative integer")
if int(value) < 0:
raise HTTPInputError("Invalid Content-Length header, must be a non-negative integer")

if norm_name in self._dict:
# Compare the existing value (which is stored as a string) to the new integer value (converted to string)
if self._dict[norm_name] != value:
raise HTTPInputError("Conflicting Content-Length headers")

# Store the Content-Length as a string
self._dict[norm_name] = value # Overwrite the existing value as a string
self._as_list[norm_name] = [value] # Reset list with the new value

else:
self[norm_name] = value
# For all other headers, append the value
if norm_name in self._dict:
self._dict[norm_name] += ',' + value # Append with a comma
self._as_list[norm_name].append(value) # Append to the list
else:
self._dict[norm_name] = value
self._as_list[norm_name] = [value] # Initialize the list with the new value

def get_list(self, name: str) -> List[str]:
"""Returns all values for the given header as a list."""
Expand Down
32 changes: 32 additions & 0 deletions tornado/test/httputil_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,38 @@ def test_data_after_final_boundary(self):


class HTTPHeadersTest(unittest.TestCase):

def test_multiple_content_length_headers(self):
headers = HTTPHeaders()

headers.parse_line("Content-Length: 123")

headers.parse_line("Content-Length: 123")
self.assertEqual(headers.get("content-length"), "123")
with self.assertRaises(HTTPInputError):
headers.parse_line("Content-Length: 456") # Should raise error
def test_invalid_content_length(self):
headers = HTTPHeaders()
with self.assertRaises(HTTPInputError):
headers.parse_line("Content-Length: abc") # Should raise error

def test_negative_content_length(self):
headers = HTTPHeaders()
with self.assertRaises(HTTPInputError):
headers.parse_line("Content-Length: -123")

def test_leading_trailing_whitespace(self):
headers = HTTPHeaders()
headers.parse_line("Content-Length: 123 ")
self.assertEqual(headers.get('content-length'), '123') # Should handle whitespace correctly

def test_zero_content_length(self):
headers = HTTPHeaders()
headers.parse_line("Content-Length: 0")
self.assertEqual(headers.get('content-length'), '0') # Should handle zero correctly



def test_multi_line(self):
# Lines beginning with whitespace are appended to the previous line
# with any leading whitespace replaced by a single space.
Expand Down