Skip to content

Commit 8e538be

Browse files
Update the webrick handler to support OPTIONS * requests. (#40)
1 parent 7a3e190 commit 8e538be

File tree

2 files changed

+76
-6
lines changed

2 files changed

+76
-6
lines changed

lib/rackup/handler/webrick.rb

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@
1616
module Rackup
1717
module Handler
1818
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
19+
# A WEBrick HTTPServer subclass that invokes the Rack app directly,
20+
# bypassing the mount table and default OPTIONS * handling.
21+
class Server < ::WEBrick::HTTPServer
22+
def initialize(app, config)
23+
super(config)
24+
@handler = Handler::WEBrick.new(self, app)
25+
end
26+
27+
def service(req, res)
28+
@handler.service(req, res)
29+
end
30+
end
31+
1932
def self.run(app, **options)
2033
environment = ENV['RACK_ENV'] || 'development'
2134
default_host = environment == 'development' ? 'localhost' : nil
@@ -28,8 +41,7 @@ def self.run(app, **options)
2841
require 'webrick/https'
2942
end
3043

31-
@server = ::WEBrick::HTTPServer.new(options)
32-
@server.mount "/", Rackup::Handler::WEBrick, app
44+
@server = Server.new(app, options)
3345
yield @server if block_given?
3446
@server.start
3547
end
@@ -102,11 +114,25 @@ def service(req, res)
102114
)
103115

104116
env[::Rack::QUERY_STRING] ||= ""
105-
unless env[::Rack::PATH_INFO] == ""
106-
path, n = req.request_uri.path, env[::Rack::SCRIPT_NAME].length
107-
env[::Rack::PATH_INFO] = path[n, path.length - n]
117+
118+
# Handle OPTIONS * requests which have no path
119+
if req.unparsed_uri == "*"
120+
env[::Rack::PATH_INFO] = "*"
121+
env[::Rack::REQUEST_PATH] = "*"
122+
123+
# Ensure SERVER_NAME and SERVER_PORT are set from server config
124+
# (WEBrick allows these to be nil for OPTIONS * requests)
125+
# See https://github.com/ruby/webrick/pull/182 for a proper fix.
126+
env[::Rack::SERVER_NAME] ||= @server[:ServerName] || @server[:BindAddress] || "localhost"
127+
env[::Rack::SERVER_PORT] ||= (@server[:Port] || 80).to_s
128+
else
129+
unless env[::Rack::PATH_INFO] == ""
130+
# Strip the script name prefix from the path to get path info
131+
script_name_length = env[::Rack::SCRIPT_NAME].length
132+
env[::Rack::PATH_INFO] = req.request_uri.path[script_name_length..-1] || ""
133+
end
134+
env[::Rack::REQUEST_PATH] ||= env[::Rack::SCRIPT_NAME] + env[::Rack::PATH_INFO]
108135
end
109-
env[::Rack::REQUEST_PATH] ||= [env[::Rack::SCRIPT_NAME], env[::Rack::PATH_INFO]].join
110136

111137
status, headers, body = @app.call(env)
112138
begin

test/spec_webrick.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,50 @@ def is_running?
220220
end
221221
end
222222

223+
it "handle OPTIONS * requests through the Rack app" do
224+
app = proc do |env|
225+
if env["REQUEST_METHOD"] == "OPTIONS" && env["PATH_INFO"] == "*"
226+
[200, {"allow" => "GET,HEAD,POST,PUT,DELETE,OPTIONS"}, [""]]
227+
else
228+
[404, {"content-type" => "text/plain"}, ["Not Found"]]
229+
end
230+
end
231+
232+
server = Rackup::Handler::WEBrick::Server.new(
233+
app,
234+
Host: @host,
235+
Port: 9203,
236+
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
237+
AccessLog: []
238+
)
239+
240+
thread = Thread.new { server.start }
241+
242+
# Wait for server to start
243+
seconds = 10
244+
wait_time = 0.1
245+
until server.status == :Running || seconds <= 0
246+
seconds -= wait_time
247+
sleep wait_time
248+
end
249+
250+
begin
251+
TCPSocket.open(@host, 9203) do |socket|
252+
socket.write "OPTIONS * HTTP/1.1\r\n"
253+
socket.write "Host: #{@host}\r\n"
254+
socket.write "Connection: close\r\n\r\n"
255+
256+
response = socket.read
257+
response.must_match(/HTTP\/1.1 200/)
258+
# The Rack app should set the Allow header, not WEBrick's default
259+
response.must_match(/Allow: GET,HEAD,POST,PUT,DELETE,OPTIONS/i)
260+
end
261+
ensure
262+
server.shutdown
263+
thread.join
264+
end
265+
end
266+
223267
after do
224268
@status_thread.join
225269
@server.shutdown

0 commit comments

Comments
 (0)