Comments (6)
Jarrod, Thank you for the report, and for using ftpd. Your analysis sounds entirely plausible. Ftpd's own tests let each instance of the server pick a port at random, and so would not notice if the thread was not shutting down the socket properly or in a timely fashion. That it works in one OS but not another makes me wonder if it is a race condition. I will have a look at it. With luck, I can reproduce the problem (I run Debian, a close relative to Ubuntu). I might not be able to get to this until next weekend--busy weekend, and busy week ahead.
Another workaround you might try for now is to let the server pick the port, if you have that flexibility. You can call #bound_port on the server to find out which port it bound to, and then pass that to the code under test via a configuration file, environment variable, command-line argument, object attribute, or via the Deep Space Network. I am not suggesting this as a permanent fix, of course--ftpd should be made to function properly, and DSN bandwidth is too hard to get.
from ftpd.
Also, if it is a race condition, it may be that a short sleep after stopping the server may help (as a workaround). It it does help, that would be evidence supporting the "race condition" hypothesis.
from ftpd.
I may have it figured out. Here's what I think I know.
Under MRI 1.8 (the Ruby under which ftpd was first written), closing the server socket was as good as a shutdown: It caused the server thread's call to TCPServerSocket#accept to raise an error, which the thread used as its signal to end. Under MRI >= 1.9.3, #close does not cause the server thread's call to TCPServerSocket#accep to raise an error. The #accept hangs, and the thread is never killed.
ftpd no longer supports 1.8.7, but it does make me feel better knowing that this did work properly once upon a time.
The race condition (at least, the one I found) is this: If the first server's thread has not gotten around to calling #accept when the second server starts, the second server will succeed. If, however, the first server's thread has gotten around to calling #accept, then the second server will fail with Errno::EADDRINUSE. Apparently, the address is not really in use until #accept is called.
Luckily, it seems as though a reliable test for this bug can be made (race conditions can be nasty to test). If the test monkey-patches the server socket to force the first server's #accept to happen before the second server is created, then the bug should show itself every time.
The fix will be to call #shutdown on the server socket before calling #close.
from ftpd.
Thanks for the quick response! I'll upgrade my gem version and see if that fixes the issue. Glad you found a root cause so easily. That's always satisfying, no?
from ftpd.
@jcarlson This was the most satisfying bug I've fixed in a long time. It was difficult to reproduce and quite puzzling at first. However, in the end, I understood the bug, got it fixed, and learned something about sockets that I didn't know before. Good times!
Now, I just hope I fixed the actual bug that's bugging you :)
from ftpd.
Unfortunately, this doesn't fix my problem. Now, on OS X, when I call server.stop, it raises an error immediately:
[6] pry(main)> server.stop
Errno::ENOTCONN: Socket is not connected
from /Users/dev/.rvm/gems/ruby-2.0.0-p353@corndog/gems/ftpd-0.14.0/lib/ftpd/server.rb:53:in `shutdown'
This is better because it fails fast, but obviously still an issue.
I'm on Ruby 2.0.0p353 on OS X 10.8.5. Our CI server is on Ubuntu.
FWIW, the reason I don't use a random port number is that my client code reads settings from a YAML file in the config directory. So when I setup my test server, I also read from that YAML file and have the server start on the port that the YAML file specifies. I guess I could hack around until I can make the port a sort of shared read/write global that the server can update when the port is known... but I don't particularly love that path.
Here's a couple test helper methods that seem to get the trick done with v0.11.0:
def start
@thread = @server.start
@running = true
end
def stop
if @running
@server.stop
@thread.exit
end
@running = false
end
from ftpd.
Related Issues (20)
- #stop causes "Errno::ENOTCONN: Socket is not connected" in OSX HOT 8
- Connection Hangs on "ls" HOT 3
- Add more handlers
- Tests fail under Ruby >= 2.2.3 HOT 2
- External IP HOT 7
- Can I hooking filesystem operations HOT 4
- [IMPROVEMENT] Cmd Size and exotic filesystem
- forwarding to private method XXX HOT 3
- FTPPermError 550 Access Denied. HOT 2
- FTP base path lack of trailing `/` results in denial of service and disclosure of the local file system path HOT 1
- add bindir in gemspec HOT 2
- ftpd windows version , the ftp server will crash if input 'dir' HOT 1
- Using ruby 2.4 fails HOT 3
- Modernize usage of File.dirname(__FILE__) to support symlinks HOT 1
- CPU usage pegged at 100% HOT 9
- rake test:features seems to be broken HOT 2
- Issue with TLS protocol
- Error Connection reset by peer @ io_fillbuf - fd:158
- Update testing to use newer Ruby versions
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ftpd.