Skip to content

Commit 4e66eca

Browse files
[3.11] gh-109845: Make test_ftplib more stable under load (GH-109912) (GH-109920)
recv() can return partial data cut in the middle of a multibyte character. Test raw binary data instead of data incorrectly decoded by parts. (cherry picked from commit 2ef2fff) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent f764abb commit 4e66eca

File tree

1 file changed

+18
-20
lines changed

1 file changed

+18
-20
lines changed

Lib/test/test_ftplib.py

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
DEFAULT_ENCODING = 'utf-8'
3535
# the dummy data returned by server over the data channel when
3636
# RETR, LIST, NLST, MLSD commands are issued
37-
RETR_DATA = 'abcde12345\r\n' * 1000 + 'non-ascii char \xAE\r\n'
37+
RETR_DATA = 'abcde\xB9\xB2\xB3\xA4\xA6\r\n' * 1000
3838
LIST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
3939
NLST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
4040
MLSD_DATA = ("type=cdir;perm=el;unique==keVO1+ZF4; test\r\n"
@@ -69,11 +69,11 @@ class DummyDTPHandler(asynchat.async_chat):
6969
def __init__(self, conn, baseclass):
7070
asynchat.async_chat.__init__(self, conn)
7171
self.baseclass = baseclass
72-
self.baseclass.last_received_data = ''
72+
self.baseclass.last_received_data = bytearray()
7373
self.encoding = baseclass.encoding
7474

7575
def handle_read(self):
76-
new_data = self.recv(1024).decode(self.encoding, 'replace')
76+
new_data = self.recv(1024)
7777
self.baseclass.last_received_data += new_data
7878

7979
def handle_close(self):
@@ -109,7 +109,7 @@ def __init__(self, conn, encoding=DEFAULT_ENCODING):
109109
self.in_buffer = []
110110
self.dtp = None
111111
self.last_received_cmd = None
112-
self.last_received_data = ''
112+
self.last_received_data = bytearray()
113113
self.next_response = ''
114114
self.next_data = None
115115
self.rest = None
@@ -592,19 +592,17 @@ def test_abort(self):
592592
self.client.abort()
593593

594594
def test_retrbinary(self):
595-
def callback(data):
596-
received.append(data.decode(self.client.encoding))
597595
received = []
598-
self.client.retrbinary('retr', callback)
599-
self.check_data(''.join(received), RETR_DATA)
596+
self.client.retrbinary('retr', received.append)
597+
self.check_data(b''.join(received),
598+
RETR_DATA.encode(self.client.encoding))
600599

601600
def test_retrbinary_rest(self):
602-
def callback(data):
603-
received.append(data.decode(self.client.encoding))
604601
for rest in (0, 10, 20):
605602
received = []
606-
self.client.retrbinary('retr', callback, rest=rest)
607-
self.check_data(''.join(received), RETR_DATA[rest:])
603+
self.client.retrbinary('retr', received.append, rest=rest)
604+
self.check_data(b''.join(received),
605+
RETR_DATA[rest:].encode(self.client.encoding))
608606

609607
def test_retrlines(self):
610608
received = []
@@ -614,7 +612,8 @@ def test_retrlines(self):
614612
def test_storbinary(self):
615613
f = io.BytesIO(RETR_DATA.encode(self.client.encoding))
616614
self.client.storbinary('stor', f)
617-
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
615+
self.check_data(self.server.handler_instance.last_received_data,
616+
RETR_DATA.encode(self.server.encoding))
618617
# test new callback arg
619618
flag = []
620619
f.seek(0)
@@ -633,7 +632,8 @@ def test_storlines(self):
633632
data = RETR_DATA.replace('\r\n', '\n').encode(self.client.encoding)
634633
f = io.BytesIO(data)
635634
self.client.storlines('stor', f)
636-
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
635+
self.check_data(self.server.handler_instance.last_received_data,
636+
RETR_DATA.encode(self.server.encoding))
637637
# test new callback arg
638638
flag = []
639639
f.seek(0)
@@ -651,7 +651,7 @@ def test_nlst(self):
651651

652652
def test_dir(self):
653653
l = []
654-
self.client.dir(lambda x: l.append(x))
654+
self.client.dir(l.append)
655655
self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
656656

657657
def test_mlsd(self):
@@ -891,12 +891,10 @@ def test_makepasv(self):
891891

892892
def test_transfer(self):
893893
def retr():
894-
def callback(data):
895-
received.append(data.decode(self.client.encoding))
896894
received = []
897-
self.client.retrbinary('retr', callback)
898-
self.assertEqual(len(''.join(received)), len(RETR_DATA))
899-
self.assertEqual(''.join(received), RETR_DATA)
895+
self.client.retrbinary('retr', received.append)
896+
self.assertEqual(b''.join(received),
897+
RETR_DATA.encode(self.client.encoding))
900898
self.client.set_pasv(True)
901899
retr()
902900
self.client.set_pasv(False)

0 commit comments

Comments
 (0)