summaryrefslogtreecommitdiff
path: root/lib/drb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/drb')
-rw-r--r--lib/drb/acl.rb11
-rw-r--r--lib/drb/drb.gemspec43
-rw-r--r--lib/drb/drb.rb345
-rw-r--r--lib/drb/eq.rb1
-rw-r--r--lib/drb/extserv.rb3
-rw-r--r--lib/drb/extservm.rb11
-rw-r--r--lib/drb/gw.rb3
-rw-r--r--lib/drb/invokemethod.rb1
-rw-r--r--lib/drb/observer.rb3
-rw-r--r--lib/drb/ssl.rb15
-rw-r--r--lib/drb/timeridconv.rb46
-rw-r--r--lib/drb/unix.rb11
-rw-r--r--lib/drb/version.rb3
-rw-r--r--lib/drb/weakidconv.rb59
14 files changed, 373 insertions, 182 deletions
diff --git a/lib/drb/acl.rb b/lib/drb/acl.rb
index 72e034e960..b004656f09 100644
--- a/lib/drb/acl.rb
+++ b/lib/drb/acl.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
# Copyright (c) 2000,2002,2003 Masatoshi SEKI
#
# acl.rb is copyrighted free software by Masatoshi SEKI.
@@ -48,6 +49,9 @@ class ACL
# +str+ may be "*" or "all" to match any address, an IP address string
# to match a specific address, an IP address mask per IPAddr, or one
# containing "*" to match part of an IPv4 address.
+ #
+ # IPAddr::InvalidPrefixError may be raised when an IP network
+ # address with an invalid netmask/prefix is given.
def initialize(str)
if str == '*' or str == 'all'
@@ -57,6 +61,10 @@ class ACL
else
begin
@pat = [:ip, IPAddr.new(str)]
+ rescue IPAddr::InvalidPrefixError
+ # In this case, `str` shouldn't be a host name pattern
+ # because it contains a slash.
+ raise
rescue ArgumentError
@pat = [:name, dot_pat(str)]
end
@@ -81,8 +89,7 @@ class ACL
# Creates a Regexp to match an address.
def dot_pat(str)
- exp = "^" + dot_pat_str(str) + "$"
- Regexp.new(exp)
+ /\A#{dot_pat_str(str)}\z/
end
public
diff --git a/lib/drb/drb.gemspec b/lib/drb/drb.gemspec
new file mode 100644
index 0000000000..c9d7e40a51
--- /dev/null
+++ b/lib/drb/drb.gemspec
@@ -0,0 +1,43 @@
+begin
+ require_relative "lib/drb/version"
+rescue LoadError # Fallback to load version file in ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "drb"
+ spec.version = DRb::VERSION
+ spec.authors = ["Masatoshi SEKI"]
+ spec.email = ["seki@ruby-lang.org"]
+
+ spec.summary = %q{Distributed object system for Ruby}
+ spec.description = %q{Distributed object system for Ruby}
+ spec.homepage = "https://github.com/ruby/drb"
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
+ spec.licenses = ["Ruby", "BSD-2-Clause"]
+
+ spec.metadata["homepage_uri"] = spec.homepage
+ spec.metadata["source_code_uri"] = spec.homepage
+
+ spec.files = %w[
+ LICENSE.txt
+ drb.gemspec
+ lib/drb.rb
+ lib/drb/acl.rb
+ lib/drb/drb.rb
+ lib/drb/eq.rb
+ lib/drb/extserv.rb
+ lib/drb/extservm.rb
+ lib/drb/gw.rb
+ lib/drb/invokemethod.rb
+ lib/drb/observer.rb
+ lib/drb/ssl.rb
+ lib/drb/timeridconv.rb
+ lib/drb/unix.rb
+ lib/drb/version.rb
+ lib/drb/weakidconv.rb
+ ]
+ spec.require_paths = ["lib"]
+
+ spec.add_dependency "ruby2_keywords"
+end
diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb
index ab352afa1d..3e23213911 100644
--- a/lib/drb/drb.rb
+++ b/lib/drb/drb.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
#
# = drb/drb.rb
#
@@ -46,9 +47,9 @@
# Translation of presentation on Ruby by Masatoshi Seki.
require 'socket'
-require 'thread'
-require 'fcntl'
-require 'drb/eq'
+require 'io/wait'
+require 'monitor'
+require_relative 'eq'
#
# == Overview
@@ -159,8 +160,6 @@ require 'drb/eq'
# # The object that handles requests on the server
# FRONT_OBJECT=TimeServer.new
#
-# $SAFE = 1 # disable eval() and friends
-#
# DRb.start_service(URI, FRONT_OBJECT)
# # Wait for the drb server thread to finish before exiting.
# DRb.thread.join
@@ -234,7 +233,7 @@ require 'drb/eq'
# def get_logger(name)
# if !@loggers.has_key? name
# # make the filename safe, then declare it to be so
-# fname = name.gsub(/[.\/\\\:]/, "_").untaint
+# fname = name.gsub(/[.\/\\\:]/, "_")
# @loggers[name] = Logger.new(name, @basedir + "/" + fname)
# end
# return @loggers[name]
@@ -244,8 +243,6 @@ require 'drb/eq'
#
# FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
#
-# $SAFE = 1 # disable eval() and friends
-#
# DRb.start_service(URI, FRONT_OBJECT)
# DRb.thread.join
#
@@ -285,10 +282,7 @@ require 'drb/eq'
# ro.instance_eval("`rm -rf *`")
#
# The dangers posed by instance_eval and friends are such that a
-# DRbServer should generally be run with $SAFE set to at least
-# level 1. This will disable eval() and related calls on strings
-# passed across the wire. The sample usage code given above follows
-# this practice.
+# DRbServer should only be used when clients are trusted.
#
# A DRbServer can be configured with an access control list to
# selectively allow or deny access from specified IP addresses. The
@@ -360,7 +354,7 @@ module DRb
# drb remains valid only while that object instance remains alive
# within the server runtime.
#
- # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb
+ # For alternative mechanisms, see DRb::TimerIdConv in drb/timeridconv.rb
# and DRbNameIdConv in sample/name.rb in the full drb distribution.
class DRbIdConv
@@ -377,7 +371,12 @@ module DRb
# This implementation returns the object's __id__ in the local
# object space.
def to_id(obj)
- obj.nil? ? nil : obj.__id__
+ case obj
+ when Object
+ obj.nil? ? nil : obj.__id__
+ when BasicObject
+ obj.__id__
+ end
end
end
@@ -560,7 +559,14 @@ module DRb
end
def dump(obj, error=false) # :nodoc:
- obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
+ case obj
+ when DRbUndumped
+ obj = make_proxy(obj, error)
+ when Object
+ # nothing
+ else
+ obj = make_proxy(obj, error)
+ end
begin
str = Marshal::dump(obj)
rescue
@@ -588,16 +594,9 @@ module DRb
raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
DRb.mutex.synchronize do
begin
- save = Thread.current[:drb_untaint]
- Thread.current[:drb_untaint] = []
Marshal::load(str)
rescue NameError, ArgumentError
DRbUnknown.new($!, str)
- ensure
- Thread.current[:drb_untaint].each do |x|
- x.untaint
- end
- Thread.current[:drb_untaint] = save
end
end
end
@@ -745,7 +744,7 @@ module DRb
end
end
if first && (config[:auto_load] != false)
- auto_load(uri, config)
+ auto_load(uri)
return open(uri, config, false)
end
raise DRbBadURI, 'can\'t parse uri:' + uri
@@ -769,7 +768,7 @@ module DRb
end
end
if first && (config[:auto_load] != false)
- auto_load(uri, config)
+ auto_load(uri)
return open_server(uri, config, false)
end
raise DRbBadURI, 'can\'t parse uri:' + uri
@@ -792,15 +791,15 @@ module DRb
end
end
if first && (config[:auto_load] != false)
- auto_load(uri, config)
+ auto_load(uri)
return uri_option(uri, config, false)
end
raise DRbBadURI, 'can\'t parse uri:' + uri
end
module_function :uri_option
- def auto_load(uri, config) # :nodoc:
- if uri =~ /^drb([a-z0-9]+):/
+ def auto_load(uri) # :nodoc:
+ if /\Adrb([a-z0-9]+):/ =~ uri
require("drb/#{$1}") rescue nil
end
end
@@ -816,13 +815,13 @@ module DRb
# :stopdoc:
private
def self.parse_uri(uri)
- if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
+ if /\Adruby:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri
host = $1
port = $2.to_i
option = $4
[host, port, option]
else
- raise(DRbBadScheme, uri) unless uri =~ /^druby:/
+ raise(DRbBadScheme, uri) unless uri.start_with?('druby:')
raise(DRbBadURI, 'can\'t parse uri:' + uri)
end
end
@@ -837,8 +836,6 @@ module DRb
# URI protocols.
def self.open(uri, config)
host, port, = parse_uri(uri)
- host.untaint
- port.untaint
soc = TCPSocket.open(host, port)
self.new(uri, soc, config)
end
@@ -847,7 +844,11 @@ module DRb
def self.getservername
host = Socket::gethostname
begin
- Socket::gethostbyname(host)[0]
+ Socket::getaddrinfo(host, nil,
+ Socket::AF_UNSPEC,
+ Socket::SOCK_STREAM,
+ 0,
+ Socket::AI_PASSIVE)[0][3]
rescue
'localhost'
end
@@ -949,6 +950,7 @@ module DRb
# returned by #open or by #accept, then it closes this particular
# client-server session.
def close
+ shutdown
if @socket
@socket.close
@socket = nil
@@ -957,14 +959,8 @@ module DRb
end
def close_shutdown_pipe
- if @shutdown_pipe_r && !@shutdown_pipe_r.closed?
- @shutdown_pipe_r.close
- @shutdown_pipe_r = nil
- end
- if @shutdown_pipe_w && !@shutdown_pipe_w.closed?
- @shutdown_pipe_w.close
- @shutdown_pipe_w = nil
- end
+ @shutdown_pipe_w.close
+ @shutdown_pipe_r.close
end
private :close_shutdown_pipe
@@ -997,13 +993,13 @@ module DRb
# Graceful shutdown
def shutdown
- @shutdown_pipe_w.close if @shutdown_pipe_w && !@shutdown_pipe_w.closed?
+ @shutdown_pipe_w.close
end
# Check to see if this connection is alive.
def alive?
return false unless @socket
- if IO.select([@socket], nil, nil, 0)
+ if @socket.to_io.wait_readable(0)
close
return false
end
@@ -1012,7 +1008,8 @@ module DRb
def set_sockopt(soc) # :nodoc:
soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
- soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ rescue IOError, Errno::ECONNRESET, Errno::EINVAL
+ # closed/shutdown socket, ignore error
end
end
@@ -1055,9 +1052,6 @@ module DRb
if DRb.here?(uri)
obj = DRb.to_obj(ref)
- if ((! obj.tainted?) && Thread.current[:drb_untaint])
- Thread.current[:drb_untaint].push(obj)
- end
return obj
end
@@ -1094,7 +1088,14 @@ module DRb
def initialize(obj, uri=nil)
@uri = nil
@ref = nil
- if obj.nil?
+ case obj
+ when Object
+ is_nil = obj.nil?
+ when BasicObject
+ is_nil = false
+ end
+
+ if is_nil
return if uri.nil?
@uri, option = DRbProtocol.uri_option(uri, DRb.config)
@ref = DRbURIOption.new(option) unless option.nil?
@@ -1130,7 +1131,7 @@ module DRb
end
# Routes method calls to the referenced remote object.
- def method_missing(msg_id, *a, &b)
+ ruby2_keywords def method_missing(msg_id, *a, &b)
if DRb.here?(@uri)
obj = DRb.to_obj(@ref)
DRb.current_server.check_insecure_method(obj, msg_id)
@@ -1173,7 +1174,7 @@ module DRb
bt = []
result.backtrace.each do |x|
break if /`__send__'$/ =~ x
- if /^\(druby:\/\// =~ x
+ if /\A\(druby:\/\// =~ x
bt.push(x)
else
bt.push(prefix + x)
@@ -1194,6 +1195,54 @@ module DRb
end
end
+ class ThreadObject
+ include MonitorMixin
+
+ def initialize(&blk)
+ super()
+ @wait_ev = new_cond
+ @req_ev = new_cond
+ @res_ev = new_cond
+ @status = :wait
+ @req = nil
+ @res = nil
+ @thread = Thread.new(self, &blk)
+ end
+
+ def alive?
+ @thread.alive?
+ end
+
+ def kill
+ @thread.kill
+ @thread.join
+ end
+
+ def method_missing(msg, *arg, &blk)
+ synchronize do
+ @wait_ev.wait_until { @status == :wait }
+ @req = [msg] + arg
+ @status = :req
+ @req_ev.broadcast
+ @res_ev.wait_until { @status == :res }
+ value = @res
+ @req = @res = nil
+ @status = :wait
+ @wait_ev.broadcast
+ return value
+ end
+ end
+
+ def _execute()
+ synchronize do
+ @req_ev.wait_until { @status == :req }
+ @res = yield(@req)
+ @status = :res
+ @res_ev.signal
+ end
+ end
+ end
+
# Class handling the connection between a DRbObject and the
# server the real object lives on.
#
@@ -1205,26 +1254,50 @@ module DRb
# not normally need to deal with it directly.
class DRbConn
POOL_SIZE = 16 # :nodoc:
- @mutex = Mutex.new
- @pool = []
- def self.open(remote_uri) # :nodoc:
- begin
- conn = nil
-
- @mutex.synchronize do
- #FIXME
- new_pool = []
- @pool.each do |c|
- if conn.nil? and c.uri == remote_uri
- conn = c if c.alive?
+ def self.make_pool
+ ThreadObject.new do |queue|
+ pool = []
+ while true
+ queue._execute do |message|
+ case(message[0])
+ when :take then
+ remote_uri = message[1]
+ conn = nil
+ new_pool = []
+ pool.each do |c|
+ if conn.nil? and c.uri == remote_uri
+ conn = c if c.alive?
+ else
+ new_pool.push c
+ end
+ end
+ pool = new_pool
+ conn
+ when :store then
+ conn = message[1]
+ pool.unshift(conn)
+ pool.pop.close while pool.size > POOL_SIZE
+ conn
else
- new_pool.push c
+ nil
end
end
- @pool = new_pool
end
+ end
+ end
+ @pool_proxy = nil
+
+ def self.stop_pool
+ @pool_proxy&.kill
+ @pool_proxy = nil
+ end
+ def self.open(remote_uri) # :nodoc:
+ begin
+ @pool_proxy = make_pool unless @pool_proxy&.alive?
+
+ conn = @pool_proxy.take(remote_uri)
conn = self.new(remote_uri) unless conn
succ, result = yield(conn)
return succ, result
@@ -1232,10 +1305,7 @@ module DRb
ensure
if conn
if succ
- @mutex.synchronize do
- @pool.unshift(conn)
- @pool.pop.close while @pool.size > POOL_SIZE
- end
+ @pool_proxy.store(conn)
else
conn.close
end
@@ -1281,9 +1351,8 @@ module DRb
@@idconv = DRbIdConv.new
@@secondary_server = nil
@@argc_limit = 256
- @@load_limit = 256 * 102400
+ @@load_limit = 0xffffffff
@@verbose = false
- @@safe_level = 0
# Set the default value for the :argc_limit option.
#
@@ -1313,13 +1382,6 @@ module DRb
@@idconv = idconv
end
- # Set the default safe level to +level+. The default safe level is 0
- #
- # See #new for more information.
- def self.default_safe_level(level)
- @@safe_level = level
- end
-
# Set the default value of the :verbose option.
#
# See #new(). The initial default value is false.
@@ -1339,7 +1401,6 @@ module DRb
:tcp_acl => @@acl,
:load_limit => @@load_limit,
:argc_limit => @@argc_limit,
- :safe_level => @@safe_level
}
default_config.update(hash)
end
@@ -1373,10 +1434,6 @@ module DRb
# :argc_limit :: the maximum number of arguments to a remote
# method accepted by the server. Defaults to
# 256.
- # :safe_level :: The safe level of the DRbServer. The attribute
- # sets $SAFE for methods performed in the main_loop.
- # Defaults to 0.
- #
# The default values of these options can be modified on
# a class-wide basis by the class methods #default_argc_limit,
# #default_load_limit, #default_acl, #default_id_conv,
@@ -1408,7 +1465,6 @@ module DRb
@front = front
@idconv = @config[:idconv]
- @safe_level = @config[:safe_level]
@grp = ThreadGroup.new
@thread = run
@@ -1435,12 +1491,6 @@ module DRb
# The configuration of this DRbServer
attr_reader :config
- # The safe level for this server. This is a number corresponding to
- # $SAFE.
- #
- # The default safe_level is 0
- attr_reader :safe_level
-
# Set whether to operate in verbose mode.
#
# In verbose mode, failed calls are logged to stdout.
@@ -1467,12 +1517,7 @@ module DRb
if Thread.current['DRb'] && Thread.current['DRb']['server'] == self
Thread.current['DRb']['stop_service'] = true
else
- if @protocol.respond_to? :shutdown
- @protocol.shutdown
- else
- @thread.kill # xxx: Thread#kill
- end
- @thread.join
+ shutdown
end
end
@@ -1491,6 +1536,18 @@ module DRb
private
+ def shutdown
+ current = Thread.current
+ if @protocol.respond_to? :shutdown
+ @protocol.shutdown
+ else
+ [@thread, *@grp.list].each { |thread|
+ thread.kill unless thread == current # xxx: Thread#kill
+ }
+ end
+ @thread.join unless @thread == current
+ end
+
##
# Starts the DRb main loop in a new thread.
@@ -1520,9 +1577,9 @@ module DRb
# Coerce an object to a string, providing our own representation if
# to_s is not defined for the object.
def any_to_s(obj)
- obj.to_s + ":#{obj.class}"
+ "#{obj}:#{obj.class}"
rescue
- sprintf("#<%s:0x%lx>", obj.class, obj.__id__)
+ Kernel.instance_method(:to_s).bind_call(obj)
end
# Check that a method is callable via dRuby.
@@ -1538,14 +1595,27 @@ module DRb
raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
- if obj.private_methods.include?(msg_id)
- desc = any_to_s(obj)
- raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
- elsif obj.protected_methods.include?(msg_id)
- desc = any_to_s(obj)
- raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
+ case obj
+ when Object
+ if obj.private_methods.include?(msg_id)
+ desc = any_to_s(obj)
+ raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
+ elsif obj.protected_methods.include?(msg_id)
+ desc = any_to_s(obj)
+ raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
+ else
+ true
+ end
else
- true
+ if Kernel.instance_method(:private_methods).bind(obj).call.include?(msg_id)
+ desc = any_to_s(obj)
+ raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
+ elsif Kernel.instance_method(:protected_methods).bind(obj).call.include?(msg_id)
+ desc = any_to_s(obj)
+ raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
+ else
+ true
+ end
end
end
public :check_insecure_method
@@ -1553,7 +1623,6 @@ module DRb
class InvokeMethod # :nodoc:
def initialize(drb_server, client)
@drb_server = drb_server
- @safe_level = drb_server.safe_level
@client = client
end
@@ -1562,34 +1631,22 @@ module DRb
@succ = false
setup_message
- if $SAFE < @safe_level
- info = Thread.current['DRb']
- if @block
- @result = Thread.new {
- Thread.current['DRb'] = info
- $SAFE = @safe_level
- perform_with_block
- }.value
- else
- @result = Thread.new {
- Thread.current['DRb'] = info
- $SAFE = @safe_level
- perform_without_block
- }.value
- end
+ if @block
+ @result = perform_with_block
else
- if @block
- @result = perform_with_block
- else
- @result = perform_without_block
- end
+ @result = perform_without_block
end
@succ = true
- if @msg_id == :to_ary && @result.class == Array
- @result = DRbArray.new(@result)
+ case @result
+ when Array
+ if @msg_id == :to_ary
+ @result = DRbArray.new(@result)
+ end
end
return @succ, @result
- rescue StandardError, ScriptError, Interrupt
+ rescue NoMemoryError, SystemExit, SystemStackError, SecurityError
+ raise
+ rescue Exception
@result = $!
return @succ, @result
end
@@ -1627,11 +1684,22 @@ module DRb
end
- require 'drb/invokemethod'
+ require_relative 'invokemethod'
class InvokeMethod
include InvokeMethod18Mixin
end
+ def error_print(exception)
+ exception.backtrace.inject(true) do |first, x|
+ if first
+ $stderr.puts "#{x}: #{exception} (#{exception.class})"
+ else
+ $stderr.puts "\tfrom #{x}"
+ end
+ false
+ end
+ end
+
# The main loop performed by a DRbServer's internal thread.
#
# Accepts a connection from a client, and starts up its own
@@ -1655,17 +1723,17 @@ module DRb
succ = false
invoke_method = InvokeMethod.new(self, client)
succ, result = invoke_method.perform
- if !succ && verbose
- p result
- result.backtrace.each do |x|
- puts x
- end
+ error_print(result) if !succ && verbose
+ unless DRbConnError === result && result.message == 'connection closed'
+ client.send_reply(succ, result)
end
- client.send_reply(succ, result) rescue nil
+ rescue Exception => e
+ error_print(e) if verbose
ensure
client.close unless succ
if Thread.current['DRb']['stop_service']
- Thread.new { stop_service }
+ shutdown
+ break
end
break unless succ
end
@@ -1816,7 +1884,7 @@ module DRb
end
module_function :install_acl
- @mutex = Mutex.new
+ @mutex = Thread::Mutex.new
def mutex # :nodoc:
@mutex
end
@@ -1846,6 +1914,11 @@ module DRb
# Removes +server+ from the list of registered servers.
def remove_server(server)
@server.delete(server.uri)
+ mutex.synchronize do
+ if @primary_server == server
+ @primary_server = nil
+ end
+ end
end
module_function :remove_server
diff --git a/lib/drb/eq.rb b/lib/drb/eq.rb
index 553f30c598..15ca5cae42 100644
--- a/lib/drb/eq.rb
+++ b/lib/drb/eq.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
module DRb
class DRbObject # :nodoc:
def ==(other)
diff --git a/lib/drb/extserv.rb b/lib/drb/extserv.rb
index 327b553a25..a93d5d1576 100644
--- a/lib/drb/extserv.rb
+++ b/lib/drb/extserv.rb
@@ -1,9 +1,10 @@
+# frozen_string_literal: false
=begin
external service
Copyright (c) 2000,2002 Masatoshi SEKI
=end
-require 'drb/drb'
+require_relative 'drb'
require 'monitor'
module DRb
diff --git a/lib/drb/extservm.rb b/lib/drb/extservm.rb
index 8a7fc316af..040e4e3e08 100644
--- a/lib/drb/extservm.rb
+++ b/lib/drb/extservm.rb
@@ -1,10 +1,10 @@
+# frozen_string_literal: false
=begin
external service manager
Copyright (c) 2000 Masatoshi SEKI
=end
-require 'drb/drb'
-require 'thread'
+require_relative 'drb'
require 'monitor'
module DRb
@@ -27,7 +27,7 @@ module DRb
@cond = new_cond
@servers = {}
@waiting = []
- @queue = Queue.new
+ @queue = Thread::Queue.new
@thread = invoke_thread
@uri = nil
end
@@ -37,7 +37,7 @@ module DRb
synchronize do
while true
server = @servers[name]
- return server if server && server.alive?
+ return server if server && server.alive? # server may be `false'
invoke_service(name)
@cond.wait
end
@@ -61,8 +61,7 @@ module DRb
private
def invoke_thread
Thread.new do
- while true
- name = @queue.pop
+ while name = @queue.pop
invoke_service_command(name, @@command[name])
end
end
diff --git a/lib/drb/gw.rb b/lib/drb/gw.rb
index b3568ab08d..65a525476e 100644
--- a/lib/drb/gw.rb
+++ b/lib/drb/gw.rb
@@ -1,4 +1,5 @@
-require 'drb/drb'
+# frozen_string_literal: false
+require_relative 'drb'
require 'monitor'
module DRb
diff --git a/lib/drb/invokemethod.rb b/lib/drb/invokemethod.rb
index 71ebec11f6..0fae6d52b6 100644
--- a/lib/drb/invokemethod.rb
+++ b/lib/drb/invokemethod.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
# for ruby-1.8.0
module DRb # :nodoc: all
diff --git a/lib/drb/observer.rb b/lib/drb/observer.rb
index cab9ebc60b..0fb7301edf 100644
--- a/lib/drb/observer.rb
+++ b/lib/drb/observer.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'observer'
module DRb
@@ -12,7 +13,7 @@ module DRb
if defined? @observer_peers
@observer_peers.each do |observer, method|
begin
- observer.send(method, *arg)
+ observer.__send__(method, *arg)
rescue
delete_observer(observer)
end
diff --git a/lib/drb/ssl.rb b/lib/drb/ssl.rb
index efd8271a78..54ab1ef395 100644
--- a/lib/drb/ssl.rb
+++ b/lib/drb/ssl.rb
@@ -1,6 +1,7 @@
+# frozen_string_literal: false
require 'socket'
require 'openssl'
-require 'drb/drb'
+require_relative 'drb'
require 'singleton'
module DRb
@@ -161,7 +162,7 @@ module DRb
return
end
- rsa = OpenSSL::PKey::RSA.new(1024){|p, n|
+ rsa = OpenSSL::PKey::RSA.new(2048){|p, n|
next unless self[:verbose]
case p
when 0; $stderr.putc "." # BN_generate_prime
@@ -195,7 +196,7 @@ module DRb
if comment = self[:SSLCertComment]
cert.add_extension(ef.create_extension("nsComment", comment))
end
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+ cert.sign(rsa, "SHA256")
@cert = cert
@pkey = rsa
@@ -225,13 +226,13 @@ module DRb
#
# Raises DRbBadScheme or DRbBadURI if +uri+ is not matching or malformed
def self.parse_uri(uri) # :nodoc:
- if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/
+ if /\Adrbssl:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri
host = $1
port = $2.to_i
option = $4
[host, port, option]
else
- raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/
+ raise(DRbBadScheme, uri) unless uri.start_with?('drbssl:')
raise(DRbBadURI, 'can\'t parse uri:' + uri)
end
end
@@ -247,8 +248,6 @@ module DRb
# configuration. Either a Hash or DRb::DRbSSLSocket::SSLConfig
def self.open(uri, config)
host, port, = parse_uri(uri)
- host.untaint
- port.untaint
soc = TCPSocket.open(host, port)
ssl_conf = SSLConfig::new(config)
ssl_conf.setup_ssl_context
@@ -335,7 +334,7 @@ module DRb
end
self.class.new(uri, ssl, @config, true)
rescue OpenSSL::SSL::SSLError
- warn("#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})") if @config[:verbose]
+ warn("#{$!.message} (#{$!.class})", uplevel: 0) if @config[:verbose]
retry
end
end
diff --git a/lib/drb/timeridconv.rb b/lib/drb/timeridconv.rb
index 423b4563fa..3ead98a7f2 100644
--- a/lib/drb/timeridconv.rb
+++ b/lib/drb/timeridconv.rb
@@ -1,4 +1,5 @@
-require 'drb/drb'
+# frozen_string_literal: false
+require_relative 'drb'
require 'monitor'
module DRb
@@ -23,7 +24,7 @@ module DRb
@gc = {}
@renew = {}
@keeping = keeping
- @expires = Time.now + @keeping
+ @expires = nil
end
def add(obj)
@@ -31,18 +32,16 @@ module DRb
rotate
key = obj.__id__
@renew[key] = obj
+ invoke_keeper
return key
end
end
- def fetch(key, dv=@sentinel)
+ def fetch(key)
synchronize do
rotate
obj = peek(key)
- if obj == @sentinel
- return dv unless dv == @sentinel
- raise InvalidIndexError
- end
+ raise InvalidIndexError if obj == @sentinel
@renew[key] = obj # KeepIt
return obj
end
@@ -50,25 +49,28 @@ module DRb
private
def peek(key)
- synchronize do
- return @renew.fetch(key) { @gc.fetch(key, @sentinel) }
- end
+ return @renew.fetch(key) { @gc.fetch(key, @sentinel) }
end
- def rotate
- synchronize do
- return if @expires > Time.now
- @gc = @renew # GCed
- @renew = {}
- @expires = Time.now + @keeping
- end
+ def invoke_keeper
+ return if @expires
+ @expires = Time.now + @keeping
+ on_gc
+ end
+
+ def on_gc
+ return unless Thread.main.alive?
+ return if @expires.nil?
+ Thread.new { rotate } if @expires < Time.now
+ ObjectSpace.define_finalizer(Object.new) {on_gc}
end
- def keeper
- Thread.new do
- loop do
- rotate
- sleep(@keeping)
+ def rotate
+ synchronize do
+ if @expires &.< Time.now
+ @gc = @renew # GCed
+ @renew = {}
+ @expires = @gc.empty? ? nil : Time.now + @keeping
end
end
end
diff --git a/lib/drb/unix.rb b/lib/drb/unix.rb
index 3fb8d0ecce..1629ad3bcd 100644
--- a/lib/drb/unix.rb
+++ b/lib/drb/unix.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: false
require 'socket'
-require 'drb/drb'
+require_relative 'drb'
require 'tmpdir'
raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer)
@@ -14,19 +15,18 @@ module DRb
class DRbUNIXSocket < DRbTCPSocket
# :stopdoc:
def self.parse_uri(uri)
- if /^drbunix:(.*?)(\?(.*))?$/ =~ uri
+ if /\Adrbunix:(.*?)(\?(.*))?\z/ =~ uri
filename = $1
option = $3
[filename, option]
else
- raise(DRbBadScheme, uri) unless uri =~ /^drbunix:/
+ raise(DRbBadScheme, uri) unless uri.start_with?('drbunix:')
raise(DRbBadURI, 'can\'t parse uri:' + uri)
end
end
def self.open(uri, config)
filename, = parse_uri(uri)
- filename.untaint
soc = UNIXSocket.open(filename)
self.new(uri, soc, config)
end
@@ -94,6 +94,7 @@ module DRb
public
def close
return unless @socket
+ shutdown # DRbProtocol#shutdown
path = @socket.path if @server_mode
@socket.close
File.unlink(path) if @server_mode
@@ -108,7 +109,7 @@ module DRb
end
def set_sockopt(soc)
- soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ # no-op for now
end
end
diff --git a/lib/drb/version.rb b/lib/drb/version.rb
new file mode 100644
index 0000000000..efaccf0319
--- /dev/null
+++ b/lib/drb/version.rb
@@ -0,0 +1,3 @@
+module DRb
+ VERSION = "2.1.0"
+end
diff --git a/lib/drb/weakidconv.rb b/lib/drb/weakidconv.rb
new file mode 100644
index 0000000000..ecf0bf515f
--- /dev/null
+++ b/lib/drb/weakidconv.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: false
+require_relative 'drb'
+require 'monitor'
+
+module DRb
+
+ # To use WeakIdConv:
+ #
+ # DRb.start_service(nil, nil, {:idconv => DRb::WeakIdConv.new})
+
+ class WeakIdConv < DRbIdConv
+ class WeakSet
+ include MonitorMixin
+ def initialize
+ super()
+ @immutable = {}
+ @map = ObjectSpace::WeakMap.new
+ end
+
+ def add(obj)
+ synchronize do
+ begin
+ @map[obj] = self
+ rescue ArgumentError
+ @immutable[obj.__id__] = obj
+ end
+ return obj.__id__
+ end
+ end
+
+ def fetch(ref)
+ synchronize do
+ @immutable.fetch(ref) {
+ @map.each { |key, _|
+ return key if key.__id__ == ref
+ }
+ raise RangeError.new("invalid reference")
+ }
+ end
+ end
+ end
+
+ def initialize()
+ super()
+ @weak_set = WeakSet.new
+ end
+
+ def to_obj(ref) # :nodoc:
+ return super if ref.nil?
+ @weak_set.fetch(ref)
+ end
+
+ def to_id(obj) # :nodoc:
+ return @weak_set.add(obj)
+ end
+ end
+end
+
+# DRb.install_id_conv(WeakIdConv.new)