Skip to content

Commit 2ef2fff

Browse files
gh-109845: Make test_ftplib more stable under load (GH-109912)
recv() can return partial data cut in the middle of a multibyte character. Test raw binary data instead of data incorrectly decoded by parts.
1 parent b1e4f6e commit 2ef2fff

File tree

1 file changed

+18
-20
lines changed

1 file changed

+18
-20
lines changed

Lib/test/test_ftplib.py

+18-20
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
DEFAULT_ENCODING = 'utf-8'
3333
# the dummy data returned by server over the data channel when
3434
# RETR, LIST, NLST, MLSD commands are issued
35-
RETR_DATA = 'abcde12345\r\n' * 1000 + 'non-ascii char \xAE\r\n'
35+
RETR_DATA = 'abcde\xB9\xB2\xB3\xA4\xA6\r\n' * 1000
3636
LIST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
3737
NLST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
3838
MLSD_DATA = ("type=cdir;perm=el;unique==keVO1+ZF4; test\r\n"
@@ -67,11 +67,11 @@ class DummyDTPHandler(asynchat.async_chat):
6767
def __init__(self, conn, baseclass):
6868
asynchat.async_chat.__init__(self, conn)
6969
self.baseclass = baseclass
70-
self.baseclass.last_received_data = ''
70+
self.baseclass.last_received_data = bytearray()
7171
self.encoding = baseclass.encoding
7272

7373
def handle_read(self):
74-
new_data = self.recv(1024).decode(self.encoding, 'replace')
74+
new_data = self.recv(1024)
7575
self.baseclass.last_received_data += new_data
7676

7777
def handle_close(self):
@@ -107,7 +107,7 @@ def __init__(self, conn, encoding=DEFAULT_ENCODING):
107107
self.in_buffer = []
108108
self.dtp = None
109109
self.last_received_cmd = None
110-
self.last_received_data = ''
110+
self.last_received_data = bytearray()
111111
self.next_response = ''
112112
self.next_data = None
113113
self.rest = None
@@ -590,19 +590,17 @@ def test_abort(self):
590590
self.client.abort()
591591

592592
def test_retrbinary(self):
593-
def callback(data):
594-
received.append(data.decode(self.client.encoding))
595593
received = []
596-
self.client.retrbinary('retr', callback)
597-
self.check_data(''.join(received), RETR_DATA)
594+
self.client.retrbinary('retr', received.append)
595+
self.check_data(b''.join(received),
596+
RETR_DATA.encode(self.client.encoding))
598597

599598
def test_retrbinary_rest(self):
600-
def callback(data):
601-
received.append(data.decode(self.client.encoding))
602599
for rest in (0, 10, 20):
603600
received = []
604-
self.client.retrbinary('retr', callback, rest=rest)
605-
self.check_data(''.join(received), RETR_DATA[rest:])
601+
self.client.retrbinary('retr', received.append, rest=rest)
602+
self.check_data(b''.join(received),
603+
RETR_DATA[rest:].encode(self.client.encoding))
606604

607605
def test_retrlines(self):
608606
received = []
@@ -612,7 +610,8 @@ def test_retrlines(self):
612610
def test_storbinary(self):
613611
f = io.BytesIO(RETR_DATA.encode(self.client.encoding))
614612
self.client.storbinary('stor', f)
615-
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
613+
self.check_data(self.server.handler_instance.last_received_data,
614+
RETR_DATA.encode(self.server.encoding))
616615
# test new callback arg
617616
flag = []
618617
f.seek(0)
@@ -631,7 +630,8 @@ def test_storlines(self):
631630
data = RETR_DATA.replace('\r\n', '\n').encode(self.client.encoding)
632631
f = io.BytesIO(data)
633632
self.client.storlines('stor', f)
634-
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
633+
self.check_data(self.server.handler_instance.last_received_data,
634+
RETR_DATA.encode(self.server.encoding))
635635
# test new callback arg
636636
flag = []
637637
f.seek(0)
@@ -649,7 +649,7 @@ def test_nlst(self):
649649

650650
def test_dir(self):
651651
l = []
652-
self.client.dir(lambda x: l.append(x))
652+
self.client.dir(l.append)
653653
self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
654654

655655
def test_mlsd(self):
@@ -889,12 +889,10 @@ def test_makepasv(self):
889889

890890
def test_transfer(self):
891891
def retr():
892-
def callback(data):
893-
received.append(data.decode(self.client.encoding))
894892
received = []
895-
self.client.retrbinary('retr', callback)
896-
self.assertEqual(len(''.join(received)), len(RETR_DATA))
897-
self.assertEqual(''.join(received), RETR_DATA)
893+
self.client.retrbinary('retr', received.append)
894+
self.assertEqual(b''.join(received),
895+
RETR_DATA.encode(self.client.encoding))
898896
self.client.set_pasv(True)
899897
retr()
900898
self.client.set_pasv(False)

0 commit comments

Comments
 (0)