dizby 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +10 -0
  3. data/LICENSE +24 -0
  4. data/dizby.gemspec +27 -0
  5. data/lib/dizby.rb +12 -0
  6. data/lib/dizby/access/control_list.rb +78 -0
  7. data/lib/dizby/access/entry.rb +61 -0
  8. data/lib/dizby/access/insecure.rb +30 -0
  9. data/lib/dizby/access/list.rb +10 -0
  10. data/lib/dizby/converter/simple.rb +12 -0
  11. data/lib/dizby/converter/timed.rb +23 -0
  12. data/lib/dizby/distributed/array.rb +30 -0
  13. data/lib/dizby/distributed/object.rb +44 -0
  14. data/lib/dizby/distributed/proxy.rb +41 -0
  15. data/lib/dizby/distributed/semi_proxy.rb +26 -0
  16. data/lib/dizby/distributed/undumpable.rb +8 -0
  17. data/lib/dizby/distributed/unknown.rb +57 -0
  18. data/lib/dizby/error.rb +29 -0
  19. data/lib/dizby/protocol/basic.rb +31 -0
  20. data/lib/dizby/protocol/manager.rb +62 -0
  21. data/lib/dizby/protocol/refined.rb +23 -0
  22. data/lib/dizby/protocols/dead.rb +24 -0
  23. data/lib/dizby/protocols/secure.rb +87 -0
  24. data/lib/dizby/protocols/tcp.rb +98 -0
  25. data/lib/dizby/protocols/unix.rb +95 -0
  26. data/lib/dizby/server/abstract.rb +42 -0
  27. data/lib/dizby/server/basic.rb +101 -0
  28. data/lib/dizby/server/registration.rb +23 -0
  29. data/lib/dizby/service.rb +64 -0
  30. data/lib/dizby/stream/client.rb +26 -0
  31. data/lib/dizby/stream/connection.rb +63 -0
  32. data/lib/dizby/stream/messenger.rb +28 -0
  33. data/lib/dizby/stream/query_ref.rb +13 -0
  34. data/lib/dizby/stream/readable.rb +65 -0
  35. data/lib/dizby/stream/writable.rb +37 -0
  36. data/lib/dizby/tunnel/abstract.rb +52 -0
  37. data/lib/dizby/tunnel/basic.rb +21 -0
  38. data/lib/dizby/tunnel/basic_spawn.rb +50 -0
  39. data/lib/dizby/tunnel/bidirectional_strategy.rb +29 -0
  40. data/lib/dizby/tunnel/factory.rb +29 -0
  41. data/lib/dizby/tunnel/local_strategy.rb +24 -0
  42. data/lib/dizby/tunnel/spawn_command.rb +49 -0
  43. data/lib/dizby/tunnel/spawned.rb +43 -0
  44. data/lib/dizby/tunnel/tunnelable_local.rb +8 -0
  45. data/lib/dizby/tunnel/tunnelable_remote.rb +21 -0
  46. data/lib/dizby/utility/classic_access.rb +25 -0
  47. data/lib/dizby/utility/configurable.rb +25 -0
  48. data/lib/dizby/utility/delegator.rb +28 -0
  49. data/lib/dizby/utility/io_barrier.rb +18 -0
  50. data/lib/dizby/utility/log.rb +23 -0
  51. data/lib/dizby/utility/monitor.rb +9 -0
  52. data/lib/dizby/utility/polymorphic_delegated.rb +58 -0
  53. data/lib/dizby/utility/self_pipe.rb +12 -0
  54. data/lib/dizby/utility/semi_built.rb +17 -0
  55. data/lib/dizby/utility/string.rb +8 -0
  56. data/lib/dizby/utility/timed_collection.rb +39 -0
  57. data/lib/dizby/utility/timed_state.rb +41 -0
  58. data/lib/dizby/version.rb +4 -0
  59. data/lib/dizby/worker/connection.rb +44 -0
  60. data/lib/dizby/worker/invoke_method.rb +55 -0
  61. data/lib/dizby/worker/server.rb +39 -0
  62. metadata +146 -0
@@ -0,0 +1,8 @@
1
+
2
+ module Dizby
3
+ module UndumpableObject
4
+ def _dump(_)
5
+ fail TypeError, 'can\'t dump'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,57 @@
1
+
2
+ require 'dizby/error'
3
+
4
+ module Dizby
5
+ class UnknownObject
6
+ def initialize(err, buf)
7
+ case err.to_s
8
+ when /uninitialized constant (\S+)/
9
+ @name = $~[1]
10
+ when %r{undefined class/module (\S+)}
11
+ @name = $~[1]
12
+ else
13
+ @name = nil
14
+ end
15
+
16
+ @buf = buf
17
+ end
18
+
19
+ attr_reader :name, :buf
20
+
21
+ def self._load(str)
22
+ Marshal.load(str)
23
+ rescue NameError, ArgumentError
24
+ UnknownObject.new($!, str)
25
+ end
26
+
27
+ def _dump(_)
28
+ Marshal.dump(@buf)
29
+ end
30
+
31
+ def reload
32
+ self.class._load @buf
33
+ end
34
+
35
+ def exception
36
+ UnknownObjectError.new self
37
+ end
38
+ end
39
+
40
+ class UnknownObjectError < DistributedError
41
+ def initialize(unknown)
42
+ @unknown = unknown
43
+ super unknown.name
44
+ end
45
+
46
+ # give access to the UnknownObject class
47
+ attr_reader :unknown
48
+
49
+ def self._load(str)
50
+ Marshal.load(str)
51
+ end
52
+
53
+ def _dump(_)
54
+ Marshal.dump(@unknown)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,29 @@
1
+
2
+ module Dizby
3
+ class DistributedError < RuntimeError; end
4
+ class ConnectionError < DistributedError; end
5
+ class ServerNotFound < DistributedError; end
6
+ class BadURI < DistributedError; end
7
+ class BadScheme < DistributedError; end
8
+ class LocalServerShutdown < DistributedError; end
9
+ class RemoteServerShutdown < ConnectionError; end
10
+ class SpawnError < DistributedError; end
11
+
12
+ class NonAcceptingServer < DistributedError
13
+ def initialize(server)
14
+ @server = server
15
+ end
16
+
17
+ attr_reader :server
18
+ end
19
+
20
+ class RemoteDistributedError < DistributedError
21
+ def initialize(error)
22
+ @reason = error.class.to_s
23
+ super("#{error.message} (#{@reason})")
24
+ set_backtrace(error.backtrace)
25
+ end
26
+
27
+ attr_reader :reason
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+
2
+ require 'dizby/protocol/manager'
3
+ require 'dizby/protocol/refined'
4
+
5
+ module Dizby
6
+ module BasicProtocol
7
+ module ClassMethods
8
+ attr_reader :scheme
9
+
10
+ def get_refinement(type)
11
+ instance_variable_get(:"@#{type}_refined")
12
+ rescue NameError
13
+ nil
14
+ end
15
+
16
+ protected
17
+
18
+ attr_writer :scheme
19
+
20
+ def refine(type, regex, &block)
21
+ refined = RefinedProtocol.new(regex, &block)
22
+ instance_variable_set(:"@#{type}_refined", refined)
23
+ end
24
+ end
25
+
26
+ def self.included(base)
27
+ base.extend ClassMethods
28
+ ProtocolManager.add_protocol(base)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,62 @@
1
+
2
+ require 'dizby/error'
3
+
4
+ module Dizby
5
+ class ProtocolManager
6
+ # TODO: all required portions of a Protocol class need to be documented
7
+ class << self
8
+ def add_protocol(klass)
9
+ @protocols << klass
10
+ end
11
+
12
+ def open_client(server, uri)
13
+ call_refined(uri, :client, server)
14
+ end
15
+
16
+ def open_server(uri, front, config)
17
+ call_refined(uri, :server, front, config)
18
+ end
19
+
20
+ def spawn_server(server, command, uri)
21
+ call_refined(uri, :spawn, server, command)
22
+ end
23
+
24
+ private
25
+
26
+ def call_refined(uri, refiner, *base_args)
27
+ klass = get_protocol(uri)
28
+ refined = refine_protocol(klass, refiner)
29
+ args = get_arguments(refined, uri)
30
+ refined.call(*base_args, args)
31
+ end
32
+
33
+ def get_protocol(uri)
34
+ scheme = '' if uri.empty?
35
+ scheme ||= uri.split(':').first
36
+ fail BadScheme, "can't retrieve scheme: #{uri}" unless scheme
37
+
38
+ protocol = @protocols.find { |klass| klass.scheme == scheme }
39
+ protocol || fail(BadScheme, "scheme not found: #{scheme}")
40
+ end
41
+
42
+ def refine_protocol(protocol, refinement)
43
+ refined = protocol.get_refinement(refinement)
44
+
45
+ unless refined
46
+ fail NotImplementedError,
47
+ "#{refinement} refinement not supported for #{protocol}"
48
+ end
49
+
50
+ refined
51
+ end
52
+
53
+ def get_arguments(refined, uri)
54
+ fail BadURI, "can't parse uri: #{uri}" unless refined.regex =~ uri
55
+
56
+ $~[1..-1]
57
+ end
58
+ end
59
+
60
+ @protocols = []
61
+ end
62
+ end
@@ -0,0 +1,23 @@
1
+
2
+ module Dizby
3
+ PROTOCOL_REGEX = {
4
+ user: '(?:(.+?)@)',
5
+ host: '(.*?)',
6
+ port: '(?::(\d+))',
7
+ file: '(.+?)',
8
+ query: '(?:\?(.*?))'
9
+ }
10
+
11
+ class RefinedProtocol
12
+ def initialize(regex, &block)
13
+ @regex = /^#{format(regex, Dizby::PROTOCOL_REGEX)}$/
14
+ @block = block
15
+ end
16
+
17
+ attr_reader :regex
18
+
19
+ def call(*args)
20
+ @block.call(*args)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+
2
+ require 'dizby/protocol/basic'
3
+ require 'dizby/server/abstract'
4
+
5
+ module Dizby
6
+ class DeadProtocol
7
+ include BasicProtocol
8
+
9
+ self.scheme = ''
10
+
11
+ refine(:server, '') do |_, config|
12
+ fail NonAcceptingServer, Server.new(config)
13
+ end
14
+
15
+ class Server < AbstractServer
16
+ # A DeadProtocol server doesn't allow backwards connections
17
+ # therefore, making a distributed object goes against that.
18
+ def make_distributed(*_)
19
+ fail DistributedError,
20
+ 'distributed objects not supported from DeadProtocol'
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,87 @@
1
+
2
+ require 'dizby/protocol/basic'
3
+ require 'dizby/stream/client'
4
+ require 'dizby/stream/connection'
5
+ require 'dizby/server/basic'
6
+ require 'dizby/tunnel/basic'
7
+ require 'dizby/tunnel/factory'
8
+
9
+ require 'socket'
10
+
11
+ module Dizby
12
+ class SecureProtocol
13
+ include BasicProtocol
14
+
15
+ self.scheme = 'drbsec'
16
+
17
+ refine(:spawn,
18
+ "#{scheme}://%{user}?%{host}%{port}?%{query}?"
19
+ ) do |server, command, (user, host, port, query)|
20
+ factory = TunnelFactory.new(server, port)
21
+ tunnel = factory.create(BasicSpawnTunnel).with(command, user, host)
22
+
23
+ socket = TCPSocket.open('localhost', tunnel.local_port)
24
+ TCProtocol.apply_sockopt(socket)
25
+
26
+ delegate = delegatable_tunnel(factory, server, tunnel)
27
+ client = BasicClient.new(delegate, socket,
28
+ "#{scheme}://#{host}:#{tunnel.remote_port}")
29
+ query &&= QueryRef.new(query)
30
+
31
+ [client, query]
32
+ end
33
+
34
+ refine(:client,
35
+ "#{scheme}://%{user}?%{host}%{port}%{query}?"
36
+ ) do |server, (user, host, port, query)|
37
+ port &&= port.to_i
38
+
39
+ factory = TunnelFactory.new(server, port)
40
+ tunnel = factory.create(BasicTunnel).with(user, host)
41
+
42
+ socket = TCPSocket.open('localhost', tunnel.local_port)
43
+ TCProtocol.apply_sockopt(socket)
44
+
45
+ delegate = delegatable_tunnel(factory, server, tunnel)
46
+ client = BasicClient.new(delegate, socket, "#{scheme}://#{host}:#{port}")
47
+ query &&= QueryRef.new(query)
48
+
49
+ [client, query]
50
+ end
51
+
52
+ def self.delegatable_tunnel(factory, server, tunnel)
53
+ if factory.bidirectional?
54
+ ResponseTunnel.new(server, tunnel)
55
+ else
56
+ DirectTunnel.new(server, tunnel)
57
+ end
58
+ end
59
+
60
+ class DirectTunnel < Delegator
61
+ def initialize(server, tunnel)
62
+ super(server)
63
+
64
+ @tunnel = tunnel
65
+ end
66
+
67
+ def close
68
+ @tunnel.close
69
+ super
70
+ @tunnel.thread.join
71
+ end
72
+ end
73
+
74
+ class ResponseTunnel < DirectTunnel
75
+ def initialize(server, tunnel)
76
+ super(server, tunnel)
77
+
78
+ @remote_port = tunnel.remote_port
79
+ end
80
+
81
+ def uri # overload the uri of the server
82
+ # we use the drb protocol for the rebound connection
83
+ "drb://localhost:#{@remote_port}"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,98 @@
1
+
2
+ require 'dizby/protocol/basic'
3
+ require 'dizby/stream/client'
4
+ require 'dizby/stream/connection'
5
+ require 'dizby/server/basic'
6
+
7
+ require 'socket'
8
+
9
+ module Dizby
10
+ class TCProtocol
11
+ include BasicProtocol
12
+
13
+ self.scheme = 'drb'
14
+
15
+ refine(:server,
16
+ "#{scheme}://%{host}?%{port}?"
17
+ ) do |front, config, (host, port)|
18
+ port &&= port.to_i
19
+
20
+ Server.new front, config, host, port
21
+ end
22
+
23
+ refine(:client,
24
+ "#{scheme}://%{host}?%{port}?%{query}?"
25
+ ) do |server, (host, port, query)|
26
+ port &&= port.to_i
27
+
28
+ socket = TCPSocket.open(host, port)
29
+ apply_sockopt(socket)
30
+
31
+ client = BasicClient.new(server, socket, "#{scheme}://#{host}:#{port}")
32
+ query &&= QueryRef.new(query)
33
+
34
+ [client, query]
35
+ end
36
+
37
+ class << self
38
+ def getservername
39
+ Socket.gethostbyname(Socket.gethostname)[0]
40
+ rescue
41
+ 'localhost'
42
+ end
43
+
44
+ def apply_sockopt(soc)
45
+ soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
46
+ end
47
+ end
48
+
49
+ class Server < BasicServer
50
+ include PolymorphicDelegated
51
+
52
+ def initialize(front, config, host, port)
53
+ port ||= 0
54
+
55
+ if host.empty?
56
+ host = TCProtocol.getservername
57
+ socket = self.class.open_socket_inaddr_any(host, port)
58
+ else
59
+ socket = TCPServer.open(host, port)
60
+ end
61
+
62
+ port = socket.addr[1] if port == 0
63
+
64
+ super("drb://#{host}:#{port}", front, socket, config)
65
+
66
+ TCProtocol.apply_sockopt(socket)
67
+
68
+ @port = port
69
+ end
70
+
71
+ attr_reader :port
72
+
73
+ def accept
74
+ socket = nil
75
+ loop do
76
+ socket = super
77
+ break if !tcp_acl || tcp_acl.allow_socket?(socket) # TODO: not tested
78
+ socket.close
79
+ end
80
+
81
+ TCProtocol.apply_sockopt(socket)
82
+ BasicConnection.new(self, socket)
83
+ end
84
+
85
+ config_reader :tcp_acl
86
+ private :tcp_acl
87
+
88
+ def self.open_socket_inaddr_any(host, port)
89
+ infos = Socket.getaddrinfo(host, nil, Socket::AF_UNSPEC,
90
+ Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)
91
+ families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten]
92
+ return TCPServer.open('0.0.0.0', port) if families.key?('AF_INET')
93
+ return TCPServer.open('::', port) if families.key?('AF_INET6')
94
+ TCPServer.open(port)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,95 @@
1
+
2
+ require 'dizby/protocol/basic'
3
+ require 'dizby/stream/client'
4
+ require 'dizby/stream/connection'
5
+ require 'dizby/server/basic'
6
+
7
+ require 'socket'
8
+ require 'tempfile'
9
+
10
+ fail LoadError, 'UNIXServer is required' unless defined?(UNIXServer)
11
+
12
+ module Dizby
13
+ class UnixProtocol
14
+ include BasicProtocol
15
+
16
+ self.scheme = 'drbunix'
17
+
18
+ refine(:server,
19
+ "#{scheme}:%{file}?"
20
+ ) do |front, config, (filename)|
21
+ Server.new front, config, filename
22
+ end
23
+
24
+ refine(:client,
25
+ "#{scheme}:%{file}%{query}?"
26
+ ) do |server, (filename, query)|
27
+ socket = UNIXSocket.open(filename)
28
+ UnixProtocol.apply_sockopt(socket)
29
+
30
+ client = BasicClient.new server, socket, "#{scheme}:#{filename}"
31
+ query &&= QueryRef.new(query)
32
+
33
+ [client, query]
34
+ end
35
+
36
+ def self.apply_sockopt(_soc)
37
+ # no-op for now
38
+ end
39
+
40
+ class Server < BasicServer
41
+ include PolymorphicDelegated
42
+
43
+ def initialize(front, config, filename)
44
+ unless filename
45
+ temp = Tempfile.new(%w( dizby-unix .socket ))
46
+ filename = temp.path
47
+ temp.close!
48
+ end
49
+
50
+ socket = UNIXServer.open(filename)
51
+ UnixProtocol.apply_sockopt(socket)
52
+
53
+ super("drbunix:#{filename}", front, socket, config)
54
+
55
+ self.class.set_permissions(filename, config)
56
+ end
57
+
58
+ def close
59
+ if stream
60
+ path = stream.path
61
+ stream.close
62
+ self.stream = nil
63
+
64
+ log.debug("unlinking #{path}")
65
+ File.unlink(path)
66
+ end
67
+
68
+ close_shutdown_pipe
69
+ end
70
+
71
+ def accept
72
+ socket = super
73
+
74
+ UnixProtocol.apply_sockopt(socket)
75
+ BasicConnection.new(self, socket)
76
+ end
77
+
78
+ def self.set_permissions(filename, config)
79
+ owner = config[:unix_owner]
80
+ group = config[:unix_group]
81
+ mode = config[:unix_mode]
82
+
83
+ if owner || group
84
+ require 'etc'
85
+ owner = Etc.getpwnam(owner).uid if owner
86
+ group = Etc.getgrnam(group).gid if group
87
+ File.chown(owner, group, filename)
88
+ end
89
+
90
+ File.chmod(mode, filename) if mode
91
+ end
92
+ private_class_method :set_permissions
93
+ end
94
+ end
95
+ end