Skip to content

Async HTTP Server does not respond on Windows if cool.io gem is loaded #175

@Watson1978

Description

@Watson1978

I'm trying to update the async gem from v1.x to v2.x, which async-http gem depends on.
fluent/fluentd#3842

Then, I found a problem that Async HTTP Server stops responding If I use the async gem and cool.io gem under Windows.
It works well under Linux and macOS even if I use them.

Curiously, if I removed https://github.com/socketry/cool.io/blame/2735948698687b087f31c0e0056078dbf6d73a7f/ext/iobuffer/iobuffer.c#L104 line from cool.io gem, then Async HTTP server works.

Looks for me that async will change the behavior at https://github.com/socketry/async/blob/10fa816bb229d4df3433d337ded3421b43d161fe/lib/async/scheduler.rb#L253-L266 if cool.io is loaded.

Environment

Reproduce code

require 'bundler/inline'
gemfile do
  source 'https://rubygems.org'
  gem 'async-http'
  gem 'cool.io' # A problem is occurred if cool.io gem is loaded
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.
end

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

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

puts "-" * 80
puts "[client request]"

Timeout.timeout(10) {
  p Net::HTTP.get(URI.parse("http://127.0.0.1:8080/api/plugins.json"))
}

Result on Windows

C:\tmp> ruby .\async-http.rb
[http server start]
--------------------------------------------------------------------------------
[client request]
C:/Ruby33-x64/lib/ruby/3.3.0/timeout.rb:43:in `rescue in handle_timeout': execution expired (Timeout::Error)
        from C:/Ruby33-x64/lib/ruby/3.3.0/timeout.rb:40:in `handle_timeout'
        from C:/Ruby33-x64/lib/ruby/3.3.0/timeout.rb:195:in `timeout'
        from ./async-http.rb:93:in `<main>'
C:/Ruby33-x64/lib/ruby/3.3.0/net/protocol.rb:229:in `wait_readable': execution expired (Timeout::ExitException)
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/protocol.rb:229:in `rbuf_fill'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/protocol.rb:199:in `readuntil'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/protocol.rb:209:in `readline'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http/response.rb:158:in `read_status_line'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http/response.rb:147:in `read_new'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:2342:in `block in transport_request'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:2333:in `catch'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:2333:in `transport_request'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:2306:in `request'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:2177:in `request_get'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:824:in `block in get_response'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:1570:in `start'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:1029:in `start'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:822:in `get_response'
        from C:/Ruby33-x64/lib/ruby/3.3.0/net/http.rb:803:in `get'
        from ./async-http.rb:94:in `block in <main>'
        from C:/Ruby33-x64/lib/ruby/3.3.0/timeout.rb:186:in `block in timeout'
        from C:/Ruby33-x64/lib/ruby/3.3.0/timeout.rb:41:in `handle_timeout'
        from C:/Ruby33-x64/lib/ruby/3.3.0/timeout.rb:195:in `timeout'
        from ./async-http.rb:93:in `<main>'

It got a timeout error because Async HTTP Server does not return any response.

Can you have any idea to solve this issue?

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