4
4
import copy
5
5
import errno
6
6
import logging
7
- import io
8
7
from random import shuffle
9
8
import socket
10
9
import time
18
17
from kafka .protocol .api import RequestHeader
19
18
from kafka .protocol .admin import SaslHandShakeRequest
20
19
from kafka .protocol .commit import GroupCoordinatorResponse
20
+ from kafka .protocol .frame import KafkaBytes
21
21
from kafka .protocol .metadata import MetadataRequest
22
22
from kafka .protocol .types import Int32
23
23
from kafka .version import __version__
@@ -204,9 +204,9 @@ def __init__(self, host, port, afi, **configs):
204
204
if self .config ['ssl_context' ] is not None :
205
205
self ._ssl_context = self .config ['ssl_context' ]
206
206
self ._sasl_auth_future = None
207
- self ._rbuffer = io .BytesIO ()
207
+ self ._header = KafkaBytes (4 )
208
+ self ._rbuffer = None
208
209
self ._receiving = False
209
- self ._next_payload_bytes = 0
210
210
self .last_attempt = 0
211
211
self ._processing = False
212
212
self ._correlation_id = 0
@@ -518,17 +518,19 @@ def close(self, error=None):
518
518
self .state = ConnectionStates .DISCONNECTED
519
519
self .last_attempt = time .time ()
520
520
self ._sasl_auth_future = None
521
- self ._receiving = False
522
- self ._next_payload_bytes = 0
523
- self ._rbuffer .seek (0 )
524
- self ._rbuffer .truncate ()
521
+ self ._reset_buffer ()
525
522
if error is None :
526
523
error = Errors .Cancelled (str (self ))
527
524
while self .in_flight_requests :
528
525
ifr = self .in_flight_requests .popleft ()
529
526
ifr .future .failure (error )
530
527
self .config ['state_change_callback' ](self )
531
528
529
+ def _reset_buffer (self ):
530
+ self ._receiving = False
531
+ self ._header .seek (0 )
532
+ self ._rbuffer = None
533
+
532
534
def send (self , request ):
533
535
"""send request, return Future()
534
536
@@ -602,11 +604,11 @@ def recv(self):
602
604
# fail all the pending request futures
603
605
if self .in_flight_requests :
604
606
self .close (Errors .ConnectionError ('Socket not connected during recv with in-flight-requests' ))
605
- return None
607
+ return ()
606
608
607
609
elif not self .in_flight_requests :
608
610
log .warning ('%s: No in-flight-requests to recv' , self )
609
- return None
611
+ return ()
610
612
611
613
response = self ._recv ()
612
614
if not response and self .requests_timed_out ():
@@ -615,93 +617,87 @@ def recv(self):
615
617
self .close (error = Errors .RequestTimedOutError (
616
618
'Request timed out after %s ms' %
617
619
self .config ['request_timeout_ms' ]))
618
- return None
620
+ return ()
619
621
return response
620
622
621
623
def _recv (self ):
622
- # Not receiving is the state of reading the payload header
623
- if not self ._receiving :
624
+ responses = []
625
+ SOCK_CHUNK_BYTES = 4096
626
+ while True :
624
627
try :
625
- bytes_to_read = 4 - self ._rbuffer .tell ()
626
- data = self ._sock .recv (bytes_to_read )
628
+ data = self ._sock .recv (SOCK_CHUNK_BYTES )
627
629
# We expect socket.recv to raise an exception if there is not
628
630
# enough data to read the full bytes_to_read
629
631
# but if the socket is disconnected, we will get empty data
630
632
# without an exception raised
631
633
if not data :
632
634
log .error ('%s: socket disconnected' , self )
633
635
self .close (error = Errors .ConnectionError ('socket disconnected' ))
634
- return None
635
- self ._rbuffer .write (data )
636
+ break
637
+ else :
638
+ responses .extend (self .receive_bytes (data ))
639
+ if len (data ) < SOCK_CHUNK_BYTES :
640
+ break
636
641
except SSLWantReadError :
637
- return None
642
+ break
638
643
except ConnectionError as e :
639
644
if six .PY2 and e .errno == errno .EWOULDBLOCK :
640
- return None
641
- log .exception ('%s: Error receiving 4-byte payload header - '
645
+ break
646
+ log .exception ('%s: Error receiving network data '
642
647
' closing socket' , self )
643
648
self .close (error = Errors .ConnectionError (e ))
644
- return None
645
- except BlockingIOError :
646
- if six .PY3 :
647
- return None
648
- raise
649
-
650
- if self ._rbuffer .tell () == 4 :
651
- self ._rbuffer .seek (0 )
652
- self ._next_payload_bytes = Int32 .decode (self ._rbuffer )
653
- # reset buffer and switch state to receiving payload bytes
654
- self ._rbuffer .seek (0 )
655
- self ._rbuffer .truncate ()
656
- self ._receiving = True
657
- elif self ._rbuffer .tell () > 4 :
658
- raise Errors .KafkaError ('this should not happen - are you threading?' )
659
-
660
- if self ._receiving :
661
- staged_bytes = self ._rbuffer .tell ()
662
- try :
663
- bytes_to_read = self ._next_payload_bytes - staged_bytes
664
- data = self ._sock .recv (bytes_to_read )
665
- # We expect socket.recv to raise an exception if there is not
666
- # enough data to read the full bytes_to_read
667
- # but if the socket is disconnected, we will get empty data
668
- # without an exception raised
669
- if bytes_to_read and not data :
670
- log .error ('%s: socket disconnected' , self )
671
- self .close (error = Errors .ConnectionError ('socket disconnected' ))
672
- return None
673
- self ._rbuffer .write (data )
674
- except SSLWantReadError :
675
- return None
676
- except ConnectionError as e :
677
- # Extremely small chance that we have exactly 4 bytes for a
678
- # header, but nothing to read in the body yet
679
- if six .PY2 and e .errno == errno .EWOULDBLOCK :
680
- return None
681
- log .exception ('%s: Error in recv' , self )
682
- self .close (error = Errors .ConnectionError (e ))
683
- return None
649
+ break
684
650
except BlockingIOError :
685
651
if six .PY3 :
686
- return None
652
+ break
687
653
raise
654
+ return responses
688
655
689
- staged_bytes = self ._rbuffer .tell ()
690
- if staged_bytes > self ._next_payload_bytes :
691
- self .close (error = Errors .KafkaError ('Receive buffer has more bytes than expected?' ))
692
-
693
- if staged_bytes != self ._next_payload_bytes :
694
- return None
656
+ def receive_bytes (self , data ):
657
+ i = 0
658
+ n = len (data )
659
+ responses = []
660
+ if self ._sensors :
661
+ self ._sensors .bytes_received .record (n )
662
+ while i < n :
663
+
664
+ # Not receiving is the state of reading the payload header
665
+ if not self ._receiving :
666
+ bytes_to_read = min (4 - self ._header .tell (), n - i )
667
+ self ._header .write (data [i :i + bytes_to_read ])
668
+ i += bytes_to_read
669
+
670
+ if self ._header .tell () == 4 :
671
+ self ._header .seek (0 )
672
+ nbytes = Int32 .decode (self ._header )
673
+ # reset buffer and switch state to receiving payload bytes
674
+ self ._rbuffer = KafkaBytes (nbytes )
675
+ self ._receiving = True
676
+ elif self ._header .tell () > 4 :
677
+ raise Errors .KafkaError ('this should not happen - are you threading?' )
678
+
679
+
680
+ if self ._receiving :
681
+ total_bytes = len (self ._rbuffer )
682
+ staged_bytes = self ._rbuffer .tell ()
683
+ bytes_to_read = min (total_bytes - staged_bytes , n - i )
684
+ self ._rbuffer .write (data [i :i + bytes_to_read ])
685
+ i += bytes_to_read
686
+
687
+ staged_bytes = self ._rbuffer .tell ()
688
+ if staged_bytes > total_bytes :
689
+ self .close (error = Errors .KafkaError ('Receive buffer has more bytes than expected?' ))
690
+
691
+ if staged_bytes != total_bytes :
692
+ break
695
693
696
- self ._receiving = False
697
- self ._next_payload_bytes = 0
698
- if self ._sensors :
699
- self ._sensors .bytes_received .record (4 + self ._rbuffer .tell ())
700
- self ._rbuffer .seek (0 )
701
- response = self ._process_response (self ._rbuffer )
702
- self ._rbuffer .seek (0 )
703
- self ._rbuffer .truncate ()
704
- return response
694
+ self ._receiving = False
695
+ self ._rbuffer .seek (0 )
696
+ resp = self ._process_response (self ._rbuffer )
697
+ if resp is not None :
698
+ responses .append (resp )
699
+ self ._reset_buffer ()
700
+ return responses
705
701
706
702
def _process_response (self , read_buffer ):
707
703
assert not self ._processing , 'Recursion not supported'
0 commit comments