blob: e8241e839aea157f9c98aa23fa70bd3bc980e5cf [file] [log] [blame]
Benjamin Peterson3c0c4832008-09-27 02:49:54 +00001"""Test script for ftplib module."""
2
Antoine Pitrouccd5e022009-11-15 17:22:09 +00003# Modified by Giampaolo Rodola' to test FTP class, IPv6 and TLS
4# environment
Benjamin Peterson3c0c4832008-09-27 02:49:54 +00005
Facundo Batista3f100992007-03-26 20:56:09 +00006import ftplib
Benjamin Peterson3c0c4832008-09-27 02:49:54 +00007import threading
8import asyncore
9import asynchat
10import socket
11import StringIO
Antoine Pitrouccd5e022009-11-15 17:22:09 +000012import errno
13import os
14try:
15 import ssl
16except ImportError:
17 ssl = None
Facundo Batista3f100992007-03-26 20:56:09 +000018
19from unittest import TestCase
20from test import test_support
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000021from test.test_support import HOST
Facundo Batista3f100992007-03-26 20:56:09 +000022
Neal Norwitzb0917c12008-02-26 04:50:37 +000023
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000024# the dummy data returned by server over the data channel when
25# RETR, LIST and NLST commands are issued
26RETR_DATA = 'abcde12345\r\n' * 1000
27LIST_DATA = 'foo\r\nbar\r\n'
28NLST_DATA = 'foo\r\nbar\r\n'
29
30
31class DummyDTPHandler(asynchat.async_chat):
32
33 def __init__(self, conn, baseclass):
34 asynchat.async_chat.__init__(self, conn)
35 self.baseclass = baseclass
36 self.baseclass.last_received_data = ''
37
38 def handle_read(self):
39 self.baseclass.last_received_data += self.recv(1024)
40
41 def handle_close(self):
42 self.baseclass.push('226 transfer complete')
43 self.close()
44
45
46class DummyFTPHandler(asynchat.async_chat):
47
Antoine Pitrouccd5e022009-11-15 17:22:09 +000048 dtp_handler = DummyDTPHandler
49
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000050 def __init__(self, conn):
51 asynchat.async_chat.__init__(self, conn)
52 self.set_terminator("\r\n")
53 self.in_buffer = []
54 self.dtp = None
55 self.last_received_cmd = None
56 self.last_received_data = ''
57 self.next_response = ''
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +000058 self.rest = None
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000059 self.push('220 welcome')
60
61 def collect_incoming_data(self, data):
62 self.in_buffer.append(data)
63
64 def found_terminator(self):
65 line = ''.join(self.in_buffer)
66 self.in_buffer = []
67 if self.next_response:
68 self.push(self.next_response)
69 self.next_response = ''
70 cmd = line.split(' ')[0].lower()
71 self.last_received_cmd = cmd
72 space = line.find(' ')
73 if space != -1:
74 arg = line[space + 1:]
75 else:
76 arg = ""
77 if hasattr(self, 'cmd_' + cmd):
78 method = getattr(self, 'cmd_' + cmd)
79 method(arg)
80 else:
81 self.push('550 command "%s" not understood.' %cmd)
82
83 def handle_error(self):
84 raise
85
86 def push(self, data):
87 asynchat.async_chat.push(self, data + '\r\n')
88
89 def cmd_port(self, arg):
90 addr = map(int, arg.split(','))
91 ip = '%d.%d.%d.%d' %tuple(addr[:4])
92 port = (addr[4] * 256) + addr[5]
93 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouccd5e022009-11-15 17:22:09 +000094 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000095 self.push('200 active data connection established')
96
97 def cmd_pasv(self, arg):
98 sock = socket.socket()
99 sock.bind((self.socket.getsockname()[0], 0))
100 sock.listen(5)
101 sock.settimeout(2)
102 ip, port = sock.getsockname()[:2]
Ezio Melottidde5b942010-02-03 05:37:26 +0000103 ip = ip.replace('.', ',')
104 p1, p2 = divmod(port, 256)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000105 self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
106 conn, addr = sock.accept()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000107 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000108
109 def cmd_eprt(self, arg):
110 af, ip, port = arg.split(arg[0])[1:-1]
111 port = int(port)
112 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000113 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000114 self.push('200 active data connection established')
115
116 def cmd_epsv(self, arg):
117 sock = socket.socket(socket.AF_INET6)
118 sock.bind((self.socket.getsockname()[0], 0))
119 sock.listen(5)
120 sock.settimeout(2)
121 port = sock.getsockname()[1]
122 self.push('229 entering extended passive mode (|||%d|)' %port)
123 conn, addr = sock.accept()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000124 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000125
126 def cmd_echo(self, arg):
127 # sends back the received string (used by the test suite)
128 self.push(arg)
129
130 def cmd_user(self, arg):
131 self.push('331 username ok')
132
133 def cmd_pass(self, arg):
134 self.push('230 password ok')
135
136 def cmd_acct(self, arg):
137 self.push('230 acct ok')
138
139 def cmd_rnfr(self, arg):
140 self.push('350 rnfr ok')
141
142 def cmd_rnto(self, arg):
143 self.push('250 rnto ok')
144
145 def cmd_dele(self, arg):
146 self.push('250 dele ok')
147
148 def cmd_cwd(self, arg):
149 self.push('250 cwd ok')
150
151 def cmd_size(self, arg):
152 self.push('250 1000')
153
154 def cmd_mkd(self, arg):
155 self.push('257 "%s"' %arg)
156
157 def cmd_rmd(self, arg):
158 self.push('250 rmd ok')
159
160 def cmd_pwd(self, arg):
161 self.push('257 "pwd ok"')
162
163 def cmd_type(self, arg):
164 self.push('200 type ok')
165
166 def cmd_quit(self, arg):
167 self.push('221 quit ok')
168 self.close()
169
170 def cmd_stor(self, arg):
171 self.push('125 stor ok')
172
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000173 def cmd_rest(self, arg):
174 self.rest = arg
175 self.push('350 rest ok')
176
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000177 def cmd_retr(self, arg):
178 self.push('125 retr ok')
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000179 if self.rest is not None:
180 offset = int(self.rest)
181 else:
182 offset = 0
183 self.dtp.push(RETR_DATA[offset:])
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000184 self.dtp.close_when_done()
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000185 self.rest = None
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000186
187 def cmd_list(self, arg):
188 self.push('125 list ok')
189 self.dtp.push(LIST_DATA)
190 self.dtp.close_when_done()
191
192 def cmd_nlst(self, arg):
193 self.push('125 nlst ok')
194 self.dtp.push(NLST_DATA)
195 self.dtp.close_when_done()
196
197
198class DummyFTPServer(asyncore.dispatcher, threading.Thread):
199
200 handler = DummyFTPHandler
201
202 def __init__(self, address, af=socket.AF_INET):
203 threading.Thread.__init__(self)
204 asyncore.dispatcher.__init__(self)
205 self.create_socket(af, socket.SOCK_STREAM)
206 self.bind(address)
207 self.listen(5)
208 self.active = False
209 self.active_lock = threading.Lock()
210 self.host, self.port = self.socket.getsockname()[:2]
211
212 def start(self):
213 assert not self.active
214 self.__flag = threading.Event()
215 threading.Thread.start(self)
216 self.__flag.wait()
217
218 def run(self):
219 self.active = True
220 self.__flag.set()
221 while self.active and asyncore.socket_map:
222 self.active_lock.acquire()
223 asyncore.loop(timeout=0.1, count=1)
224 self.active_lock.release()
225 asyncore.close_all(ignore_all=True)
226
227 def stop(self):
228 assert self.active
229 self.active = False
230 self.join()
231
232 def handle_accept(self):
233 conn, addr = self.accept()
234 self.handler = self.handler(conn)
Benjamin Petersone14267b2008-09-28 20:57:21 +0000235 self.close()
236
237 def handle_connect(self):
238 self.close()
239 handle_read = handle_connect
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000240
241 def writable(self):
242 return 0
243
244 def handle_error(self):
245 raise
246
247
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000248if ssl is not None:
249
250 CERTFILE = os.path.join(os.path.dirname(__file__), "keycert.pem")
251
252 class SSLConnection(object, asyncore.dispatcher):
253 """An asyncore.dispatcher subclass supporting TLS/SSL."""
254
255 _ssl_accepting = False
256
257 def secure_connection(self):
258 self.socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
259 certfile=CERTFILE, server_side=True,
260 do_handshake_on_connect=False,
261 ssl_version=ssl.PROTOCOL_SSLv23)
262 self._ssl_accepting = True
263
264 def _do_ssl_handshake(self):
265 try:
266 self.socket.do_handshake()
267 except ssl.SSLError, err:
268 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
269 ssl.SSL_ERROR_WANT_WRITE):
270 return
271 elif err.args[0] == ssl.SSL_ERROR_EOF:
272 return self.handle_close()
273 raise
274 except socket.error, err:
275 if err.args[0] == errno.ECONNABORTED:
276 return self.handle_close()
277 else:
278 self._ssl_accepting = False
279
280 def handle_read_event(self):
281 if self._ssl_accepting:
282 self._do_ssl_handshake()
283 else:
284 super(SSLConnection, self).handle_read_event()
285
286 def handle_write_event(self):
287 if self._ssl_accepting:
288 self._do_ssl_handshake()
289 else:
290 super(SSLConnection, self).handle_write_event()
291
292 def send(self, data):
293 try:
294 return super(SSLConnection, self).send(data)
295 except ssl.SSLError, err:
296 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
297 return 0
298 raise
299
300 def recv(self, buffer_size):
301 try:
302 return super(SSLConnection, self).recv(buffer_size)
303 except ssl.SSLError, err:
304 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
305 self.handle_close()
306 return ''
307 raise
308
309 def handle_error(self):
310 raise
311
312 def close(self):
313 try:
314 if isinstance(self.socket, ssl.SSLSocket):
315 if self.socket._sslobj is not None:
316 self.socket.unwrap()
317 finally:
318 super(SSLConnection, self).close()
319
320
321 class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
322 """A DummyDTPHandler subclass supporting TLS/SSL."""
323
324 def __init__(self, conn, baseclass):
325 DummyDTPHandler.__init__(self, conn, baseclass)
326 if self.baseclass.secure_data_channel:
327 self.secure_connection()
328
329
330 class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
331 """A DummyFTPHandler subclass supporting TLS/SSL."""
332
333 dtp_handler = DummyTLS_DTPHandler
334
335 def __init__(self, conn):
336 DummyFTPHandler.__init__(self, conn)
337 self.secure_data_channel = False
338
339 def cmd_auth(self, line):
340 """Set up secure control channel."""
341 self.push('234 AUTH TLS successful')
342 self.secure_connection()
343
344 def cmd_pbsz(self, line):
345 """Negotiate size of buffer for secure data transfer.
346 For TLS/SSL the only valid value for the parameter is '0'.
347 Any other value is accepted but ignored.
348 """
349 self.push('200 PBSZ=0 successful.')
350
351 def cmd_prot(self, line):
352 """Setup un/secure data channel."""
353 arg = line.upper()
354 if arg == 'C':
355 self.push('200 Protection set to Clear')
356 self.secure_data_channel = False
357 elif arg == 'P':
358 self.push('200 Protection set to Private')
359 self.secure_data_channel = True
360 else:
361 self.push("502 Unrecognized PROT type (use C or P).")
362
363
364 class DummyTLS_FTPServer(DummyFTPServer):
365 handler = DummyTLS_FTPHandler
366
367
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000368class TestFTPClass(TestCase):
369
370 def setUp(self):
371 self.server = DummyFTPServer((HOST, 0))
372 self.server.start()
373 self.client = ftplib.FTP(timeout=2)
374 self.client.connect(self.server.host, self.server.port)
375
376 def tearDown(self):
377 self.client.close()
378 self.server.stop()
379
380 def test_getwelcome(self):
381 self.assertEqual(self.client.getwelcome(), '220 welcome')
382
383 def test_sanitize(self):
384 self.assertEqual(self.client.sanitize('foo'), repr('foo'))
385 self.assertEqual(self.client.sanitize('pass 12345'), repr('pass *****'))
386 self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
387
388 def test_exceptions(self):
389 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
390 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
391 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
392 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 599')
393 self.assertRaises(ftplib.error_proto, self.client.sendcmd, 'echo 999')
394
395 def test_all_errors(self):
396 exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
397 ftplib.error_proto, ftplib.Error, IOError, EOFError)
398 for x in exceptions:
399 try:
400 raise x('exception not included in all_errors set')
401 except ftplib.all_errors:
402 pass
403
404 def test_set_pasv(self):
405 # passive mode is supposed to be enabled by default
406 self.assertTrue(self.client.passiveserver)
407 self.client.set_pasv(True)
408 self.assertTrue(self.client.passiveserver)
409 self.client.set_pasv(False)
410 self.assertFalse(self.client.passiveserver)
411
412 def test_voidcmd(self):
413 self.client.voidcmd('echo 200')
414 self.client.voidcmd('echo 299')
415 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
416 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
417
418 def test_login(self):
419 self.client.login()
420
421 def test_acct(self):
422 self.client.acct('passwd')
423
424 def test_rename(self):
425 self.client.rename('a', 'b')
426 self.server.handler.next_response = '200'
427 self.assertRaises(ftplib.error_reply, self.client.rename, 'a', 'b')
428
429 def test_delete(self):
430 self.client.delete('foo')
431 self.server.handler.next_response = '199'
432 self.assertRaises(ftplib.error_reply, self.client.delete, 'foo')
433
434 def test_size(self):
435 self.client.size('foo')
436
437 def test_mkd(self):
438 dir = self.client.mkd('/foo')
439 self.assertEqual(dir, '/foo')
440
441 def test_rmd(self):
442 self.client.rmd('foo')
443
444 def test_pwd(self):
445 dir = self.client.pwd()
446 self.assertEqual(dir, 'pwd ok')
447
448 def test_quit(self):
449 self.assertEqual(self.client.quit(), '221 quit ok')
450 # Ensure the connection gets closed; sock attribute should be None
451 self.assertEqual(self.client.sock, None)
452
453 def test_retrbinary(self):
454 received = []
455 self.client.retrbinary('retr', received.append)
456 self.assertEqual(''.join(received), RETR_DATA)
457
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000458 def test_retrbinary_rest(self):
459 for rest in (0, 10, 20):
460 received = []
461 self.client.retrbinary('retr', received.append, rest=rest)
462 self.assertEqual(''.join(received), RETR_DATA[rest:],
463 msg='rest test case %d %d %d' % (rest,
464 len(''.join(received)),
465 len(RETR_DATA[rest:])))
466
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000467 def test_retrlines(self):
468 received = []
469 self.client.retrlines('retr', received.append)
470 self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', ''))
471
472 def test_storbinary(self):
473 f = StringIO.StringIO(RETR_DATA)
474 self.client.storbinary('stor', f)
475 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
476 # test new callback arg
477 flag = []
478 f.seek(0)
479 self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
480 self.assertTrue(flag)
481
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000482 def test_storbinary_rest(self):
483 f = StringIO.StringIO(RETR_DATA)
484 for r in (30, '30'):
485 f.seek(0)
486 self.client.storbinary('stor', f, rest=r)
487 self.assertEqual(self.server.handler.rest, str(r))
488
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000489 def test_storlines(self):
490 f = StringIO.StringIO(RETR_DATA.replace('\r\n', '\n'))
491 self.client.storlines('stor', f)
492 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
493 # test new callback arg
494 flag = []
495 f.seek(0)
496 self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
497 self.assertTrue(flag)
498
499 def test_nlst(self):
500 self.client.nlst()
501 self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
502
503 def test_dir(self):
504 l = []
505 self.client.dir(lambda x: l.append(x))
506 self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
507
508 def test_makeport(self):
509 self.client.makeport()
510 # IPv4 is in use, just make sure send_eprt has not been used
511 self.assertEqual(self.server.handler.last_received_cmd, 'port')
512
513 def test_makepasv(self):
514 host, port = self.client.makepasv()
515 conn = socket.create_connection((host, port), 2)
Facundo Batista93c33682007-03-30 13:00:35 +0000516 conn.close()
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000517 # IPv4 is in use, just make sure send_epsv has not been used
518 self.assertEqual(self.server.handler.last_received_cmd, 'pasv')
Facundo Batista3f100992007-03-26 20:56:09 +0000519
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000520
521class TestIPv6Environment(TestCase):
522
523 def setUp(self):
524 self.server = DummyFTPServer((HOST, 0), af=socket.AF_INET6)
525 self.server.start()
526 self.client = ftplib.FTP()
527 self.client.connect(self.server.host, self.server.port)
528
529 def tearDown(self):
530 self.client.close()
531 self.server.stop()
532
533 def test_af(self):
534 self.assertEqual(self.client.af, socket.AF_INET6)
535
536 def test_makeport(self):
537 self.client.makeport()
538 self.assertEqual(self.server.handler.last_received_cmd, 'eprt')
539
540 def test_makepasv(self):
541 host, port = self.client.makepasv()
542 conn = socket.create_connection((host, port), 2)
543 conn.close()
544 self.assertEqual(self.server.handler.last_received_cmd, 'epsv')
545
546 def test_transfer(self):
547 def retr():
548 received = []
549 self.client.retrbinary('retr', received.append)
550 self.assertEqual(''.join(received), RETR_DATA)
551 self.client.set_pasv(True)
552 retr()
553 self.client.set_pasv(False)
554 retr()
555
556
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000557class TestTLS_FTPClassMixin(TestFTPClass):
558 """Repeat TestFTPClass tests starting the TLS layer for both control
559 and data connections first.
560 """
561
562 def setUp(self):
563 self.server = DummyTLS_FTPServer((HOST, 0))
564 self.server.start()
565 self.client = ftplib.FTP_TLS(timeout=2)
566 self.client.connect(self.server.host, self.server.port)
567 # enable TLS
568 self.client.auth()
569 self.client.prot_p()
570
571
572class TestTLS_FTPClass(TestCase):
573 """Specific TLS_FTP class tests."""
574
575 def setUp(self):
576 self.server = DummyTLS_FTPServer((HOST, 0))
577 self.server.start()
578 self.client = ftplib.FTP_TLS(timeout=2)
579 self.client.connect(self.server.host, self.server.port)
580
581 def tearDown(self):
582 self.client.close()
583 self.server.stop()
584
585 def test_control_connection(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000586 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000587 self.client.auth()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000588 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000589
590 def test_data_connection(self):
591 # clear text
592 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000593 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000594 sock.close()
595 self.client.voidresp()
596
597 # secured, after PROT P
598 self.client.prot_p()
599 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000600 self.assertIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000601 sock.close()
602 self.client.voidresp()
603
604 # PROT C is issued, the connection must be in cleartext again
605 self.client.prot_c()
606 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000607 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000608 sock.close()
609 self.client.voidresp()
610
611 def test_login(self):
612 # login() is supposed to implicitly secure the control connection
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000613 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000614 self.client.login()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000615 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000616 # make sure that AUTH TLS doesn't get issued again
617 self.client.login()
618
619 def test_auth_issued_twice(self):
620 self.client.auth()
621 self.assertRaises(ValueError, self.client.auth)
622
623 def test_auth_ssl(self):
624 try:
625 self.client.ssl_version = ssl.PROTOCOL_SSLv3
626 self.client.auth()
627 self.assertRaises(ValueError, self.client.auth)
628 finally:
629 self.client.ssl_version = ssl.PROTOCOL_TLSv1
630
631
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000632class TestTimeouts(TestCase):
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000633
Facundo Batista3f100992007-03-26 20:56:09 +0000634 def setUp(self):
Facundo Batista3f100992007-03-26 20:56:09 +0000635 self.evt = threading.Event()
Trent Nelsone41b0062008-04-08 23:47:30 +0000636 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
637 self.sock.settimeout(3)
638 self.port = test_support.bind_port(self.sock)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000639 threading.Thread(target=self.server, args=(self.evt,self.sock)).start()
Neal Norwitzb0917c12008-02-26 04:50:37 +0000640 # Wait for the server to be ready.
641 self.evt.wait()
642 self.evt.clear()
Trent Nelsone41b0062008-04-08 23:47:30 +0000643 ftplib.FTP.port = self.port
Facundo Batista3f100992007-03-26 20:56:09 +0000644
645 def tearDown(self):
646 self.evt.wait()
647
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000648 def server(self, evt, serv):
649 # This method sets the evt 3 times:
650 # 1) when the connection is ready to be accepted.
651 # 2) when it is safe for the caller to close the connection
652 # 3) when we have closed the socket
653 serv.listen(5)
654 # (1) Signal the caller that we are ready to accept the connection.
655 evt.set()
656 try:
657 conn, addr = serv.accept()
658 except socket.timeout:
659 pass
660 else:
661 conn.send("1 Hola mundo\n")
662 # (2) Signal the caller that it is safe to close the socket.
663 evt.set()
664 conn.close()
665 finally:
666 serv.close()
667 # (3) Signal the caller that we are done.
668 evt.set()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000669
Facundo Batista3f100992007-03-26 20:56:09 +0000670 def testTimeoutDefault(self):
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000671 # default -- use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000672 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000673 socket.setdefaulttimeout(30)
674 try:
675 ftp = ftplib.FTP("localhost")
676 finally:
677 socket.setdefaulttimeout(None)
678 self.assertEqual(ftp.sock.gettimeout(), 30)
679 self.evt.wait()
680 ftp.close()
681
682 def testTimeoutNone(self):
683 # no timeout -- do not use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000684 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000685 socket.setdefaulttimeout(30)
686 try:
687 ftp = ftplib.FTP("localhost", timeout=None)
688 finally:
689 socket.setdefaulttimeout(None)
Facundo Batista3f100992007-03-26 20:56:09 +0000690 self.assertTrue(ftp.sock.gettimeout() is None)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000691 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000692 ftp.close()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000693
Facundo Batista3f100992007-03-26 20:56:09 +0000694 def testTimeoutValue(self):
695 # a value
Trent Nelsone41b0062008-04-08 23:47:30 +0000696 ftp = ftplib.FTP(HOST, timeout=30)
Facundo Batista3f100992007-03-26 20:56:09 +0000697 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000698 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000699 ftp.close()
Facundo Batista3f100992007-03-26 20:56:09 +0000700
Facundo Batista93c33682007-03-30 13:00:35 +0000701 def testTimeoutConnect(self):
702 ftp = ftplib.FTP()
Trent Nelsone41b0062008-04-08 23:47:30 +0000703 ftp.connect(HOST, timeout=30)
Facundo Batista93c33682007-03-30 13:00:35 +0000704 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000705 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000706 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000707
708 def testTimeoutDifferentOrder(self):
709 ftp = ftplib.FTP(timeout=30)
Trent Nelsone41b0062008-04-08 23:47:30 +0000710 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000711 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000712 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000713 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000714
715 def testTimeoutDirectAccess(self):
716 ftp = ftplib.FTP()
717 ftp.timeout = 30
Trent Nelsone41b0062008-04-08 23:47:30 +0000718 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000719 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000720 self.evt.wait()
Facundo Batista3f100992007-03-26 20:56:09 +0000721 ftp.close()
722
723
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000724def test_main():
725 tests = [TestFTPClass, TestTimeouts]
726 if socket.has_ipv6:
727 try:
728 DummyFTPServer((HOST, 0), af=socket.AF_INET6)
729 except socket.error:
730 pass
731 else:
732 tests.append(TestIPv6Environment)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000733
734 if ssl is not None:
735 tests.extend([TestTLS_FTPClassMixin, TestTLS_FTPClass])
736
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000737 thread_info = test_support.threading_setup()
738 try:
739 test_support.run_unittest(*tests)
740 finally:
741 test_support.threading_cleanup(*thread_info)
742
Facundo Batista3f100992007-03-26 20:56:09 +0000743
744if __name__ == '__main__':
745 test_main()