io-endpoint 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/io/endpoint/address_endpoint.rb +1 -1
- data/lib/io/endpoint/composite_endpoint.rb +2 -4
- data/lib/io/endpoint/generic.rb +0 -17
- data/lib/io/endpoint/host_endpoint.rb +15 -15
- data/lib/io/endpoint/shared_endpoint.rb +60 -25
- data/lib/io/endpoint/ssl_endpoint.rb +3 -3
- data/lib/io/endpoint/unix_endpoint.rb +4 -2
- data/lib/io/endpoint/version.rb +1 -1
- data/lib/io/endpoint/wrapper.rb +28 -5
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbc52bab1fde2330c0b971cf987db15dc5865f9f3ce72a130121db97d00671ba
|
4
|
+
data.tar.gz: a7503b553781fd02e86a082015e82f1c5af24b5fa3a735dbdeb7415498901da8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40343e43190bd695c0152fb5ec45946efe281245eb033eacae1eeda9291c37b4bc975dffac6db9d007b060bf9d375cdf3beed31f70a81f344c65f17b6772bff4
|
7
|
+
data.tar.gz: 40a34915f55409d96fddfaea9925c71c5bcddee36c0f6a6cc001982ec1e884a7694a3f4d89aad534353df3c182eb9b94f618619fdec9a77fc36d723620b5af09
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -34,9 +34,7 @@ module IO::Endpoint
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
CompositeEndpoint.new(endpoints, **options)
|
40
|
-
end
|
37
|
+
def self.composite(*endpoints, **options)
|
38
|
+
CompositeEndpoint.new(endpoints, **options)
|
41
39
|
end
|
42
40
|
end
|
data/lib/io/endpoint/generic.rb
CHANGED
@@ -83,23 +83,6 @@ module IO::Endpoint
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
# Map all endpoints by invoking `#bind`.
|
87
|
-
# @yield the bound wrapper.
|
88
|
-
def bound
|
89
|
-
wrappers = []
|
90
|
-
|
91
|
-
self.each do |endpoint|
|
92
|
-
wrapper = endpoint.bind
|
93
|
-
wrappers << wrapper
|
94
|
-
|
95
|
-
yield wrapper
|
96
|
-
end
|
97
|
-
|
98
|
-
return wrappers
|
99
|
-
ensure
|
100
|
-
wrappers.each(&:close) if $!
|
101
|
-
end
|
102
|
-
|
103
86
|
# Create an Endpoint instance by URI scheme. The host and port of the URI will be passed to the Endpoint factory method, along with any options.
|
104
87
|
#
|
105
88
|
# @param string [String] URI as string. Scheme will decide implementation used.
|
@@ -19,9 +19,8 @@ module IO::Endpoint
|
|
19
19
|
"\#<#{self.class} name=#{nodename.inspect} service=#{service.inspect} family=#{family.inspect} type=#{socktype.inspect} protocol=#{protocol.inspect} flags=#{flags.inspect}>"
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
22
|
+
|
23
|
+
attr :specification
|
25
24
|
|
26
25
|
def hostname
|
27
26
|
@specification.first
|
@@ -31,13 +30,14 @@ module IO::Endpoint
|
|
31
30
|
# @yield [Socket] the socket which is being connected, may be invoked more than once
|
32
31
|
# @return [Socket] the connected socket
|
33
32
|
# @raise if no connection could complete successfully
|
34
|
-
def connect
|
33
|
+
def connect(wrapper = Wrapper.default, &block)
|
35
34
|
last_error = nil
|
36
35
|
|
37
36
|
Addrinfo.foreach(*@specification) do |address|
|
38
37
|
begin
|
39
|
-
socket =
|
38
|
+
socket = wrapper.connect(@address, **@options)
|
40
39
|
rescue Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::EAGAIN => last_error
|
40
|
+
# Try again unless if possible, otherwise raise...
|
41
41
|
else
|
42
42
|
return socket unless block_given?
|
43
43
|
|
@@ -55,9 +55,9 @@ module IO::Endpoint
|
|
55
55
|
# Invokes the given block for every address which can be bound to.
|
56
56
|
# @yield [Socket] the bound socket
|
57
57
|
# @return [Array<Socket>] an array of bound sockets
|
58
|
-
def bind(&block)
|
58
|
+
def bind(wrapper = Wrapper.default, &block)
|
59
59
|
Addrinfo.foreach(*@specification).map do |address|
|
60
|
-
|
60
|
+
wrapper.bind(address, **@options, &block)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -71,23 +71,23 @@ module IO::Endpoint
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
# @param
|
74
|
+
# @param arguments nodename, service, family, socktype, protocol, flags. `socktype` will be set to Socket::SOCK_STREAM.
|
75
75
|
# @param options keyword arguments passed on to {HostEndpoint#initialize}
|
76
76
|
#
|
77
77
|
# @return [HostEndpoint]
|
78
|
-
def self.tcp(*
|
79
|
-
|
78
|
+
def self.tcp(*arguments, **options)
|
79
|
+
arguments[3] = ::Socket::SOCK_STREAM
|
80
80
|
|
81
|
-
HostEndpoint.new(
|
81
|
+
HostEndpoint.new(arguments, **options)
|
82
82
|
end
|
83
83
|
|
84
|
-
# @param
|
84
|
+
# @param arguments nodename, service, family, socktype, protocol, flags. `socktype` will be set to Socket::SOCK_DGRAM.
|
85
85
|
# @param options keyword arguments passed on to {HostEndpoint#initialize}
|
86
86
|
#
|
87
87
|
# @return [HostEndpoint]
|
88
|
-
def self.udp(*
|
89
|
-
|
88
|
+
def self.udp(*arguments, **options)
|
89
|
+
arguments[3] = ::Socket::SOCK_DGRAM
|
90
90
|
|
91
|
-
HostEndpoint.new(
|
91
|
+
HostEndpoint.new(arguments, **options)
|
92
92
|
end
|
93
93
|
end
|
@@ -5,13 +5,18 @@
|
|
5
5
|
|
6
6
|
require_relative 'generic'
|
7
7
|
require_relative 'composite_endpoint'
|
8
|
+
require_relative 'socket_endpoint'
|
8
9
|
|
9
10
|
module IO::Endpoint
|
10
11
|
# Pre-connect and pre-bind sockets so that it can be used between processes.
|
11
12
|
class SharedEndpoint < Generic
|
12
13
|
# Create a new `SharedEndpoint` by binding to the given endpoint.
|
13
|
-
def self.bound(endpoint, backlog: Socket::SOMAXCONN, close_on_exec: false)
|
14
|
-
|
14
|
+
def self.bound(endpoint, backlog: Socket::SOMAXCONN, close_on_exec: false, **options)
|
15
|
+
sockets = []
|
16
|
+
|
17
|
+
endpoint.each do |server_endpoint|
|
18
|
+
server = server_endpoint.bind(**options)
|
19
|
+
|
15
20
|
# This is somewhat optional. We want to have a generic interface as much as possible so that users of this interface can just call it without knowing a lot of internal details. Therefore, we ignore errors here if it's because the underlying socket does not support the operation.
|
16
21
|
begin
|
17
22
|
server.listen(backlog)
|
@@ -20,9 +25,11 @@ module IO::Endpoint
|
|
20
25
|
end
|
21
26
|
|
22
27
|
server.close_on_exec = close_on_exec
|
28
|
+
|
29
|
+
sockets << server
|
23
30
|
end
|
24
31
|
|
25
|
-
return self.new(endpoint,
|
32
|
+
return self.new(endpoint, sockets)
|
26
33
|
end
|
27
34
|
|
28
35
|
# Create a new `SharedEndpoint` by connecting to the given endpoint.
|
@@ -34,18 +41,20 @@ module IO::Endpoint
|
|
34
41
|
return self.new(endpoint, [wrapper])
|
35
42
|
end
|
36
43
|
|
37
|
-
def initialize(endpoint,
|
44
|
+
def initialize(endpoint, sockets, **options)
|
38
45
|
super(**options)
|
39
46
|
|
47
|
+
raise TypeError, "sockets must be an Array" unless sockets.is_a?(Array)
|
48
|
+
|
40
49
|
@endpoint = endpoint
|
41
|
-
@
|
50
|
+
@sockets = sockets
|
42
51
|
end
|
43
52
|
|
44
53
|
attr :endpoint
|
45
|
-
attr :
|
54
|
+
attr :sockets
|
46
55
|
|
47
56
|
def local_address_endpoint(**options)
|
48
|
-
endpoints = @
|
57
|
+
endpoints = @sockets.map do |wrapper|
|
49
58
|
AddressEndpoint.new(wrapper.to_io.local_address)
|
50
59
|
end
|
51
60
|
|
@@ -53,51 +62,77 @@ module IO::Endpoint
|
|
53
62
|
end
|
54
63
|
|
55
64
|
def remote_address_endpoint(**options)
|
56
|
-
endpoints = @
|
65
|
+
endpoints = @sockets.map do |wrapper|
|
57
66
|
AddressEndpoint.new(wrapper.to_io.remote_address)
|
58
67
|
end
|
59
68
|
|
60
69
|
return CompositeEndpoint.new(endpoints, **options)
|
61
70
|
end
|
62
71
|
|
63
|
-
# Close all the internal
|
72
|
+
# Close all the internal sockets.
|
64
73
|
def close
|
65
|
-
@
|
66
|
-
@
|
74
|
+
@sockets.each(&:close)
|
75
|
+
@sockets.clear
|
76
|
+
end
|
77
|
+
|
78
|
+
def each(&block)
|
79
|
+
return to_enum unless block_given?
|
80
|
+
|
81
|
+
@sockets.each do |socket|
|
82
|
+
yield SocketEndpoint.new(socket.dup)
|
83
|
+
end
|
67
84
|
end
|
68
85
|
|
69
|
-
def bind
|
70
|
-
@
|
86
|
+
def bind(wrapper = Wrapper.default, &block)
|
87
|
+
@sockets.each.map do |server|
|
71
88
|
server = server.dup
|
72
89
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
90
|
+
if block_given?
|
91
|
+
wrapper.async do
|
92
|
+
begin
|
93
|
+
yield server
|
94
|
+
ensure
|
95
|
+
server.close
|
96
|
+
end
|
97
|
+
end
|
98
|
+
else
|
99
|
+
server
|
77
100
|
end
|
78
101
|
end
|
79
102
|
end
|
80
103
|
|
81
|
-
def connect
|
82
|
-
@
|
83
|
-
|
104
|
+
def connect(wrapper = Wrapper.default, &block)
|
105
|
+
@sockets.each do |socket|
|
106
|
+
socket = socket.dup
|
107
|
+
|
108
|
+
return socket unless block_given?
|
84
109
|
|
85
110
|
begin
|
86
|
-
yield
|
111
|
+
return yield(socket)
|
87
112
|
ensure
|
88
|
-
|
113
|
+
socket.close
|
89
114
|
end
|
90
115
|
end
|
91
116
|
end
|
92
117
|
|
93
|
-
def accept(
|
118
|
+
def accept(**options, &block)
|
94
119
|
bind do |server|
|
95
|
-
server.
|
120
|
+
server.accept(&block)
|
96
121
|
end
|
97
122
|
end
|
98
123
|
|
99
124
|
def to_s
|
100
|
-
"\#<#{self.class} #{@
|
125
|
+
"\#<#{self.class} #{@sockets.size} descriptors for #{@endpoint}>"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class Generic
|
130
|
+
def bound(**options)
|
131
|
+
SharedEndpoint.bound(self, **options)
|
132
|
+
end
|
133
|
+
|
134
|
+
def connected(**options)
|
135
|
+
SharedEndpoint.connected(self, **options)
|
101
136
|
end
|
102
137
|
end
|
103
138
|
end
|
@@ -111,13 +111,13 @@ module IO::Endpoint
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
-
# @param
|
114
|
+
# @param arguments
|
115
115
|
# @param ssl_context [OpenSSL::SSL::SSLContext, nil]
|
116
116
|
# @param hostname [String, nil]
|
117
117
|
# @param options keyword arguments passed through to {Endpoint.tcp}
|
118
118
|
#
|
119
119
|
# @return [SSLEndpoint]
|
120
|
-
def self.ssl(*
|
121
|
-
SSLEndpoint.new(self.tcp(*
|
120
|
+
def self.ssl(*arguments, ssl_context: nil, hostname: nil, **options)
|
121
|
+
SSLEndpoint.new(self.tcp(*arguments, **options), ssl_context: ssl_context, hostname: hostname)
|
122
122
|
end
|
123
123
|
end
|
@@ -27,14 +27,16 @@ module IO::Endpoint
|
|
27
27
|
end
|
28
28
|
rescue Errno::ECONNREFUSED
|
29
29
|
return false
|
30
|
+
rescue Errno::ENOENT
|
31
|
+
return false
|
30
32
|
end
|
31
33
|
|
32
34
|
def bind(&block)
|
33
35
|
super
|
34
36
|
rescue Errno::EADDRINUSE
|
35
37
|
# If you encounter EADDRINUSE from `bind()`, you can check if the socket is actually accepting connections by attempting to `connect()` to it. If the socket is still bound by an active process, the connection will succeed. Otherwise, it should be safe to `unlink()` the path and try again.
|
36
|
-
if !bound?
|
37
|
-
File.unlink(@path)
|
38
|
+
if !bound?
|
39
|
+
File.unlink(@path) rescue nil
|
38
40
|
retry
|
39
41
|
else
|
40
42
|
raise
|
data/lib/io/endpoint/version.rb
CHANGED
data/lib/io/endpoint/wrapper.rb
CHANGED
@@ -9,16 +9,32 @@ module IO::Endpoint
|
|
9
9
|
class Wrapper
|
10
10
|
include ::Socket::Constants
|
11
11
|
|
12
|
-
if
|
13
|
-
def
|
12
|
+
if IO.method_defined?(:timeout=)
|
13
|
+
def set_timeout(io, timeout)
|
14
14
|
io.timeout = timeout
|
15
15
|
end
|
16
16
|
else
|
17
|
-
def
|
17
|
+
def set_timeout(io, timeout)
|
18
18
|
warn "IO#timeout= not supported on this platform."
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
def set_buffered(socket, buffered)
|
23
|
+
case buffered
|
24
|
+
when true
|
25
|
+
socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 0)
|
26
|
+
when false
|
27
|
+
socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
|
28
|
+
end
|
29
|
+
rescue Errno::EINVAL
|
30
|
+
# On Darwin, sometimes occurs when the connection is not yet fully formed. Empirically, TCP_NODELAY is enabled despite this result.
|
31
|
+
rescue Errno::EOPNOTSUPP
|
32
|
+
# Some platforms may simply not support the operation.
|
33
|
+
# Console.logger.warn(self) {"Unable to set sync=#{value}!"}
|
34
|
+
rescue Errno::ENOPROTOOPT
|
35
|
+
# It may not be supported by the protocol (e.g. UDP). ¯\_(ツ)_/¯
|
36
|
+
end
|
37
|
+
|
22
38
|
def async
|
23
39
|
raise NotImplementedError
|
24
40
|
end
|
@@ -26,7 +42,9 @@ module IO::Endpoint
|
|
26
42
|
# Build and wrap the underlying io.
|
27
43
|
# @option reuse_port [Boolean] Allow this port to be bound in multiple processes.
|
28
44
|
# @option reuse_address [Boolean] Allow this port to be bound in multiple processes.
|
29
|
-
|
45
|
+
# @option linger [Boolean] Wait for data to be sent before closing the socket.
|
46
|
+
# @option buffered [Boolean] Enable or disable Nagle's algorithm for TCP sockets.
|
47
|
+
def build(*arguments, timeout: nil, reuse_address: true, reuse_port: nil, linger: nil, buffered: false)
|
30
48
|
socket = ::Socket.new(*arguments)
|
31
49
|
|
32
50
|
# Set the timeout:
|
@@ -43,7 +61,11 @@ module IO::Endpoint
|
|
43
61
|
end
|
44
62
|
|
45
63
|
if linger
|
46
|
-
socket.setsockopt(SOL_SOCKET, SO_LINGER,
|
64
|
+
socket.setsockopt(SOL_SOCKET, SO_LINGER, 1)
|
65
|
+
end
|
66
|
+
|
67
|
+
if buffered == false
|
68
|
+
set_buffered(socket, buffered)
|
47
69
|
end
|
48
70
|
|
49
71
|
yield socket if block_given?
|
@@ -51,6 +73,7 @@ module IO::Endpoint
|
|
51
73
|
return socket
|
52
74
|
rescue
|
53
75
|
socket&.close
|
76
|
+
raise
|
54
77
|
end
|
55
78
|
|
56
79
|
# Establish a connection to a given `remote_address`.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-endpoint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -37,7 +37,7 @@ cert_chain:
|
|
37
37
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
38
38
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
39
39
|
-----END CERTIFICATE-----
|
40
|
-
date: 2023-
|
40
|
+
date: 2023-12-16 00:00:00.000000000 Z
|
41
41
|
dependencies:
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: bake
|
@@ -120,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
120
|
- !ruby/object:Gem::Version
|
121
121
|
version: '0'
|
122
122
|
requirements: []
|
123
|
-
rubygems_version: 3.4.
|
123
|
+
rubygems_version: 3.4.22
|
124
124
|
signing_key:
|
125
125
|
specification_version: 4
|
126
126
|
summary: Provides a separation of concerns interface for IO endpoints.
|
metadata.gz.sig
CHANGED
Binary file
|