dizby 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +10 -0
- data/LICENSE +24 -0
- data/dizby.gemspec +27 -0
- data/lib/dizby.rb +12 -0
- data/lib/dizby/access/control_list.rb +78 -0
- data/lib/dizby/access/entry.rb +61 -0
- data/lib/dizby/access/insecure.rb +30 -0
- data/lib/dizby/access/list.rb +10 -0
- data/lib/dizby/converter/simple.rb +12 -0
- data/lib/dizby/converter/timed.rb +23 -0
- data/lib/dizby/distributed/array.rb +30 -0
- data/lib/dizby/distributed/object.rb +44 -0
- data/lib/dizby/distributed/proxy.rb +41 -0
- data/lib/dizby/distributed/semi_proxy.rb +26 -0
- data/lib/dizby/distributed/undumpable.rb +8 -0
- data/lib/dizby/distributed/unknown.rb +57 -0
- data/lib/dizby/error.rb +29 -0
- data/lib/dizby/protocol/basic.rb +31 -0
- data/lib/dizby/protocol/manager.rb +62 -0
- data/lib/dizby/protocol/refined.rb +23 -0
- data/lib/dizby/protocols/dead.rb +24 -0
- data/lib/dizby/protocols/secure.rb +87 -0
- data/lib/dizby/protocols/tcp.rb +98 -0
- data/lib/dizby/protocols/unix.rb +95 -0
- data/lib/dizby/server/abstract.rb +42 -0
- data/lib/dizby/server/basic.rb +101 -0
- data/lib/dizby/server/registration.rb +23 -0
- data/lib/dizby/service.rb +64 -0
- data/lib/dizby/stream/client.rb +26 -0
- data/lib/dizby/stream/connection.rb +63 -0
- data/lib/dizby/stream/messenger.rb +28 -0
- data/lib/dizby/stream/query_ref.rb +13 -0
- data/lib/dizby/stream/readable.rb +65 -0
- data/lib/dizby/stream/writable.rb +37 -0
- data/lib/dizby/tunnel/abstract.rb +52 -0
- data/lib/dizby/tunnel/basic.rb +21 -0
- data/lib/dizby/tunnel/basic_spawn.rb +50 -0
- data/lib/dizby/tunnel/bidirectional_strategy.rb +29 -0
- data/lib/dizby/tunnel/factory.rb +29 -0
- data/lib/dizby/tunnel/local_strategy.rb +24 -0
- data/lib/dizby/tunnel/spawn_command.rb +49 -0
- data/lib/dizby/tunnel/spawned.rb +43 -0
- data/lib/dizby/tunnel/tunnelable_local.rb +8 -0
- data/lib/dizby/tunnel/tunnelable_remote.rb +21 -0
- data/lib/dizby/utility/classic_access.rb +25 -0
- data/lib/dizby/utility/configurable.rb +25 -0
- data/lib/dizby/utility/delegator.rb +28 -0
- data/lib/dizby/utility/io_barrier.rb +18 -0
- data/lib/dizby/utility/log.rb +23 -0
- data/lib/dizby/utility/monitor.rb +9 -0
- data/lib/dizby/utility/polymorphic_delegated.rb +58 -0
- data/lib/dizby/utility/self_pipe.rb +12 -0
- data/lib/dizby/utility/semi_built.rb +17 -0
- data/lib/dizby/utility/string.rb +8 -0
- data/lib/dizby/utility/timed_collection.rb +39 -0
- data/lib/dizby/utility/timed_state.rb +41 -0
- data/lib/dizby/version.rb +4 -0
- data/lib/dizby/worker/connection.rb +44 -0
- data/lib/dizby/worker/invoke_method.rb +55 -0
- data/lib/dizby/worker/server.rb +39 -0
- metadata +146 -0
@@ -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
|
data/lib/dizby/error.rb
ADDED
@@ -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
|