Skip to content

Commit cb42484

Browse files
committed
Improve error messages on HTTP parsing errors.
1 parent 560f6ee commit cb42484

File tree

4 files changed

+33
-28
lines changed

4 files changed

+33
-28
lines changed

src/websockets/http11.py

+17-13
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,15 @@ def parse(
135135
raise EOFError("connection closed while reading HTTP request line") from exc
136136

137137
try:
138-
method, raw_path, version = request_line.split(b" ", 2)
138+
method, raw_path, protocol = request_line.split(b" ", 2)
139139
except ValueError: # not enough values to unpack (expected 3, got 1-2)
140140
raise ValueError(f"invalid HTTP request line: {d(request_line)}") from None
141-
141+
if protocol != b"HTTP/1.1":
142+
raise ValueError(
143+
f"unsupported protocol; expected HTTP/1.1: {d(request_line)}"
144+
)
142145
if method != b"GET":
143-
raise ValueError(f"unsupported HTTP method: {d(method)}")
144-
if version != b"HTTP/1.1":
145-
raise ValueError(f"unsupported HTTP version: {d(version)}")
146+
raise ValueError(f"unsupported HTTP method; expected GET; got {d(method)}")
146147
path = raw_path.decode("ascii", "surrogateescape")
147148

148149
headers = yield from parse_headers(read_line)
@@ -236,23 +237,26 @@ def parse(
236237
raise EOFError("connection closed while reading HTTP status line") from exc
237238

238239
try:
239-
version, raw_status_code, raw_reason = status_line.split(b" ", 2)
240+
protocol, raw_status_code, raw_reason = status_line.split(b" ", 2)
240241
except ValueError: # not enough values to unpack (expected 3, got 1-2)
241242
raise ValueError(f"invalid HTTP status line: {d(status_line)}") from None
242-
243-
if version != b"HTTP/1.1":
244-
raise ValueError(f"unsupported HTTP version: {d(version)}")
243+
if protocol != b"HTTP/1.1":
244+
raise ValueError(
245+
f"unsupported protocol; expected HTTP/1.1: {d(status_line)}"
246+
)
245247
try:
246248
status_code = int(raw_status_code)
247249
except ValueError: # invalid literal for int() with base 10
248250
raise ValueError(
249-
f"invalid HTTP status code: {d(raw_status_code)}"
251+
f"invalid status code; expected integer; got {d(raw_status_code)}"
250252
) from None
251-
if not 100 <= status_code < 1000:
252-
raise ValueError(f"unsupported HTTP status code: {d(raw_status_code)}")
253+
if not 100 <= status_code < 600:
254+
raise ValueError(
255+
f"invalid status code; expected 100–599; got {d(raw_status_code)}"
256+
)
253257
if not _value_re.fullmatch(raw_reason):
254258
raise ValueError(f"invalid HTTP reason phrase: {d(raw_reason)}")
255-
reason = raw_reason.decode()
259+
reason = raw_reason.decode("ascii", "surrogateescape")
256260

257261
headers = yield from parse_headers(read_line)
258262

tests/asyncio/test_client.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,8 @@ async def junk(reader, writer):
426426
self.fail("did not raise")
427427
self.assertEqual(
428428
str(raised.exception),
429-
"unsupported HTTP version: 220",
429+
"unsupported protocol; expected HTTP/1.1: "
430+
"220 smtp.invalid ESMTP Postfix",
430431
)
431432

432433

tests/sync/test_client.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ def handle(self):
177177
self.fail("did not raise")
178178
self.assertEqual(
179179
str(raised.exception),
180-
"unsupported HTTP version: 220",
180+
"unsupported protocol; expected HTTP/1.1: "
181+
"220 smtp.invalid ESMTP Postfix",
181182
)
182183
finally:
183184
server.shutdown()

tests/test_http11.py

+12-13
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,22 @@ def test_parse_invalid_request_line(self):
5050
"invalid HTTP request line: GET /",
5151
)
5252

53-
def test_parse_unsupported_method(self):
54-
self.reader.feed_data(b"OPTIONS * HTTP/1.1\r\n\r\n")
53+
def test_parse_unsupported_protocol(self):
54+
self.reader.feed_data(b"GET /chat HTTP/1.0\r\n\r\n")
5555
with self.assertRaises(ValueError) as raised:
5656
next(self.parse())
5757
self.assertEqual(
5858
str(raised.exception),
59-
"unsupported HTTP method: OPTIONS",
59+
"unsupported protocol; expected HTTP/1.1: GET /chat HTTP/1.0",
6060
)
6161

62-
def test_parse_unsupported_version(self):
63-
self.reader.feed_data(b"GET /chat HTTP/1.0\r\n\r\n")
62+
def test_parse_unsupported_method(self):
63+
self.reader.feed_data(b"OPTIONS * HTTP/1.1\r\n\r\n")
6464
with self.assertRaises(ValueError) as raised:
6565
next(self.parse())
6666
self.assertEqual(
6767
str(raised.exception),
68-
"unsupported HTTP version: HTTP/1.0",
68+
"unsupported HTTP method; expected GET; got OPTIONS",
6969
)
7070

7171
def test_parse_invalid_header(self):
@@ -171,31 +171,30 @@ def test_parse_invalid_status_line(self):
171171
"invalid HTTP status line: Hello!",
172172
)
173173

174-
def test_parse_unsupported_version(self):
174+
def test_parse_unsupported_protocol(self):
175175
self.reader.feed_data(b"HTTP/1.0 400 Bad Request\r\n\r\n")
176176
with self.assertRaises(ValueError) as raised:
177177
next(self.parse())
178178
self.assertEqual(
179179
str(raised.exception),
180-
"unsupported HTTP version: HTTP/1.0",
180+
"unsupported protocol; expected HTTP/1.1: HTTP/1.0 400 Bad Request",
181181
)
182182

183-
def test_parse_invalid_status(self):
183+
def test_parse_non_integer_status(self):
184184
self.reader.feed_data(b"HTTP/1.1 OMG WTF\r\n\r\n")
185185
with self.assertRaises(ValueError) as raised:
186186
next(self.parse())
187187
self.assertEqual(
188188
str(raised.exception),
189-
"invalid HTTP status code: OMG",
189+
"invalid status code; expected integer; got OMG",
190190
)
191191

192-
def test_parse_unsupported_status(self):
192+
def test_parse_non_three_digit_status(self):
193193
self.reader.feed_data(b"HTTP/1.1 007 My name is Bond\r\n\r\n")
194194
with self.assertRaises(ValueError) as raised:
195195
next(self.parse())
196196
self.assertEqual(
197-
str(raised.exception),
198-
"unsupported HTTP status code: 007",
197+
str(raised.exception), "invalid status code; expected 100–599; got 007"
199198
)
200199

201200
def test_parse_invalid_reason(self):

0 commit comments

Comments
 (0)