This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'webrick' | |
# How much of this is solved by frozen string literal? | |
Ractor.make_shareable(WEBrick::Config::HTTP) | |
Ractor.make_shareable(WEBrick::LF) | |
Ractor.make_shareable(WEBrick::CRLF) | |
Ractor.make_shareable(WEBrick::HTTPRequest::BODY_CONTAINABLE_METHODS) | |
Ractor.make_shareable(WEBrick::HTTPStatus::StatusMessage) | |
# To pick up changes from https://github.com/ruby/ruby/pull/4007 | |
Object.send(:remove_const, :URI) | |
require '/Users/kir/src/github.com/ruby/ruby/lib/uri.rb' | |
require 'rack' | |
# Make it frozen | |
Ractor.make_shareable(Rack::VERSION) | |
# https://github.com/ruby/ruby/pull/4008 | |
Ractor.make_shareable(Time::RFC2822_DAY_NAME) | |
Ractor.make_shareable(Time::RFC2822_MONTH_NAME) | |
require 'uri' | |
Ractor.make_shareable(URI::RFC3986_PARSER) | |
Ractor.make_shareable(URI::DEFAULT_PARSER) | |
require 'stringio' | |
pipe = Ractor.new do | |
loop do | |
Ractor.yield(Ractor.recv, move: true) | |
end | |
end | |
# has to be explicitly required from the main thread: | |
# https://bugs.ruby-lang.org/issues/17477 | |
require 'pp' | |
def env_from_request(req) | |
env = req.meta_vars | |
env.delete_if { |k, v| v.nil? } | |
rack_input = StringIO.new(req.body.to_s) | |
rack_input.set_encoding(Encoding::BINARY) | |
env.update( | |
Rack::RACK_VERSION => Rack::VERSION, | |
Rack::RACK_INPUT => rack_input, | |
Rack::RACK_ERRORS => $stderr, | |
Rack::RACK_MULTITHREAD => true, | |
Rack::RACK_MULTIPROCESS => false, | |
Rack::RACK_RUNONCE => false, | |
Rack::RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[Rack::HTTPS]) ? "https" : "http" | |
) | |
env[Rack::QUERY_STRING] ||= "" | |
unless env[Rack::PATH_INFO] == "" | |
path, n = req.request_uri.path, env[Rack::SCRIPT_NAME].length | |
env[Rack::PATH_INFO] = path[n, path.length - n] | |
end | |
env[Rack::REQUEST_PATH] ||= [env[Rack::SCRIPT_NAME], env[Rack::PATH_INFO]].join | |
env | |
end | |
CPU_COUNT = 4 | |
workers = CPU_COUNT.times.map do | |
Ractor.new(pipe) do |pipe| | |
app = lambda do |e| | |
[200, {'Content-Type' => 'text/html'}, ['hello world']] | |
end | |
loop do | |
s = pipe.take | |
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP.merge(RequestTimeout: nil)) | |
req.parse(s) | |
env = env_from_request(req) | |
status, headers, body = app.call(env) | |
resp = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP) | |
begin | |
resp.status = status.to_i | |
io_lambda = nil | |
headers.each { |k, vs| | |
if k.downcase == "set-cookie" | |
resp.cookies.concat vs.split("\n") | |
else | |
# Since WEBrick won't accept repeated headers, | |
# merge the values per RFC 1945 section 4.2. | |
resp[k] = vs.split("\n").join(", ") | |
end | |
} | |
body.each { |part| | |
resp.body << part | |
} | |
ensure | |
body.close if body.respond_to? :close | |
end | |
pp env | |
resp.send_response(s) | |
end | |
end | |
end | |
listener = Ractor.new(pipe) do |pipe| | |
server = TCPServer.new(8080) | |
loop do | |
conn, _ = server.accept | |
pipe.send(conn, move: true) | |
end | |
end | |
loop do | |
Ractor.select(listener, *workers) | |
# if the line above returned, one of the workers or the listener has crashed | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment