Skip to content

Commit 7cdb8c6

Browse files
committed
Make it harder to generate invalid HTTP messages.
1 parent 9ff5c77 commit 7cdb8c6

3 files changed

Lines changed: 17 additions & 1 deletion

File tree

src/websockets/datastructures.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import re
34
from collections.abc import Iterable, Iterator, Mapping, MutableMapping
45
from typing import Any, Protocol
56

@@ -24,6 +25,10 @@ def __str__(self) -> str:
2425
return super().__str__()
2526

2627

28+
# Obsolete line folding for header values is not supported.
29+
is_valid_header_value = re.compile(r"[\x09\x20-\x7e\x80-\xff]*").fullmatch
30+
31+
2732
class Headers(MutableMapping[str, str]):
2833
"""
2934
Efficient data structure for manipulating HTTP headers.
@@ -107,6 +112,8 @@ def __getitem__(self, key: str) -> str:
107112
raise MultipleValuesError(key)
108113

109114
def __setitem__(self, key: str, value: str) -> None:
115+
if not is_valid_header_value(str(value)):
116+
raise InvalidHeaderValue(value)
110117
self._dict.setdefault(key.lower(), []).append(value)
111118
self._list.append((key, value))
112119

@@ -181,3 +188,7 @@ def __getitem__(self, key: str) -> str: ...
181188
keys and values are :class:`str`.
182189
183190
"""
191+
192+
193+
# At the bottom to break an import cycle.
194+
from .exceptions import InvalidHeaderValue # noqa: E402

tests/test_datastructures.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22

33
from websockets.datastructures import *
4+
from websockets.exceptions import InvalidHeaderValue
45

56

67
class MultipleValuesErrorTests(unittest.TestCase):
@@ -121,6 +122,10 @@ def test_setitem_case_insensitive(self):
121122
self.headers["upgrade"] = "websocket"
122123
self.assertEqual(self.headers["Upgrade"], "websocket")
123124

125+
def test_setitem_invalid_value(self):
126+
with self.assertRaises(InvalidHeaderValue):
127+
self.headers["X-Invalid"] = "multi\r\nline"
128+
124129
def test_delitem(self):
125130
del self.headers["Connection"]
126131
with self.assertRaises(KeyError):

tests/test_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def test_send_response_without_accept_or_reject(self, _formatdate):
145145
Headers(
146146
{
147147
"Connection": "close",
148-
"Content-Length": 6,
148+
"Content-Length": "6",
149149
"Content-Type": "text/plain",
150150
}
151151
),

0 commit comments

Comments
 (0)