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

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

Closed
Watson1978 opened this issue Aug 28, 2024 · 2 comments
Closed

Comments

@Watson1978
Copy link

Watson1978 commented Aug 28, 2024

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?

@ioquatix
Copy link
Member

It looks like cool.io is unlikely to be compatible with Ruby 3.1+ as it introduces a class IO::Buffer which conflicts with Ruby's IO::Buffer (introduced in 3.1).

Unfortunately, cool.io should be considered legacy/deprecated and removed/replaced.

@Watson1978
Copy link
Author

Thank you for quick reply.
OK, I see...
We have to plan to replace cool.io or something....

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

No branches or pull requests

2 participants