mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Update to ruby/spec@330c641
This commit is contained in:
parent
ac4d53bd46
commit
73085c8d8e
6 changed files with 129 additions and 104 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue