giampaolo / pyftpdlib

Extremely fast and scalable Python FTP server library
MIT License
1.66k stars 266 forks source link

Include fileno() in try-except, to support custom IO #542

Closed AllSeeingEyeTolledEweSew closed 4 years ago

AllSeeingEyeTolledEweSew commented 4 years ago

Currently, pyftpdlib tests for sendfile() support on a file object by testing for the existence of a fileno attribute. This is appropriate for some abstract IO objects like BytesIO.

However, for complex IO objects which inherit from io.IOBase, the fileno() method always exists but raises io.UnsupportedOperation when called. pyftpdlib thinks the object supports sendfile but crashes when calling the method.

(the io module APIs are a convoluted mess, which creates this confusion in the first place, but it's also why inheriting from their base objects is the most reliable way to ensure conformance)

My use case is that I'm writing an FTP server that accesses bittorrent data downloaded on demand, instead of the server's filesystem. I do this by creating objects which inherit from io.BufferedIOBase and feeding them to pyftpdlib. Currently, I need to disable sendfile globally by setting handler.use_sendfile = False. This is fine for now, but I will likely want to intermix sendfile- and non-sendfile IO objects in the future, due to some variance in how I can access data from libtorrent.

This fix just puts the call to fileno() inside the try-except around sendfile, so it can use the fallback to regular send.

giampaolo commented 4 years ago

Nice, and I appreciate the extra effort re. the unit tests. I would only ask a change: to do fileno() and try/except in the class constructor (more performant as sendfile() is called repeatedly).

giampaolo commented 4 years ago

(sent from my phone so I will get a better look soon)

AllSeeingEyeTolledEweSew commented 4 years ago

Updated. I moved it to use_sendfile(). Has the additional benefit that it doesn't generate a log warning in this case.

coveralls commented 4 years ago

Coverage Status

Coverage remained the same at 0.0% when pulling de7c6e67f5a8d6abba6b75459b1f5a71cd3d7f31 on AllSeeingEyeTolledEweSew:asetes-fileno into bdf50a23d61506fa87708bc06a41b46cfec7bbdd on giampaolo:master.