Skip to content

Commit a72e937

Browse files
Extract host and port from Host header for CONNECT and OPTIONS * requests. (#182)
Previously, `CONNECT` and `OPTIONS *` requests returned early from parse() before extracting host and port from the `Host` header. This caused meta_vars to return nil for `SERVER_NAME` and empty string for `SERVER_PORT`, breaking CGI variable generation for these request types. This change: - Extracts the host/port logic into a new extract_host_port method. - Calls `setup_forwarded_info` and `extract_host_port` for `CONNECT/OPTIONS *`. - Refactors `parse_uri` to use `extract_host_port internally`. - Adds tests for `OPTIONS *` requests with Host header.
1 parent b1684ce commit a72e937

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

lib/webrick/httprequest.rb

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,17 @@ def parse(socket=nil)
210210
@accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding'])
211211
@accept_language = HTTPUtils.parse_qvalues(self['accept-language'])
212212
end
213-
return if @request_method == "CONNECT"
214-
return if @unparsed_uri == "*"
213+
214+
setup_forwarded_info
215+
216+
if @request_method == "CONNECT" || @unparsed_uri == "*"
217+
# For CONNECT and OPTIONS * requests, we still need to extract
218+
# host and port from the Host header for CGI meta variables.
219+
@host, @port = extract_host_port
220+
return
221+
end
215222

216223
begin
217-
setup_forwarded_info
218224
@request_uri = parse_uri(@unparsed_uri)
219225
@path = HTTPUtils::unescape(@request_uri.path)
220226
@path = HTTPUtils::normalize_path(@path)
@@ -500,25 +506,36 @@ def read_header(socket)
500506
end
501507
end
502508

503-
def parse_uri(str, scheme="http")
504-
if @config[:Escape8bitURI]
505-
str = HTTPUtils::escape8bit(str)
506-
end
507-
str.sub!(%r{\A/+}o, '/')
508-
uri = URI::parse(str)
509-
return uri if uri.absolute?
509+
# Extracts host and port from request headers (Host, X-Forwarded-Host, etc.)
510+
# or falls back to socket address or server config.
511+
# Returns [host, port] where port is an Integer or nil.
512+
def extract_host_port
510513
if @forwarded_host
511514
host, port = @forwarded_host, @forwarded_port
512515
elsif self["host"]
513516
host, port = parse_host_request_line(self["host"])
517+
port = port.to_i if port
514518
elsif @addr.size > 0
515519
host, port = @addr[2], @addr[1]
516520
else
517521
host, port = @config[:ServerName], @config[:Port]
518522
end
523+
524+
[host, port]
525+
end
526+
527+
def parse_uri(str, scheme="http")
528+
if @config[:Escape8bitURI]
529+
str = HTTPUtils::escape8bit(str)
530+
end
531+
str.sub!(%r{\A/+}, '/')
532+
uri = URI::parse(str)
533+
return uri if uri.absolute?
534+
535+
host, port = extract_host_port
519536
uri.scheme = @forwarded_proto || scheme
520537
uri.host = host
521-
uri.port = port ? port.to_i : nil
538+
uri.port = port
522539
return URI::parse(uri.to_s)
523540
end
524541

test/webrick/test_httprequest.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,4 +701,42 @@ def test_cookie_join
701701
assert_equal 2, req.cookies.length
702702
assert_equal 'a=1; b=2', req['cookie']
703703
end
704+
705+
def test_options_asterisk
706+
# Test that OPTIONS * requests properly extract host and port from Host header
707+
msg = <<~HTTP.gsub("\n", "\r\n")
708+
OPTIONS * HTTP/1.1
709+
Host: test.ruby-lang.org:8080
710+
711+
HTTP
712+
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
713+
req.parse(StringIO.new(msg))
714+
assert_equal("OPTIONS", req.request_method)
715+
assert_equal("*", req.unparsed_uri)
716+
assert_equal("test.ruby-lang.org", req.host)
717+
assert_equal(8080, req.port)
718+
719+
# Verify meta_vars includes correct SERVER_NAME and SERVER_PORT
720+
meta = req.meta_vars
721+
assert_equal("test.ruby-lang.org", meta["SERVER_NAME"])
722+
assert_equal("8080", meta["SERVER_PORT"])
723+
end
724+
725+
def test_options_asterisk_default_port
726+
# Test OPTIONS * with Host header without explicit port
727+
msg = <<~HTTP.gsub("\n", "\r\n")
728+
OPTIONS * HTTP/1.1
729+
Host: test.ruby-lang.org
730+
731+
HTTP
732+
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
733+
req.parse(StringIO.new(msg))
734+
assert_equal("OPTIONS", req.request_method)
735+
assert_equal("*", req.unparsed_uri)
736+
assert_equal("test.ruby-lang.org", req.host)
737+
assert_nil(req.port) # Port is nil when not specified
738+
739+
meta = req.meta_vars
740+
assert_equal("test.ruby-lang.org", meta["SERVER_NAME"])
741+
end
704742
end

0 commit comments

Comments
 (0)