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

Crystal 0.20 Error while spawning 2 HTTP::Server on the same port - SO_REUSEADDRESS #3586

Closed
adrien-thierry opened this issue Nov 26, 2016 · 7 comments

Comments

@adrien-thierry
Copy link

I'm trying to make a multithreaded/forked http server, but I can't set reuse_address, and it should be true by default.

Code :

require "http/server"
spawn do
  server = HTTP::Server.new(8080) do |context|
    context.response.content_type = "text/plain"
    context.response.print "Hello world, got #{context.request.path}!"
  end
  puts "Listening on http://127.0.0.1:8080"
  server.listen
end

spawn do
  server = HTTP::Server.new(8080) do |context|
    context.response.content_type = "text/plain"
    context.response.print "Hello world, got #{context.request.path}!"
  end
  puts "Listening on http://127.0.0.1:8080"
  server.listen
end

sleep

Error :

Error binding TCP server at 127.0.0.1:8080: Address already in use (Errno)
0x470917: *CallStack::unwind:Array(Pointer(Void)) at ??
0x4708aa: *CallStack#initialize:Array(Pointer(Void)) at ??
0x47087a: *CallStack::new:CallStack at ??
0x467bde: *raise<Errno>:NoReturn at ??
0x517e34: *TCPServer#initialize<String, Int32, Int32>:Nil at ??
0x5178c1: *TCPServer#initialize<String, Int32>:Nil at ??
0x517874: *TCPServer::new<String, Int32>:TCPServer at ??
0x50fbfc: *HTTP::Server#bind:TCPServer at ??
0x50fb2a: *HTTP::Server#listen:Nil at ??
0x46dcf8: ~procProc(Nil) at ??
0x47fa10: *Fiber#run:(IO::FileDescriptor | Nil) at ??
0x46b0b6: ~proc2Proc(Fiber, (IO::FileDescriptor | Nil)) at ??
0x0: ??? at ??

in crystal/src/socket/tcp_server.cr :

Line 20 : self.reuse_address = true

@ysbaddaden
Copy link
Contributor

There is a misunderstanding in how SO_REUSEADDR works, and binding to the exact same IP:PORT is invalid. This (incredible) stackoverflow answer explains why: http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t#14388707

@adrien-thierry
Copy link
Author

adrien-thierry commented Nov 26, 2016

Thx, I read it

@Sija
Copy link
Contributor

Sija commented Dec 4, 2016

@ysbaddaden is it possible to use SO_REUSEPORT with HTTP::Server ATM?

@adrien-thierry
Copy link
Author

@Sija @ysbaddaden I've compiled crystal with SO_REUSEPORT on Linux x64, works great, but not tested on other platforms

@ysbaddaden
Copy link
Contributor

@Sija there is no limitation to use it, as long as you can set the option, which I'm unsure with the current Crystal API.

BTW, if I recall correctly, we do set SO_REUSEADDR to be able to immediately rebind a socket that was just unbound, but still lingering (e.g. restarting a server), which would prevent binding for at least one minute. We don't set SO_REUSEPORT because we don't expect this behavior by default.

@luislavena
Copy link
Contributor

@ysbaddaden @bcardiff perhaps we can introduce Socket#reuse_port= and Socket#reuse_port? and later expose that something like shared: true on the HTTP::Server interface? (just a thought)

@adrien-thierry
Copy link
Author

If someone want to test on other platform :

self.reuse_port = true under self.reuse_address = true in class TCPServer in txp_server.cr

SO_REUSEPORT = 15 under SO_REUSEADDR = 2 in LibC binding in /sys/socket.cr

And this :

def reuse_port?
  getsockopt_bool LibC::SO_REUSEPORT
end

def reuse_port=(val : Bool)
  setsockopt_bool LibC::SO_REUSEPORT, val
end

in class Socket in socket.cr

I used this to benchmark Crystal with multithreading NodeJS vs Crystal vs Golang

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

No branches or pull requests

5 participants