Search code examples
rubyprocessforkwebrick

Fork WEBrick and wait for start


I have the following code, where a WEBrick instance is forked, and I want to wait till the webrick is started up, before continuing with the rest of the code:

require 'webrick'

pid = fork do
  server = WEBrick::HTTPServer.new({:Port => 3333, :BindAddress => "localhost"})
  trap("INT") { server.shutdown }
  sleep 10 # here is code that take some time to setup
  server.start
end
# here I want to wait till the fork is complete or the WEBrick server is started and accepts connections
puts `curl localhost:3333 --max-time 1` # then I can talk to the webrick
Process.kill('INT', pid) # finally the webrick should be killed

So how can I wait till the fork is complete, or even better till the WEBrick is ready to accept connections? I found a piece of code where they deal with a IO.pipe and a reader and a writer. But that doesn't wait for webrick to load.

Unfortunately I haven't found anything for this specific case. Hope someone can help.


Solution

  • WEBRick::GenericServer has some callback hooks which are undocumented (sadly, in fact, the whole webrick library is poorly documented!), such as :StartCallback, :StopCallback, :AcceptCallback. You can provide hooks when initializing a WEBRick::HTTPServer instance.

    So, combined with IO.pipe, you can write your code like this:

    require 'webrick'
    
    PORT = 3333
    
    rd, wt = IO.pipe
    
    pid = fork do
      rd.close
      server = WEBrick::HTTPServer.new({
        :Port => PORT,
        :BindAddress => "localhost",
        :StartCallback => Proc.new {
          wt.write(1)  # write "1", signal a server start message
          wt.close
        }
      })
      trap("INT") { server.shutdown }
      server.start
    end
    
    wt.close
    rd.read(1)  # read a byte for the server start signal
    rd.close
    
    puts `curl localhost:#{PORT} --max-time 1` # then I can talk to the webrick
    Process.kill('INT', pid) # finally the webrick should be killed