Ticket #4180: 4180-list-checks-2.diff
| File 4180-list-checks-2.diff, 11.9 KB (added by adiroiban, 10 months ago) |
|---|
-
twisted/protocols/ftp.py
diff --git a/twisted/protocols/ftp.py b/twisted/protocols/ftp.py index b035f04..05f1f7a 100644
a b 25 25 26 26 # Twisted Imports 27 27 from twisted import copyright 28 from twisted.internet import reactor, interfaces, protocol, error, defer28 from twisted.internet import defer, error, interfaces, protocol, reactor, task 29 29 from twisted.protocols import basic, policies 30 30 31 31 from twisted.python import log, failure, filepath … … 72 72 73 73 SVC_NOT_AVAIL_CLOSING_CTRL_CNX = "421.1" 74 74 TOO_MANY_CONNECTIONS = "421.2" 75 CANT_OPEN_DATA_CNX = "425" 75 CANT_OPEN_DATA_CNX = "425.1" 76 DTP_TIMEOUT = "425.2" 76 77 CNX_CLOSED_TXFR_ABORTED = "426" 77 78 REQ_ACTN_ABRTD_FILE_UNAVAIL = "450" 78 79 REQ_ACTN_ABRTD_LOCAL_ERR = "451" … … 141 142 SVC_NOT_AVAIL_CLOSING_CTRL_CNX: '421 Service not available, closing control connection.', 142 143 TOO_MANY_CONNECTIONS: '421 Too many users right now, try again in a few minutes.', 143 144 CANT_OPEN_DATA_CNX: "425 Can't open data connection.", 145 DTP_TIMEOUT: '425 Data channel initialization timed out.', 144 146 CNX_CLOSED_TXFR_ABORTED: '426 Transfer aborted. Data connection closed.', 145 147 146 148 REQ_ACTN_ABRTD_FILE_UNAVAIL: '450 Requested action aborted. File unavailable.', … … 777 779 (self.passivePortRange,)) 778 780 779 781 782 def _checkDataTransportStarted(self, command): 783 """Checks that data transport is ready. 784 785 If data transport was not requested using PORT, PASV etc it raises 786 L{BadCmdSequenceError}. 787 """ 788 if self.dtpInstance is None: 789 raise BadCmdSequenceError( 790 'PORT or PASV required before %s' % (command,)) 791 780 792 def ftp_USER(self, username): 781 793 """ 782 794 First part of login. Get the username the peer wants to … … 829 841 830 842 831 843 def ftp_PASV(self): 832 """Request for a passive connection 844 """ 845 Request for a passive connection 833 846 834 847 from the rfc:: 835 848 … … 876 889 877 890 878 891 def ftp_LIST(self, path=''): 879 """ This command causes a list to be sent from the server to the 880 passive DTP. If the pathname specifies a directory or other 892 """ 893 This command causes a list to be sent from the server to the 894 passive DTP. 895 896 If the pathname specifies a directory or other 881 897 group of files, the server should transfer a list of files 882 in the specified directory. If the pathname specifies a 883 file then the server should send current information on the 884 file. A null argument implies the user's current working or 885 default directory. 898 in the specified directory. 899 If the pathname specifies a file then the server should send current 900 information on the file. 901 A null argument implies the user's current working or default 902 directory. 886 903 """ 887 # Uh, for now, do this retarded thing. 888 if self.dtpInstance is None or not self.dtpInstance.isConnected: 889 return defer.fail(BadCmdSequenceError('must send PORT or PASV before RETR')) 904 self._checkDataTransportStarted('LIST') 890 905 891 906 # bug in konqueror 892 907 if path == "-a": … … 917 932 segments, 918 933 ('size', 'directory', 'permissions', 'hardlinks', 919 934 'modified', 'owner', 'group')) 935 d.addCallback(self._cbWaitDTPConnectionWithTimeout) 920 936 d.addCallback(gotListing) 921 937 return d 922 938 … … 936 952 @return: a L{Deferred} which will be fired when the listing request 937 953 is finished. 938 954 """ 939 # XXX: why is this check different from ftp_RETR/ftp_STOR? See #4180 940 if self.dtpInstance is None or not self.dtpInstance.isConnected: 941 return defer.fail( 942 BadCmdSequenceError('must send PORT or PASV before RETR')) 943 955 self._checkDataTransportStarted('NLST') 944 956 try: 945 957 segments = toSegments(self.workingDirectory, path) 946 958 except InvalidPath: … … 995 1007 '*' in segments[-1] or '?' in segments[-1] or 996 1008 ('[' in segments[-1] and ']' in segments[-1])): 997 1009 d = self.shell.list(segments[:-1]) 1010 d.addCallback(self._cbWaitDTPConnectionWithTimeout) 998 1011 d.addCallback(cbGlob) 999 1012 else: 1000 1013 d = self.shell.list(segments) 1014 d.addCallback(self._cbWaitDTPConnectionWithTimeout) 1001 1015 d.addCallback(cbList) 1002 # self.shell.list will generate an error if the path is invalid 1003 d.addErrback(listErr) 1016 1017 # self.shell.list will generate an error if the path is invalid 1018 d.addErrback(listErr) 1004 1019 return d 1005 1020 1021 def _cbWaitDTPConnectionWithTimeout(self, result): 1022 """ 1023 Helper callback that waits for DTP instance to be connected. 1024 1025 It will raise a C{PortConnectionError} if DTP instance is not 1026 connected after the interval defined by self.factory.timeOut. 1027 """ 1028 def ebDTPTimeout(failure): 1029 """ 1030 Called at data transport port timeout. 1031 """ 1032 failure.trap(defer.CancelledError) 1033 self.reply(DTP_TIMEOUT) 1034 return defer.fail( 1035 PortConnectionError( 1036 defer.TimeoutError("DTP connection timeout"))) 1037 1038 def cbContinueCommand(ignore, timeoutCall): 1039 if timeoutCall is not None and timeoutCall.active(): 1040 timeoutCall.cancel() 1041 return result 1042 1043 def cbCallTimeout(ignore): 1044 self.dtpFactory.deferred.cancel() 1045 1046 timeoutCall = self.factory._reactor.callLater( 1047 self.factory.timeOut, cbCallTimeout, None) 1048 1049 self.dtpFactory.deferred.addCallback(cbContinueCommand, timeoutCall) 1050 self.dtpFactory.deferred.addErrback(ebDTPTimeout) 1051 1052 return self.dtpFactory.deferred 1006 1053 1007 1054 def ftp_CWD(self, path): 1008 1055 try: … … 1038 1085 @rtype: L{Deferred} 1039 1086 @return: a L{Deferred} which will be fired when the transfer is done. 1040 1087 """ 1041 if self.dtpInstance is None: 1042 raise BadCmdSequenceError('PORT or PASV required before RETR') 1088 self._checkDataTransportStarted('RETR') 1043 1089 1044 1090 try: 1045 1091 newsegs = toSegments(self.workingDirectory, path) … … 1100 1146 1101 1147 1102 1148 def ftp_STOR(self, path): 1103 if self.dtpInstance is None: 1104 raise BadCmdSequenceError('PORT or PASV required before STOR') 1149 """ 1150 This command causes the server-DTP to accept the data 1151 transferred via the data connection and to store the data as 1152 a file at the server site. If the file specified in the 1153 pathname exists at the server site, then its contents shall 1154 be replaced by the data being transferred. A new file is 1155 created at the server site if the file specified in the 1156 pathname does not already exist. 1157 1158 @type path: C{str} 1159 @param path: The file path where the content should be stored. 1160 1161 @rtype: L{Deferred} 1162 @return: a L{Deferred} which will be fired when the transfer 1163 is finished. 1164 """ 1165 self._checkDataTransportStarted('STOR') 1105 1166 1106 1167 try: 1107 1168 newsegs = toSegments(self.workingDirectory, path) … … 1289 1350 1290 1351 1291 1352 def cleanupDTP(self): 1292 """call when DTP connection exits 1353 """ 1354 Called when DTP connection exits. 1293 1355 """ 1294 1356 log.msg('cleanupDTP', debug=True) 1295 1357 … … 1329 1391 1330 1392 passivePortRange = xrange(0, 1) 1331 1393 1332 def __init__(self, portal=None, userAnonymous='anonymous' ):1394 def __init__(self, portal=None, userAnonymous='anonymous', reactor=None): 1333 1395 self.portal = portal 1334 1396 self.userAnonymous = userAnonymous 1335 1397 self.instances = [] 1398 if reactor is None: 1399 from twisted.internet import reactor 1400 self._reactor = reactor 1401 1336 1402 1337 1403 def buildProtocol(self, addr): 1338 1404 p = policies.LimitTotalConnectionsFactory.buildProtocol(self, addr) -
twisted/test/test_ftp.py
diff --git a/twisted/test/test_ftp.py b/twisted/test/test_ftp.py index 23ffcba..06e38ee 100644
a b 463 463 self.assertEqual(portRange, protocol.wrappedProtocol.passivePortRange) 464 464 465 465 466 def _startDataConnection(self): 467 """ 468 Prepare data transport protocol to look like it was created by 469 a previous call to PASV or PORT 470 """ 471 self.serverProtocol.dtpFactory = ftp.DTPFactory(self.serverProtocol) 472 self.serverProtocol.dtpFactory.buildProtocol('ignore_address') 473 474 self.serverProtocol.dtpPort = self.serverProtocol.listenFactory( 475 6000, self.serverProtocol.dtpFactory) 476 477 dtpTransport = proto_helpers.StringTransportWithDisconnection() 478 dtpTransport.protocol = ftp.DTP() 479 self.serverProtocol.dtpInstance.transport = dtpTransport 480 481 482 def test_LISTWithoutDataChannel(self): 483 """ 484 Calling LIST without prior setup of data connection will result in a 485 incorrect sequence of commands error. 486 """ 487 d = self._anonymousLogin() 488 self.assertCommandFailed( 489 'LIST .', 490 ["503 Incorrect sequence of commands: " 491 "PORT or PASV required before LIST"], 492 chainDeferred=d) 493 return d 494 495 496 def test_LISTTimeout(self): 497 """ 498 LIST will timeout if setting up the DTP instance will take to long. 499 """ 500 # Set timeout to a very small value to not slow down tests. 501 self.factory.timeOut = 0.01 502 503 d = self._anonymousLogin() 504 self._startDataConnection() 505 506 self.assertCommandFailed( 507 'LIST .', 508 ["425 Data channel initialization timed out."], 509 chainDeferred=d) 510 return d 511 512 513 def test_NLSTWithoutDataChannel(self): 514 """ 515 Calling NLST without prior setup of data connection will result in a 516 incorrect sequence of commands error. 517 """ 518 d = self._anonymousLogin() 519 self.assertCommandFailed( 520 'NLST .', 521 ["503 Incorrect sequence of commands: " 522 "PORT or PASV required before NLST"], 523 chainDeferred=d) 524 return d 525 526 527 def test_NLSTTimeout(self): 528 """ 529 NLST will timeout if setting up the DTP instance will take to long. 530 """ 531 self.factory.timeOut = 0.01 532 #self.factory._reactor = task.Clock() 533 534 d = self._anonymousLogin() 535 self._startDataConnection() 536 537 # Set timeout to a very small value to not slow down tests. 538 self.assertCommandFailed( 539 'NLST .', 540 ["425 Data channel initialization timed out."], 541 chainDeferred=d) 542 543 def cbAdvanceClock(result, clock): 544 clock.advance(6) 545 return result 546 #d.addCallback(cbAdvanceClock, self.factory._reactor) 547 548 return d 549 550 466 551 467 552 class FTPServerTestCaseAdvancedClient(FTPServerTestCase): 468 553 """
