Skip to content

"private method `raise' called for nil (NoMethodError)" error if stop Async HTTP server #176

@Watson1978

Description

@Watson1978

I'm trying to launch an Async HTTP server like following:

  Async do |task|
    @server_task = task.async do
      @server.run
    end
  end

Then, I called stop method on a task created with task.async to stop the http server, like:

  def stop
    @server_task&.stop
  end

and, I got a NoMethodError error.
Is there something wrong with my code?
Please see Reproduction code and Result sections in below.

Environment

  • Ruby 3.3.4
  • async 2.16.1
  • async-http 0.71.0

Reproduction code

require 'bundler/inline'
gemfile do
  source 'https://rubygems.org'
  gem 'async-http'
end

require 'net/http'
require 'async/http/protocol'
require 'timeout'

module HttpServer
  class Router
    def initialize
      @router = { get: {} }
    end

    def mount(method, path, app)
      @router[method][path] = app
    end

    def route!(method, path, request)
      @router.fetch(method).fetch(path).call(request)
    end
  end

  class Server
    class App
      def initialize(router)
        @router = router
      end

      def call(request)
        method = request.method
        resp = get(request)

        Protocol::HTTP::Response[*resp]
      rescue => e
        Protocol::HTTP::Response[500, { 'Content-Type' => 'text/plain' }, 'Internal Server Error']
      end

      def get(request)
        @router.route!(:get, request.path, request)
      end
    end

    def initialize(addr:, port:, tls_context: nil)
      @uri = URI("http://#{addr}:#{port}").to_s
      @router = Router.new
      @server_task = nil

      @server = Async::HTTP::Server.new(App.new(@router), Async::HTTP::Endpoint.parse(@uri))
    end

    def start
      Async do |task|
        @server_task = task.async do
          @server.run
        end
      end
    end

    def stop
      @server_task&.stop
    end

    def get(path, app = nil, &block)
      @router.mount(:get, path, app || block)
    end
  end
end


def http_server_start(addr:, port:)
  server = HttpServer::Server.new(addr: addr, port: port)
  server.get('/api/plugins.json') { |req| [200, { 'Content-Type' => 'text/html' }, "Hello"] }

  Thread.new do
    server.start
  end

  sleep 1 # Wait until the server starts up.

  server
end

#################################################

puts "[http server start]"
server = http_server_start(addr: "127.0.0.1", port: "8080")

server.stop

Result

$ ruby async-http.rb
[http server start]
/home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/task.rb:288:in `stop': private method `raise' called for nil (NoMethodError)

                                                Fiber.scheduler.raise(@fiber, Stop)
                                                               ^^^^^^
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/node.rb:291:in `block in stop_children'
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/list.rb:290:in `each'
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/list.rb:303:in `each'
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/list.rb:178:in `each'
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/node.rb:290:in `stop_children'
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/task.rb:401:in `stopped!'
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/task.rb:414:in `stop!'
        from /home/watson/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/async-2.16.1/lib/async/task.rb:296:in `stop'
        from t.rb:63:in `stop'
        from t.rb:91:in `<main>'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions