Skip to content

Commit

Permalink
More detailed errors when connection closed during message body
Browse files Browse the repository at this point in the history
Intended as at least a first step towards gh-23.
  • Loading branch information
njsmith committed Dec 3, 2016
1 parent 421d322 commit 75e271c
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
19 changes: 15 additions & 4 deletions h11/_readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# - or, for body readers, a dict of per-framing reader factories

import re
from ._util import LocalProtocolError, validate
from ._util import LocalProtocolError, RemoteProtocolError, validate
from ._state import *
from ._events import *

Expand Down Expand Up @@ -177,16 +177,22 @@ def maybe_read_from_SEND_RESPONSE_server(buf):
class ContentLengthReader:
def __init__(self, length):
self._length = length
self._remaining = length

def __call__(self, buf):
if self._length == 0:
if self._remaining == 0:
return EndOfMessage()
data = buf.maybe_extract_at_most(self._length)
data = buf.maybe_extract_at_most(self._remaining)
if data is None:
return None
self._length -= len(data)
self._remaining -= len(data)
return Data(data=data)

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

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

def read_eof(self):
raise RemoteProtocolError(
"peer closed connection without sending complete message body "
"(incomplete chunked read)")


class Http10Reader(object):
def __call__(self, buf):
Expand Down
28 changes: 28 additions & 0 deletions h11/tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -915,3 +915,31 @@ def setup(method, http_version):
("Transfer-Encoding", "chunked")]))
== b"HTTP/1.1 200 \r\n"
b"transfer-encoding: chunked\r\n\r\n")

def test_special_exceptions_for_lost_connection_in_message_body():
c = Connection(SERVER)
c.receive_data(b"POST / HTTP/1.1\r\n"
b"Host: example.com\r\n"
b"Content-Length: 100\r\n\r\n")
assert type(c.next_event()) is Request
assert c.next_event() is NEED_DATA
c.receive_data(b"12345")
assert c.next_event() == Data(data=b"12345")
c.receive_data(b"")
with pytest.raises(RemoteProtocolError) as excinfo:
c.next_event()
assert "received 5 bytes" in str(excinfo.value)
assert "expected 100" in str(excinfo.value)

c = Connection(SERVER)
c.receive_data(b"POST / HTTP/1.1\r\n"
b"Host: example.com\r\n"
b"Transfer-Encoding: chunked\r\n\r\n")
assert type(c.next_event()) is Request
assert c.next_event() is NEED_DATA
c.receive_data(b"8\r\n012345")
assert c.next_event().data == b"012345"
c.receive_data(b"")
with pytest.raises(RemoteProtocolError) as excinfo:
c.next_event()
assert "incomplete chunked read" in str(excinfo.value)

0 comments on commit 75e271c

Please sign in to comment.