Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@headius
Last active June 29, 2020 11:35
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/cb184868d6d8b709b8a3f62cd0c275eb to your computer and use it in GitHub Desktop.
Save headius/cb184868d6d8b709b8a3f62cd0c275eb to your computer and use it in GitHub Desktop.
Diffs from WEBrick 1.4.2 in the ruby/webrick repository versus CRuby's ruby_2_6 branch lib/webrick
diff --git a/../ruby/lib/webrick/.document b/../ruby/lib/webrick/.document
new file mode 100644
index 0000000..c62f890
--- /dev/null
+++ b/../ruby/lib/webrick/.document
@@ -0,0 +1,6 @@
+# Add files to this as they become documented
+
+*.rb
+
+httpauth
+httpservlet
diff --git a/lib/webrick/cgi.rb b/../ruby/lib/webrick/cgi.rb
index 94f385f..bb0ae2f 100644
--- a/lib/webrick/cgi.rb
+++ b/../ruby/lib/webrick/cgi.rb
@@ -8,9 +8,9 @@
#
# $Id$
-require "webrick/httprequest"
-require "webrick/httpresponse"
-require "webrick/config"
+require_relative "httprequest"
+require_relative "httpresponse"
+require_relative "config"
require "stringio"
module WEBrick
@@ -265,6 +265,10 @@ def <<(data)
@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 --git a/lib/webrick/config.rb b/../ruby/lib/webrick/config.rb
index af4b561..9f2ab44 100644
--- a/lib/webrick/config.rb
+++ b/../ruby/lib/webrick/config.rb
@@ -9,11 +9,11 @@
#
# $IPR: config.rb,v 1.52 2003/07/22 19:20:42 gotoyuzo Exp $
-require 'webrick/version'
-require 'webrick/httpversion'
-require 'webrick/httputils'
-require 'webrick/utils'
-require 'webrick/log'
+require_relative 'version'
+require_relative 'httpversion'
+require_relative 'httputils'
+require_relative 'utils'
+require_relative 'log'
module WEBrick
module Config
diff --git a/lib/webrick/cookie.rb b/../ruby/lib/webrick/cookie.rb
index 24bf92e..5fd3bfb 100644
--- a/lib/webrick/cookie.rb
+++ b/../ruby/lib/webrick/cookie.rb
@@ -10,7 +10,7 @@
# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $
require 'time'
-require 'webrick/httputils'
+require_relative 'httputils'
module WEBrick
diff --git a/lib/webrick/httpauth/basicauth.rb b/../ruby/lib/webrick/httpauth/basicauth.rb
index e23420f..7d0a9cf 100644
--- a/lib/webrick/httpauth/basicauth.rb
+++ b/../ruby/lib/webrick/httpauth/basicauth.rb
@@ -8,9 +8,9 @@
#
# $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
-require 'webrick/config'
-require 'webrick/httpstatus'
-require 'webrick/httpauth/authenticator'
+require_relative '../config'
+require_relative '../httpstatus'
+require_relative 'authenticator'
module WEBrick
module HTTPAuth
@@ -24,7 +24,7 @@ module HTTPAuth
#
# config = { :Realm => 'BasicAuth example realm' }
#
- # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt
# htpasswd.set_passwd config[:Realm], 'username', 'password'
# htpasswd.flush
#
@@ -81,7 +81,15 @@ def authenticate(req, res)
error("%s: the user is not allowed.", userid)
challenge(req, res)
end
- if password.crypt(encpass) != encpass
+
+ case encpass
+ when /\A\$2[aby]\$/
+ password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password
+ else
+ password_matches = password.crypt(encpass) == encpass
+ end
+
+ unless password_matches
error("%s: password unmatch.", userid)
challenge(req, res)
end
diff --git a/lib/webrick/httpauth/digestauth.rb b/../ruby/lib/webrick/httpauth/digestauth.rb
index 375ec20..3cf1289 100644
--- a/lib/webrick/httpauth/digestauth.rb
+++ b/../ruby/lib/webrick/httpauth/digestauth.rb
@@ -12,9 +12,9 @@
#
# $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
-require 'webrick/config'
-require 'webrick/httpstatus'
-require 'webrick/httpauth/authenticator'
+require_relative '../config'
+require_relative '../httpstatus'
+require_relative 'authenticator'
require 'digest/md5'
require 'digest/sha1'
@@ -235,9 +235,11 @@ def _authenticate(req, res)
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 _authenticate(req, res)
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 --git a/lib/webrick/httpauth/htdigest.rb b/../ruby/lib/webrick/httpauth/htdigest.rb
index c35b384..93b18e2 100644
--- a/lib/webrick/httpauth/htdigest.rb
+++ b/../ruby/lib/webrick/httpauth/htdigest.rb
@@ -8,8 +8,8 @@
#
# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
-require 'webrick/httpauth/userdb'
-require 'webrick/httpauth/digestauth'
+require_relative 'userdb'
+require_relative 'digestauth'
require 'tempfile'
module WEBrick
diff --git a/lib/webrick/httpauth/htgroup.rb b/../ruby/lib/webrick/httpauth/htgroup.rb
index 399a62c..e06c441 100644
--- a/lib/webrick/httpauth/htgroup.rb
+++ b/../ruby/lib/webrick/httpauth/htgroup.rb
@@ -63,15 +63,18 @@ def reload
def flush(output=nil)
output ||= @path
- tmp = Tempfile.new("htgroup", File::dirname(output))
+ tmp = Tempfile.create("htgroup", File::dirname(output))
begin
@group.keys.sort.each{|group|
tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
}
+ ensure
tmp.close
- File::rename(tmp.path, output)
- rescue
- tmp.close(true)
+ if $!
+ File.unlink(tmp.path)
+ else
+ return File.rename(tmp.path, output)
+ end
end
end
diff --git a/lib/webrick/httpauth/htpasswd.rb b/../ruby/lib/webrick/httpauth/htpasswd.rb
index 976eeeb..abca305 100644
--- a/lib/webrick/httpauth/htpasswd.rb
+++ b/../ruby/lib/webrick/httpauth/htpasswd.rb
@@ -8,8 +8,8 @@
#
# $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
-require 'webrick/httpauth/userdb'
-require 'webrick/httpauth/basicauth'
+require_relative 'userdb'
+require_relative 'basicauth'
require 'tempfile'
module WEBrick
@@ -35,11 +35,29 @@ class Htpasswd
##
# Open a password database at +path+
- def initialize(path)
+ def initialize(path, password_hash: nil)
@path = path
@mtime = Time.at(0)
@passwd = Hash.new
@auth_type = BasicAuth
+ @password_hash = password_hash
+
+ case @password_hash
+ when nil
+ # begin
+ # require "string/crypt"
+ # rescue LoadError
+ # warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt")
+ # end
+ @password_hash = :crypt
+ when :crypt
+ # require "string/crypt"
+ when :bcrypt
+ require "bcrypt"
+ else
+ raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument"
+ end
+
File.open(@path,"a").close unless File.exist?(@path)
reload
end
@@ -56,6 +74,14 @@ def reload
line.chomp!
case line
when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
+ if @password_hash == :bcrypt
+ raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported"
+ end
+ user, pass = line.split(":")
+ when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z!
+ if @password_hash == :crypt
+ raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported"
+ end
user, pass = line.split(":")
when /:\$/, /:{SHA}/
raise NotImplementedError,
@@ -102,7 +128,14 @@ def get_passwd(realm, user, reload_db)
# Sets a password in the database for +user+ in +realm+ to +pass+.
def set_passwd(realm, user, pass)
- @passwd[user] = make_passwd(realm, user, pass)
+ if @password_hash == :bcrypt
+ # Cost of 5 to match Apache default, and because the
+ # bcrypt default of 10 will introduce significant delays
+ # for every request.
+ @passwd[user] = BCrypt::Password.create(pass, :cost=>5)
+ else
+ @passwd[user] = make_passwd(realm, user, pass)
+ end
end
##
diff --git a/lib/webrick/httpauth.rb b/../ruby/lib/webrick/httpauth.rb
index bbb6776..f8bf09a 100644
--- a/lib/webrick/httpauth.rb
+++ b/../ruby/lib/webrick/httpauth.rb
@@ -9,11 +9,11 @@
#
# $IPR: httpauth.rb,v 1.14 2003/07/22 19:20:42 gotoyuzo Exp $
-require 'webrick/httpauth/basicauth'
-require 'webrick/httpauth/digestauth'
-require 'webrick/httpauth/htpasswd'
-require 'webrick/httpauth/htdigest'
-require 'webrick/httpauth/htgroup'
+require_relative 'httpauth/basicauth'
+require_relative 'httpauth/digestauth'
+require_relative 'httpauth/htpasswd'
+require_relative 'httpauth/htdigest'
+require_relative 'httpauth/htgroup'
module WEBrick
diff --git a/lib/webrick/httpproxy.rb b/../ruby/lib/webrick/httpproxy.rb
index be5531f..d05d595 100644
--- a/lib/webrick/httpproxy.rb
+++ b/../ruby/lib/webrick/httpproxy.rb
@@ -10,7 +10,7 @@
# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
-require "webrick/httpserver"
+require_relative "httpserver"
require "net/http"
module WEBrick
@@ -211,21 +211,15 @@ def do_CONNECT(req, res)
end
def do_GET(req, res)
- perform_proxy_request(req, res) do |http, path, header|
- http.get(path, header)
- end
+ perform_proxy_request(req, res, Net::HTTP::Get)
end
def do_HEAD(req, res)
- perform_proxy_request(req, res) do |http, path, header|
- http.head(path, header)
- end
+ perform_proxy_request(req, res, Net::HTTP::Head)
end
def do_POST(req, res)
- perform_proxy_request(req, res) do |http, path, header|
- http.post(path, req.body || "", header)
- end
+ perform_proxy_request(req, res, Net::HTTP::Post, req.body_reader)
end
def do_OPTIONS(req, res)
@@ -301,38 +295,56 @@ def setup_upstream_proxy_authentication(req, res, header)
return FakeProxyURI
end
- def perform_proxy_request(req, res)
+ def perform_proxy_request(req, res, req_class, body_stream = nil)
uri = req.request_uri
path = uri.path.dup
path << "?" << uri.query if uri.query
header = setup_proxy_header(req, res)
upstream = setup_upstream_proxy_authentication(req, res, header)
- response = nil
+ body_tmp = []
http = Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
- http.start do
- if @config[:ProxyTimeout]
- ################################## these issues are
- http.open_timeout = 30 # secs # necessary (maybe because
- http.read_timeout = 60 # secs # Ruby's bug, but why?)
- ##################################
+ req_fib = Fiber.new do
+ http.start do
+ if @config[:ProxyTimeout]
+ ################################## these issues are
+ http.open_timeout = 30 # secs # necessary (maybe because
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
+ ##################################
+ end
+ if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i
+ header['Transfer-Encoding'] = 'chunked'
+ end
+ http_req = req_class.new(path, header)
+ http_req.body_stream = body_stream if body_stream
+ http.request(http_req) do |response|
+ # Persistent connection requirements are mysterious for me.
+ # So I will close the connection in every response.
+ res['proxy-connection'] = "close"
+ res['connection'] = "close"
+
+ # stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
+ res.status = response.code.to_i
+ res.chunked = response.chunked?
+ choose_header(response, res)
+ set_cookie(response, res)
+ set_via(res)
+ response.read_body do |buf|
+ body_tmp << buf
+ Fiber.yield # wait for res.body Proc#call
+ end
+ end # http.request
+ end
+ end
+ req_fib.resume # read HTTP response headers and first chunk of the body
+ res.body = ->(socket) do
+ while buf = body_tmp.shift
+ socket.write(buf)
+ buf.clear
+ req_fib.resume # continue response.read_body
end
- response = yield(http, path, header)
end
-
- # Persistent connection requirements are mysterious for me.
- # So I will close the connection in every response.
- res['proxy-connection'] = "close"
- res['connection'] = "close"
-
- # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
- res.status = response.code.to_i
- choose_header(response, res)
- set_cookie(response, res)
- set_via(res)
- res.body = response.body
end
-
# :stopdoc:
end
end
diff --git a/lib/webrick/httprequest.rb b/../ruby/lib/webrick/httprequest.rb
index 10cf72d..e402099 100644
--- a/lib/webrick/httprequest.rb
+++ b/../ruby/lib/webrick/httprequest.rb
@@ -10,10 +10,10 @@
# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
require 'uri'
-require 'webrick/httpversion'
-require 'webrick/httpstatus'
-require 'webrick/httputils'
-require 'webrick/cookie'
+require_relative 'httpversion'
+require_relative 'httpstatus'
+require_relative 'httputils'
+require_relative 'cookie'
module WEBrick
@@ -257,6 +257,32 @@ def body(&block) # :yields: body_chunk
@body.empty? ? nil : @body
end
+ ##
+ # Prepares the HTTPRequest object for use as the
+ # source for IO.copy_stream
+
+ def body_reader
+ @body_tmp = []
+ @body_rd = Fiber.new do
+ body do |buf|
+ @body_tmp << buf
+ Fiber.yield
+ end
+ end
+ @body_rd.resume # grab the first chunk and yield
+ self
+ end
+
+ # for IO.copy_stream. Note: we may return a larger string than +size+
+ # here; but IO.copy_stream does not care.
+ def readpartial(size, buf = ''.b) # :nodoc
+ res = @body_tmp.shift or raise EOFError, 'end of file reached'
+ buf.replace(res)
+ res.clear
+ @body_rd.resume # get more chunks
+ buf
+ end
+
##
# Request query as a Hash
@@ -414,13 +440,19 @@ def meta_vars
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
+ raise HTTPStatus::EOFError unless @request_line
+
+ @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
- raise HTTPStatus::EOFError unless @request_line
if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
@request_method = $1
@unparsed_uri = $2
@@ -435,6 +467,9 @@ def read_header(socket)
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 +537,16 @@ def read_chunk_size(socket)
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 --git a/lib/webrick/httpresponse.rb b/../ruby/lib/webrick/httpresponse.rb
index d76310f..08f7797 100644
--- a/lib/webrick/httpresponse.rb
+++ b/../ruby/lib/webrick/httpresponse.rb
@@ -10,10 +10,11 @@
# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
require 'time'
-require 'webrick/httpversion'
-require 'webrick/htmlutils'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
+require 'uri'
+require_relative 'httpversion'
+require_relative 'htmlutils'
+require_relative 'httputils'
+require_relative 'httpstatus'
module WEBrick
##
@@ -21,6 +22,8 @@ module WEBrick
# WEBrick HTTP Servlet.
class HTTPResponse
+ class InvalidHeader < StandardError
+ end
##
# HTTP Response version
@@ -251,7 +254,7 @@ def setup_header() # :nodoc:
@header.delete('content-length')
elsif @header['content-length'].nil?
unless @body.is_a?(IO)
- @header['content-length'] = @body ? @body.bytesize : 0
+ @header['content-length'] = (@body ? @body.bytesize : 0).to_s
end
end
@@ -274,7 +277,7 @@ def setup_header() # :nodoc:
# Location is a single absoluteURI.
if location = @header['location']
if @request_uri
- @header['location'] = @request_uri.merge(location)
+ @header['location'] = @request_uri.merge(location).to_s
end
end
end
@@ -287,14 +290,19 @@ def send_header(socket) # :nodoc:
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
##
@@ -324,8 +332,9 @@ def to_s # :nodoc:
# res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
def set_redirect(status, url)
+ url = URI(url).to_s
@body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
- @header['location'] = url.to_s
+ @header['location'] = url
raise status
end
@@ -359,6 +368,15 @@ def set_error(ex, backtrace=false)
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 +419,29 @@ def send_body_io(socket)
@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 +458,13 @@ def send_body_string(socket)
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 +475,7 @@ def send_body_proc(socket)
# 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 +490,25 @@ def initialize(socket, resp)
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 --git a/lib/webrick/https.rb b/../ruby/lib/webrick/https.rb
index 4826654..b0a49bc 100644
--- a/lib/webrick/https.rb
+++ b/../ruby/lib/webrick/https.rb
@@ -9,8 +9,8 @@
#
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
-require 'webrick/ssl'
-require 'webrick/httpserver'
+require_relative 'ssl'
+require_relative 'httpserver'
module WEBrick
module Config
diff --git a/lib/webrick/httpserver.rb b/../ruby/lib/webrick/httpserver.rb
index e46b3bd..e85d059 100644
--- a/lib/webrick/httpserver.rb
+++ b/../ruby/lib/webrick/httpserver.rb
@@ -10,13 +10,13 @@
# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
require 'io/wait'
-require 'webrick/server'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
-require 'webrick/httprequest'
-require 'webrick/httpresponse'
-require 'webrick/httpservlet'
-require 'webrick/accesslog'
+require_relative 'server'
+require_relative 'httputils'
+require_relative 'httpstatus'
+require_relative 'httprequest'
+require_relative 'httpresponse'
+require_relative 'httpservlet'
+require_relative 'accesslog'
module WEBrick
class HTTPServerError < ServerError; end
@@ -68,8 +68,8 @@ def initialize(config={}, default=Config::HTTP)
def run(sock)
while true
- res = HTTPResponse.new(@config)
- req = HTTPRequest.new(@config)
+ req = create_request(@config)
+ res = create_response(@config)
server = self
begin
timeout = @config[:RequestTimeout]
@@ -224,6 +224,20 @@ def access_log(config, req, res)
}
end
+ ##
+ # Creates the HTTPRequest used when handling the HTTP
+ # request. Can be overridden by subclasses.
+ def create_request(with_webrick_config)
+ HTTPRequest.new(with_webrick_config)
+ end
+
+ ##
+ # Creates the HTTPResponse used when handling the HTTP
+ # request. Can be overridden by subclasses.
+ def create_response(with_webrick_config)
+ HTTPResponse.new(with_webrick_config)
+ end
+
##
# Mount table for the path a servlet is mounted on in the directory space
# of the server. Users of WEBrick can only access this indirectly via
diff --git a/lib/webrick/httpservlet/abstract.rb b/../ruby/lib/webrick/httpservlet/abstract.rb
index fc4cd22..bccb091 100644
--- a/lib/webrick/httpservlet/abstract.rb
+++ b/../ruby/lib/webrick/httpservlet/abstract.rb
@@ -9,9 +9,9 @@
#
# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $
-require 'webrick/htmlutils'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
+require_relative '../htmlutils'
+require_relative '../httputils'
+require_relative '../httpstatus'
module WEBrick
module HTTPServlet
diff --git a/lib/webrick/httpservlet/cgihandler.rb b/../ruby/lib/webrick/httpservlet/cgihandler.rb
index ba6b0b6..981f649 100644
--- a/lib/webrick/httpservlet/cgihandler.rb
+++ b/../ruby/lib/webrick/httpservlet/cgihandler.rb
@@ -11,8 +11,8 @@
require 'rbconfig'
require 'tempfile'
-require 'webrick/config'
-require 'webrick/httpservlet/abstract'
+require_relative '../config'
+require_relative 'abstract'
module WEBrick
module HTTPServlet
@@ -65,9 +65,7 @@ def do_GET(req, res)
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 --git a/lib/webrick/httpservlet/erbhandler.rb b/../ruby/lib/webrick/httpservlet/erbhandler.rb
index aa02ce8..cd09e5f 100644
--- a/lib/webrick/httpservlet/erbhandler.rb
+++ b/../ruby/lib/webrick/httpservlet/erbhandler.rb
@@ -9,7 +9,7 @@
#
# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
-require 'webrick/httpservlet/abstract.rb'
+require_relative 'abstract'
require 'erb'
diff --git a/lib/webrick/httpservlet/filehandler.rb b/../ruby/lib/webrick/httpservlet/filehandler.rb
index 2c02d0f..601882e 100644
--- a/lib/webrick/httpservlet/filehandler.rb
+++ b/../ruby/lib/webrick/httpservlet/filehandler.rb
@@ -11,9 +11,9 @@
require 'time'
-require 'webrick/htmlutils'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
+require_relative '../htmlutils'
+require_relative '../httputils'
+require_relative '../httpstatus'
module WEBrick
module HTTPServlet
@@ -55,7 +55,7 @@ def do_GET(req, res)
else
mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes])
res['content-type'] = mtype
- res['content-length'] = st.size
+ res['content-length'] = st.size.to_s
res['last-modified'] = mtime.httpdate
res.body = File.open(@local_path, "rb")
end
@@ -86,6 +86,35 @@ def not_modified?(req, res, mtime, etag)
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 @@ def make_partial_content(req, res, filename, filesize)
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['content-length'] = (last - first + 1).to_s
+ res.body = io.dup
else
raise HTTPStatus::BadRequest
end
diff --git a/lib/webrick/httpservlet/prochandler.rb b/../ruby/lib/webrick/httpservlet/prochandler.rb
index c1f454e..599ffc4 100644
--- a/lib/webrick/httpservlet/prochandler.rb
+++ b/../ruby/lib/webrick/httpservlet/prochandler.rb
@@ -9,7 +9,7 @@
#
# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
-require 'webrick/httpservlet/abstract.rb'
+require_relative 'abstract'
module WEBrick
module HTTPServlet
diff --git a/lib/webrick/httpservlet.rb b/../ruby/lib/webrick/httpservlet.rb
index 1ee04ec..da49a14 100644
--- a/lib/webrick/httpservlet.rb
+++ b/../ruby/lib/webrick/httpservlet.rb
@@ -9,11 +9,11 @@
#
# $IPR: httpservlet.rb,v 1.21 2003/02/23 12:24:46 gotoyuzo Exp $
-require 'webrick/httpservlet/abstract'
-require 'webrick/httpservlet/filehandler'
-require 'webrick/httpservlet/cgihandler'
-require 'webrick/httpservlet/erbhandler'
-require 'webrick/httpservlet/prochandler'
+require_relative 'httpservlet/abstract'
+require_relative 'httpservlet/filehandler'
+require_relative 'httpservlet/cgihandler'
+require_relative 'httpservlet/erbhandler'
+require_relative 'httpservlet/prochandler'
module WEBrick
module HTTPServlet
diff --git a/lib/webrick/httpstatus.rb b/../ruby/lib/webrick/httpstatus.rb
index 0630219..c811f21 100644
--- a/lib/webrick/httpstatus.rb
+++ b/../ruby/lib/webrick/httpstatus.rb
@@ -9,7 +9,7 @@
#
# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $
-require 'webrick/accesslog'
+require_relative 'accesslog'
module WEBrick
diff --git a/lib/webrick/server.rb b/../ruby/lib/webrick/server.rb
index 88e160d..4a6e74c 100644
--- a/lib/webrick/server.rb
+++ b/../ruby/lib/webrick/server.rb
@@ -10,8 +10,8 @@
# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $
require 'socket'
-require 'webrick/config'
-require 'webrick/log'
+require_relative 'config'
+require_relative 'log'
module WEBrick
diff --git a/lib/webrick/ssl.rb b/../ruby/lib/webrick/ssl.rb
index 8a334ea..d125083 100644
--- a/lib/webrick/ssl.rb
+++ b/../ruby/lib/webrick/ssl.rb
@@ -130,7 +130,7 @@ def create_self_signed_cert(bits, cn, comment)
aki = ef.create_extension("authorityKeyIdentifier",
"keyid:always,issuer:always")
cert.add_extension(aki)
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+ cert.sign(rsa, OpenSSL::Digest::SHA256.new)
return [ cert, rsa ]
end
@@ -181,7 +181,7 @@ def setup_ssl_context(config) # :nodoc:
unless config[:SSLCertificate]
cn = config[:SSLCertName]
comment = config[:SSLCertComment]
- cert, key = Utils::create_self_signed_cert(1024, cn, comment)
+ cert, key = Utils::create_self_signed_cert(2048, cn, comment)
config[:SSLCertificate] = cert
config[:SSLPrivateKey] = key
end
diff --git a/../ruby/lib/webrick/webrick.gemspec b/../ruby/lib/webrick/webrick.gemspec
new file mode 100644
index 0000000..611ec13
--- /dev/null
+++ b/../ruby/lib/webrick/webrick.gemspec
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+begin
+ require_relative 'lib/webrick/version'
+rescue LoadError
+ # for Ruby core repository
+ require_relative 'version'
+end
+
+Gem::Specification.new do |s|
+ s.name = "webrick"
+ s.version = WEBrick::VERSION
+ s.summary = "HTTP server toolkit"
+ s.description = "WEBrick is an HTTP server toolkit that can be configured as an HTTPS server, a proxy server, and a virtual-host server."
+
+ s.require_path = %w{lib}
+ s.files = ["lib/webrick.rb", "lib/webrick/accesslog.rb", "lib/webrick/cgi.rb", "lib/webrick/compat.rb", "lib/webrick/config.rb", "lib/webrick/cookie.rb", "lib/webrick/htmlutils.rb", "lib/webrick/httpauth.rb", "lib/webrick/httpauth/authenticator.rb", "lib/webrick/httpauth/basicauth.rb", "lib/webrick/httpauth/digestauth.rb", "lib/webrick/httpauth/htdigest.rb", "lib/webrick/httpauth/htgroup.rb", "lib/webrick/httpauth/htpasswd.rb", "lib/webrick/httpauth/userdb.rb", "lib/webrick/httpauth.rb", "lib/webrick/httpproxy.rb", "lib/webrick/httprequest.rb", "lib/webrick/httpresponse.rb", "lib/webrick/https.rb", "lib/webrick/httpserver.rb", "lib/webrick/httpservlet.rb", "lib/webrick/httpservlet/abstract.rb", "lib/webrick/httpservlet/cgi_runner.rb", "lib/webrick/httpservlet/cgihandler.rb", "lib/webrick/httpservlet/erbhandler.rb", "lib/webrick/httpservlet/filehandler.rb", "lib/webrick/httpservlet/prochandler.rb", "lib/webrick/httpservlet.rb", "lib/webrick/httpstatus.rb", "lib/webrick/httputils.rb", "lib/webrick/httpversion.rb", "lib/webrick/log.rb", "lib/webrick/server.rb", "lib/webrick/ssl.rb", "lib/webrick/utils.rb", "lib/webrick/version.rb"]
+ s.required_ruby_version = ">= 2.3.0"
+
+ s.authors = ["TAKAHASHI Masayoshi", "GOTOU YUUZOU", "Eric Wong"]
+ s.email = [nil, nil, 'normal@ruby-lang.org']
+ s.homepage = "https://www.ruby-lang.org"
+ s.license = "BSD-2-Clause"
+
+ if s.respond_to?(:metadata=)
+ s.metadata = {
+ "bug_tracker_uri" => "https://bugs.ruby-lang.org/projects/ruby-trunk/issues",
+ "homepage_uri" => "https://www.ruby-lang.org",
+ "source_code_uri" => "https://svn.ruby-lang.org/repos/ruby"
+ }
+ end
+
+ s.add_development_dependency "rake"
+end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment