1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
This commit is contained in:
Benoit Daloze 2021-08-13 18:09:14 +02:00
parent ac4d53bd46
commit 73085c8d8e
6 changed files with 129 additions and 104 deletions

View file

@ -4,7 +4,11 @@ describe "GC.stat" do
it "returns hash of values" do
stat = GC.stat
stat.should be_kind_of(Hash)
stat.keys.should include(:count)
stat.keys.should.include?(:count)
end
it "the values are all Integer since rb_gc_stat() returns size_t" do
GC.stat.values.each { |value| value.should be_kind_of(Integer) }
end
it "can return a single value" do

View file

@ -45,6 +45,10 @@ describe "A number literal" do
eval('-3r').should == Rational(-3, 1)
end
it "can be an float literal with trailing 'r' to represent a Rational in a canonical form" do
eval('1.0r').should == Rational(1, 1)
end
it "can be a float literal with trailing 'r' to represent a Rational" do
eval('0.0174532925199432957r').should == Rational(174532925199432957, 10000000000000000000)
end

View file

@ -1,5 +1,4 @@
require 'webrick'
require 'webrick/httpservlet/abstract'
require 'socket'
module NetHTTPSpecs
class NullWriter
@ -9,102 +8,117 @@ module NetHTTPSpecs
def printf(*args) end
end
class SpecServlet < WEBrick::HTTPServlet::AbstractServlet
def handle(req, res)
reply(req, res)
class SmallHTTPServer
def initialize(bind_address)
@server = TCPServer.new(bind_address, 0)
@running = Mutex.new
@thread = Thread.new {
Thread.current.abort_on_exception = true
listen
}
end
%w{ do_GET do_HEAD do_POST do_PUT do_PROPPATCH do_LOCK do_UNLOCK
do_OPTIONS do_PROPFIND do_DELETE do_MOVE do_COPY
do_MKCOL do_TRACE }.each do |method|
alias_method method.to_sym, :handle
def port
@server.addr[1]
end
end
class RequestServlet < SpecServlet
def reply(req, res)
res.content_type = "text/plain"
res.body = "Request type: #{req.request_method}"
end
end
def listen
loop do
begin
client = @server.accept
rescue IOError => e
if @running.locked? # close
break
else
raise e
end
end
class RequestBodyServlet < SpecServlet
def reply(req, res)
res.content_type = "text/plain"
res.body = req.body
end
end
class RequestHeaderServlet < SpecServlet
def reply(req, res)
res.content_type = "text/plain"
res.body = req.header.inspect
end
end
class RequestBasicAuthServlet < SpecServlet
def reply(req, res)
res.content_type = "text/plain"
WEBrick::HTTPAuth.basic_auth(req, res, "realm") do |user, pass|
res.body = "username: #{user}\npassword: #{pass}"
true
handle_client(client)
end
end
def handle_client(client)
begin
until client.closed?
request = client.gets("\r\n\r\n")
break unless request
handle_request(client, request)
end
ensure
client.close
end
end
def parse_request(request)
request, *headers = request.chomp.lines.map { |line| line.chomp }
request_method, request_uri, _http_version = request.split
headers = headers.map { |line| line.split(': ', 2) }.to_h
[request_method, request_uri, headers]
end
def handle_request(client, request)
request_method, request_uri, headers = parse_request(request)
if headers.include? 'Content-Length'
request_body_size = Integer(headers['Content-Length'])
request_body = client.read(request_body_size)
end
case request_uri
when '/'
raise request_method unless request_method == 'GET'
reply(client, "This is the index page.", request_method)
when '/request'
reply(client, "Request type: #{request_method}", request_method)
when '/request/body'
reply(client, request_body, request_method)
when '/request/header'
reply(client, headers.inspect, request_method)
when '/request/basic_auth'
reply(client, "username: \npassword: ", request_method)
else
raise request_uri
end
end
def reply(client, body, request_method)
client.print "HTTP/1.1 200 OK\r\n"
if request_method == 'HEAD'
client.close
else
client.print "Content-Type: text/plain\r\n"
client.print "Content-Length: #{body.bytesize}\r\n"
client.print "\r\n"
client.print body
end
end
def close
@running.lock
@server.close
@thread.join
end
end
@server = nil
@server_thread = nil
class << self
def port
raise "server not started" unless @server
@server.config[:Port]
@server.port
end
def start_server
bind_address = platform_is(:windows) ? "localhost" : "127.0.0.1"
server_config = {
BindAddress: bind_address,
Port: 0,
Logger: WEBrick::Log.new(NullWriter.new),
AccessLog: [],
ServerType: Thread
}
@server = WEBrick::HTTPServer.new(server_config)
@server.mount_proc('/') do |req, res|
res.content_type = "text/plain"
res.body = "This is the index page."
end
@server.mount('/request', RequestServlet)
@server.mount("/request/body", RequestBodyServlet)
@server.mount("/request/header", RequestHeaderServlet)
@server.mount("/request/basic_auth", RequestBasicAuthServlet)
@server_thread = @server.start
@server = SmallHTTPServer.new(bind_address)
end
def stop_server
if @server
begin
@server.shutdown
rescue Errno::EPIPE, Errno::EBADF
# Because WEBrick is not thread-safe and only catches IOError
# EBADF can happen because WEBrick @server_thread concurrently closes the shutdown pipe
# once @status = :Shutdown, while the current thread does write_nonblock("\0").
# On MRI this EBADF is replaced by IOError due to the GIL around both #close and #write_nonblock.
end
@server.close
@server = nil
end
if @server_thread
@server_thread.join
@server_thread = nil
end
timeout = WEBrick::Utils::TimeoutHandler
timeout.terminate if timeout.respond_to?(:terminate)
end
end
end

