io-endpoint 0.15.2 → 0.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ab97a46f319ca04819bf0c53b225163977979f70242a77d84f09f2a4da438be
4
- data.tar.gz: 396ba42209012cc921b2ecff5ef51eedcba214f215d7b4fcb62d4cb1f52347ea
3
+ metadata.gz: 99b21a2454e10904ebbf717bf4c1f5dcc337db1a50c88cefea5ef25116759ef2
4
+ data.tar.gz: 4f789220815ddc651cf8397697e30924660c231bebcdb2375f14eac1785fed39
5
5
  SHA512:
6
- metadata.gz: 77457ee3835cc1daa48a72ad6e025e855061dadbafbbd154090e81bf2854b2e96abe0a70f82eef58eb1893eaf1e968f668d6e58485879d2216ad0680a3a4263b
7
- data.tar.gz: 0f01d192468b9e6c38ec6e410086674bfe7a6ff96622ab82cd8739b5627e02c055bfbb193dbcd806fc4a87bea7880bf234c1a6c197e0a4f2d426dc66a49352f5
6
+ metadata.gz: 4c1720d279b749cd199455eb42ef4fe023a62287100230734d989b6dd8a944b8585c5f5565fa5899aa30b8563ae6350e938632e0c7b2a0b816d80f7273ddf81a
7
+ data.tar.gz: 06baff00190678e64cd396e6ac90e4f6a3e136295fabbaa92b6e7ab7d3894ab9345e084430745d6e0ba0633af7ed240a8478009fafc12134d3886aad4c642e05
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,113 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to get started with `io-endpoint`, a library that provides a separation of concerns interface for network I/O endpoints.
4
+
5
+ ## Installation
6
+
7
+ Add the gem to your project:
8
+
9
+ ~~~ bash
10
+ $ bundle add io-endpoint
11
+ ~~~
12
+
13
+ ## Core Concepts
14
+
15
+ `io-endpoint` provides a unified interface for working with network endpoints, allowing you to write code that is agnostic to the underlying transport mechanism (TCP, UDP, UNIX sockets, SSL/TLS). This separation of concerns makes it easier to:
16
+
17
+ - **Write transport-agnostic code**: Your application logic doesn't need to know whether it's using TCP, UDP, or UNIX sockets.
18
+ - **Test with different transports**: Easily swap between transports during testing.
19
+ - **Handle multiple addresses**: Automatically handle IPv4 and IPv6 addresses.
20
+ - **Compose endpoints**: Combine multiple endpoints for failover or load distribution.
21
+
22
+ The library centers around the {ruby IO::Endpoint::Generic} class, which represents a network endpoint that can be bound (for servers) or connected to (for clients). Different endpoint types handle different scenarios:
23
+
24
+ - {ruby IO::Endpoint::HostEndpoint} - Resolves hostnames to addresses (e.g., "localhost:8080")
25
+ - {ruby IO::Endpoint::AddressEndpoint} - Works with specific network addresses
26
+ - {ruby IO::Endpoint::UNIXEndpoint} - Handles UNIX domain sockets
27
+ - {ruby IO::Endpoint::SSLEndpoint} - Wraps endpoints with SSL/TLS encryption
28
+ - {ruby IO::Endpoint::CompositeEndpoint} - Combines multiple endpoints
29
+
30
+ ## Usage
31
+
32
+ ### Creating a TCP Server
33
+
34
+ When you need to create a server that listens on a specific port, you can use {ruby IO::Endpoint.tcp} to create a TCP endpoint:
35
+
36
+ ```ruby
37
+ require "io/endpoint"
38
+
39
+ # Create a TCP endpoint listening on localhost port 8080:
40
+ endpoint = IO::Endpoint.tcp("localhost", 8080)
41
+
42
+ # Bind to the endpoint and accept connections:
43
+ endpoint.bind do |server|
44
+ # The server socket is automatically closed when the block exits
45
+ server.listen(10)
46
+
47
+ loop do
48
+ client, address = server.accept
49
+ # Handle the client connection
50
+ client.close
51
+ end
52
+ end
53
+ ```
54
+
55
+ ### Creating a TCP Client
56
+
57
+ To connect to a remote server, use the `connect` method:
58
+
59
+ ```ruby
60
+ require "io/endpoint"
61
+
62
+ # Create a TCP endpoint for the remote server:
63
+ endpoint = IO::Endpoint.tcp("example.com", 80)
64
+
65
+ # Connect to the server:
66
+ endpoint.connect do |socket|
67
+ # The socket is automatically closed when the block exits
68
+ socket.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
69
+ response = socket.read
70
+ puts response
71
+ end
72
+ ```
73
+
74
+ ### Using UNIX Domain Sockets
75
+
76
+ For inter-process communication on the same machine, UNIX domain sockets provide better performance than TCP:
77
+
78
+ ```ruby
79
+ require "io/endpoint"
80
+
81
+ # Create a UNIX socket endpoint:
82
+ endpoint = IO::Endpoint.unix("/tmp/myapp.sock")
83
+
84
+ # Bind to the socket:
85
+ endpoint.bind do |server|
86
+ server.listen(10)
87
+
88
+ loop do
89
+ client, address = server.accept
90
+ # Handle the client connection
91
+ client.close
92
+ end
93
+ end
94
+ ```
95
+
96
+ ### Using SSL/TLS
97
+
98
+ To add encryption to your connections, wrap a TCP endpoint with SSL:
99
+
100
+ ```ruby
101
+ require "io/endpoint"
102
+
103
+ # Create an SSL endpoint:
104
+ endpoint = IO::Endpoint.ssl("example.com", 443, hostname: "example.com")
105
+
106
+ # Connect with SSL encryption:
107
+ endpoint.connect do |socket|
108
+ # The socket is automatically encrypted
109
+ socket.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
110
+ response = socket.read
111
+ puts response
112
+ end
113
+ ```
@@ -0,0 +1,17 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: Provides a separation of concerns interface for IO endpoints.
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/io-endpoint
7
+ source_code_uri: https://github.com/socketry/io-endpoint.git
8
+ files:
9
+ - path: getting-started.md
10
+ title: Getting Started
11
+ description: This guide explains how to get started with `io-endpoint`, a library
12
+ that provides a separation of concerns interface for network I/O endpoints.
13
+ - path: named-endpoints.md
14
+ title: Named Endpoints
15
+ description: This guide explains how to use `IO::Endpoint::NamedEndpoints` to manage
16
+ multiple endpoints by name, enabling scenarios like running the same application
17
+ on different protocols or ports.
@@ -0,0 +1,140 @@
1
+ # Named Endpoints
2
+
3
+ This guide explains how to use `IO::Endpoint::NamedEndpoints` to manage multiple endpoints by name, enabling scenarios like running the same application on different protocols or ports.
4
+
5
+ ## Overview
6
+
7
+ `NamedEndpoints` is a collection of endpoints that can be accessed by symbolic names. Unlike {ruby IO::Endpoint::CompositeEndpoint}, which treats endpoints as an ordered list for failover, `NamedEndpoints` allows you to:
8
+
9
+ - **Access endpoints by name**: Use symbolic keys like `:http1` or `:http2` instead of array indices.
10
+ - **Run multiple configurations**: Serve the same application on different protocols, ports, or transports simultaneously.
11
+ - **Iterate over endpoints**: Process all endpoints while maintaining their names for configuration lookup.
12
+
13
+ ## When to Use NamedEndpoints
14
+
15
+ Use `NamedEndpoints` when you need to:
16
+
17
+ - Run the same server application on multiple endpoints with different configurations (e.g., HTTP/1 and HTTP/2).
18
+ - Access endpoints by symbolic names rather than position.
19
+ - Bind multiple endpoints and create servers for each one.
20
+ - Manage a collection of endpoints where each has a specific role or configuration.
21
+
22
+ If you need failover behavior (trying endpoints in order until one succeeds), use {ruby IO::Endpoint::CompositeEndpoint} instead.
23
+
24
+ ## Creating Named Endpoints
25
+
26
+ ### Using the Constructor
27
+
28
+ Create a `NamedEndpoints` instance by passing a hash of endpoints:
29
+
30
+ ```ruby
31
+ require "io/endpoint"
32
+
33
+ http1_endpoint = IO::Endpoint.tcp("localhost", 8080)
34
+ http2_endpoint = IO::Endpoint.tcp("localhost", 8090)
35
+
36
+ named = IO::Endpoint::NamedEndpoints.new(
37
+ http1: http1_endpoint,
38
+ http2: http2_endpoint
39
+ )
40
+ ```
41
+
42
+ ### Using the Factory Method
43
+
44
+ The `IO::Endpoint.named` factory method provides a convenient way to create named endpoints:
45
+
46
+ ```ruby
47
+ require "io/endpoint"
48
+
49
+ named = IO::Endpoint.named(
50
+ http1: IO::Endpoint.tcp("localhost", 8080),
51
+ http2: IO::Endpoint.tcp("localhost", 8090),
52
+ https: IO::Endpoint.ssl("localhost", 8443)
53
+ )
54
+ ```
55
+
56
+ ## Accessing Endpoints
57
+
58
+ Access endpoints by their names using the `[]` operator:
59
+
60
+ ```ruby
61
+ named = IO::Endpoint.named(
62
+ http1: IO::Endpoint.tcp("localhost", 8080),
63
+ http2: IO::Endpoint.tcp("localhost", 8090)
64
+ )
65
+
66
+ # Access by name
67
+ http1 = named[:http1]
68
+ http2 = named[:http2]
69
+
70
+ # Returns nil if not found
71
+ missing = named[:nonexistent] # => nil
72
+ ```
73
+
74
+ ## Iterating Over Endpoints
75
+
76
+ ### Using `each`
77
+
78
+ The `each` method yields both the name and endpoint:
79
+
80
+ ```ruby
81
+ named = IO::Endpoint.named(
82
+ http1: IO::Endpoint.tcp("localhost", 8080),
83
+ http2: IO::Endpoint.tcp("localhost", 8090)
84
+ )
85
+
86
+ named.each do |name, endpoint|
87
+ puts "Endpoint #{name} is bound to #{endpoint}"
88
+ end
89
+ ```
90
+
91
+ To map over endpoint values, use `endpoints.values.map`:
92
+
93
+ ```ruby
94
+ protocols = named.endpoints.values.map do |endpoint|
95
+ endpoint.protocol.to_s
96
+ end
97
+
98
+ # => ["HTTP1", "HTTP2"]
99
+ ```
100
+
101
+ ## Binding Endpoints
102
+
103
+ To bind endpoints, iterate over the collection and bind each endpoint individually, or use the `bound` method to create a new collection with all endpoints bound.
104
+
105
+ The `bound` method creates a new `NamedEndpoints` instance where all endpoints are bound:
106
+
107
+ ```ruby
108
+ named = IO::Endpoint.named(
109
+ http1: IO::Endpoint.tcp("localhost", 8080),
110
+ http2: IO::Endpoint.tcp("localhost", 8090)
111
+ )
112
+
113
+ bound_named = named.bound(reuse_address: true)
114
+
115
+ # All endpoints are now bound
116
+ bound_named.each do |name, bound_endpoint|
117
+ server = bound_endpoint.sockets.first
118
+ server.listen(10)
119
+ end
120
+ ```
121
+
122
+ ## Connecting to Endpoints
123
+
124
+ To connect to a specific endpoint, access it by name and call `connect` on that endpoint:
125
+
126
+ ```ruby
127
+ named = IO::Endpoint.named(
128
+ primary: IO::Endpoint.tcp("server1.example.com", 80),
129
+ secondary: IO::Endpoint.tcp("server2.example.com", 80)
130
+ )
131
+
132
+ # Connect to a specific endpoint by name
133
+ named[:primary].connect do |socket|
134
+ socket.write("GET / HTTP/1.1\r\n\r\n")
135
+ response = socket.read
136
+ puts response
137
+ end
138
+ ```
139
+
140
+ If you need failover behavior (trying endpoints in order until one succeeds), use {ruby IO::Endpoint::CompositeEndpoint} instead.
@@ -9,13 +9,19 @@ require_relative "generic"
9
9
  require_relative "wrapper"
10
10
 
11
11
  module IO::Endpoint
12
+ # Represents an endpoint for a specific network address.
12
13
  class AddressEndpoint < Generic
14
+ # Initialize a new address endpoint.
15
+ # @parameter address [Address] The network address for this endpoint.
16
+ # @parameter options [Hash] Additional options to pass to the parent class.
13
17
  def initialize(address, **options)
14
18
  super(**options)
15
19
 
16
20
  @address = address
17
21
  end
18
22
 
23
+ # Get a string representation of the endpoint.
24
+ # @returns [String] A string representation of the endpoint address.
19
25
  def to_s
20
26
  case @address.afamily
21
27
  when Socket::AF_INET
@@ -27,22 +33,25 @@ module IO::Endpoint
27
33
  end
28
34
  end
29
35
 
36
+ # Get a detailed string representation of the endpoint.
37
+ # @returns [String] A detailed string representation including the address.
30
38
  def inspect
31
39
  "\#<#{self.class} address=#{@address.inspect}>"
32
40
  end
33
41
 
42
+ # @attribute [Address] The network address for this endpoint.
34
43
  attr :address
35
44
 
36
45
  # Bind a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
37
- # @yield {|socket| ...} An optional block which will be passed the socket.
38
- # @parameter socket [Socket] The socket which has been bound.
39
- # @return [Array(Socket)] the bound socket
46
+ # @yields {|socket| ...} If a block is given, yields the bound socket.
47
+ # @parameter socket [Socket] The socket which has been bound.
48
+ # @returns [Array(Socket)] the bound socket
40
49
  def bind(wrapper = self.wrapper, &block)
41
50
  [wrapper.bind(@address, **@options, &block)]
42
51
  end
43
52
 
44
53
  # Connects a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
45
- # @return [Socket] the connected socket
54
+ # @returns [Socket] the connected socket
46
55
  def connect(wrapper = self.wrapper, &block)
47
56
  wrapper.connect(@address, **@options, &block)
48
57
  end
@@ -8,7 +8,13 @@ require_relative "composite_endpoint"
8
8
  require_relative "address_endpoint"
9
9
 
10
10
  module IO::Endpoint
11
+ # Represents an endpoint that has been bound to one or more sockets.
11
12
  class BoundEndpoint < Generic
13
+ # Create a bound endpoint from an existing endpoint.
14
+ # @parameter endpoint [Generic] The endpoint to bind.
15
+ # @option backlog [Integer] The maximum length of the queue of pending connections.
16
+ # @option close_on_exec [Boolean] Whether to close sockets on exec.
17
+ # @returns [BoundEndpoint] A new bound endpoint instance.
12
18
  def self.bound(endpoint, backlog: Socket::SOMAXCONN, close_on_exec: false)
13
19
  sockets = endpoint.bind
14
20
 
@@ -19,6 +25,10 @@ module IO::Endpoint
19
25
  return self.new(endpoint, sockets, **endpoint.options)
20
26
  end
21
27
 
28
+ # Initialize a new bound endpoint.
29
+ # @parameter endpoint [Generic] The original endpoint that was bound.
30
+ # @parameter sockets [Array(Socket)] The sockets that were bound.
31
+ # @parameter options [Hash] Additional options to pass to the parent class.
22
32
  def initialize(endpoint, sockets, **options)
23
33
  super(**options)
24
34
 
@@ -26,7 +36,9 @@ module IO::Endpoint
26
36
  @sockets = sockets
27
37
  end
28
38
 
39
+ # @attribute [Generic] The original endpoint that was bound.
29
40
  attr :endpoint
41
+ # @attribute [Array(Socket)] The sockets that were bound.
30
42
  attr :sockets
31
43
 
32
44
  # A endpoint for the local end of the bound socket.
@@ -49,19 +61,29 @@ module IO::Endpoint
49
61
  return CompositeEndpoint.new(endpoints)
50
62
  end
51
63
 
64
+ # Close all bound sockets.
52
65
  def close
53
66
  @sockets.each(&:close)
54
67
  @sockets.clear
55
68
  end
56
69
 
70
+ # Get a string representation of the bound endpoint.
71
+ # @returns [String] A string representation of the bound endpoint.
57
72
  def to_s
58
73
  "bound:#{@endpoint}"
59
74
  end
60
75
 
76
+ # Get a detailed string representation of the bound endpoint.
77
+ # @returns [String] A detailed string representation including socket count.
61
78
  def inspect
62
79
  "\#<#{self.class} #{@sockets.size} bound sockets for #{@endpoint}>"
63
80
  end
64
81
 
82
+ # Bind the endpoint using the given wrapper.
83
+ # @parameter wrapper [Wrapper] The wrapper to use for binding.
84
+ # @yields {|socket| ...} If a block is given, yields each bound socket.
85
+ # @parameter socket [Socket] A bound socket.
86
+ # @returns [Array(Socket)] An array of bound sockets.
65
87
  def bind(wrapper = self.wrapper, &block)
66
88
  @sockets.map do |server|
67
89
  if block_given?
@@ -76,6 +98,9 @@ module IO::Endpoint
76
98
  end
77
99
 
78
100
  class Generic
101
+ # Create a bound endpoint from this endpoint.
102
+ # @parameter options [Hash] Options to pass to {BoundEndpoint.bound}.
103
+ # @returns [BoundEndpoint] A new bound endpoint instance.
79
104
  def bound(**options)
80
105
  BoundEndpoint.bound(self, **options)
81
106
  end
@@ -8,6 +8,9 @@ require_relative "generic"
8
8
  module IO::Endpoint
9
9
  # A composite endpoint is a collection of endpoints that are used in order.
10
10
  class CompositeEndpoint < Generic
11
+ # Initialize a new composite endpoint.
12
+ # @parameter endpoints [Array(Generic)] The endpoints to compose.
13
+ # @parameter options [Hash] Additional options to pass to the parent class and propagate to endpoints.
11
14
  def initialize(endpoints, **options)
12
15
  super(**options)
13
16
 
@@ -19,18 +22,26 @@ module IO::Endpoint
19
22
  @endpoints = endpoints
20
23
  end
21
24
 
25
+ # Get a string representation of the composite endpoint.
26
+ # @returns [String] A string representation listing all endpoints.
22
27
  def to_s
23
28
  "composite:#{@endpoints.join(",")}"
24
29
  end
25
30
 
31
+ # Get a detailed string representation of the composite endpoint.
32
+ # @returns [String] A detailed string representation including all endpoints.
26
33
  def inspect
27
34
  "\#<#{self.class} endpoints=#{@endpoints}>"
28
35
  end
29
36
 
37
+ # Create a new composite endpoint with merged options.
38
+ # @parameter options [Hash] Additional options to merge with existing options.
39
+ # @returns [CompositeEndpoint] A new composite endpoint instance with merged options.
30
40
  def with(**options)
31
41
  self.class.new(endpoints.map{|endpoint| endpoint.with(**options)}, **@options.merge(options))
32
42
  end
33
43
 
44
+ # @attribute [Array(Generic)] The endpoints in this composite endpoint.
34
45
  attr :endpoints
35
46
 
36
47
  # The number of endpoints in the composite endpoint.
@@ -38,12 +49,21 @@ module IO::Endpoint
38
49
  @endpoints.size
39
50
  end
40
51
 
52
+ # Enumerate all endpoints in the composite endpoint.
53
+ # @yields {|endpoint| ...} For each endpoint in the composite, yields it.
54
+ # @parameter endpoint [Generic] An endpoint in the composite.
41
55
  def each(&block)
42
56
  @endpoints.each do |endpoint|
43
57
  endpoint.each(&block)
44
58
  end
45
59
  end
46
60
 
61
+ # Connect to the first endpoint that succeeds.
62
+ # @parameter wrapper [Wrapper] The wrapper to use for connecting.
63
+ # @yields {|socket| ...} If a block is given, yields the connected socket from the first successful endpoint.
64
+ # @parameter socket [Socket] The connected socket.
65
+ # @returns [Socket] The connected socket.
66
+ # @raises [Exception] If all endpoints fail to connect, raises the last error encountered.
47
67
  def connect(wrapper = self.wrapper, &block)
48
68
  last_error = nil
49
69
 
@@ -57,6 +77,11 @@ module IO::Endpoint
57
77
  raise last_error
58
78
  end
59
79
 
80
+ # Bind all endpoints in the composite.
81
+ # @parameter wrapper [Wrapper] The wrapper to use for binding.
82
+ # @yields {|socket| ...} For each endpoint that is bound, yields the bound socket.
83
+ # @parameter socket [Socket] A bound socket.
84
+ # @returns [Array(Socket)] An array of bound sockets if no block is given.
60
85
  def bind(wrapper = self.wrapper, &block)
61
86
  if block_given?
62
87
  @endpoints.each do |endpoint|
@@ -68,6 +93,10 @@ module IO::Endpoint
68
93
  end
69
94
  end
70
95
 
96
+ # Create a composite endpoint from multiple endpoints.
97
+ # @parameter endpoints [Array(Generic)] The endpoints to compose.
98
+ # @parameter options [Hash] Additional options to pass to the composite endpoint.
99
+ # @returns [CompositeEndpoint] A new composite endpoint instance.
71
100
  def self.composite(*endpoints, **options)
72
101
  CompositeEndpoint.new(endpoints, **options)
73
102
  end
@@ -10,7 +10,12 @@ require_relative "socket_endpoint"
10
10
  require "openssl"
11
11
 
12
12
  module IO::Endpoint
13
+ # Represents an endpoint that has been connected to a socket.
13
14
  class ConnectedEndpoint < Generic
15
+ # Create a connected endpoint from an existing endpoint.
16
+ # @parameter endpoint [Generic] The endpoint to connect.
17
+ # @option close_on_exec [Boolean] Whether to close the socket on exec.
18
+ # @returns [ConnectedEndpoint] A new connected endpoint instance.
14
19
  def self.connected(endpoint, close_on_exec: false)
15
20
  socket = endpoint.connect
16
21
 
@@ -19,6 +24,10 @@ module IO::Endpoint
19
24
  return self.new(endpoint, socket, **endpoint.options)
20
25
  end
21
26
 
27
+ # Initialize a new connected endpoint.
28
+ # @parameter endpoint [Generic] The original endpoint that was connected.
29
+ # @parameter socket [Socket] The socket that was connected.
30
+ # @parameter options [Hash] Additional options to pass to the parent class.
22
31
  def initialize(endpoint, socket, **options)
23
32
  super(**options)
24
33
 
@@ -26,7 +35,9 @@ module IO::Endpoint
26
35
  @socket = socket
27
36
  end
28
37
 
38
+ # @attribute [Generic] The original endpoint that was connected.
29
39
  attr :endpoint
40
+ # @attribute [Socket] The socket that was connected.
30
41
  attr :socket
31
42
 
32
43
  # A endpoint for the local end of the bound socket.
@@ -41,6 +52,11 @@ module IO::Endpoint
41
52
  AddressEndpoint.new(socket.to_io.remote_address, **options)
42
53
  end
43
54
 
55
+ # Connect using the already connected socket.
56
+ # @parameter wrapper [Wrapper] The wrapper to use (unused, socket is already connected).
57
+ # @yields {|socket| ...} If a block is given, yields the connected socket.
58
+ # @parameter socket [Socket] The connected socket.
59
+ # @returns [Socket] The connected socket or a duplicate if no block is given.
44
60
  def connect(wrapper = self.wrapper, &block)
45
61
  if block_given?
46
62
  yield @socket
@@ -49,6 +65,7 @@ module IO::Endpoint
49
65
  end
50
66
  end
51
67
 
68
+ # Close the connected socket.
52
69
  def close
53
70
  if @socket
54
71
  @socket.close
@@ -56,16 +73,23 @@ module IO::Endpoint
56
73
  end
57
74
  end
58
75
 
76
+ # Get a string representation of the connected endpoint.
77
+ # @returns [String] A string representation of the connected endpoint.
59
78
  def to_s
60
79
  "connected:#{@endpoint}"
61
80
  end
62
81
 
82
+ # Get a detailed string representation of the connected endpoint.
83
+ # @returns [String] A detailed string representation including the socket.
63
84
  def inspect
64
85
  "\#<#{self.class} #{@socket} connected for #{@endpoint}>"
65
86
  end
66
87
  end
67
-
88
+
68
89
  class Generic
90
+ # Create a connected endpoint from this endpoint.
91
+ # @parameter options [Hash] Options to pass to {ConnectedEndpoint.connected}.
92
+ # @returns [ConnectedEndpoint] A new connected endpoint instance.
69
93
  def connected(**options)
70
94
  ConnectedEndpoint.connected(self, **options)
71
95
  end
@@ -12,10 +12,15 @@ module IO::Endpoint
12
12
 
13
13
  # Endpoints represent a way of connecting or binding to an address.
14
14
  class Generic
15
+ # Initialize a new generic endpoint.
16
+ # @parameter options [Hash] Configuration options for the endpoint.
15
17
  def initialize(**options)
16
18
  @options = options.freeze
17
19
  end
18
20
 
21
+ # Create a new endpoint with merged options.
22
+ # @parameter options [Hash] Additional options to merge with existing options.
23
+ # @returns [Generic] A new endpoint instance with merged options.
19
24
  def with(**options)
20
25
  dup = self.dup
21
26
 
@@ -26,43 +31,43 @@ module IO::Endpoint
26
31
 
27
32
  attr_accessor :options
28
33
 
29
- # @return [String] The hostname of the bound socket.
34
+ # @returns [String] The hostname of the bound socket.
30
35
  def hostname
31
36
  @options[:hostname]
32
37
  end
33
38
 
34
39
  # If `SO_REUSEPORT` is enabled on a socket, the socket can be successfully bound even if there are existing sockets bound to the same address, as long as all prior bound sockets also had `SO_REUSEPORT` set before they were bound.
35
- # @return [Boolean, nil] The value for `SO_REUSEPORT`.
40
+ # @returns [Boolean, nil] The value for `SO_REUSEPORT`.
36
41
  def reuse_port?
37
42
  @options[:reuse_port]
38
43
  end
39
44
 
40
45
  # If `SO_REUSEADDR` is enabled on a socket prior to binding it, the socket can be successfully bound unless there is a conflict with another socket bound to exactly the same combination of source address and port. Additionally, when set, binding a socket to the address of an existing socket in `TIME_WAIT` is not an error.
41
- # @return [Boolean] The value for `SO_REUSEADDR`.
46
+ # @returns [Boolean] The value for `SO_REUSEADDR`.
42
47
  def reuse_address?
43
48
  @options[:reuse_address]
44
49
  end
45
50
 
46
51
  # Controls SO_LINGER. The amount of time the socket will stay in the `TIME_WAIT` state after being closed.
47
- # @return [Integer, nil] The value for SO_LINGER.
52
+ # @returns [Integer, nil] The value for SO_LINGER.
48
53
  def linger
49
54
  @options[:linger]
50
55
  end
51
56
 
52
- # @return [Numeric] The default timeout for socket operations.
57
+ # @returns [Numeric] The default timeout for socket operations.
53
58
  def timeout
54
59
  @options[:timeout]
55
60
  end
56
61
 
57
- # @return [Address] the address to bind to before connecting.
62
+ # @returns [Address] the address to bind to before connecting.
58
63
  def local_address
59
64
  @options[:local_address]
60
65
  end
61
66
 
62
67
  # Bind a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
63
68
  # @parameter wrapper [Wrapper] The wrapper to use for binding.
64
- # @yields {|socket| ...} An optional block which will be passed the socket.
65
- # @parameter socket [Socket] The socket which has been bound.
69
+ # @yields {|socket| ...} If a block is given, yields the bound socket.
70
+ # @parameter socket [Socket] The socket which has been bound.
66
71
  # @returns [Array(Socket)] the bound socket
67
72
  def bind(wrapper = self.wrapper, &block)
68
73
  raise NotImplementedError
@@ -70,14 +75,15 @@ module IO::Endpoint
70
75
 
71
76
  # Connects a socket to the given address. If a block is given, the socket will be automatically closed when the block exits.
72
77
  # @parameter wrapper [Wrapper] The wrapper to use for connecting.
73
- # @return [Socket] the connected socket
78
+ # @returns [Socket] the connected socket
74
79
  def connect(wrapper = self.wrapper, &block)
75
80
  raise NotImplementedError
76
81
  end
77
82
 
78
83
  # Bind and accept connections on the given address.
79
84
  # @parameter wrapper [Wrapper] The wrapper to use for accepting connections.
80
- # @yields [Socket] The accepted socket.
85
+ # @yields {|socket| ...} For each accepted connection, yields the socket.
86
+ # @parameter socket [Socket] The accepted socket.
81
87
  def accept(wrapper = self.wrapper, &block)
82
88
  bind(wrapper) do |server|
83
89
  wrapper.accept(server, **@options, &block)
@@ -85,7 +91,7 @@ module IO::Endpoint
85
91
  end
86
92
 
87
93
  # Enumerate all discrete paths as endpoints.
88
- # @yields {|endpoint| ...} A block which will be passed each endpoint.
94
+ # @yields {|endpoint| ...} For each endpoint, yields it.
89
95
  # @parameter endpoint [Endpoint] The endpoint.
90
96
  def each
91
97
  return to_enum unless block_given?
@@ -97,8 +103,8 @@ module IO::Endpoint
97
103
  #
98
104
  # You should not use untrusted input as it may execute arbitrary code.
99
105
  #
100
- # @param string [String] URI as string. Scheme will decide implementation used.
101
- # @param options keyword arguments passed through to {#initialize}
106
+ # @parameter string [String] URI as string. Scheme will decide implementation used.
107
+ # @parameter options keyword arguments passed through to {#initialize}
102
108
  #
103
109
  # @see Endpoint.ssl ssl - invoked when parsing a URL with the ssl scheme "ssl://127.0.0.1"
104
110
  # @see Endpoint.tcp tcp - invoked when parsing a URL with the tcp scheme: "tcp://127.0.0.1"