Async HTTP Server does not respond on Windows if cool.io gem is loaded
Closed this issue · 2 comments
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
- Windows 11
- Ruby 3.3.4
- async 2.16.1
- async-http 0.71.0
- cool.io 1.8.1
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?
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.
Thank you for quick reply.
OK, I see...
We have to plan to replace cool.io or something....