Skip to content

Commit

Permalink
Merge pull request #756 from tumb1er/fix_multipart_chunk
Browse files Browse the repository at this point in the history
Fix multipart chunk
  • Loading branch information
kxepal committed Jan 31, 2016
2 parents c28e2f8 + 3010579 commit 0ef2382
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 5 deletions.
10 changes: 7 additions & 3 deletions aiohttp/multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,20 +305,24 @@ def _read_chunk_from_stream(self, size):
"""
assert size >= len(self._boundary) + 2, \
'Chunk size must be greater or equal than boundary length + 2'
if self._prev_chunk is None:
first_chunk = self._prev_chunk is None
if first_chunk:
self._prev_chunk = yield from self._content.read(size)

chunk = yield from self._content.read(size)

window = self._prev_chunk + chunk
sub = b'\r\n' + self._boundary
idx = window.find(sub, len(self._prev_chunk) - len(sub))
if first_chunk:
idx = window.find(sub)
else:
idx = window.find(sub, len(self._prev_chunk) - len(sub))
if idx >= 0:
# pushing boundary back to content
self._content.unread_data(window[idx:])
if size > idx:
self._prev_chunk = self._prev_chunk[:idx]
chunk = window[size:idx]
chunk = window[len(self._prev_chunk):idx]
if not chunk:
self._at_eof = True
result = self._prev_chunk
Expand Down
2 changes: 0 additions & 2 deletions aiohttp/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,6 @@ def wait_eof(self):
def unread_data(self, data):
""" rollback reading some data from stream, inserting it to buffer head.
"""
assert not self._eof, 'unread_data after feed_eof'

if not data:
return

Expand Down
23 changes: 23 additions & 0 deletions tests/test_multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,29 @@ def test_read_chunk_without_content_length(self):
self.assertEqual(c1 + c2, b'Hello, world!')
self.assertEqual(c3, b'')

def test_read_incomplete_chunk(self):
stream = Stream(b'')

def prepare(data):
f = asyncio.Future(loop=self.loop)
f.set_result(data)
return f

with mock.patch.object(stream, 'read', side_effect=[
prepare(b'Hello, '),
prepare(b'World'),
prepare(b'!\r\n--:'),
prepare(b'')
]):
obj = aiohttp.multipart.BodyPartReader(
self.boundary, {}, stream)
c1 = yield from obj.read_chunk(8)
self.assertEqual(c1, b'Hello, ')
c2 = yield from obj.read_chunk(8)
self.assertEqual(c2, b'World')
c3 = yield from obj.read_chunk(8)
self.assertEqual(c3, b'!')

def test_multi_read_chunk(self):
stream = Stream(b'Hello,\r\n--:\r\n\r\nworld!\r\n--:--')
obj = aiohttp.multipart.BodyPartReader(self.boundary, {}, stream)
Expand Down
5 changes: 5 additions & 0 deletions tests/test_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,11 @@ def test_unread_data(self):
data = self.loop.run_until_complete(stream.read(4))
self.assertEqual(b'line', data)

stream.feed_eof()
stream.unread_data(b'at_eof')
data = self.loop.run_until_complete(stream.read(6))
self.assertEqual(b'at_eof', data)

def test_exception(self):
stream = self._make_one()
self.assertIsNone(stream.exception())
Expand Down

0 comments on commit 0ef2382

Please sign in to comment.