Hi Sebb

Here are my observations:

1)
When retrieving a file, FTP servers send the transfer status right after they 
send the EOF on the data socket, but before the client closes the socket.
This may lead to a race condition:
If we've sent a NOOP not longer than a second ago and want to consume the NOOP 
reply, we may receive the transfer status instead. Subsequent call to
completePendingCommand() will consume the NOOP reply.

Maybe it's not a big problem, because we will have either
        226-File successfully transferred
or
        200 NOOP command successful.
as our last message and a high-level FTPClient method will return true.


2) Pure-FTPd:

During a transmission Pure-FTPd replies to NOOPs immediately with:
        500 Unknown command
(perhaps because it only expects ABOR in this state)

At the end of transmission it sends the transmission status (usually 226)


3) BulletProof FTP Server:

During a transmission Pure-FTPd replies to NOOPs immediately with:
        200 NOOP command successful.

If there was at least one NOOP and the command is STOR, the transmission status 
is not sent.
If there were no NOOPs or the command is RETR, then the status is returned 
(usually 226)

4) Most other FTP servers:
NOOP replies are delayed. After a transmission completes, servers send 226 
first and the NOOP replies after that.



Here are possible response sequences:

- Pure-FTPd success
        500,500,500,500,226
        500,500,500,226,200 (in case of race condition)

- Pure-FTPd error (RETR data socket closed by client)
        500,500,500,500,150
        500,500,500,150,200 (in case of race condition)



- BulletProof FTP Server STOR, success/error (STOR data socket closed by client)
        200,200,200,200,---
        200,200,200,---,200 (in case of race condition)
        226 (when no NOOPs sent)

- BulletProof FTP Server RETR, success
        226 (when no NOOPs sent)
        200,200,200,200,---
        200,200,200,---,200 (in case of race condition)

- BulletProof FTP Server RETR, error (RETR data socket closed by client)
        426 (when no NOOPs sent)
        200,200,200,200,426
        200,200,200,426,200 (in case of race condition)



- Most other FTP servers success
        226,200,200,200,200 (no race condition possible)

- Most other FTP servers error (RETR data socket closed by client)
        426,200,200,200,200 (no race condition possible)



Conclusion:
If we consume NOOP responses during the transmission, most of the time 
completePendingCommand() consumes the right response:
If a server does not support asynchronous NOOPs, then completePendingCommand() 
consumes the status and cleanUp() consumes all NOOP responses.
If a server supports asynchronous NOOPs, then all NOOP replies are consumed 
before completePendingCommand() is called.
(Except for BulletProof STOR, that forgets to send 226, so 
completePendingCommand() fails with SocketTimeoutException)

In case of a race condition (for asyncronous servers):
If transmission succeeds, completePendingCommand() consumes either 226 or "200 
NOOP command successful." and return true in either case.
If transmission fails, Util.copyStream() may throw something. If it doesn't, 
completePendingCommand() consumes "200 NOOP command successful." and returns 
false positive.
This can happen if last data block was sent by client, but never received by 
server.

If we delay consuming NOOP responses from asynchronous servers, 
completePendingCommand() always consumes the first NOOP response.
In case of Pure-FTPd, it is "500 Unknown command", so the method returns false 
negative.
For other asynchronous servers it is "200 NOOP command successful." and a 
possible 426 status is eaten by cleanUp(), so the method returns false positive.

On the other hand, completePendingCommand() won't throw SocketTimeoutException 
for BulletProof STOR and besides, for servers that don't support async NOOPs we 
don't have to
wait in __noop()


I think that what we need to do is to collect (#NOOPs + 1) responses at the end 
with a read timeout 10s, filter out 500 and 200 and the remaining status will 
be 226 or an
error status.




On 19.10.2017 20:46, Basin Ilya wrote:
> Ok, so I found these two that support it:
> - BulletProof FTP Server
> - Pure-FTPd
> 
> Will test further.
> 
> On 17.10.2017 18:29, sebb wrote:
>> On 17 October 2017 at 16:01, Basin Ilya <basini...@gmail.com> wrote:
>>> Hi sebb
>>>
>>>> No, because some FTP servers *do* support asynchronous control channels.
>>> Do you know any?
>>
>> I cannot remember the name, but I know I came across at least one when 
>> testing.
>>
>> But even if there were currently no such servers, AFAIK it is
>> permitted by the RFCs so NET should not assume there are none.
>>
>>>
>>> On 17.10.2017 17:54, sebb wrote:
>>>> On 17 October 2017 at 12:34, Basin Ilya <basini...@gmail.com> wrote:
>>>>> Hi.
>>>>> I'm using
>>>>> FTPClient.retrieveFileStream()
>>>>>
>>>>> and therefore I need to implement keepalive mechanism by my own.
>>>>
>>>> Not necessarily.
>>>>
>>>> The FTP server does not need the NOOPs.
>>>> They are only needed to deal with routers that detect the inactive
>>>> control channel and decide to drop the connection even though the data
>>>> channel is active.
>>>>
>>>>> I wanted to mimic the implementation from FTPClient.CSL, but then I 
>>>>> thought:
>>>>>
>>>>> Most FTP servers don't reply to NOOPs until transmission has finished and 
>>>>> yet, sending NOOPs helps to keep them alive.
>>>>
>>>> No, the FTP servers don't need the NOOPs.
>>>> And some do reply before data transmission completes.
>>>>
>>>>> So we can safely assume that no FTP servers support this and let 
>>>>> CSL.cleanUp() collect all the responses at the end.
>>>>
>>>> No, because some FTP servers *do* support asynchronous control channels.
>>>>
>>>>> My tests show vsftpd 2.2.2 does not support that and you can send up to 
>>>>> 27000 NOOP commands to it until the socket write gets blocked.
>>>>>
>>>>> On the other hand, on most FTP servers for each NOOP keepalive FTPClient 
>>>>> waits for 1000 milliseconds for a reply that never comes. This slows 
>>>>> things down a little.
>>>>>
>>>>> What bad thing will happen, if we remove __getReplyNoReport() from 
>>>>> FTP.__noop() and increment notAcked unconditionally?
>>>>
>>>> Try it and see?
>>>>
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
>>>>> For additional commands, e-mail: dev-h...@commons.apache.org
>>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
>>>> For additional commands, e-mail: dev-h...@commons.apache.org
>>>>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
For additional commands, e-mail: dev-h...@commons.apache.org

Reply via email to