@@ -928,37 +928,77 @@ def on_disconnect(self) -> bytes:
928
928
929
929
raise ClientDisconnected ()
930
930
931
- def exhaust (self , chunk_size : int = 1024 * 64 ) -> None :
932
- """Exhaust the stream. This consumes all the data left until the
933
- limit is reached.
931
+ def _exhaust_chunks (self , chunk_size : int = 1024 * 64 ) -> t .Iterator [bytes ]:
932
+ """Exhaust the stream by reading until the limit is reached or the client
933
+ disconnects, yielding each chunk.
934
+
935
+ :param chunk_size: How many bytes to read at a time.
936
+
937
+ :meta private:
934
938
935
- :param chunk_size: the size for a chunk. It will read the chunk
936
- until the stream is exhausted and throw away
937
- the results.
939
+ .. versionadded:: 2.2.3
938
940
"""
939
941
to_read = self .limit - self ._pos
940
- chunk = chunk_size
942
+
941
943
while to_read > 0 :
942
- chunk = min (to_read , chunk )
943
- self .read (chunk )
944
- to_read -= chunk
944
+ chunk = self .read (min (to_read , chunk_size ))
945
+ yield chunk
946
+ to_read -= len (chunk )
947
+
948
+ def exhaust (self , chunk_size : int = 1024 * 64 ) -> None :
949
+ """Exhaust the stream by reading until the limit is reached or the client
950
+ disconnects, discarding the data.
951
+
952
+ :param chunk_size: How many bytes to read at a time.
953
+
954
+ .. versionchanged:: 2.2.3
955
+ Handle case where wrapped stream returns fewer bytes than requested.
956
+ """
957
+ for _ in self ._exhaust_chunks (chunk_size ):
958
+ pass
945
959
946
960
def read (self , size : t .Optional [int ] = None ) -> bytes :
947
- """Read `size` bytes or if size is not provided everything is read.
961
+ """Read up to ``size`` bytes from the underlying stream. If size is not
962
+ provided, read until the limit.
963
+
964
+ If the limit is reached, :meth:`on_exhausted` is called, which returns empty
965
+ bytes.
948
966
949
- :param size: the number of bytes read.
967
+ If no bytes are read and the limit is not reached, or if an error occurs during
968
+ the read, :meth:`on_disconnect` is called, which raises
969
+ :exc:`.ClientDisconnected`.
970
+
971
+ :param size: The number of bytes to read. ``None``, default, reads until the
972
+ limit is reached.
973
+
974
+ .. versionchanged:: 2.2.3
975
+ Handle case where wrapped stream returns fewer bytes than requested.
950
976
"""
951
977
if self ._pos >= self .limit :
952
978
return self .on_exhausted ()
953
- if size is None or size == - 1 : # -1 is for consistence with file
954
- size = self .limit
979
+
980
+ if size is None or size == - 1 : # -1 is for consistency with file
981
+ # Keep reading from the wrapped stream until the limit is reached. Can't
982
+ # rely on stream.read(size) because it's not guaranteed to return size.
983
+ buf = bytearray ()
984
+
985
+ for chunk in self ._exhaust_chunks ():
986
+ buf .extend (chunk )
987
+
988
+ return bytes (buf )
989
+
955
990
to_read = min (self .limit - self ._pos , size )
991
+
956
992
try :
957
993
read = self ._read (to_read )
958
994
except (OSError , ValueError ):
959
995
return self .on_disconnect ()
960
- if to_read and len (read ) != to_read :
996
+
997
+ if to_read and not len (read ):
998
+ # If no data was read, treat it as a disconnect. As long as some data was
999
+ # read, a subsequent call can still return more before reaching the limit.
961
1000
return self .on_disconnect ()
1001
+
962
1002
self ._pos += len (read )
963
1003
return read
964
1004
0 commit comments