Skip to content

Commit

Permalink
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 44 deletions.
8 changes: 4 additions & 4 deletions lib/webrick/httpproxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,13 @@ def do_CONNECT(req, res)
begin
while fds = IO::select([ua, os])
if fds[0].member?(ua)
buf = ua.sysread(1024);
buf = ua.readpartial(1024);
@logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
os.syswrite(buf)
os.write(buf)
elsif fds[0].member?(os)
buf = os.sysread(1024);
buf = os.readpartial(1024);
@logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
ua.syswrite(buf)
ua.write(buf)
end
end
rescue
Expand Down
66 changes: 50 additions & 16 deletions lib/webrick/httpresponse.rb
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ def send_header(socket) # :nodoc:
def send_body(socket) # :nodoc:
if @body.respond_to? :readpartial then
send_body_io(socket)
elsif @body.respond_to?(:call) then
send_body_proc(socket)
else
send_body_string(socket)
end
Expand Down Expand Up @@ -394,19 +396,18 @@ def send_body_io(socket)
if @request_method == "HEAD"
# do nothing
elsif chunked?
buf = ''
begin
buf = ''
data = ''
while true
@body.readpartial( @buffer_size, buf ) # there is no need to clear buf?
data << format("%x", buf.bytesize) << CRLF
data << buf << CRLF
_write_data(socket, data)
data.clear
@sent_size += buf.bytesize
end
rescue EOFError # do nothing
end
@body.readpartial(@buffer_size, buf)
size = buf.bytesize
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
_write_data(socket, data)
data.clear
@sent_size += size
rescue EOFError
break
end while true
buf.clear
_write_data(socket, "0#{CRLF}#{CRLF}")
else
size = @header['content-length'].to_i
Expand All @@ -425,11 +426,11 @@ def send_body_string(socket)
body ? @body.bytesize : 0
while buf = @body[@sent_size, @buffer_size]
break if buf.empty?
data = ""
data << format("%x", buf.bytesize) << CRLF
data << buf << CRLF
size = buf.bytesize
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
buf.clear
_write_data(socket, data)
@sent_size += buf.bytesize
@sent_size += size
end
_write_data(socket, "0#{CRLF}#{CRLF}")
else
Expand All @@ -440,6 +441,39 @@ def send_body_string(socket)
end
end

def send_body_proc(socket)
if @request_method == "HEAD"
# do nothing
elsif chunked?
@body.call(ChunkedWrapper.new(socket, self))
_write_data(socket, "0#{CRLF}#{CRLF}")
else
size = @header['content-length'].to_i
@body.call(socket)
@sent_size = size
end
end

class ChunkedWrapper
def initialize(socket, resp)
@socket = socket
@resp = resp
end

def write(buf)
return if buf.empty?
socket = @socket
@resp.instance_eval {
size = buf.bytesize
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
_write_data(socket, data)
data.clear
@sent_size += size
}
end
alias :<< :write
end

def _send_file(output, input, offset, size)
while offset > 0
sz = @buffer_size < size ? @buffer_size : size
Expand Down
2 changes: 0 additions & 2 deletions lib/webrick/httpservlet/abstract.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
#
# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $

require 'thread'

require 'webrick/htmlutils'
require 'webrick/httputils'
require 'webrick/httpstatus'
Expand Down
1 change: 0 additions & 1 deletion lib/webrick/httpservlet/filehandler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#
# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $

require 'thread'
require 'time'

require 'webrick/htmlutils'
Expand Down
4 changes: 0 additions & 4 deletions lib/webrick/httpstatus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ module HTTPStatus
##
# Root of the HTTP status class hierarchy
class Status < StandardError
def initialize(*args) # :nodoc:
args[0] = AccessLog.escape(args[0]) unless args.empty?
super(*args)
end
class << self
attr_reader :code, :reason_phrase # :nodoc:
end
Expand Down
1 change: 1 addition & 0 deletions lib/webrick/httputils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def normalize_path(path)
"jpeg" => "image/jpeg",
"jpg" => "image/jpeg",
"js" => "application/javascript",
"json" => "application/json",
"lha" => "application/octet-stream",
"lzh" => "application/octet-stream",
"mov" => "video/quicktime",
Expand Down
4 changes: 2 additions & 2 deletions lib/webrick/log.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ def debug?; @level >= DEBUG; end
# * Otherwise it will return +arg+.inspect.
def format(arg)
if arg.is_a?(Exception)
"#{arg.class}: #{arg.message}\n\t" <<
"#{arg.class}: #{AccessLog.escape(arg.message)}\n\t" <<
arg.backtrace.join("\n\t") << "\n"
elsif arg.respond_to?(:to_str)
arg.to_str
AccessLog.escape(arg.to_str)
else
arg.inspect
end
Expand Down
43 changes: 30 additions & 13 deletions lib/webrick/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#
# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $

require 'thread'
require 'socket'
require 'webrick/config'
require 'webrick/log'
Expand Down Expand Up @@ -168,7 +167,7 @@ def start(&block)
while @status == :Running
begin
sp = shutdown_pipe[0]
if svrs = IO.select([sp, *@listeners], nil, nil, 2.0)
if svrs = IO.select([sp, *@listeners])
if svrs[0].include? sp
# swallow shutdown pipe
buf = String.new
Expand Down Expand Up @@ -252,18 +251,26 @@ def run(sock)
# the client socket.

