Skip to content

Add cgi_mode to parse_header to support LF in CGI headers #166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/webrick/httpservlet/cgihandler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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

Expand Down
21 changes: 14 additions & 7 deletions lib/webrick/httputils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion sig/httputils.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
11 changes: 11 additions & 0 deletions test/webrick/test_cgi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions test/webrick/webrick_bare_lf.cgi
Original file line number Diff line number Diff line change
@@ -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