@@ -780,7 +780,11 @@ async def peek_and_react(self, expect_frame: bool = False) -> bool:
780780 return False
781781
782782 try :
783- self ._protocol .bytes_received (peek_data )
783+ if isinstance (peek_data , list ):
784+ for gro_segment in peek_data :
785+ self ._protocol .bytes_received (gro_segment )
786+ else :
787+ self ._protocol .bytes_received (peek_data )
784788 except self ._protocol .exceptions ():
785789 return False
786790
@@ -819,7 +823,7 @@ async def __exchange_until(
819823 maximal_data_in_read = None
820824
821825 data_out : bytes
822- data_in : bytes
826+ data_in : bytes | list [ bytes ]
823827
824828 data_in_len : int = 0
825829
@@ -839,13 +843,21 @@ async def __exchange_until(
839843 reach_socket : bool = False
840844 if not self ._protocol .has_pending_event (stream_id = stream_id ):
841845 if receive_first is False :
846+ tbs = []
847+
842848 while True :
843849 data_out = self ._protocol .bytes_to_send ()
844850
845851 if not data_out :
846852 break
847853
848- await self .sock .sendall (data_out )
854+ tbs .append (data_out )
855+
856+ if self ._svn is HttpVersion .h3 and len (tbs ) > 1 :
857+ await self .sock .sendall (tbs )
858+ else :
859+ for chunk in tbs :
860+ await self .sock .sendall (chunk )
849861
850862 try :
851863 data_in = await self .sock .recv (self .blocksize )
@@ -880,30 +892,62 @@ async def __exchange_until(
880892 else :
881893 self ._protocol .connection_lost ()
882894 else :
883- if data_in_len_from is None :
884- data_in_len += len (data_in )
895+ if isinstance (data_in , list ):
896+ for udp_gro_segment in data_in :
897+ if data_in_len_from is None :
898+ data_in_len += len (udp_gro_segment )
885899
886- try :
887- self ._protocol .bytes_received (data_in )
888- except self ._protocol .exceptions () as e :
889- # h2 has a dedicated exception for IncompleteRead (InvalidBodyLengthError)
890- # we convert the exception to our "IncompleteRead" instead.
891- if hasattr (e , "expected_length" ) and hasattr (
892- e , "actual_length"
893- ):
894- raise IncompleteRead (
895- partial = e .actual_length , expected = e .expected_length
896- ) from e # Defensive:
897- raise ProtocolError (e ) from e # Defensive:
900+ try :
901+ self ._protocol .bytes_received (udp_gro_segment )
902+ except self ._protocol .exceptions () as e :
903+ # h2 has a dedicated exception for IncompleteRead (InvalidBodyLengthError)
904+ # we convert the exception to our "IncompleteRead" instead.
905+ if hasattr (e , "expected_length" ) and hasattr (
906+ e , "actual_length"
907+ ):
908+ raise IncompleteRead (
909+ partial = e .actual_length ,
910+ expected = e .expected_length ,
911+ ) from e # Defensive:
912+ raise ProtocolError (e ) from e # Defensive:
913+ else :
914+ incoming_buffer_size = len (data_in )
915+ self ._recv_size_ema = (
916+ self ._recv_size_ema * 0.7 + incoming_buffer_size * 0.3
917+ )
918+
919+ if data_in_len_from is None :
920+ data_in_len += incoming_buffer_size
921+
922+ try :
923+ self ._protocol .bytes_received (data_in )
924+ except self ._protocol .exceptions () as e :
925+ # h2 has a dedicated exception for IncompleteRead (InvalidBodyLengthError)
926+ # we convert the exception to our "IncompleteRead" instead.
927+ if hasattr (e , "expected_length" ) and hasattr (
928+ e , "actual_length"
929+ ):
930+ raise IncompleteRead (
931+ partial = e .actual_length , expected = e .expected_length
932+ ) from e # Defensive:
933+ raise ProtocolError (e ) from e # Defensive:
898934
899935 if receive_first is True :
936+ tbs = []
937+
900938 while True :
901939 data_out = self ._protocol .bytes_to_send ()
902940
903941 if not data_out :
904942 break
905943
906- await self .sock .sendall (data_out )
944+ tbs .append (data_out )
945+
946+ if self ._svn is HttpVersion .h3 and len (tbs ) > 1 :
947+ await self .sock .sendall (tbs )
948+ else :
949+ for chunk in tbs :
950+ await self .sock .sendall (chunk )
907951
908952 for event in self ._protocol .events (stream_id = stream_id ): # type: Event
909953 stream_related_event : bool = hasattr (event , "stream_id" )
@@ -1157,15 +1201,21 @@ def putheader(self, header: str, *values: str) -> None:
11571201 f"Invalid content-length set. Given '{ values [0 ]} ' when only digits are allowed."
11581202 )
11591203 elif self .__legacy_host_entry is None and encoded_header == b"host" :
1160- self .__legacy_host_entry = (
1161- values [0 ].encode ("idna" ) if isinstance (values [0 ], str ) else values [0 ]
1162- )
1204+ if isinstance (values [0 ], str ):
1205+ self .__legacy_host_entry = values [0 ].encode ("idna" )
1206+ elif isinstance (values [0 ], bytes ):
1207+ self .__legacy_host_entry = values [0 ]
1208+ else :
1209+ self .__legacy_host_entry = str (values [0 ]).encode ("iso-8859-1" )
11631210 return
11641211
11651212 for value in values :
1166- encoded_value = (
1167- value .encode ("iso-8859-1" ) if isinstance (value , str ) else value
1168- )
1213+ if isinstance (value , str ):
1214+ encoded_value = value .encode ("iso-8859-1" )
1215+ elif isinstance (value , bytes ):
1216+ encoded_value = value
1217+ else : # best effort branch
1218+ encoded_value = str (value ).encode ("iso-8859-1" )
11691219
11701220 if encoded_header .startswith (b":" ):
11711221 if encoded_header == b":protocol" :
@@ -1256,11 +1306,19 @@ async def endheaders( # type: ignore[override]
12561306 raise ProtocolError (e ) from e # Defensive:
12571307
12581308 try :
1309+ tbs = []
1310+
12591311 while True :
12601312 buf = self ._protocol .bytes_to_send ()
12611313 if not buf :
12621314 break
1263- await self .sock .sendall (buf )
1315+ tbs .append (buf )
1316+
1317+ if self ._svn is HttpVersion .h3 and len (tbs ) > 1 :
1318+ await self .sock .sendall (tbs )
1319+ else :
1320+ for chunk in tbs :
1321+ await self .sock .sendall (chunk )
12641322 except BrokenPipeError as e :
12651323 rp = ResponsePromise (self , self ._stream_id , self .__headers )
12661324 self ._promises [rp .uid ] = rp
@@ -1294,7 +1352,13 @@ async def __write_st(
12941352 self ._protocol .should_wait_remote_flow_control (__stream_id , len (__buf ))
12951353 is True
12961354 ):
1297- self ._protocol .bytes_received (await self .sock .recv (self .blocksize ))
1355+ data_in = await self .sock .recv (self .blocksize )
1356+
1357+ if isinstance (data_in , list ):
1358+ for gro_segment in data_in :
1359+ self ._protocol .bytes_received (gro_segment )
1360+ else :
1361+ self ._protocol .bytes_received (data_in )
12981362
12991363 while True :
13001364 data_out = self ._protocol .bytes_to_send ()
@@ -1612,7 +1676,13 @@ async def send( # type: ignore[override]
16121676 )
16131677 is True
16141678 ):
1615- self ._protocol .bytes_received (await self .sock .recv (self .blocksize ))
1679+ data_in = await self .sock .recv (self .blocksize )
1680+
1681+ if not isinstance (data_in , list ):
1682+ self ._protocol .bytes_received (data_in )
1683+ else :
1684+ for gro_segment in data_in :
1685+ self ._protocol .bytes_received (gro_segment )
16161686
16171687 # this is a bad sign. we should stop sending and instead retrieve the response.
16181688 if self ._protocol .has_pending_event (
@@ -1758,3 +1828,4 @@ async def close(self) -> None: # type: ignore[override]
17581828 self ._cached_http_vsn = None
17591829 self ._connected_at = None
17601830 self ._last_used_at = time .monotonic ()
1831+ self ._recv_size_ema = 0.0
0 commit comments