def accept_client(svr)
sock = nil
begin
sock = svr.accept
sock.sync = true
Utils::set_non_blocking(sock)
rescue Errno::ECONNRESET, Errno::ECONNABORTED,
Errno::EPROTO, Errno::EINVAL
rescue StandardError => ex
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
@logger.error msg
case sock = svr.to_io.accept_nonblock(exception: false)
when :wait_readable
nil
else
if svr.respond_to?(:start_immediately)
sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
sock.sync_close = true
# we cannot do OpenSSL::SSL::SSLSocket#accept here because
# a slow client can prevent us from accepting connections
# from other clients
end
sock
end
return sock
rescue Errno::ECONNRESET, Errno::ECONNABORTED,
Errno::EPROTO, Errno::EINVAL
nil
rescue StandardError => ex
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
@logger.error msg
nil
end

##
Expand All @@ -286,6 +293,16 @@ def start_thread(sock, &block)
@logger.debug "accept: <address unknown>"
raise
end
if sock.respond_to?(:sync_close=) && @config[:SSLStartImmediately]
WEBrick::Utils.timeout(@config[:RequestTimeout]) do
begin
sock.accept # OpenSSL::SSL::SSLSocket#accept
rescue Errno::ECONNRESET, Errno::ECONNABORTED,
Errno::EPROTO, Errno::EINVAL
Thread.exit
end
end
end
call_callback(:AcceptCallback, sock)
block ? block.call(sock) : run(sock)
rescue Errno::ENOTCONN
Expand Down
1 change: 0 additions & 1 deletion lib/webrick/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ def random_string(len)

###########

require "thread"
require "timeout"
require "singleton"

Expand Down
36 changes: 36 additions & 0 deletions test/webrick/test_httpauth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,42 @@ def test_basic_auth3
}
end

def test_bad_username_with_control_characters
log_tester = lambda {|log, access_log|
assert_equal(2, log.length)
assert_match(/ERROR Basic WEBrick's realm: foo\\ebar: the user is not allowed./, log[0])
assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, log[1])
}
TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
realm = "WEBrick's realm"
path = "/basic_auth"

Tempfile.create("test_webrick_auth") {|tmpfile|
tmpfile.close
tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
tmp_pass.flush

htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
users = []
htpasswd.each{|user, pass| users << user }
server.mount_proc(path){|req, res|
auth = WEBrick::HTTPAuth::BasicAuth.new(
:Realm => realm, :UserDB => htpasswd,
:Logger => server.logger
)
auth.authenticate(req, res)
res.body = "hoge"
}
http = Net::HTTP.new(addr, port)
g = Net::HTTP::Get.new(path)
g.basic_auth("foo\ebar", "passwd")
http.request(g){|res| assert_not_equal("hoge", res.body, log.call) }
}
}
end

DIGESTRES_ = /
([a-zA-Z\-]+)
[ \t]*(?:\r\n[ \t]*)*
Expand Down
23 changes: 23 additions & 0 deletions test/webrick/test_httpresponse.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,29 @@ def test_send_body_string_io_chunked
assert_equal 0, logger.messages.length
end

def test_send_body_proc
@res.body = Proc.new { |out| out.write('hello') }
IO.pipe do |r, w|
@res.send_body(w)
w.close
r.binmode
assert_equal 'hello', r.read
end
assert_equal 0, logger.messages.length
end

def test_send_body_proc_chunked
@res.body = Proc.new { |out| out.write('hello') }
@res.chunked = true
IO.pipe do |r, w|
@res.send_body(w)
w.close
r.binmode
assert_equal "5\r\nhello\r\n0\r\n\r\n", r.read
end
assert_equal 0, logger.messages.length
end

def test_set_error
status = 400
message = 'missing attribute'
Expand Down
27 changes: 27 additions & 0 deletions test/webrick/test_ssl_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require "webrick"
require "webrick/ssl"
require_relative "utils"
require 'timeout'

class TestWEBrickSSLServer < Test::Unit::TestCase
class Echo < WEBrick::GenericServer
Expand Down Expand Up @@ -37,4 +38,30 @@ def assert_self_signed_cert(config)
io.close
}
end

def test_slow_connect
poke = lambda do |io, msg|
begin
sock = OpenSSL::SSL::SSLSocket.new(io)
sock.connect
sock.puts(msg)
assert_equal "#{msg}\n", sock.gets, msg
ensure
sock&.close
io.close
end
end
config = {
:SSLEnable => true,
:SSLCertName => "/C=JP/O=www.ruby-lang.org/CN=Ruby",
}
Timeout.timeout(10) do
TestWEBrick.start_server(Echo, config) do |server, addr, port, log|
outer = TCPSocket.new(addr, port)
inner = TCPSocket.new(addr, port)
poke.call(inner, 'fast TLS negotiation')
poke.call(outer, 'slow TLS negotiation')
end
end
end
end
10 changes: 9 additions & 1 deletion webrick.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ Gem::Specification.new do |s|

s.authors = ["TAKAHASHI Masayoshi", "GOTOU YUUZOU"]
s.email = [nil, nil]
s.homepage = "https://github.com/ruby/webrick"
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

0 comments on commit 4ac0f38

Please sign in to comment.