Last active
June 29, 2020 13:34
-
-
Save headius/9e70146870d5e6f329a5857fe91c8948 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Only in ../ruby/lib/webrick/: .document | |
diff -r -u lib/webrick/cgi.rb ../ruby/lib/webrick/cgi.rb | |
--- lib/webrick/cgi.rb 2020-06-29 08:32:08.000000000 -0500 | |
+++ ../ruby/lib/webrick/cgi.rb 2020-06-29 07:03:43.000000000 -0500 | |
@@ -265,6 +265,10 @@ | |
@out_port << data | |
end | |
+ def write(data) | |
+ @out_port.write(data) | |
+ end | |
+ | |
def cert | |
return nil unless defined?(OpenSSL) | |
if pem = @env["SSL_SERVER_CERT"] | |
diff -r -u lib/webrick/httpauth/digestauth.rb ../ruby/lib/webrick/httpauth/digestauth.rb | |
--- lib/webrick/httpauth/digestauth.rb 2020-06-29 08:32:08.000000000 -0500 | |
+++ ../ruby/lib/webrick/httpauth/digestauth.rb 2020-06-29 07:03:43.000000000 -0500 | |
@@ -235,9 +235,11 @@ | |
ha2 = hexdigest(req.request_method, auth_req['uri']) | |
ha2_res = hexdigest("", auth_req['uri']) | |
elsif auth_req['qop'] == "auth-int" | |
- ha2 = hexdigest(req.request_method, auth_req['uri'], | |
- hexdigest(req.body)) | |
- ha2_res = hexdigest("", auth_req['uri'], hexdigest(res.body)) | |
+ body_digest = @h.new | |
+ req.body { |chunk| body_digest.update(chunk) } | |
+ body_digest = body_digest.hexdigest | |
+ ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest) | |
+ ha2_res = hexdigest("", auth_req['uri'], body_digest) | |
end | |
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int" | |
@@ -288,23 +290,8 @@ | |
def split_param_value(string) | |
ret = {} | |
- while string.bytesize != 0 | |
- case string | |
- when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/ | |
- key = $1 | |
- matched = $2 | |
- string = $' | |
- ret[key] = matched.gsub(/\\(.)/, "\\1") | |
- when /^\s*([\w\-\.\*\%\!]+)=\s*([^,\"]*),?/ | |
- key = $1 | |
- matched = $2 | |
- string = $' | |
- ret[key] = matched.clone | |
- when /^s*^,/ | |
- string = $' | |
- else | |
- break | |
- end | |
+ string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do | |
+ ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1") | |
end | |
ret | |
end | |
diff -r -u lib/webrick/httprequest.rb ../ruby/lib/webrick/httprequest.rb | |
--- lib/webrick/httprequest.rb 2020-06-29 08:32:08.000000000 -0500 | |
+++ ../ruby/lib/webrick/httprequest.rb 2020-06-29 07:03:43.000000000 -0500 | |
@@ -414,9 +414,13 @@ | |
MAX_URI_LENGTH = 2083 # :nodoc: | |
+ # same as Mongrel, Thin and Puma | |
+ MAX_HEADER_LENGTH = (112 * 1024) # :nodoc: | |
+ | |
def read_request_line(socket) | |
@request_line = read_line(socket, MAX_URI_LENGTH) if socket | |
- if @request_line.bytesize >= MAX_URI_LENGTH and @request_line[-1, 1] != LF | |
+ @request_bytes = @request_line.bytesize | |
+ if @request_bytes >= MAX_URI_LENGTH and @request_line[-1, 1] != LF | |
raise HTTPStatus::RequestURITooLarge | |
end | |
@request_time = Time.now | |
@@ -435,6 +439,9 @@ | |
if socket | |
while line = read_line(socket) | |
break if /\A(#{CRLF}|#{LF})\z/om =~ line | |
+ if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH | |
+ raise HTTPStatus::RequestEntityTooLarge, 'headers too large' | |
+ end | |
@raw_header << line | |
end | |
end | |
@@ -502,12 +509,16 @@ | |
def read_chunked(socket, block) | |
chunk_size, = read_chunk_size(socket) | |
while chunk_size > 0 | |
- data = read_data(socket, chunk_size) # read chunk-data | |
- if data.nil? || data.bytesize != chunk_size | |
- raise BadRequest, "bad chunk data size." | |
- end | |
+ begin | |
+ sz = [ chunk_size, @buffer_size ].min | |
+ data = read_data(socket, sz) # read chunk-data | |
+ if data.nil? || data.bytesize != sz | |
+ raise HTTPStatus::BadRequest, "bad chunk data size." | |
+ end | |
+ block.call(data) | |
+ end while (chunk_size -= sz) > 0 | |
+ | |
read_line(socket) # skip CRLF | |
- block.call(data) | |
chunk_size, = read_chunk_size(socket) | |
end | |
read_header(socket) # trailer + CRLF | |
diff -r -u lib/webrick/httpresponse.rb ../ruby/lib/webrick/httpresponse.rb | |
--- lib/webrick/httpresponse.rb 2020-06-29 08:32:08.000000000 -0500 | |
+++ ../ruby/lib/webrick/httpresponse.rb 2020-06-29 07:03:43.000000000 -0500 | |
@@ -21,6 +21,8 @@ | |
# WEBrick HTTP Servlet. | |
class HTTPResponse | |
+ class InvalidHeader < StandardError | |
+ end | |
## | |
# HTTP Response version | |
@@ -287,14 +289,19 @@ | |
data = status_line() | |
@header.each{|key, value| | |
tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase } | |
- data << "#{tmp}: #{value}" << CRLF | |
+ data << "#{tmp}: #{check_header(value)}" << CRLF | |
} | |
@cookies.each{|cookie| | |
- data << "Set-Cookie: " << cookie.to_s << CRLF | |
+ data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF | |
} | |
data << CRLF | |
- _write_data(socket, data) | |
+ socket.write(data) | |
end | |
+ rescue InvalidHeader => e | |
+ @header.clear | |
+ @cookies.clear | |
+ set_error e | |
+ retry | |
end | |
## | |
@@ -359,6 +366,15 @@ | |
private | |
+ def check_header(header_value) | |
+ header_value = header_value.to_s | |
+ if /[\r\n]/ =~ header_value | |
+ raise InvalidHeader | |
+ else | |
+ header_value | |
+ end | |
+ end | |
+ | |
# :stopdoc: | |
def error_body(backtrace, ex, host, port) | |
@@ -401,18 +417,29 @@ | |
@body.readpartial(@buffer_size, buf) | |
size = buf.bytesize | |
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" | |
- _write_data(socket, data) | |
+ socket.write(data) | |
data.clear | |
@sent_size += size | |
rescue EOFError | |
break | |
end while true | |
buf.clear | |
- _write_data(socket, "0#{CRLF}#{CRLF}") | |
+ socket.write("0#{CRLF}#{CRLF}") | |
else | |
- size = @header['content-length'].to_i | |
- _send_file(socket, @body, 0, size) | |
- @sent_size = size | |
+ if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range'] | |
+ offset = $1.to_i | |
+ size = $2.to_i - offset + 1 | |
+ else | |
+ offset = nil | |
+ size = @header['content-length'] | |
+ size = size.to_i if size | |
+ end | |
+ begin | |
+ @sent_size = IO.copy_stream(@body, socket, size, offset) | |
+ rescue NotImplementedError | |
+ @body.seek(offset, IO::SEEK_SET) | |
+ @sent_size = IO.copy_stream(@body, socket, size) | |
+ end | |
end | |
ensure | |
@body.close | |
@@ -429,13 +456,13 @@ | |
size = buf.bytesize | |
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" | |
buf.clear | |
- _write_data(socket, data) | |
+ socket.write(data) | |
@sent_size += size | |
end | |
- _write_data(socket, "0#{CRLF}#{CRLF}") | |
+ socket.write("0#{CRLF}#{CRLF}") | |
else | |
if @body && @body.bytesize > 0 | |
- _write_data(socket, @body) | |
+ socket.write(@body) | |
@sent_size = @body.bytesize | |
end | |
end | |
@@ -446,7 +473,7 @@ | |
# do nothing | |
elsif chunked? | |
@body.call(ChunkedWrapper.new(socket, self)) | |
- _write_data(socket, "0#{CRLF}#{CRLF}") | |
+ socket.write("0#{CRLF}#{CRLF}") | |
else | |
size = @header['content-length'].to_i | |
@body.call(socket) | |
@@ -461,40 +488,25 @@ | |
end | |
def write(buf) | |
- return if buf.empty? | |
+ return 0 if buf.empty? | |
socket = @socket | |
@resp.instance_eval { | |
size = buf.bytesize | |
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" | |
- _write_data(socket, data) | |
+ socket.write(data) | |
data.clear | |
@sent_size += size | |
+ size | |
} | |
end | |
- alias :<< :write | |
- end | |
- | |
- def _send_file(output, input, offset, size) | |
- while offset > 0 | |
- sz = @buffer_size < size ? @buffer_size : size | |
- buf = input.read(sz) | |
- offset -= buf.bytesize | |
- end | |
- if size == 0 | |
- while buf = input.read(@buffer_size) | |
- _write_data(output, buf) | |
- end | |
- else | |
- while size > 0 | |
- sz = @buffer_size < size ? @buffer_size : size | |
- buf = input.read(sz) | |
- _write_data(output, buf) | |
- size -= buf.bytesize | |
- end | |
+ def <<(*buf) | |
+ write(buf) | |
+ self | |
end | |
end | |
+ # preserved for compatibility with some 3rd-party handlers | |
def _write_data(socket, data) | |
socket << data | |
end | |
diff -r -u lib/webrick/httpservlet/cgihandler.rb ../ruby/lib/webrick/httpservlet/cgihandler.rb | |
--- lib/webrick/httpservlet/cgihandler.rb 2020-06-29 08:32:08.000000000 -0500 | |
+++ ../ruby/lib/webrick/httpservlet/cgihandler.rb 2020-06-29 07:03:43.000000000 -0500 | |
@@ -65,9 +65,7 @@ | |
cgi_in.write("%8d" % dump.bytesize) | |
cgi_in.write(dump) | |
- if req.body and req.body.bytesize > 0 | |
- cgi_in.write(req.body) | |
- end | |
+ req.body { |chunk| cgi_in.write(chunk) } | |
ensure | |
cgi_in.close | |
status = $?.exitstatus | |
diff -r -u lib/webrick/httpservlet/filehandler.rb ../ruby/lib/webrick/httpservlet/filehandler.rb | |
--- lib/webrick/httpservlet/filehandler.rb 2020-06-29 08:32:08.000000000 -0500 | |
+++ ../ruby/lib/webrick/httpservlet/filehandler.rb 2020-06-29 07:03:43.000000000 -0500 | |
@@ -86,6 +86,35 @@ | |
return false | |
end | |
+ # returns a lambda for webrick/httpresponse.rb send_body_proc | |
+ def multipart_body(body, parts, boundary, mtype, filesize) | |
+ lambda do |socket| | |
+ begin | |
+ begin | |
+ first = parts.shift | |
+ last = parts.shift | |
+ socket.write( | |
+ "--#{boundary}#{CRLF}" \ | |
+ "Content-Type: #{mtype}#{CRLF}" \ | |
+ "Content-Range: bytes #{first}-#{last}/#{filesize}#{CRLF}" \ | |
+ "#{CRLF}" | |
+ ) | |
+ | |
+ begin | |
+ IO.copy_stream(body, socket, last - first + 1, first) | |
+ rescue NotImplementedError | |
+ body.seek(first, IO::SEEK_SET) | |
+ IO.copy_stream(body, socket, last - first + 1) | |
+ end | |
+ socket.write(CRLF) | |
+ end while parts[0] | |
+ socket.write("--#{boundary}--#{CRLF}") | |
+ ensure | |
+ body.close | |
+ end | |
+ end | |
+ end | |
+ | |
def make_partial_content(req, res, filename, filesize) | |
mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes]) | |
unless ranges = HTTPUtils::parse_range_header(req['range']) | |
@@ -96,37 +125,27 @@ | |
if ranges.size > 1 | |
time = Time.now | |
boundary = "#{time.sec}_#{time.usec}_#{Process::pid}" | |
- body = '' | |
- ranges.each{|range| | |
- first, last = prepare_range(range, filesize) | |
- next if first < 0 | |
- io.pos = first | |
- content = io.read(last-first+1) | |
- body << "--" << boundary << CRLF | |
- body << "Content-Type: #{mtype}" << CRLF | |
- body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF | |
- body << CRLF | |
- body << content | |
- body << CRLF | |
+ parts = [] | |
+ ranges.each {|range| | |
+ prange = prepare_range(range, filesize) | |
+ next if prange[0] < 0 | |
+ parts.concat(prange) | |
} | |
- raise HTTPStatus::RequestRangeNotSatisfiable if body.empty? | |
- body << "--" << boundary << "--" << CRLF | |
+ raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty? | |
res["content-type"] = "multipart/byteranges; boundary=#{boundary}" | |
- res.body = body | |
+ if req.http_version < '1.1' | |
+ res['connection'] = 'close' | |
+ else | |
+ res.chunked = true | |
+ end | |
+ res.body = multipart_body(io.dup, parts, boundary, mtype, filesize) | |
elsif range = ranges[0] | |
first, last = prepare_range(range, filesize) | |
raise HTTPStatus::RequestRangeNotSatisfiable if first < 0 | |
- if last == filesize - 1 | |
- content = io.dup | |
- content.pos = first | |
- else | |
- io.pos = first | |
- content = io.read(last-first+1) | |
- end | |
res['content-type'] = mtype | |
res['content-range'] = "bytes #{first}-#{last}/#{filesize}" | |
res['content-length'] = last - first + 1 | |
- res.body = content | |
+ res.body = io.dup | |
else | |
raise HTTPStatus::BadRequest | |
end | |
Only in ../ruby/lib/webrick/: webrick.gemspec |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment