|  | 
| 3 | 3 | import io | 
| 4 | 4 | import json | 
| 5 | 5 | import pathlib | 
|  | 6 | +import sys | 
| 6 | 7 | import zlib | 
| 7 | 8 | from typing import Any, Optional | 
| 8 | 9 | from unittest import mock | 
| @@ -657,6 +658,66 @@ async def test_invalid_boundary(self) -> None: | 
| 657 | 658 |             with pytest.raises(ValueError): | 
| 658 | 659 |                 await reader.next() | 
| 659 | 660 | 
 | 
|  | 661 | +    @pytest.mark.skipif(sys.version_info < (3, 10), reason="Needs anext()") | 
|  | 662 | +    async def test_read_boundary_across_chunks(self) -> None: | 
|  | 663 | +        class SplitBoundaryStream: | 
|  | 664 | +            def __init__(self) -> None: | 
|  | 665 | +                self.content = [ | 
|  | 666 | +                    b"--foobar\r\n\r\n", | 
|  | 667 | +                    b"Hello,\r\n-", | 
|  | 668 | +                    b"-fo", | 
|  | 669 | +                    b"ob", | 
|  | 670 | +                    b"ar\r\n", | 
|  | 671 | +                    b"\r\nwor", | 
|  | 672 | +                    b"ld!", | 
|  | 673 | +                    b"\r\n--f", | 
|  | 674 | +                    b"oobar--", | 
|  | 675 | +                ] | 
|  | 676 | + | 
|  | 677 | +            async def read(self, size: Optional[Any] = None) -> bytes: | 
|  | 678 | +                chunk = self.content.pop(0) | 
|  | 679 | +                assert len(chunk) <= size | 
|  | 680 | +                return chunk | 
|  | 681 | + | 
|  | 682 | +            def at_eof(self) -> bool: | 
|  | 683 | +                return not self.content | 
|  | 684 | + | 
|  | 685 | +            async def readline(self) -> bytes: | 
|  | 686 | +                line = b"" | 
|  | 687 | +                while self.content and b"\n" not in line: | 
|  | 688 | +                    line += self.content.pop(0) | 
|  | 689 | +                line, *extra = line.split(b"\n", maxsplit=1) | 
|  | 690 | +                if extra and extra[0]: | 
|  | 691 | +                    self.content.insert(0, extra[0]) | 
|  | 692 | +                return line + b"\n" | 
|  | 693 | + | 
|  | 694 | +            def unread_data(self, data: bytes) -> None: | 
|  | 695 | +                if self.content: | 
|  | 696 | +                    self.content[0] = data + self.content[0] | 
|  | 697 | +                else: | 
|  | 698 | +                    self.content.append(data) | 
|  | 699 | + | 
|  | 700 | +        stream = SplitBoundaryStream() | 
|  | 701 | +        reader = aiohttp.MultipartReader( | 
|  | 702 | +            {CONTENT_TYPE: 'multipart/related;boundary="foobar"'}, stream | 
|  | 703 | +        ) | 
|  | 704 | +        part = await anext(reader) | 
|  | 705 | +        result = await part.read_chunk(10) | 
|  | 706 | +        assert result == b"Hello," | 
|  | 707 | +        result = await part.read_chunk(10) | 
|  | 708 | +        assert result == b"" | 
|  | 709 | +        assert part.at_eof() | 
|  | 710 | + | 
|  | 711 | +        part = await anext(reader) | 
|  | 712 | +        result = await part.read_chunk(10) | 
|  | 713 | +        assert result == b"world!" | 
|  | 714 | +        result = await part.read_chunk(10) | 
|  | 715 | +        assert result == b"" | 
|  | 716 | +        assert part.at_eof() | 
|  | 717 | + | 
|  | 718 | +        with pytest.raises(StopAsyncIteration): | 
|  | 719 | +            await anext(reader) | 
|  | 720 | + | 
| 660 | 721 |     async def test_release(self) -> None: | 
| 661 | 722 |         with Stream( | 
| 662 | 723 |             b"--:\r\n" | 
|  | 
0 commit comments