diff --git a/lib/webrick/httpservlet/cgihandler.rb b/lib/webrick/httpservlet/cgihandler.rb index 450aa38..873ddd5 100644 --- a/lib/webrick/httpservlet/cgihandler.rb +++ b/lib/webrick/httpservlet/cgihandler.rb @@ -96,7 +96,9 @@ def do_GET(req, res) "Premature end of script headers: #{@script_filename}" if body.nil? begin - header = HTTPUtils::parse_header(raw_header) + header = HTTPUtils::parse_header(raw_header, + header_line_regexp: REGEXP_CGI_HEADER_LINE , + continued_header_line_regexp: REGEXP_CONTINUED_CGI_HEADER_LINE) if /^(\d+)/ =~ header['status'][0] res.status = $1.to_i header.delete('status') @@ -119,6 +121,10 @@ def do_GET(req, res) end alias do_POST do_GET + REGEXP_CGI_HEADER_LINE = /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):([^\r\n\0]*?)\r?\n\z/m + REGEXP_CONTINUED_CGI_HEADER_LINE = /^[ \t]+([^\r\n\0]*?)\r?\n/m + private_constant :REGEXP_CGI_HEADER_LINE, :REGEXP_CONTINUED_CGI_HEADER_LINE + # :startdoc: end diff --git a/lib/webrick/httputils.rb b/lib/webrick/httputils.rb index e2c2a40..53c9ba2 100644 --- a/lib/webrick/httputils.rb +++ b/lib/webrick/httputils.rb @@ -148,10 +148,6 @@ def mime_type(filename, mime_tab) end module_function :mime_type - ## - # Parses an HTTP header +raw+ into a hash of header fields with an Array - # of values. - class SplitHeader < Array def join(separator = ", ") super @@ -168,17 +164,28 @@ def join(separator = "; ") "cookie" => CookieHeader, }) - def parse_header(raw) + REGEXP_HEADER_LINE = /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):([^\r\n\0]*?)\r\n\z/m + REGEXP_CONTINUED_HEADER_LINE = /^[ \t]+([^\r\n\0]*?)\r\n/m + + ## + # Parses an HTTP header +raw+ into a hash of header fields with an Array + # of values. The header is expected to end with \r\n. If the header is in CGI + # format, use +header_line_regexp+ and +continued_header_line_regexp+ to + # parse the header lines. The default values are REGEXP_HEADER_LINE and + # REGEXP_CONTINUED_HEADER_LINE. + + def parse_header(raw, header_line_regexp: REGEXP_HEADER_LINE, continued_header_line_regexp: REGEXP_CONTINUED_HEADER_LINE) header = Hash.new([].freeze) field = nil + raw.each_line{|line| case line - when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):([^\r\n\0]*?)\r\n\z/om + when header_line_regexp field, value = $1, $2 field.downcase! header[field] = HEADER_CLASSES[field].new unless header.has_key?(field) header[field] << value - when /^[ \t]+([^\r\n\0]*?)\r\n/om + when continued_header_line_regexp unless field raise HTTPStatus::BadRequest, "bad header '#{line}'." end diff --git a/sig/httputils.rbs b/sig/httputils.rbs index a554cdf..1c69cd4 100644 --- a/sig/httputils.rbs +++ b/sig/httputils.rbs @@ -26,7 +26,7 @@ module WEBrick HEADER_CLASSES: Hash[String, untyped] - def self?.parse_header: (String raw) -> Hash[String, Array[String]] + def self?.parse_header: (String raw, ?header_line_regexp: Regexp, ?continued_header_line_regexp: Regexp) -> Hash[String, Array[String]] def self?.split_header_value: (String str) -> Array[String] diff --git a/test/webrick/test_cgi.rb b/test/webrick/test_cgi.rb index a9be8f3..f8e1312 100644 --- a/test/webrick/test_cgi.rb +++ b/test/webrick/test_cgi.rb @@ -145,4 +145,15 @@ def test_bad_header assert_not_match(CtrlPat, s) } end + + def test_bare_lf_in_cgi_header + TestWEBrick.start_cgi_server do |server, addr, port, log| + http = Net::HTTP.new(addr, port) + req = Net::HTTP::Get.new("/webrick_bare_lf.cgi") + assert_nothing_raised do + res = http.request(req) + assert_equal res['Content-Type'], 'text/plain' + end + end + end end diff --git a/test/webrick/webrick_bare_lf.cgi b/test/webrick/webrick_bare_lf.cgi new file mode 100644 index 0000000..add92fe --- /dev/null +++ b/test/webrick/webrick_bare_lf.cgi @@ -0,0 +1,8 @@ +#!ruby + +body = "test for bare LF in cgi header" + +print "Content-Type: text/plain\n" +print "Content-Length: #{body.size}\n" +print "\n" +print body