From 3e52e91af22c569f58164a41581ec40682825f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Primo=C5=BE=20Godec?= Date: Thu, 26 Oct 2017 14:15:56 +0200 Subject: [PATCH] Fix: no end of the stream when (length of body) % MAX_CHUNK == MAX_CHUNK --- HISTORY.rst | 4 +++ hyper/http20/stream.py | 25 ++++++++++--------- test/test_hyper.py | 55 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index c45c08ce..dab8eed7 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,10 @@ Release History dev --- +*Bugfixes* + +- Stream end flag when length of last chunk equal to MAX_CHUNK + v0.7.0 (2016-09-27) ------------------- diff --git a/hyper/http20/stream.py b/hyper/http20/stream.py index 598a1490..3c064783 100644 --- a/hyper/http20/stream.py +++ b/hyper/http20/stream.py @@ -122,8 +122,18 @@ def file_iterator(fobj): chunks = (data[i:i+MAX_CHUNK] for i in range(0, len(data), MAX_CHUNK)) - for chunk in chunks: - self._send_chunk(chunk, final) + # since we need to know when we have a last package we need to know + # if there is another package in advance + cur_chunk = None + try: + cur_chunk = next(chunks) + while True: + next_chunk = next(chunks) + self._send_chunk(cur_chunk, False) + cur_chunk = next_chunk + except StopIteration: + if cur_chunk is not None: # cur_chunk none when no chunks to send + self._send_chunk(cur_chunk, final) def _read(self, amt=None): """ @@ -323,19 +333,12 @@ def _send_chunk(self, data, final): while len(data) > self._out_flow_control_window: self._recv_cb() - # If the length of the data is less than MAX_CHUNK, we're probably - # at the end of the file. If this is the end of the data, mark it - # as END_STREAM. - end_stream = False - if len(data) < MAX_CHUNK and final: - end_stream = True - # Send the frame and decrement the flow control window. with self._conn as conn: conn.send_data( - stream_id=self.stream_id, data=data, end_stream=end_stream + stream_id=self.stream_id, data=data, end_stream=final ) self._send_outstanding_data() - if end_stream: + if final: self.local_closed = True diff --git a/test/test_hyper.py b/test/test_hyper.py index f4a5994d..373384a5 100644 --- a/test/test_hyper.py +++ b/test/test_hyper.py @@ -211,6 +211,61 @@ def data_callback(chunk, **kwargs): assert frames[1].data == b'hello there' assert frames[1].flags == set(['END_STREAM']) + def test_request_correctly_sent_max_chunk(self, frame_buffer): + """ + Test that request correctly sent when data length multiple + max chunk. We check last chunk has a end flag and correct number + of chunks. + """ + def data_callback(chunk, **kwargs): + frame_buffer.add_data(chunk) + + # one chunk + c = HTTP20Connection('www.google.com') + c._sock = DummySocket() + c._send_cb = data_callback + c.putrequest('GET', '/') + c.endheaders(message_body=b'1'*1024, final=True) + + frames = list(frame_buffer) + assert len(frames) == 2 + assert isinstance(frames[1], DataFrame) + assert frames[1].flags == set(['END_STREAM']) + + # two chunks + c = HTTP20Connection('www.google.com') + c._sock = DummySocket() + c._send_cb = data_callback + c.putrequest('GET', '/') + c.endheaders(message_body=b'1' * 2024, final=True) + + frames = list(frame_buffer) + assert len(frames) == 3 + assert isinstance(frames[1], DataFrame) + assert frames[2].flags == set(['END_STREAM']) + + # two chunks with last chunk < 1024 + c = HTTP20Connection('www.google.com') + c._sock = DummySocket() + c._send_cb = data_callback + c.putrequest('GET', '/') + c.endheaders(message_body=b'1' * 2000, final=True) + + frames = list(frame_buffer) + assert len(frames) == 3 + assert isinstance(frames[1], DataFrame) + assert frames[2].flags == set(['END_STREAM']) + + # no chunks + c = HTTP20Connection('www.google.com') + c._sock = DummySocket() + c._send_cb = data_callback + c.putrequest('GET', '/') + c.endheaders(message_body=b'', final=True) + + frames = list(frame_buffer) + assert len(frames) == 1 + def test_that_we_correctly_send_over_the_socket(self): sock = DummySocket() c = HTTP20Connection('www.google.com')