diff options
Diffstat (limited to 'lib/drb')
| -rw-r--r-- | lib/drb/acl.rb | 11 | ||||
| -rw-r--r-- | lib/drb/drb.gemspec | 43 | ||||
| -rw-r--r-- | lib/drb/drb.rb | 345 | ||||
| -rw-r--r-- | lib/drb/eq.rb | 1 | ||||
| -rw-r--r-- | lib/drb/extserv.rb | 3 | ||||
| -rw-r--r-- | lib/drb/extservm.rb | 11 | ||||
| -rw-r--r-- | lib/drb/gw.rb | 3 | ||||
| -rw-r--r-- | lib/drb/invokemethod.rb | 1 | ||||
| -rw-r--r-- | lib/drb/observer.rb | 3 | ||||
| -rw-r--r-- | lib/drb/ssl.rb | 15 | ||||
| -rw-r--r-- | lib/drb/timeridconv.rb | 46 | ||||
| -rw-r--r-- | lib/drb/unix.rb | 11 | ||||
| -rw-r--r-- | lib/drb/version.rb | 3 | ||||
| -rw-r--r-- | lib/drb/weakidconv.rb | 59 |
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) |
