diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb index 7f9c002..c6efaf5 100644 --- a/lib/webrick/httprequest.rb +++ b/lib/webrick/httprequest.rb @@ -210,11 +210,17 @@ def parse(socket=nil) @accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding']) @accept_language = HTTPUtils.parse_qvalues(self['accept-language']) end - return if @request_method == "CONNECT" - return if @unparsed_uri == "*" + + setup_forwarded_info + + if @request_method == "CONNECT" || @unparsed_uri == "*" + # For CONNECT and OPTIONS * requests, we still need to extract + # host and port from the Host header for CGI meta variables. + @host, @port = extract_host_port + return + end begin - setup_forwarded_info @request_uri = parse_uri(@unparsed_uri) @path = HTTPUtils::unescape(@request_uri.path) @path = HTTPUtils::normalize_path(@path) @@ -500,25 +506,36 @@ def read_header(socket) end end - def parse_uri(str, scheme="http") - if @config[:Escape8bitURI] - str = HTTPUtils::escape8bit(str) - end - str.sub!(%r{\A/+}o, '/') - uri = URI::parse(str) - return uri if uri.absolute? + # Extracts host and port from request headers (Host, X-Forwarded-Host, etc.) + # or falls back to socket address or server config. + # Returns [host, port] where port is an Integer or nil. + def extract_host_port if @forwarded_host host, port = @forwarded_host, @forwarded_port elsif self["host"] host, port = parse_host_request_line(self["host"]) + port = port.to_i if port elsif @addr.size > 0 host, port = @addr[2], @addr[1] else host, port = @config[:ServerName], @config[:Port] end + + [host, port] + end + + def parse_uri(str, scheme="http") + if @config[:Escape8bitURI] + str = HTTPUtils::escape8bit(str) + end + str.sub!(%r{\A/+}, '/') + uri = URI::parse(str) + return uri if uri.absolute? + + host, port = extract_host_port uri.scheme = @forwarded_proto || scheme uri.host = host - uri.port = port ? port.to_i : nil + uri.port = port return URI::parse(uri.to_s) end diff --git a/test/webrick/test_httprequest.rb b/test/webrick/test_httprequest.rb index d1283d4..3d02573 100644 --- a/test/webrick/test_httprequest.rb +++ b/test/webrick/test_httprequest.rb @@ -701,4 +701,42 @@ def test_cookie_join assert_equal 2, req.cookies.length assert_equal 'a=1; b=2', req['cookie'] end + + def test_options_asterisk + # Test that OPTIONS * requests properly extract host and port from Host header + msg = <<~HTTP.gsub("\n", "\r\n") + OPTIONS * HTTP/1.1 + Host: test.ruby-lang.org:8080 + + HTTP + req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) + req.parse(StringIO.new(msg)) + assert_equal("OPTIONS", req.request_method) + assert_equal("*", req.unparsed_uri) + assert_equal("test.ruby-lang.org", req.host) + assert_equal(8080, req.port) + + # Verify meta_vars includes correct SERVER_NAME and SERVER_PORT + meta = req.meta_vars + assert_equal("test.ruby-lang.org", meta["SERVER_NAME"]) + assert_equal("8080", meta["SERVER_PORT"]) + end + + def test_options_asterisk_default_port + # Test OPTIONS * with Host header without explicit port + msg = <<~HTTP.gsub("\n", "\r\n") + OPTIONS * HTTP/1.1 + Host: test.ruby-lang.org + + HTTP + req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) + req.parse(StringIO.new(msg)) + assert_equal("OPTIONS", req.request_method) + assert_equal("*", req.unparsed_uri) + assert_equal("test.ruby-lang.org", req.host) + assert_nil(req.port) # Port is nil when not specified + + meta = req.meta_vars + assert_equal("test.ruby-lang.org", meta["SERVER_NAME"]) + end end