io-endpoint 0.13.1 → 0.15.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 +17 -6
- data/lib/io/endpoint/bound_endpoint.rb +10 -6
- data/lib/io/endpoint/composite_endpoint.rb +12 -4
- data/lib/io/endpoint/connected_endpoint.rb +10 -6
- data/lib/io/endpoint/generic.rb +11 -6
- data/lib/io/endpoint/host_endpoint.rb +8 -4
- data/lib/io/endpoint/shared_endpoint.rb +2 -2
- data/lib/io/endpoint/socket_endpoint.rb +6 -2
- data/lib/io/endpoint/ssl_endpoint.rb +8 -4
- data/lib/io/endpoint/unix_endpoint.rb +7 -3
- data/lib/io/endpoint/version.rb +1 -1
- data/lib/io/endpoint/wrapper.rb +42 -33
- data/license.md +1 -1
- data.tar.gz.sig +0 -0
- metadata +3 -8
- metadata.gz.sig +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85f53ad778e7ab62a17def218d84235d361df69805fa9f0edbf08b4ec0dfebc1
|
4
|
+
data.tar.gz: db08fed38c6ff791c0d4b98af3a661af718c5c44595f135cb0bf77147a36155b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c00c73e293110af0cb9caed8e7a8477b1c18eabe896b523297e42f3daf689586e916c989e103c33b9d4be37ce75e67a3433d2bfed92cdd5e366177076454bfe
|
7
|
+
data.tar.gz: a1bb99c8199618a8984deca2678c9884f382542b38c83c3fe6a08b1896a13fd5f9a57efb125b487a259f9b0a0e6fc4425446aea083ce8423b27acc1420178331
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2023, by Samuel Williams.
|
4
|
+
# Copyright, 2023-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "socket"
|
7
7
|
|
8
|
-
require_relative
|
9
|
-
require_relative
|
8
|
+
require_relative "generic"
|
9
|
+
require_relative "wrapper"
|
10
10
|
|
11
11
|
module IO::Endpoint
|
12
12
|
class AddressEndpoint < Generic
|
@@ -17,6 +17,17 @@ module IO::Endpoint
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_s
|
20
|
+
case @address.afamily
|
21
|
+
when Socket::AF_INET
|
22
|
+
"inet:#{@address.inspect_sockaddr}"
|
23
|
+
when Socket::AF_INET6
|
24
|
+
"inet6:#{@address.inspect_sockaddr}"
|
25
|
+
else
|
26
|
+
"address:#{@address.inspect_sockaddr}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
20
31
|
"\#<#{self.class} address=#{@address.inspect}>"
|
21
32
|
end
|
22
33
|
|
@@ -26,13 +37,13 @@ module IO::Endpoint
|
|
26
37
|
# @yield {|socket| ...} An optional block which will be passed the socket.
|
27
38
|
# @parameter socket [Socket] The socket which has been bound.
|
28
39
|
# @return [Array(Socket)] the bound socket
|
29
|
-
def bind(wrapper =
|
40
|
+
def bind(wrapper = self.wrapper, &block)
|
30
41
|
[wrapper.bind(@address, **@options, &block)]
|
31
42
|
end
|
32
43
|
|
33
44
|
# Connects a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
|
34
45
|
# @return [Socket] the connected socket
|
35
|
-
def connect(wrapper =
|
46
|
+
def connect(wrapper = self.wrapper, &block)
|
36
47
|
wrapper.connect(@address, **@options, &block)
|
37
48
|
end
|
38
49
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
6
|
+
require_relative "generic"
|
7
|
+
require_relative "composite_endpoint"
|
8
|
+
require_relative "address_endpoint"
|
9
9
|
|
10
10
|
module IO::Endpoint
|
11
11
|
class BoundEndpoint < Generic
|
@@ -55,13 +55,17 @@ module IO::Endpoint
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def to_s
|
58
|
+
"bound:#{@endpoint}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def inspect
|
58
62
|
"\#<#{self.class} #{@sockets.size} bound sockets for #{@endpoint}>"
|
59
63
|
end
|
60
64
|
|
61
|
-
def bind(wrapper =
|
65
|
+
def bind(wrapper = self.wrapper, &block)
|
62
66
|
@sockets.map do |server|
|
63
67
|
if block_given?
|
64
|
-
wrapper.
|
68
|
+
wrapper.schedule do
|
65
69
|
yield server
|
66
70
|
end
|
67
71
|
else
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2023-
|
4
|
+
# Copyright, 2023-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "generic"
|
7
7
|
|
8
8
|
module IO::Endpoint
|
9
9
|
# A composite endpoint is a collection of endpoints that are used in order.
|
@@ -19,6 +19,14 @@ module IO::Endpoint
|
|
19
19
|
@endpoints = endpoints
|
20
20
|
end
|
21
21
|
|
22
|
+
def to_s
|
23
|
+
"composite:#{@endpoints.join(",")}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"\#<#{self.class} endpoints=#{@endpoints}>"
|
28
|
+
end
|
29
|
+
|
22
30
|
def with(**options)
|
23
31
|
self.class.new(endpoints.map{|endpoint| endpoint.with(**options)}, **@options.merge(options))
|
24
32
|
end
|
@@ -36,7 +44,7 @@ module IO::Endpoint
|
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
39
|
-
def connect(wrapper =
|
47
|
+
def connect(wrapper = self.wrapper, &block)
|
40
48
|
last_error = nil
|
41
49
|
|
42
50
|
@endpoints.each do |endpoint|
|
@@ -49,7 +57,7 @@ module IO::Endpoint
|
|
49
57
|
raise last_error
|
50
58
|
end
|
51
59
|
|
52
|
-
def bind(wrapper =
|
60
|
+
def bind(wrapper = self.wrapper, &block)
|
53
61
|
if block_given?
|
54
62
|
@endpoints.each do |endpoint|
|
55
63
|
endpoint.bind(&block)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
6
|
+
require_relative "generic"
|
7
|
+
require_relative "composite_endpoint"
|
8
|
+
require_relative "socket_endpoint"
|
9
9
|
|
10
|
-
require
|
10
|
+
require "openssl"
|
11
11
|
|
12
12
|
module IO::Endpoint
|
13
13
|
class ConnectedEndpoint < Generic
|
@@ -41,7 +41,7 @@ module IO::Endpoint
|
|
41
41
|
AddressEndpoint.new(socket.to_io.remote_address, **options)
|
42
42
|
end
|
43
43
|
|
44
|
-
def connect(wrapper =
|
44
|
+
def connect(wrapper = self.wrapper, &block)
|
45
45
|
if block_given?
|
46
46
|
yield @socket
|
47
47
|
else
|
@@ -57,6 +57,10 @@ module IO::Endpoint
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def to_s
|
60
|
+
"connected:#{@endpoint}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
60
64
|
"\#<#{self.class} #{@socket} connected for #{@endpoint}>"
|
61
65
|
end
|
62
66
|
end
|
data/lib/io/endpoint/generic.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2023-
|
4
|
+
# Copyright, 2023-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
# require_relative 'address'
|
7
|
-
require
|
8
|
-
require
|
7
|
+
require "uri"
|
8
|
+
require "socket"
|
9
9
|
|
10
10
|
module IO::Endpoint
|
11
11
|
Address = Addrinfo
|
@@ -64,21 +64,21 @@ module IO::Endpoint
|
|
64
64
|
# @yields {|socket| ...} An optional block which will be passed the socket.
|
65
65
|
# @parameter socket [Socket] The socket which has been bound.
|
66
66
|
# @returns [Array(Socket)] the bound socket
|
67
|
-
def bind(wrapper =
|
67
|
+
def bind(wrapper = self.wrapper, &block)
|
68
68
|
raise NotImplementedError
|
69
69
|
end
|
70
70
|
|
71
71
|
# Connects a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
|
72
72
|
# @parameter wrapper [Wrapper] The wrapper to use for connecting.
|
73
73
|
# @return [Socket] the connected socket
|
74
|
-
def connect(wrapper =
|
74
|
+
def connect(wrapper = self.wrapper, &block)
|
75
75
|
raise NotImplementedError
|
76
76
|
end
|
77
77
|
|
78
78
|
# Bind and accept connections on the given address.
|
79
79
|
# @parameter wrapper [Wrapper] The wrapper to use for accepting connections.
|
80
80
|
# @yields [Socket] The accepted socket.
|
81
|
-
def accept(wrapper =
|
81
|
+
def accept(wrapper = self.wrapper, &block)
|
82
82
|
bind(wrapper) do |server|
|
83
83
|
wrapper.accept(server, **@options, &block)
|
84
84
|
end
|
@@ -109,5 +109,10 @@ module IO::Endpoint
|
|
109
109
|
|
110
110
|
IO::Endpoint.public_send(uri.scheme, uri.host, uri.port, **options)
|
111
111
|
end
|
112
|
+
|
113
|
+
# The default wrapper to use for binding, connecting, and accepting connections.
|
114
|
+
def wrapper
|
115
|
+
@options[:wrapper] || Wrapper.default
|
116
|
+
end
|
112
117
|
end
|
113
118
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2023, by Samuel Williams.
|
4
|
+
# Copyright, 2023-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "address_endpoint"
|
7
7
|
|
8
8
|
module IO::Endpoint
|
9
9
|
class HostEndpoint < Generic
|
@@ -14,6 +14,10 @@ module IO::Endpoint
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def to_s
|
17
|
+
"host:#{@specification[0]}:#{@specification[1]}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
17
21
|
nodename, service, family, socktype, protocol, flags = @specification
|
18
22
|
|
19
23
|
"\#<#{self.class} name=#{nodename.inspect} service=#{service.inspect} family=#{family.inspect} type=#{socktype.inspect} protocol=#{protocol.inspect} flags=#{flags.inspect}>"
|
@@ -34,7 +38,7 @@ module IO::Endpoint
|
|
34
38
|
# @yield [Socket] the socket which is being connected, may be invoked more than once
|
35
39
|
# @return [Socket] the connected socket
|
36
40
|
# @raise if no connection could complete successfully
|
37
|
-
def connect(wrapper =
|
41
|
+
def connect(wrapper = self.wrapper, &block)
|
38
42
|
last_error = nil
|
39
43
|
|
40
44
|
Addrinfo.foreach(*@specification) do |address|
|
@@ -59,7 +63,7 @@ module IO::Endpoint
|
|
59
63
|
# Invokes the given block for every address which can be bound to.
|
60
64
|
# @yield [Socket] the bound socket
|
61
65
|
# @return [Array<Socket>] an array of bound sockets
|
62
|
-
def bind(wrapper =
|
66
|
+
def bind(wrapper = self.wrapper, &block)
|
63
67
|
Addrinfo.foreach(*@specification).map do |address|
|
64
68
|
wrapper.bind(address, **@options, &block)
|
65
69
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2023, by Samuel Williams.
|
4
|
+
# Copyright, 2023-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "generic"
|
7
7
|
|
8
8
|
module IO::Endpoint
|
9
9
|
# This class doesn't exert ownership over the specified socket, wraps a native ::IO.
|
@@ -15,6 +15,10 @@ module IO::Endpoint
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_s
|
18
|
+
"socket:#{@socket}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
18
22
|
"\#<#{self.class} #{@socket.inspect}>"
|
19
23
|
end
|
20
24
|
|
@@ -3,10 +3,10 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2023-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "host_endpoint"
|
7
|
+
require_relative "generic"
|
8
8
|
|
9
|
-
require
|
9
|
+
require "openssl"
|
10
10
|
|
11
11
|
module OpenSSL
|
12
12
|
module SSL
|
@@ -87,7 +87,11 @@ module IO::Endpoint
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def to_s
|
90
|
-
"
|
90
|
+
"ssl:#{@endpoint}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
"\#<#{self.class} endpoint=#{@endpoint.inspect}>"
|
91
95
|
end
|
92
96
|
|
93
97
|
def address
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2023, by Samuel Williams.
|
4
|
+
# Copyright, 2023-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "address_endpoint"
|
7
7
|
|
8
8
|
module IO::Endpoint
|
9
9
|
# This class doesn't exert ownership over the specified unix socket and ensures exclusive access by using `flock` where possible.
|
@@ -16,7 +16,11 @@ module IO::Endpoint
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def to_s
|
19
|
-
"
|
19
|
+
"unix:#{@path}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"\#<#{self.class} path=#{@path.inspect}>"
|
20
24
|
end
|
21
25
|
|
22
26
|
attr :path
|
data/lib/io/endpoint/version.rb
CHANGED
data/lib/io/endpoint/wrapper.rb
CHANGED
@@ -1,14 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2023-
|
4
|
+
# Copyright, 2023-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "socket"
|
7
7
|
|
8
8
|
module IO::Endpoint
|
9
9
|
class Wrapper
|
10
10
|
include ::Socket::Constants
|
11
11
|
|
12
|
+
if Fiber.respond_to?(:scheduler)
|
13
|
+
def schedule(&block)
|
14
|
+
if Fiber.scheduler
|
15
|
+
Fiber.schedule(&block)
|
16
|
+
else
|
17
|
+
Thread.new(&block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
else
|
21
|
+
def schedule(&block)
|
22
|
+
Thread.new(&block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
12
26
|
def set_timeout(io, timeout)
|
13
27
|
if io.respond_to?(:timeout=)
|
14
28
|
io.timeout = timeout
|
@@ -30,8 +44,13 @@ module IO::Endpoint
|
|
30
44
|
# It may not be supported by the protocol (e.g. UDP). ¯\_(ツ)_/¯
|
31
45
|
end
|
32
46
|
|
33
|
-
|
34
|
-
|
47
|
+
# Connect a socket to a remote address.
|
48
|
+
# This is an extension point for subclasses to provide additional functionality.
|
49
|
+
#
|
50
|
+
# @parameter socket [Socket] The socket to connect.
|
51
|
+
# @parameter remote_address [Address] The remote address to connect to.
|
52
|
+
def socket_connect(socket, remote_address)
|
53
|
+
socket.connect(remote_address.to_sockaddr)
|
35
54
|
end
|
36
55
|
|
37
56
|
# Establish a connection to a given `remote_address`.
|
@@ -72,7 +91,7 @@ module IO::Endpoint
|
|
72
91
|
end
|
73
92
|
|
74
93
|
begin
|
75
|
-
socket
|
94
|
+
socket_connect(socket, remote_address)
|
76
95
|
rescue Exception
|
77
96
|
socket.close
|
78
97
|
raise
|
@@ -142,7 +161,7 @@ module IO::Endpoint
|
|
142
161
|
|
143
162
|
return socket unless block_given?
|
144
163
|
|
145
|
-
|
164
|
+
schedule do
|
146
165
|
begin
|
147
166
|
yield socket
|
148
167
|
ensure
|
@@ -151,11 +170,21 @@ module IO::Endpoint
|
|
151
170
|
end
|
152
171
|
end
|
153
172
|
|
173
|
+
# Accept a connection from a bound socket.
|
174
|
+
# This is an extension point for subclasses to provide additional functionality.
|
175
|
+
#
|
176
|
+
# @parameter server [Socket] The bound server socket.
|
177
|
+
# @returns [Tuple(Socket, Address)] The connected socket and the remote address.
|
178
|
+
def socket_accept(server)
|
179
|
+
server.accept
|
180
|
+
end
|
181
|
+
|
154
182
|
# Bind to a local address and accept connections in a loop.
|
155
183
|
def accept(server, timeout: nil, linger: nil, **options, &block)
|
156
184
|
# Ensure we use a `loop do ... end` so that state is not leaked between iterations:
|
185
|
+
|
157
186
|
loop do
|
158
|
-
socket, address = server
|
187
|
+
socket, address = socket_accept(server)
|
159
188
|
|
160
189
|
if linger
|
161
190
|
socket.setsockopt(SOL_SOCKET, SO_LINGER, 1)
|
@@ -165,7 +194,7 @@ module IO::Endpoint
|
|
165
194
|
set_timeout(socket, timeout)
|
166
195
|
end
|
167
196
|
|
168
|
-
|
197
|
+
schedule do
|
169
198
|
# Some sockets, notably SSL sockets, need application level negotiation before they are ready:
|
170
199
|
if socket.respond_to?(:start)
|
171
200
|
begin
|
@@ -183,31 +212,11 @@ module IO::Endpoint
|
|
183
212
|
end
|
184
213
|
end
|
185
214
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
def
|
190
|
-
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
class FiberWrapper < Wrapper
|
195
|
-
def async(&block)
|
196
|
-
::Fiber.schedule(&block)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
if Fiber.respond_to?(:scheduler)
|
201
|
-
def Wrapper.default
|
202
|
-
if Fiber.scheduler
|
203
|
-
FiberWrapper.new
|
204
|
-
else
|
205
|
-
ThreadWrapper.new
|
206
|
-
end
|
207
|
-
end
|
208
|
-
else
|
209
|
-
def Wrapper.default
|
210
|
-
ThreadWrapper.new
|
215
|
+
|
216
|
+
DEFAULT = new
|
217
|
+
|
218
|
+
def self.default
|
219
|
+
DEFAULT
|
211
220
|
end
|
212
221
|
end
|
213
222
|
end
|
data/license.md
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-endpoint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain:
|
11
10
|
- |
|
@@ -37,10 +36,8 @@ cert_chain:
|
|
37
36
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
38
37
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
39
38
|
-----END CERTIFICATE-----
|
40
|
-
date:
|
39
|
+
date: 2025-02-11 00:00:00.000000000 Z
|
41
40
|
dependencies: []
|
42
|
-
description:
|
43
|
-
email:
|
44
41
|
executables: []
|
45
42
|
extensions: []
|
46
43
|
extra_rdoc_files: []
|
@@ -66,7 +63,6 @@ licenses:
|
|
66
63
|
metadata:
|
67
64
|
documentation_uri: https://socketry.github.io/io-endpoint
|
68
65
|
source_code_uri: https://github.com/socketry/io-endpoint.git
|
69
|
-
post_install_message:
|
70
66
|
rdoc_options: []
|
71
67
|
require_paths:
|
72
68
|
- lib
|
@@ -81,8 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
77
|
- !ruby/object:Gem::Version
|
82
78
|
version: '0'
|
83
79
|
requirements: []
|
84
|
-
rubygems_version: 3.
|
85
|
-
signing_key:
|
80
|
+
rubygems_version: 3.6.2
|
86
81
|
specification_version: 4
|
87
82
|
summary: Provides a separation of concerns interface for IO endpoints.
|
88
83
|
test_files: []
|
metadata.gz.sig
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
�
|
4
|
-
|
5
|
-
|
6
|
-
������2Dz^
|
1
|
+
��UP���8�fp& ��f�����Y'mH���"wN1é�GL��Ǻ��nUoe>�
|
2
|
+
�
|
3
|
+
m\oXk�Al&�,x���2��x��7�qx��À��
|
4
|
+
��ݯ"�d�B�3O�=.
|
5
|
+
���;b�-���z\%VK8��6�y}~od$g�S���y0If%G�Ȭ�i1Jd�!J/S��ڲg��3�x5�uڔQ0E���J`�k�����ҥ���0���#�ʃ3�N?$�Y�3Ml���[UG�6��@1��жT`�t��3{I���ZAUz?1j3�Mh/�\O.�@��.�q�3���Qf�|Iޔ6E�uӸ������}��_R��o�H�f\b�\p��7ʰ�%�n�uN���Y�J]I�h�b�!���9�|�
|