Skip to content

Commit 75e271c

Browse files
committed
More detailed errors when connection closed during message body
Intended as at least a first step towards gh-23.
1 parent 421d322 commit 75e271c

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

h11/_readers.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# - or, for body readers, a dict of per-framing reader factories
1818

1919
import re
20-
from ._util import LocalProtocolError, validate
20+
from ._util import LocalProtocolError, RemoteProtocolError, validate
2121
from ._state import *
2222
from ._events import *
2323

@@ -177,16 +177,22 @@ def maybe_read_from_SEND_RESPONSE_server(buf):
177177
class ContentLengthReader:
178178
def __init__(self, length):
179179
self._length = length
180+
self._remaining = length
180181

181182
def __call__(self, buf):
182-
if self._length == 0:
183+
if self._remaining == 0:
183184
return EndOfMessage()
184-
data = buf.maybe_extract_at_most(self._length)
185+
data = buf.maybe_extract_at_most(self._remaining)
185186
if data is None:
186187
return None
187-
self._length -= len(data)
188+
self._remaining -= len(data)
188189
return Data(data=data)
189190

191+
def read_eof(self):
192+
raise RemoteProtocolError(
193+
"peer closed connection without sending complete message body "
194+
"(received {} bytes, expected {})"
195+
.format(self._length - self._remaining, self._length))
190196

191197
HEXDIG = r"[0-9A-Fa-f]"
192198
# Actually
@@ -260,6 +266,11 @@ def __call__(self, buf):
260266
chunk_end = False
261267
return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end)
262268

269+
def read_eof(self):
270+
raise RemoteProtocolError(
271+
"peer closed connection without sending complete message body "
272+
"(incomplete chunked read)")
273+
263274

264275
class Http10Reader(object):
265276
def __call__(self, buf):

h11/tests/test_connection.py

+28
Original file line numberDiff line numberDiff line change
@@ -915,3 +915,31 @@ def setup(method, http_version):
915915
("Transfer-Encoding", "chunked")]))
916916
== b"HTTP/1.1 200 \r\n"
917917
b"transfer-encoding: chunked\r\n\r\n")
918+
919+
def test_special_exceptions_for_lost_connection_in_message_body():
920+
c = Connection(SERVER)
921+
c.receive_data(b"POST / HTTP/1.1\r\n"
922+
b"Host: example.com\r\n"
923+
b"Content-Length: 100\r\n\r\n")
924+
assert type(c.next_event()) is Request
925+
assert c.next_event() is NEED_DATA
926+
c.receive_data(b"12345")
927+
assert c.next_event() == Data(data=b"12345")
928+
c.receive_data(b"")
929+
with pytest.raises(RemoteProtocolError) as excinfo:
930+
c.next_event()
931+
assert "received 5 bytes" in str(excinfo.value)
932+
assert "expected 100" in str(excinfo.value)
933+
934+
c = Connection(SERVER)
935+
c.receive_data(b"POST / HTTP/1.1\r\n"
936+
b"Host: example.com\r\n"
937+
b"Transfer-Encoding: chunked\r\n\r\n")
938+
assert type(c.next_event()) is Request
939+
assert c.next_event() is NEED_DATA
940+
c.receive_data(b"8\r\n012345")
941+
assert c.next_event().data == b"012345"
942+
c.receive_data(b"")
943+
with pytest.raises(RemoteProtocolError) as excinfo:
944+
c.next_event()
945+
assert "incomplete chunked read" in str(excinfo.value)

0 commit comments

Comments
 (0)