Skip to content
Merged
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
6 changes: 6 additions & 0 deletions docs/project/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ Improvements
* :func:`~asyncio.client.connect` now closes connections with close code 1011
(internal error) when exiting the context manager with an exception.

Bug fixes
.........

* Fixed a regression from 16.1 where the legacy implementation rejected
non-ASCII headers.

.. _17.1:

17.1
Expand Down
4 changes: 3 additions & 1 deletion src/websockets/legacy/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ async def read_headers(stream: asyncio.StreamReader) -> Headers:

name = raw_name.decode("ascii") # guaranteed to be ASCII at this point
value = raw_value.decode("ascii", "surrogateescape")
headers[name] = value

# Since we just validated raw_value, we don't need to revalidate it.
headers.set_insecure(name, value)

else:
raise SecurityError("too many HTTP headers")
Expand Down
4 changes: 2 additions & 2 deletions src/websockets/sync/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,8 @@ def handler(websocket):
with serve(handler, ...) as server:
server.serve_forever()

To stop the server gracefully, call its :meth:`~Server.shutdown` method
from another thread.
To stop the server gracefully, call its :meth:`~Server.shutdown` method from
another thread.

Args:
handler: Connection handler. It receives the WebSocket connection,
Expand Down
10 changes: 8 additions & 2 deletions tests/legacy/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,18 @@ async def test_read_response_invalid_header(self):
"invalid HTTP header line: Oops",
)

async def test_header_name(self):
async def test_iso_8859_1_header_value(self):
self.stream.feed_data(b"X-Drink: caf\xe9\r\n\r\n")
headers = await read_headers(self.stream)
# Non-ASCII characters are represented with surrogate escapes.
self.assertEqual(headers["X-Drink"], "caf\udce9")

async def test_invalid_header_name(self):
self.stream.feed_data(b"foo bar: baz qux\r\n\r\n")
with self.assertRaises(ValueError):
await read_headers(self.stream)

async def test_header_value(self):
async def test_invalid_header_value(self):
self.stream.feed_data(b"foo: \x00\x00\x0f\r\n\r\n")
with self.assertRaises(ValueError):
await read_headers(self.stream)
Expand Down