Bug 63252 - [PATCH] FTP prevent No Transfer Timeout during long listings
Summary: [PATCH] FTP prevent No Transfer Timeout during long listings
Status: RESOLVED FIXED
Alias: None
Product: Ant
Classification: Unclassified
Component: Optional Tasks (show other bugs)
Version: 1.10.5
Hardware: All All
: P2 enhancement (vote)
Target Milestone: ---
Assignee: Ant Notifications List
URL:
Keywords: PatchAvailable
Depends on:
Blocks:
 
Reported: 2019-03-10 08:49 UTC by Eugène Adell
Modified: 2019-07-12 15:03 UTC (History)
1 user (show)



Attachments
patch (2.86 KB, patch)
2019-03-10 09:04 UTC, Eugène Adell
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Eugène Adell 2019-03-10 08:49:38 UTC
Under some circumstances (for example when listing a directory containing many symlinks), no data connection will be opened for a while and the server could consider this as an abnormal situation and close the command connection. An example of this behavior is met with TimeoutNoTransfer directive of proFTPd being set on a server (see http://www.proftpd.org/docs/directives/linked/config_ref_TimeoutNoTransfer.html).

Adding an attribute to regularly call a command that we know for sure that will trigger a data connection will solve this issue, if the TimeoutNoTransfer is higher than the value we set.
This attribute should have no side effect and a minimal performance impact.

target example of a long listing with many symlinks ending with a 60s Timeout :

  <target name="listingWithSymlinks">
    <ftp action="list"
       listing="listingWithSymlinks.txt"
       server="ftp.ietf.org"
       port="21"
       remotedir="/rfc/bcp"
       userid="anonymous"
       password="anonymous" >
       <fileset dir="." defaultexcludes="no" includes="*" />
    </ftp>
  </target>

output :

$ time ant -d -f rfc.xml -lib lib listingWithSymlinks
Apache Ant(TM) version 1.10.5 compiled on March 9 2019
Buildfile: rfc.xml
Adding reference: ant.PropertyHelper
Detected Java version: 1.8 in: /usr/local/java/jdk1.8.0_121/jre
Detected OS: Linux
Adding reference: ant.ComponentHelper
Setting ro project property: ant.file -> rfc.xml
Setting ro project property: ant.file.type -> file
Setting ro project property: ant.project.invoked-targets -> listingWithSymlinks
Adding reference: ant.projectHelper
Adding reference: ant.parsing.context
Adding reference: ant.targets
parsing buildfile rfc.xml with URI = file:rfc.xml
Setting ro project property: ant.project.name -> FTP test
Adding reference: FTP test
Setting ro project property: ant.project.default-target -> listing
Setting ro project property: ant.file.FTP test -> rfc.xml
Setting ro project property: ant.file.type.FTP test -> file
Project base dir set to: /home/eadell/tests/ftpant
 +Target:
 +Target: feat
 +Target: listingWithSymlinks
 +Target: listingWithoutSymlinks
 +Target: download
Adding reference: ant.LocalProperties
parsing buildfile jar:file:/home/eadell/base_ant/apache-ant-1.10.5.compiled/dist/lib/ant.jar!/org/apache/tools/ant/antlib.xml with URI = ja                                                          r:file:/home/eadell/base_ant/apache-ant-1.10.5.compiled/dist/lib/ant.jar!/org/apache/tools/ant/antlib.xml from a zip file
Setting project property: lib -> /home/eadell/tests/ftpant/lib
Adding reference: lib
Setting ro project property: ant.project.invoked-targets -> listingWithSymlinks
Attempting to create object of type org.apache.tools.ant.helper.DefaultExecutor
Adding reference: ant.executor
Build sequence for target(s) `listingWithSymlinks' is [listingWithSymlinks]
Complete build sequence is [listingWithSymlinks, ]

listingWithSymlinks:
      [ftp] Opening FTP connection to ftp.ietf.org
      [ftp] connected
      [ftp] logging in to FTP server
      [ftp] login succeeded
      [ftp] changing the remote directory to /rfc/bcp
      [ftp] listing files
fileset: Setup scanner in dir /home/eadell/tests/ftpant with patternSet{ includes: [*] excludes: [] }
could not find current working directory /rfc/bcp while checking a symlink
...
...
could not find current working directory /rfc/bcp while checking a symlink
      [ftp] disconnecting

BUILD FAILED
/home/eadell/tests/ftpant/rfc.xml:32: Error while communicating with FTP server:
        at org.apache.tools.ant.taskdefs.optional.net.FTP$FTPDirectoryScanner.scandir(FTP.java:564)
        at org.apache.tools.ant.taskdefs.optional.net.FTP$FTPDirectoryScanner.checkIncludePatterns(FTP.java:427)
        at org.apache.tools.ant.taskdefs.optional.net.FTP$FTPDirectoryScanner.scan(FTP.java:390)
        at org.apache.tools.ant.taskdefs.optional.net.FTP.transferFiles(FTP.java:1853)
        at org.apache.tools.ant.taskdefs.optional.net.FTP.transferFiles(FTP.java:1952)
        at org.apache.tools.ant.taskdefs.optional.net.FTP.execute(FTP.java:2666)
        at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:292)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:99)
        at org.apache.tools.ant.Task.perform(Task.java:350)
        at org.apache.tools.ant.Target.execute(Target.java:449)
        at org.apache.tools.ant.Target.performTasks(Target.java:470)
        at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1388)
        at org.apache.tools.ant.Project.executeTarget(Project.java:1361)
        at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
        at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
        at org.apache.tools.ant.Main.runBuild(Main.java:834)
        at org.apache.tools.ant.Main.startAnt(Main.java:223)
        at org.apache.tools.ant.launch.Launcher.run(Launcher.java:284)
        at org.apache.tools.ant.launch.Launcher.main(Launcher.java:101)
Caused by: org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
        at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:324)
        at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:300)
        at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:523)
        at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:648)
        at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:622)
        at org.apache.commons.net.ftp.FTP.cdup(FTP.java:886)
        at org.apache.commons.net.ftp.FTPClient.changeToParentDirectory(FTPClient.java:1185)
        at org.apache.tools.ant.taskdefs.optional.net.FTP$FTPDirectoryScanner.scandir(FTP.java:562)
        ... 22 more

Total time: 1 minute 4 seconds

real    1m5.001s
user    0m4.389s
sys     0m0.516s

The network capture shows this message sent from the server :

421 No transfer timeout (60 seconds): closing control connection
Comment 1 Eugène Adell 2019-03-10 09:04:42 UTC
Created attachment 36481 [details]
patch

This patch introduces a new attribute wakeUpTransferInterval indicating a time in seconds, to be used with listings that take too much time and trigger a Timeout.

When not set, or if the value is <= 0, the old behavior is kept.

As there is no way to know in advance if there is such Timeout on the server, and which value is set, we only can discover the timeout value with a first test without this attribute or assume the timeout is likely to be at 60s minimum (probably the common practice among administrators).

The command used to trigger a data connection is a LIST on the current file being parsed by the listing.

Tested succesfully with this target :

  <target name="listingWithSymlinks">
    <ftp action="list"
       listing="listingWithSymlinks.txt"
       server="ftp.ietf.org"
       port="21"
       wakeUpTransferInterval="53"
       remotedir="/rfc/bcp"
       userid="anonymous"
       password="anonymous" >
       <fileset dir="." defaultexcludes="no" includes="*" />
    </ftp>
  </target>

output : 

Buildfile: /home/eadell/tests/ftpant/rfc.xml
Adding reference: ant.PropertyHelper
Detected Java version: 1.8 in: /usr/local/java/jdk1.8.0_121/jre
Detected OS: Linux
Adding reference: ant.ComponentHelper
Setting ro project property: ant.file -> /home/eadell/tests/ftpant/rfc.xml
Setting ro project property: ant.file.type -> file
Setting ro project property: ant.project.invoked-targets -> listingWithSymlinks
Adding reference: ant.projectHelper
Adding reference: ant.parsing.context
Adding reference: ant.targets
parsing buildfile /home/eadell/tests/ftpant/rfc.xml with URI = file:/home/eadell/tests/ftpant/rfc.xml
Setting ro project property: ant.project.name -> FTP test
Adding reference: FTP test
Setting ro project property: ant.project.default-target -> listing
Setting ro project property: ant.file.FTP test -> /home/eadell/tests/ftpant/rfc.xml
Setting ro project property: ant.file.type.FTP test -> file
Project base dir set to: /home/eadell/tests/ftpant
 +Target:
 +Target: feat
 +Target: listingWithSymlinks
 +Target: listingWithoutSymlinks
 +Target: download
Adding reference: ant.LocalProperties
parsing buildfile jar:file:/home/eadell/base_ant/apache-ant-1.10.5.compiled/dist/lib/ant.jar!/org/apache/tools/ant/antlib.xml with URI = ja                                                          r:file:/home/eadell/base_ant/apache-ant-1.10.5.compiled/dist/lib/ant.jar!/org/apache/tools/ant/antlib.xml from a zip file
Setting project property: lib -> /home/eadell/tests/ftpant/lib
Adding reference: lib
Setting ro project property: ant.project.invoked-targets -> listingWithSymlinks
Attempting to create object of type org.apache.tools.ant.helper.DefaultExecutor
Adding reference: ant.executor
Build sequence for target(s) `listingWithSymlinks' is [listingWithSymlinks]
Complete build sequence is [listingWithSymlinks, download, listingWithoutSymlinks, feat, ]

listingWithSymlinks:
      [ftp] Opening FTP connection to ftp.ietf.org
      [ftp] connected
      [ftp] logging in to FTP server
      [ftp] login succeeded
      [ftp] changing the remote directory to /rfc/bcp
      [ftp] listing files
fileset: Setup scanner in dir /home/eadell/tests/ftpant with patternSet{ includes: [*] excludes: [] }
wakeUpTransferInterval is reached, trigger a data connection
wakeUpTransferInterval is reached, trigger a data connection
      [ftp] 221 files listed
      [ftp] disconnecting

BUILD SUCCESSFUL
Total time: 5 minutes 4 seconds

real    5m5.130s
user    0m5.852s
sys     0m1.016s


This patch doesn't prevent other timeouts (such as 421 Session Timeout) that come much later with some servers, that should be adressed in another way.
Comment 2 Jaikiran Pai 2019-05-17 04:43:53 UTC
I had a look at the patch. Upon the "idle" timeout being reached, as you note, you trigger a command from the client, which happens to be:

> The command used to trigger a data connection is a LIST on the current file being parsed by the listing.

IMO, the list command can be potentially expensive (imagine it getting triggered on a directory), more so because we aren't really interested in what it returns. Instead of issuing a list command, can you instead issue a "NOOP" command, available via the FTPClient#sendNoOp() which as per the javadoc of this method is meant to be used in cases like this one?
Comment 3 Eugène Adell 2019-05-18 19:26:09 UTC
NOOP logically doesn't open a data connection so we can't use it for our problem (I tested to confirm).

Listing a directory is not necessarily expensive although it's of course more expensive than listing a single file. In fact ANT is slow for doing its own listing because it's doing a loop and sends as many lists commands as it finds directories & files, but an FTP list command in itself is not meant to be slow. 

The added list commands can be compared to the total number of list commands performed, with my test it's like 2% overhead.
Comment 4 J.M. (Martijn) Kruithof 2019-07-12 15:03:46 UTC
Thanks for the patch, applied.
As default is not to do this anyway and gets you from not working to working an optimization on what to list would be nice but I'd say not required.