io-endpoint 0.13.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80426a4ee162f18eb945d9ce2fd75bde3cf23e9d5bb8333faa2ff627f6b87a23
4
- data.tar.gz: 37d79a722c7371c993476fc3061d5c63e7c10f6051dcdc5215d079bac88d5965
3
+ metadata.gz: 85f53ad778e7ab62a17def218d84235d361df69805fa9f0edbf08b4ec0dfebc1
4
+ data.tar.gz: db08fed38c6ff791c0d4b98af3a661af718c5c44595f135cb0bf77147a36155b
5
5
  SHA512:
6
- metadata.gz: b89867de53a90531d5b9f1484d529e3f556b6ac75b30b6174c2bfc7f3c0199b80208b7699f6998d8c61b8e0eef666b72f66e10377090b566ae4058b4d229859d
7
- data.tar.gz: a7c98d22e20fd3cdd558b8266b92bfb88fc110c6f0a144769daf58b505447cc0e0fff04a4b07d0bbe490327a3a46d5f7b353f436cb72651040cae8e350523f54
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 'socket'
6
+ require "socket"
7
7
 
8
- require_relative 'generic'
9
- require_relative 'wrapper'
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 = Wrapper.default, &block)
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 = Wrapper.default, &block)
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 'generic'
7
- require_relative 'composite_endpoint'
8
- require_relative 'address_endpoint'
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 = Wrapper.default, &block)
65
+ def bind(wrapper = self.wrapper, &block)
62
66
  @sockets.map do |server|
63
67
  if block_given?
64
- wrapper.async do
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-2024, by Samuel Williams.
4
+ # Copyright, 2023-2025, by Samuel Williams.
5
5
 
6
- require_relative 'generic'
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 = Wrapper.default, &block)
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 = Wrapper.default, &block)
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 'generic'
7
- require_relative 'composite_endpoint'
8
- require_relative 'socket_endpoint'
6
+ require_relative "generic"
7
+ require_relative "composite_endpoint"
8
+ require_relative "socket_endpoint"
9
9
 
10
- require 'openssl'
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 = Wrapper.default, &block)
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
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2024, by Samuel Williams.
4
+ # Copyright, 2023-2025, by Samuel Williams.
5
5
 
6
6
  # require_relative 'address'
7
- require 'uri'
8
- require 'socket'
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 = Wrapper.default, &block)
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 = Wrapper.default, &block)
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 = Wrapper.default, &block)
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 'address_endpoint'
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 = Wrapper.default, &block)
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 = Wrapper.default, &block)
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
@@ -3,5 +3,5 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
- require_relative 'bound_endpoint'
7
- require_relative 'connected_endpoint'
6
+ require_relative "bound_endpoint"
7
+ require_relative "connected_endpoint"
@@ -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 'generic'
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 'host_endpoint'
7
- require_relative 'generic'
6
+ require_relative "host_endpoint"
7
+ require_relative "generic"
8
8
 
9
- require 'openssl'
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
- "\#<#{self.class} #{@endpoint}>"
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 'address_endpoint'
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
- "\#<#{self.class} #{@path.inspect}>"
19
+ "unix:#{@path}"
20
+ end
21
+
22
+ def inspect
23
+ "\#<#{self.class} path=#{@path.inspect}>"
20
24
  end
21
25
 
22
26
  attr :path
@@ -5,6 +5,6 @@
5
5
 
6
6
  class IO
7
7
  module Endpoint
8
- VERSION = "0.13.1"
8
+ VERSION = "0.15.0"
9
9
  end
10
10
  end
@@ -1,14 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2024, by Samuel Williams.
4
+ # Copyright, 2023-2025, by Samuel Williams.
5
5
 
6
- require 'socket'
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
- def async
34
- raise NotImplementedError
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.connect(remote_address.to_sockaddr)
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
- async do
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.accept
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
- async do
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
- end
187
-
188
- class ThreadWrapper < Wrapper
189
- def async(&block)
190
- ::Thread.new(&block)
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
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2023-2024, by Samuel Williams.
3
+ Copyright, 2023-2025, by Samuel Williams.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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.13.1
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: 2024-08-27 00:00:00.000000000 Z
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.5.11
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
- ^-a�pы }x�7�!ipn�K��h��EWl}�Ă��ソ]) �?��n)3�[��x�,ό��|�1�K���9�S-F��g BZ�<�Oթ Rj{�:���\��ZC>6S��K_�}6_��$G�`���~bp,*���$
2
- ��ix��`}r����]M���S i� 7<����
3
-
4
- ��%�^��47��{��ptl$��t���-=&YU&��ҁ��av���I�i4s
5
- >)����cC�٣Ŧ���{:-N.K#
6
- ������2Dz^
1
+ ��UP���8fp& ��f�޻����Y'mH���"wN1é�GL��Ǻ��nUoe>�
2
+
3
+ m\oXkAl&�,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�|