Repository: puma/puma
File: test/test_puma_server.rb (Ruby)

Code before:
    handler = lambda {|err, env, status| [500, {"Content-Type" => "application/json"}, ["{}\n"]]}
    @server = Puma::Server.new @app, @events, {:lowlevel_error_handler => handler, :force_shutdown_after => 2}

    server_run app: ->(env) do
      @server.stop
      sleep 5
    end

    data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"

    assert_match(/HTTP\/1.0 500 Internal Server Error/, data)
    assert_match(/Content-Type: application\/json/, data)
    assert_match(/{}\n$/, data)
  end

  def test_force_shutdown_error_default
    @server = Puma::Server.new @app, @events, {:force_shutdown_after => 2}

    server_run app: ->(env) do
      @server.stop
      sleep 5
    end

    data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"

    assert_match(/HTTP\/1.0 503 Service Unavailable/, data)
    assert_match(/Puma caught this error.+Puma::ThreadPool::ForceShutdown/, data)
  end

  def test_prints_custom_error
    re = lambda { |err| [302, {'Content-Type' => 'text', 'Location' => 'foo.html'}, ['302 found']] }
    @server = Puma::Server.new @app, @events, {:lowlevel_error_handler => re}

    server_run app: ->(env) { raise "don't leak me bro" }

    data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"

    assert_match(/HTTP\/1.0 302 Found/, data)
  end

  def test_leh_gets_env_as_well
    re = lambda { |err,env|
      env['REQUEST_PATH'] || raise('where is env?')
      [302, {'Content-Type' => 'text', 'Location' => 'foo.html'}, ['302 found']]
    }

    @server = Puma::Server.new @app, @events, {:lowlevel_error_handler => re}

    server_run app: ->(env) { raise "don't leak me bro" }

    data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"

Review comment (bug):
I think this is not a reliable test for this issue because:
* Not all Ruby implementations might expose no backtrace for a stack overflow (that's pretty unhelpful, isn't it?)
* a stack overflow is a regular exception but given it messes the VM badly you can consider the VM corrupted after it, so maybe stack overflows should become fatal in the future, since it's very rarely safe to rescue it (you would need no `ensure` and no `finally` in the VM, and no second stack overflow while handling the stack overflow very close to the limit, and probably other things which cannot be guaranteed)
* More details on https://github.com/puma/puma/pull/2811

Maybe we can just test creating an exception and using `set_backtrace(nil)`?

Code after:
    handler = lambda {|err, env, status| [500, {"Content-Type" => "application/json"}, ["{}\n"]]}
    @server = Puma::Server.new @app, @events, {:lowlevel_error_handler => handler, :force_shutdown_after => 2}

    server_run app: ->(env) do
      @server.stop
      sleep 5
    end

    data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"

    assert_match(/HTTP\/1.0 500 Internal Server Error/, data)
    assert_match(/Content-Type: application\/json/, data)
    assert_match(/{}\n$/, data)
  end

  def test_lowlevel_error_message
    skip_if :windows
    @server = Puma::Server.new @app, @events, {:force_shutdown_after => 2}

    server_run app: ->(env) do
      require 'json'

      # will raise fatal: machine stack overflow in critical region
      obj = {}
      obj['cycle'] = obj
      ::JSON.dump(obj)
    end

    data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"

    assert_match(/HTTP\/1.0 500 Internal Server Error/, data)
    assert (data.size > 0), "Expected response message to be not empty"
  end

  def test_force_shutdown_error_default
    @server = Puma::Server.new @app, @events, {:force_shutdown_after => 2}

    server_run app: ->(env) do
      @server.stop
      sleep 5
    end

    data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"

    assert_match(/HTTP\/1.0 503 Service Unavailable/, data)
    assert_match(/Puma caught this error.+Puma::ThreadPool::ForceShutdown/, data)
  end

  def test_prints_custom_error
    re = lambda { |err| [302, {'Content-Type' => 'text', 'Location' => 'foo.html'}, ['302 found']] }
    @server = Puma::Server.new @app, @events, {:lowlevel_error_handler => re}