View file

@ -27,7 +27,7 @@ describe "Net::HTTP.post" do
it "sends Content-Type: application/x-www-form-urlencoded by default" do
response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request/header"), "test=test")
response.body.should include('"content-type"=>["application/x-www-form-urlencoded"]')
response.body.should include('"Content-Type"=>"application/x-www-form-urlencoded"')
end
it "does not support HTTP Basic Auth" do

View file

@ -54,7 +54,7 @@ describe "Net::HTTP#send_request" do
@methods.each do |method|
response = @http.send_request(method, "/request/header", "test=test", "referer" => referer)
response.body.should include('"referer"=>["' + referer + '"]')
response.body.should include('"Referer"=>"' + referer + '"')
end
end
end

View file

@ -1,34 +1,37 @@
require_relative '../spec_helper'
require "webrick"
require "stringio"
require "net/http"
# webrick is no longer in stdlib in Ruby 3+
ruby_version_is ""..."3.0" do
require "webrick"
require "stringio"
require "net/http"
describe "WEBrick" do
describe "resists CVE-2017-17742" do
it "for a response splitting headers" do
config = WEBrick::Config::HTTP
res = WEBrick::HTTPResponse.new config
res['X-header'] = "malicious\r\nCookie: hack"
io = StringIO.new
res.send_response io
io.rewind
res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
res.code.should == '500'
io.string.should_not =~ /hack/
end
describe "WEBrick" do
describe "resists CVE-2017-17742" do
it "for a response splitting headers" do
config = WEBrick::Config::HTTP
res = WEBrick::HTTPResponse.new config
res['X-header'] = "malicious\r\nCookie: hack"
io = StringIO.new
res.send_response io
io.rewind
res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
res.code.should == '500'
io.string.should_not =~ /hack/
end
it "for a response splitting cookie headers" do
user_input = "malicious\r\nCookie: hack"
config = WEBrick::Config::HTTP
res = WEBrick::HTTPResponse.new config
res.cookies << WEBrick::Cookie.new('author', user_input)
io = StringIO.new
res.send_response io
io.rewind
res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
res.code.should == '500'
io.string.should_not =~ /hack/
it "for a response splitting cookie headers" do
user_input = "malicious\r\nCookie: hack"
config = WEBrick::Config::HTTP
res = WEBrick::HTTPResponse.new config
res.cookies << WEBrick::Cookie.new('author', user_input)
io = StringIO.new
res.send_response io
io.rewind
res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
res.code.should == '500'
io.string.should_not =~ /hack/
end
end
end
end