Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TCPServer.for_fd raises IOError: closed stream #4715

Closed
dekellum opened this issue Jul 13, 2017 · 6 comments
Closed

TCPServer.for_fd raises IOError: closed stream #4715

dekellum opened this issue Jul 13, 2017 · 6 comments

Comments

@dekellum
Copy link
Contributor

I originally encountered this and reported it as puma/puma#1367. I'm reporting it here because its a jruby-specific issue and I intend to provide a minimized reproduction outside of Puma.

I find other references to for_fd() possibly being implemented on jruby: #940, #3494 Only for UNIX Server/Socket? It is possible to just do the same thing for TCP Server/Socket?

Minimum test ruby script is below. This can be easily tested on a Linux system with systemd and its testing command systemd-socket-activate, then using nc for the connecting client. These commands should be available on all recent mainstream Linux distributions including Debian (8+), Ubuntu (16+), Fedora, Arch, etc.

activate.rb

require 'socket'

sock = TCPServer.for_fd(3)

port, addr = Socket.unpack_sockaddr_in(sock.getsockname)

puts "Activated #{addr} #{port}"

client = sock.accept
client.puts "Hi! Goodbye!"
client.close

Run as follows (substitute jruby as needed):

% systemd-socket-activate -l 0.0.0.0:2000 ruby -v ./activate.rb

Run this in one terminal, then use nc in another terminal to connect to the socket. (You will need to ^C to disconnect.) This is shown below with a successful (MRI ruby) run:

% nc 127.0.0.1 2000
Hi! Goodbye!
^C

With MRI ruby the output on the server side is the following expected output:

% systemd-socket-activate -l 0.0.0.0:2000 ruby -v ./activate.rb
Listening on 0.0.0.0:2000 as 3.
Communication attempt on fd 3.
Execing ruby (ruby -v ./activate.rb)
ruby 2.2.7p470 (2017-03-28 revision 58194) [x86_64-linux]
Activated 0.0.0.0 2000

With jruby 9.1, an IOError is instead raised from the initial for_fd call:

% systemd-socket-activate -l 0.0.0.0:2000 jruby -v ./activate.rb
Listening on 0.0.0.0:2000 as 3.
Communication attempt on fd 3.
Execing jruby (jruby -v ./activate.rb)
jruby 9.1.12.0 (2.3.3) 2017-06-15 33c6439 OpenJDK 64-Bit Server VM 25.131-b11 on 1.8.0_131-b11 +jit [linux-x86_64]
IOError: closed stream
  for_fd at org/jruby/ext/socket/RubyBasicSocket.java:109
  <main> at ./activate.rb:5
@dekellum
Copy link
Contributor Author

Just to confirm this isn't a recent regression, on jruby 1.7.27 the Error is different but raised in the same place:

systemd-socket-activate -l 0.0.0.0:2000 jruby17 -v ./activate.rb 
Listening on 0.0.0.0:2000 as 3.
Communication attempt on fd 3.
Execing jruby17 (jruby17 -v ./activate.rb)
jruby 1.7.27 (1.9.3p551) 2017-05-11 8cdb01a on OpenJDK 64-Bit Server VM 1.8.0_131-b11 +jit [linux-amd64]
Errno::EINVAL: Invalid argument - Invalid file
  for_fd at org/jruby/ext/socket/RubyBasicSocket.java:117
  (root) at ./activate.rb:5

@ahorek
Copy link
Contributor

ahorek commented Aug 20, 2017

The problem is, that jruby is looking into a map of existing (registered) file descriptors, but if you use a descriptor that wasn't created inside jruby process it fails.

https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/ext/socket/RubyBasicSocket.java#L106

@kares @headius

@ahorek
Copy link
Contributor

ahorek commented Jan 29, 2018

#5008

@headius
Copy link
Member

headius commented Feb 13, 2018

Yes, this is not an easy one to fix. Our socket logic is based around JVM APIs, which means a TCPSocket always needs a Java TCPSocket underneath it. At the same time, we do try to carve out the real filedescriptor, usually without storing it somewhere. Then when you try to for_fd, we see a real FD, find no existing TCPSocket, and are unable to produce one.

This is another one that will be fixed by going with a fully-native socket API, likely in 9.2. See #4748 and others.

@headius headius added this to the JRuby 9.2.0.0 milestone Feb 13, 2018
@enebo enebo modified the milestones: JRuby 9.2.0.0, JRuby 9.2.1.0 May 24, 2018
@headius
Copy link
Member

headius commented Oct 11, 2018

This never happened for 9.2 and is certainly too big for a maintenance release.

@headius headius modified the milestones: JRuby 9.2.1.0, JRuby 9.3.0.0 Oct 11, 2018
@headius
Copy link
Member

headius commented Jul 23, 2020

This would be fixed by supporting the FFI-based socket library, and is superseded by #6334. Until then it is not possible to resolve, since we can't create a TCPServer on the JDK from a raw file descriptor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants