@@ -654,20 +654,14 @@ mod mlsd {
654654 }
655655 }
656656
657- async fn read_response ( stream : & TcpStream ) -> String {
658- let mut buffer = vec ! [ 0u8 ; 4096 ] ;
659- loop {
660- stream. readable ( ) . await . unwrap ( ) ;
661- match stream. try_read ( & mut buffer) {
662- Ok ( n) => return String :: from_utf8_lossy ( & buffer[ 0 ..n] ) . to_string ( ) ,
663- Err ( ref e) if e. kind ( ) == std:: io:: ErrorKind :: WouldBlock => continue ,
664- Err ( e) => panic ! ( "Failed to read response: {}" , e) ,
665- } ;
666- }
657+ async fn connect ( addr : & str ) -> FtpStream {
658+ let mut ftp_stream = FtpStream :: connect ( addr) . await . unwrap ( ) ;
659+ ftp_stream. login ( "hoi" , "jij" ) . await . unwrap ( ) ;
660+ ftp_stream
667661 }
668662
669663 fn parse_pasv_response ( response : & str ) -> u16 {
670- // Parse "227 Entering Passive Mode (127,0,0,1,p1,p2)"
664+ // Parse "Entering Passive Mode (127,0,0,1,p1,p2)"
671665 // Port = p1 * 256 + p2
672666 let start = response. find ( '(' ) . unwrap ( ) + 1 ;
673667 let end = response. find ( ')' ) . unwrap ( ) ;
@@ -678,43 +672,28 @@ mod mlsd {
678672 p1 * 256 + p2
679673 }
680674
681- async fn connect ( addr : & str ) -> TcpStream {
682- let stream = TcpStream :: connect ( addr) . await . unwrap ( ) ;
683-
684- let greeting = read_response ( & stream) . await ;
685- assert ! ( greeting. starts_with( "220" ) , "Expected greeting, got: {}" , greeting) ;
686-
687- // Authenticate
688- send_command ( & stream, "USER hoi" ) . await ;
689- let response = read_response ( & stream) . await ;
690- assert ! ( response. starts_with( "331" ) , "Expected password request, got: {}" , response) ;
691-
692- send_command ( & stream, "PASS jij" ) . await ;
693- let response = read_response ( & stream) . await ;
694- assert ! ( response. starts_with( "230" ) , "Expected login success, got: {}" , response) ;
695-
696- stream
697- }
698-
699- async fn connect_pasv ( stream : & TcpStream ) -> TcpStream {
700- send_command ( & stream, "PASV" ) . await ;
701- let pasv_response = read_response ( & stream) . await ;
702- assert ! ( pasv_response. starts_with( "227" ) , "Expected PASV response, got: {}" , pasv_response) ;
703- let data_port = parse_pasv_response ( & pasv_response) ;
675+ async fn connect_pasv ( ftp : & mut FtpStream ) -> TcpStream {
676+ send_command ( ftp. get_ref ( ) , "PASV" ) . await ;
677+ let pasv_response = ftp. read_response ( 227 ) . await . unwrap ( ) ;
678+ let data_port = parse_pasv_response ( & pasv_response. 1 ) ;
704679 TcpStream :: connect ( format ! ( "127.0.0.1:{}" , data_port) ) . await . unwrap ( )
705680 }
706681
707- async fn read_all_data ( stream : & TcpStream ) -> String {
682+ async fn read_all ( stream : & TcpStream , until_eof : bool ) -> String {
708683 let mut data = String :: new ( ) ;
709684 let mut buffer = vec ! [ 0u8 ; 1024 ] ;
710-
685+ let mut blocks = 0 ;
711686 loop {
712687 match stream. try_read ( & mut buffer) {
713688 Ok ( 0 ) => break , // EOF
714689 Ok ( n) => {
715690 data. push_str ( & String :: from_utf8_lossy ( & buffer[ 0 ..n] ) ) ;
716691 }
717692 Err ( ref e) if e. kind ( ) == std:: io:: ErrorKind :: WouldBlock => {
693+ blocks += 1 ;
694+ if !until_eof && blocks >= 2 {
695+ break ;
696+ }
718697 // Wait a bit for more data or connection close
719698 tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) . await ;
720699 continue ;
@@ -740,20 +719,14 @@ mod mlsd {
740719 }
741720 }
742721
743- async fn consume_226 ( stream : & TcpStream ) {
744- let response = read_response ( & stream) . await ;
745- let response = response. strip_suffix ( "\r \n " ) . unwrap ( ) ;
746- assert ! ( response. starts_with( "226 " ) && !response. contains( "\r \n " ) , "Expected 226, got: {}" , response) ;
747- }
748-
749722 #[ rstest]
750723 #[ awt]
751724 #[ tokio:: test]
752725 async fn basic_mlsd ( #[ future] harness : Harness ) {
753726 setup_files ( & harness. root ) ;
754727
755- let stream = connect ( & harness. addr ) . await ;
756- let data_stream = connect_pasv ( & stream ) . await ;
728+ let mut ftp = connect ( & harness. addr ) . await ;
729+ let data_stream = connect_pasv ( & mut ftp ) . await ;
757730
758731 // MLSD uses data channel. Example from RFC 3659 (truncated):
759732 //
@@ -764,16 +737,10 @@ mod mlsd {
764737 // D> Type=file;Size=1830;Modify=19940916055648;Perm=r; hatch.c
765738 // ...
766739 // S> 226 MLSD completed
767- send_command ( & stream, "MLSD" ) . await ;
768- let response = read_response ( & stream) . await ;
769- let response = response. strip_suffix ( "\r \n " ) . unwrap ( ) ;
770- assert ! (
771- response. starts_with( "150 " ) && !response. trim( ) . contains( "\r \n " ) ,
772- "Expected data connection opening, got: {}" ,
773- response
774- ) ;
775-
776- let data = read_all_data ( & data_stream) . await ;
740+ send_command ( ftp. get_ref ( ) , "MLSD" ) . await ;
741+ ftp. read_response ( 150 ) . await . unwrap ( ) ;
742+
743+ let data = read_all ( & data_stream, true ) . await ;
777744 let mut entries = data. split ( "\r \n " ) . collect :: < Vec < _ > > ( ) ;
778745 assert ! ( entries. pop( ) . unwrap( ) . is_empty( ) , "Last line should be empty due to trailing CRLF" ) ;
779746
@@ -787,7 +754,7 @@ mod mlsd {
787754 check_facts ( file_line, & [ "type=file" , "size=12" ] ) ;
788755 check_facts ( dir_line, & [ "type=dir" ] ) ;
789756
790- consume_226 ( & stream ) . await ;
757+ ftp . read_response ( 226 ) . await . unwrap ( ) ;
791758 }
792759
793760 #[ rstest]
@@ -796,14 +763,13 @@ mod mlsd {
796763 async fn mlsd_with_path ( #[ future] harness : Harness ) {
797764 setup_files ( & harness. root ) ;
798765
799- let stream = connect ( & harness. addr ) . await ;
800- let data_stream = connect_pasv ( & stream ) . await ;
766+ let mut ftp = connect ( & harness. addr ) . await ;
767+ let data_stream = connect_pasv ( & mut ftp ) . await ;
801768
802- send_command ( & stream, "MLSD test_dir" ) . await ;
803- let response = read_response ( & stream) . await ;
804- assert ! ( response. starts_with( "150 " ) , "Expected data connection opening, got: {}" , response) ;
769+ send_command ( ftp. get_ref ( ) , "MLSD test_dir" ) . await ;
770+ ftp. read_response ( 150 ) . await . unwrap ( ) ;
805771
806- let data = read_all_data ( & data_stream) . await ;
772+ let data = read_all ( & data_stream, true ) . await ;
807773 let mut entries = data. split ( "\r \n " ) . collect :: < Vec < _ > > ( ) ;
808774 assert ! ( entries. pop( ) . unwrap( ) . is_empty( ) , "Last line should be empty due to trailing CRLF" ) ;
809775 assert_eq ! ( entries. len( ) , 1 ) ;
@@ -815,7 +781,7 @@ mod mlsd {
815781
816782 check_facts ( & file_line, & [ "type=file" , "size=0" ] ) ;
817783
818- consume_226 ( & stream ) . await ;
784+ ftp . read_response ( 226 ) . await . unwrap ( ) ;
819785 }
820786
821787 #[ rstest]
@@ -824,15 +790,16 @@ mod mlsd {
824790 async fn mlst ( #[ future] harness : Harness ) {
825791 setup_files ( & harness. root ) ;
826792
827- let stream = connect ( & harness. addr ) . await ;
793+ let ftp = connect ( & harness. addr ) . await ;
828794
829795 // MLST uses control channel, not data channel. Format:
830796 //
831797 // 250- Listing
832798 // Type=file;Size=1234;... filename.txt
833799 // 250 End
834- send_command ( & stream, "MLST test_file.txt" ) . await ;
835- let response = read_response ( & stream) . await ;
800+ let stream = ftp. get_ref ( ) ;
801+ send_command ( stream, "MLST test_file.txt" ) . await ;
802+ let response = read_all ( stream, false ) . await ;
836803 let line = response
837804 . strip_prefix ( "250- Listing\r \n " )
838805 . expect ( "Wrong prefix" )
@@ -842,8 +809,8 @@ mod mlsd {
842809 println ! ( "line {line}" ) ;
843810 check_facts ( line, & [ "type=file" , "size=12" ] ) ;
844811
845- send_command ( & stream, "MLST test_dir" ) . await ;
846- let response = read_response ( & stream) . await ;
812+ send_command ( stream, "MLST test_dir" ) . await ;
813+ let response = read_all ( stream, false ) . await ;
847814
848815 let line = response
849816 . strip_prefix ( "250- Listing\r \n " )
0 commit comments