Skip to content

Instantly share code, notes, and snippets.

@headius
Last active June 29, 2020 13:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save headius/9e70146870d5e6f329a5857fe91c8948 to your computer and use it in GitHub Desktop.
Save headius/9e70146870d5e6f329a5857fe91c8948 to your computer and use it in GitHub Desktop.
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