io-endpoint 0.2.0 → 0.4.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/connected.rb +27 -0
- data/lib/io/endpoint/address_endpoint.rb +4 -3
- data/lib/io/endpoint/composite_endpoint.rb +13 -5
- data/lib/io/endpoint/generic.rb +6 -18
- data/lib/io/endpoint/host_endpoint.rb +6 -2
- data/lib/io/endpoint/shared_endpoint.rb +14 -16
- data/lib/io/endpoint/ssl_endpoint.rb +42 -15
- data/lib/io/endpoint/version.rb +1 -1
- data/lib/io/endpoint/wrapper.rb +6 -7
- data/readme.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +5 -46
- 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: 10203d535af830bf9ff71034262b5851ec323024a9de4db527f061cb1cd76102
|
4
|
+
data.tar.gz: b150061a0414c379afb7fb5f2c142767afc5ed1822e5b106b78ece023d1cf459
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82930d72359dee91627b19c771f79c9f2c6cc43dc93b9514a372a4568d1fb92c653ff2da1403c8519eddc4470dc14ef2356abe41cd202fba80bc35ba10e2d053
|
7
|
+
data.tar.gz: 556e87a95eb514e643a3e6bb40468ac851cceea5da6fb6f72db37767dca18882bb9b63e61e05d2e69629a1e4eef1859c3a2b3e0797b36e0e4eb1202684f8d44c
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/io/connected.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
class IO
|
4
|
+
def connected?
|
5
|
+
!closed?
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Socket
|
10
|
+
def connected?
|
11
|
+
# Is it likely that the socket is still connected?
|
12
|
+
# May return false positive, but won't return false negative.
|
13
|
+
return false unless super
|
14
|
+
|
15
|
+
# If we can wait for the socket to become readable, we know that the socket may still be open.
|
16
|
+
result = to_io.recv_nonblock(1, MSG_PEEK, exception: false)
|
17
|
+
|
18
|
+
# No data was available - newer Ruby can return nil instead of empty string:
|
19
|
+
return false if result.nil?
|
20
|
+
|
21
|
+
# Either there was some data available, or we can wait to see if there is data avaialble.
|
22
|
+
return !result.empty? || result == :wait_readable
|
23
|
+
rescue Errno::ECONNRESET
|
24
|
+
# This might be thrown by recv_nonblock.
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
end
|
@@ -23,10 +23,11 @@ module IO::Endpoint
|
|
23
23
|
attr :address
|
24
24
|
|
25
25
|
# Bind a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
|
26
|
-
# @yield
|
27
|
-
#
|
26
|
+
# @yield {|socket| ...} An optional block which will be passed the socket.
|
27
|
+
# @parameter socket [Socket] The socket which has been bound.
|
28
|
+
# @return [Array(Socket)] the bound socket
|
28
29
|
def bind(wrapper = Wrapper.default, &block)
|
29
|
-
wrapper.bind(@address, **@options, &block)
|
30
|
+
[wrapper.bind(@address, **@options, &block)]
|
30
31
|
end
|
31
32
|
|
32
33
|
# Connects a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
|
@@ -13,15 +13,17 @@ module IO::Endpoint
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def each(&block)
|
16
|
-
@endpoints.each
|
16
|
+
@endpoints.each do |endpoint|
|
17
|
+
endpoint.each(&block)
|
18
|
+
end
|
17
19
|
end
|
18
20
|
|
19
|
-
def connect(&block)
|
21
|
+
def connect(wrapper = Wrapper.default, &block)
|
20
22
|
last_error = nil
|
21
23
|
|
22
24
|
@endpoints.each do |endpoint|
|
23
25
|
begin
|
24
|
-
return endpoint.connect(&block)
|
26
|
+
return endpoint.connect(wrapper, &block)
|
25
27
|
rescue => last_error
|
26
28
|
end
|
27
29
|
end
|
@@ -29,8 +31,14 @@ module IO::Endpoint
|
|
29
31
|
raise last_error
|
30
32
|
end
|
31
33
|
|
32
|
-
def bind(&block)
|
33
|
-
|
34
|
+
def bind(wrapper = Wrapper.default, &block)
|
35
|
+
if block_given?
|
36
|
+
@endpoints.each do |endpoint|
|
37
|
+
endpoint.bind(&block)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@endpoints.map(&:bind).flatten.compact
|
41
|
+
end
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
data/lib/io/endpoint/generic.rb
CHANGED
@@ -69,22 +69,16 @@ module IO::Endpoint
|
|
69
69
|
|
70
70
|
# Accept connections from the specified endpoint.
|
71
71
|
# @param backlog [Integer] the number of connections to listen for.
|
72
|
-
def accept(
|
73
|
-
bind do |server|
|
74
|
-
|
75
|
-
|
76
|
-
while true
|
77
|
-
socket, address = server.accept
|
78
|
-
|
79
|
-
Fiber.schedule do
|
80
|
-
yield accepted(socket), address
|
81
|
-
end
|
82
|
-
end
|
72
|
+
def accept(wrapper = Wrapper.default, *arguments, **options, &block)
|
73
|
+
bind(wrapper, *arguments, **options) do |server|
|
74
|
+
wrapper.accept(server, &block)
|
83
75
|
end
|
84
76
|
end
|
85
77
|
|
86
78
|
# 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.
|
87
79
|
#
|
80
|
+
# You should not use untrusted input as it may execute arbitrary code.
|
81
|
+
#
|
88
82
|
# @param string [String] URI as string. Scheme will decide implementation used.
|
89
83
|
# @param options keyword arguments passed through to {#initialize}
|
90
84
|
#
|
@@ -95,13 +89,7 @@ module IO::Endpoint
|
|
95
89
|
def self.parse(string, **options)
|
96
90
|
uri = URI.parse(string)
|
97
91
|
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
protected
|
102
|
-
|
103
|
-
def accepted(socket)
|
104
|
-
socket
|
92
|
+
IO::Endpoint.public_send(uri.scheme, uri.host, uri.port, **options)
|
105
93
|
end
|
106
94
|
end
|
107
95
|
end
|
@@ -23,7 +23,11 @@ module IO::Endpoint
|
|
23
23
|
attr :specification
|
24
24
|
|
25
25
|
def hostname
|
26
|
-
@specification
|
26
|
+
@specification[0]
|
27
|
+
end
|
28
|
+
|
29
|
+
def service
|
30
|
+
@specification[1]
|
27
31
|
end
|
28
32
|
|
29
33
|
# Try to connect to the given host by connecting to each address in sequence until a connection is made.
|
@@ -35,7 +39,7 @@ module IO::Endpoint
|
|
35
39
|
|
36
40
|
Addrinfo.foreach(*@specification) do |address|
|
37
41
|
begin
|
38
|
-
socket = wrapper.connect(
|
42
|
+
socket = wrapper.connect(address, **@options)
|
39
43
|
rescue Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::EAGAIN => last_error
|
40
44
|
# Try again unless if possible, otherwise raise...
|
41
45
|
else
|
@@ -7,16 +7,16 @@ require_relative 'generic'
|
|
7
7
|
require_relative 'composite_endpoint'
|
8
8
|
require_relative 'socket_endpoint'
|
9
9
|
|
10
|
+
require 'openssl'
|
11
|
+
|
10
12
|
module IO::Endpoint
|
11
13
|
# Pre-connect and pre-bind sockets so that it can be used between processes.
|
12
14
|
class SharedEndpoint < Generic
|
13
15
|
# Create a new `SharedEndpoint` by binding to the given endpoint.
|
14
16
|
def self.bound(endpoint, backlog: Socket::SOMAXCONN, close_on_exec: false, **options)
|
15
|
-
sockets =
|
17
|
+
sockets = endpoint.bind(**options)
|
16
18
|
|
17
|
-
|
18
|
-
server = server_endpoint.bind(**options)
|
19
|
-
|
19
|
+
sockets.each do |server|
|
20
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.
|
21
21
|
begin
|
22
22
|
server.listen(backlog)
|
@@ -25,8 +25,6 @@ module IO::Endpoint
|
|
25
25
|
end
|
26
26
|
|
27
27
|
server.close_on_exec = close_on_exec
|
28
|
-
|
29
|
-
sockets << server
|
30
28
|
end
|
31
29
|
|
32
30
|
return self.new(endpoint, sockets)
|
@@ -34,11 +32,11 @@ module IO::Endpoint
|
|
34
32
|
|
35
33
|
# Create a new `SharedEndpoint` by connecting to the given endpoint.
|
36
34
|
def self.connected(endpoint, close_on_exec: false)
|
37
|
-
|
35
|
+
socket = endpoint.connect
|
38
36
|
|
39
|
-
|
37
|
+
socket.close_on_exec = close_on_exec
|
40
38
|
|
41
|
-
return self.new(endpoint, [
|
39
|
+
return self.new(endpoint, [socket])
|
42
40
|
end
|
43
41
|
|
44
42
|
def initialize(endpoint, sockets, **options)
|
@@ -55,18 +53,18 @@ module IO::Endpoint
|
|
55
53
|
|
56
54
|
def local_address_endpoint(**options)
|
57
55
|
endpoints = @sockets.map do |wrapper|
|
58
|
-
AddressEndpoint.new(wrapper.to_io.local_address)
|
56
|
+
AddressEndpoint.new(wrapper.to_io.local_address, **options)
|
59
57
|
end
|
60
58
|
|
61
|
-
return CompositeEndpoint.new(endpoints
|
59
|
+
return CompositeEndpoint.new(endpoints)
|
62
60
|
end
|
63
61
|
|
64
62
|
def remote_address_endpoint(**options)
|
65
63
|
endpoints = @sockets.map do |wrapper|
|
66
|
-
AddressEndpoint.new(wrapper.to_io.remote_address)
|
64
|
+
AddressEndpoint.new(wrapper.to_io.remote_address, **options)
|
67
65
|
end
|
68
66
|
|
69
|
-
return CompositeEndpoint.new(endpoints
|
67
|
+
return CompositeEndpoint.new(endpoints)
|
70
68
|
end
|
71
69
|
|
72
70
|
# Close all the internal sockets.
|
@@ -115,9 +113,9 @@ module IO::Endpoint
|
|
115
113
|
end
|
116
114
|
end
|
117
115
|
|
118
|
-
def accept(
|
119
|
-
bind do |server|
|
120
|
-
|
116
|
+
def accept(wrapper = Wrapper.default, &block)
|
117
|
+
bind(wrapper) do |server|
|
118
|
+
wrapper.accept(server, &block)
|
121
119
|
end
|
122
120
|
end
|
123
121
|
|
@@ -8,6 +8,38 @@ require_relative 'generic'
|
|
8
8
|
|
9
9
|
require 'openssl'
|
10
10
|
|
11
|
+
module OpenSSL::SSL::SocketForwarder
|
12
|
+
unless method_defined?(:close_on_exec=)
|
13
|
+
def close_on_exec=(value)
|
14
|
+
to_io.close_on_exec = value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
unless method_defined?(:close_on_exec)
|
19
|
+
def local_address
|
20
|
+
to_io.local_address
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
unless method_defined?(:wait)
|
25
|
+
def wait(*arguments)
|
26
|
+
to_io.wait(*arguments)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
unless method_defined?(:wait_readable)
|
31
|
+
def wait_readable(*arguments)
|
32
|
+
to_io.wait_readable(*arguments)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
unless method_defined?(:wait_writable)
|
37
|
+
def wait_writable(*arguments)
|
38
|
+
to_io.wait_writable(*arguments)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
11
43
|
module IO::Endpoint
|
12
44
|
class SSLEndpoint < Generic
|
13
45
|
def initialize(endpoint, **options)
|
@@ -41,13 +73,13 @@ module IO::Endpoint
|
|
41
73
|
@options[:ssl_params]
|
42
74
|
end
|
43
75
|
|
44
|
-
def build_context(context = OpenSSL::SSL::SSLContext.new)
|
76
|
+
def build_context(context = ::OpenSSL::SSL::SSLContext.new)
|
45
77
|
if params = self.params
|
46
78
|
context.set_params(params)
|
47
79
|
end
|
48
80
|
|
49
|
-
context.setup
|
50
|
-
context.freeze
|
81
|
+
# context.setup
|
82
|
+
# context.freeze
|
51
83
|
|
52
84
|
return context
|
53
85
|
end
|
@@ -59,13 +91,15 @@ module IO::Endpoint
|
|
59
91
|
# Connect to the underlying endpoint and establish a SSL connection.
|
60
92
|
# @yield [Socket] the socket which is being connected
|
61
93
|
# @return [Socket] the connected socket
|
62
|
-
def bind
|
94
|
+
def bind(*arguments, **options, &block)
|
63
95
|
if block_given?
|
64
|
-
@endpoint.bind do |server|
|
65
|
-
yield OpenSSL::SSL::SSLServer.new(server, context)
|
96
|
+
@endpoint.bind(*arguments, **options) do |server|
|
97
|
+
yield ::OpenSSL::SSL::SSLServer.new(server, self.context)
|
66
98
|
end
|
67
99
|
else
|
68
|
-
|
100
|
+
@endpoint.bind(*arguments, **options).map do |server|
|
101
|
+
::OpenSSL::SSL::SSLServer.new(server, self.context)
|
102
|
+
end
|
69
103
|
end
|
70
104
|
end
|
71
105
|
|
@@ -73,7 +107,7 @@ module IO::Endpoint
|
|
73
107
|
# @yield [Socket] the socket which is being connected
|
74
108
|
# @return [Socket] the connected socket
|
75
109
|
def connect(&block)
|
76
|
-
socket = OpenSSL::SSL::SSLSocket.new(@endpoint.connect, context)
|
110
|
+
socket = ::OpenSSL::SSL::SSLSocket.new(@endpoint.connect, self.context)
|
77
111
|
|
78
112
|
if hostname = self.hostname
|
79
113
|
socket.hostname = hostname
|
@@ -102,13 +136,6 @@ module IO::Endpoint
|
|
102
136
|
yield self.class.new(endpoint, **@options)
|
103
137
|
end
|
104
138
|
end
|
105
|
-
|
106
|
-
protected
|
107
|
-
|
108
|
-
def accepted(socket)
|
109
|
-
socket.accept
|
110
|
-
socket
|
111
|
-
end
|
112
139
|
end
|
113
140
|
|
114
141
|
# @param arguments
|
data/lib/io/endpoint/version.rb
CHANGED
data/lib/io/endpoint/wrapper.rb
CHANGED
@@ -30,7 +30,6 @@ module IO::Endpoint
|
|
30
30
|
# On Darwin, sometimes occurs when the connection is not yet fully formed. Empirically, TCP_NODELAY is enabled despite this result.
|
31
31
|
rescue Errno::EOPNOTSUPP
|
32
32
|
# Some platforms may simply not support the operation.
|
33
|
-
# Console.logger.warn(self) {"Unable to set sync=#{value}!"}
|
34
33
|
rescue Errno::ENOPROTOOPT
|
35
34
|
# It may not be supported by the protocol (e.g. UDP). ¯\_(ツ)_/¯
|
36
35
|
end
|
@@ -131,14 +130,14 @@ module IO::Endpoint
|
|
131
130
|
end
|
132
131
|
|
133
132
|
# Bind to a local address and accept connections in a loop.
|
134
|
-
def accept(
|
135
|
-
|
136
|
-
server.
|
133
|
+
def accept(server, timeout: server.timeout, &block)
|
134
|
+
while true
|
135
|
+
socket, address = server.accept
|
136
|
+
|
137
|
+
socket.timeout = timeout if timeout != false
|
137
138
|
|
138
139
|
async do
|
139
|
-
|
140
|
-
server.accept(&block)
|
141
|
-
end
|
140
|
+
yield socket, address
|
142
141
|
end
|
143
142
|
end
|
144
143
|
end
|
data/readme.md
CHANGED
@@ -18,6 +18,14 @@ We welcome contributions to this project.
|
|
18
18
|
4. Push to the branch (`git push origin my-new-feature`).
|
19
19
|
5. Create new Pull Request.
|
20
20
|
|
21
|
+
### Developer Certificate of Origin
|
22
|
+
|
23
|
+
This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
|
24
|
+
|
25
|
+
### Contributor Covenant
|
26
|
+
|
27
|
+
This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
|
28
|
+
|
21
29
|
## See Also
|
22
30
|
|
23
31
|
- [async-io](https://github.com/socketry/async-io) — Asynchronous IO primitives.
|
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -37,56 +37,15 @@ cert_chain:
|
|
37
37
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
38
38
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
39
39
|
-----END CERTIFICATE-----
|
40
|
-
date:
|
41
|
-
dependencies:
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: bake
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: covered
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: sus
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - ">="
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '0'
|
40
|
+
date: 2024-01-01 00:00:00.000000000 Z
|
41
|
+
dependencies: []
|
84
42
|
description:
|
85
43
|
email:
|
86
44
|
executables: []
|
87
45
|
extensions: []
|
88
46
|
extra_rdoc_files: []
|
89
47
|
files:
|
48
|
+
- lib/io/connected.rb
|
90
49
|
- lib/io/endpoint.rb
|
91
50
|
- lib/io/endpoint/address_endpoint.rb
|
92
51
|
- lib/io/endpoint/composite_endpoint.rb
|
@@ -120,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
79
|
- !ruby/object:Gem::Version
|
121
80
|
version: '0'
|
122
81
|
requirements: []
|
123
|
-
rubygems_version: 3.4.
|
82
|
+
rubygems_version: 3.4.10
|
124
83
|
signing_key:
|
125
84
|
specification_version: 4
|
126
85
|
summary: Provides a separation of concerns interface for IO endpoints.
|
metadata.gz.sig
CHANGED
Binary file
|