Skip to content

Commit cfea2c2

Browse files
committed
Add support for HTTP/1.0 proxies.
It is legal to answer an HTTP/1.1 request with an HTTP/1.0 response. Fix #1609.
1 parent b22f1da commit cfea2c2

File tree

5 files changed

+36
-4
lines changed

5 files changed

+36
-4
lines changed

Diff for: docs/project/changelog.rst

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ notice.
3232

3333
*In development*
3434

35+
Improvements
36+
............
37+
38+
* Added support for HTTP/1.0 proxies.
39+
3540
15.0.1
3641
------
3742

Diff for: src/websockets/asyncio/client.py

+1
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ def __init__(
755755
self.reader.read_exact,
756756
self.reader.read_to_eof,
757757
include_body=False,
758+
allow_http10=True,
758759
)
759760

760761
loop = asyncio.get_running_loop()

Diff for: src/websockets/http11.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ def parse(
214214
read_exact: Callable[[int], Generator[None, None, bytes]],
215215
read_to_eof: Callable[[int], Generator[None, None, bytes]],
216216
include_body: bool = True,
217+
allow_http10: bool = False,
217218
) -> Generator[None, None, Response]:
218219
"""
219220
Parse a WebSocket handshake response.
@@ -249,10 +250,17 @@ def parse(
249250
protocol, raw_status_code, raw_reason = status_line.split(b" ", 2)
250251
except ValueError: # not enough values to unpack (expected 3, got 1-2)
251252
raise ValueError(f"invalid HTTP status line: {d(status_line)}") from None
252-
if protocol != b"HTTP/1.1":
253-
raise ValueError(
254-
f"unsupported protocol; expected HTTP/1.1: {d(status_line)}"
255-
)
253+
if allow_http10: # some proxies still use HTTP/1.0
254+
if protocol not in [b"HTTP/1.1", b"HTTP/1.0"]:
255+
raise ValueError(
256+
f"unsupported protocol; expected HTTP/1.1 or HTTP/1.0: "
257+
f"{d(status_line)}"
258+
)
259+
else:
260+
if protocol != b"HTTP/1.1":
261+
raise ValueError(
262+
f"unsupported protocol; expected HTTP/1.1: {d(status_line)}"
263+
)
256264
try:
257265
status_code = int(raw_status_code)
258266
except ValueError: # invalid literal for int() with base 10

Diff for: src/websockets/sync/client.py

+1
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ def read_connect_response(sock: socket.socket, deadline: Deadline) -> Response:
499499
reader.read_exact,
500500
reader.read_to_eof,
501501
include_body=False,
502+
allow_http10=True,
502503
)
503504
try:
504505
while True:

Diff for: tests/test_http11.py

+17
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,23 @@ def test_parse_without_body(self):
333333
response = self.assertGeneratorReturns(self.parse(include_body=False))
334334
self.assertEqual(response.body, b"")
335335

336+
def test_parse_http10(self):
337+
self.reader.feed_data(b"HTTP/1.0 200 OK\r\n\r\n")
338+
response = self.assertGeneratorReturns(self.parse(allow_http10=True))
339+
self.assertEqual(response.status_code, 200)
340+
self.assertEqual(response.reason_phrase, "OK")
341+
self.assertEqual(response.body, b"")
342+
343+
def test_parse_http10_unsupported_protocol(self):
344+
self.reader.feed_data(b"HTTP/1.2 400 Bad Request\r\n\r\n")
345+
with self.assertRaises(ValueError) as raised:
346+
next(self.parse(allow_http10=True))
347+
self.assertEqual(
348+
str(raised.exception),
349+
"unsupported protocol; expected HTTP/1.1 or HTTP/1.0: "
350+
"HTTP/1.2 400 Bad Request",
351+
)
352+
336353
def test_serialize(self):
337354
# Example from the protocol overview in RFC 6455
338355
response = Response(

0 commit comments

Comments
 (0)