io-endpoint 0.17.1 → 0.17.2

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: 6a1fd8b9900456445fa95fc7de22135fc86c96c9ed05586acec98c492de11ab7
4
- data.tar.gz: b179c79964d3fba2d92753fc4494fd0fa2a88bcb825dc20561a61784adc60fec
3
+ metadata.gz: af9622a36778938631f679dda8abc06668454c5693551761173757b2df01ec76
4
+ data.tar.gz: e3d58e00ad0e067628d53d7950fd4a801a588fe7935e53f74d0bdc3e5f810e8a
5
5
  SHA512:
6
- metadata.gz: 6370da98fec490d732f957fd5cf947396551bed010d88211a4e6684529d4ea930bff17753ae672c3cc8e482315f96bd2d31ee4365a77bb85476fa12da23ace4f
7
- data.tar.gz: c478357360832dd7c225979f2531025025f711fca06616b4efd2a7398d700a1c248021a5d2be6749fd9b5dc5950cd04a953ac383527c8d2cb301745ee5ac5038
6
+ metadata.gz: a341c62ee7a064bc18cdecb7fcfc08cc40b2a9f863baf12b0423d8389af1c0cf90b21025d78b4dce04e7abe3007f2c32ef0e0bb6f46bc9cecaee620d1e29a967
7
+ data.tar.gz: f1abb49094a40133a3861d55a58be3efb2d868ad40cddf27b33a080e4596a872f422e7a2c70cca0ead57ebff47b193c5b823ef55c7cd8cac5fd5c855ae9749a1
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2025, by Samuel Williams.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
5
 
6
6
  require "socket"
7
7
 
@@ -28,6 +28,8 @@ module IO::Endpoint
28
28
  "inet:#{@address.inspect_sockaddr}"
29
29
  when Socket::AF_INET6
30
30
  "inet6:#{@address.inspect_sockaddr}"
31
+ when Socket::AF_UNIX
32
+ "unix:#{@address.unix_path}"
31
33
  else
32
34
  "address:#{@address.inspect_sockaddr}"
33
35
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2025, by Samuel Williams.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "address_endpoint"
7
7
 
@@ -31,7 +31,6 @@ module IO::Endpoint
31
31
  "\#<#{self.class} name=#{nodename.inspect} service=#{service.inspect} family=#{family.inspect} type=#{socktype.inspect} protocol=#{protocol.inspect} flags=#{flags.inspect}>"
32
32
  end
33
33
 
34
-
35
34
  # @attribute [Array] The host specification array.
36
35
  attr :specification
37
36
 
@@ -1,22 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2025, by Samuel Williams.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
+ # Copyright, 2026, by Delton Ding.
6
+
7
+ require "digest"
8
+ require "fileutils"
9
+ require "tmpdir"
5
10
 
6
11
  require_relative "address_endpoint"
7
12
 
8
13
  module IO::Endpoint
9
14
  # This class doesn't exert ownership over the specified unix socket and ensures exclusive access by using `flock` where possible.
10
15
  class UNIXEndpoint < AddressEndpoint
16
+ # Compute a stable temporary UNIX socket path for an overlong path.
17
+ # @parameter path [String] The original (possibly overlong) path.
18
+ # @returns [String] A short, stable path suitable for {Address.unix}.
19
+ def self.short_path_for(path)
20
+ # We need to ensure the path is absolute and canonical, otherwise the SHA1 hash will not be consistent:
21
+ path = File.expand_path(path)
22
+
23
+ # We then use the SHA1 hash of the path to create a short, stable path:
24
+ File.join(Dir.tmpdir, Digest::SHA1.hexdigest(path) + ".ipc")
25
+ end
26
+
11
27
  # Initialize a new UNIX domain socket endpoint.
12
28
  # @parameter path [String] The path to the UNIX socket.
13
29
  # @parameter type [Integer] The socket type (defaults to Socket::SOCK_STREAM).
14
30
  # @parameter options [Hash] Additional options to pass to the parent class.
15
31
  def initialize(path, type = Socket::SOCK_STREAM, **options)
16
- # I wonder if we should implement chdir behaviour in here if path is longer than 104 characters.
17
- super(Address.unix(path, type), **options)
18
-
19
32
  @path = path
33
+
34
+ begin
35
+ address = Address.unix(path, type)
36
+ rescue ArgumentError
37
+ path = self.class.short_path_for(path)
38
+ address = Address.unix(path, type)
39
+ end
40
+
41
+ super(address, **options)
20
42
  end
21
43
 
22
44
  # Get a string representation of the UNIX endpoint.
@@ -28,11 +50,28 @@ module IO::Endpoint
28
50
  # Get a detailed string representation of the UNIX endpoint.
29
51
  # @returns [String] A detailed string representation including the path.
30
52
  def inspect
31
- "\#<#{self.class} path=#{@path.inspect}>"
53
+ target_path = @address.unix_path
54
+
55
+ if @path == target_path
56
+ "\#<#{self.class} path=#{@path.inspect}>"
57
+ else
58
+ "\#<#{self.class} path=#{@path.inspect} target=#{target_path.inspect}>"
59
+ end
32
60
  end
33
61
 
34
62
  # @attribute [String] The path to the UNIX socket.
35
- attr :path
63
+ def path
64
+ @path
65
+ end
66
+
67
+ # Check if a symlink is used for this endpoint.
68
+ #
69
+ # A symlink is created when the original path exceeds the system's maximum UNIX socket path length and a shorter temporary path is used for the actual socket.
70
+ #
71
+ # @returns [Boolean] True if the original path differs from the socket path, indicating a symlink is required.
72
+ def symlink?
73
+ File.symlink?(@path)
74
+ end
36
75
 
37
76
  # Check if the socket is currently bound and accepting connections.
38
77
  # @returns [Boolean] True if the socket is bound and accepting connections, false otherwise.
@@ -52,16 +91,61 @@ module IO::Endpoint
52
91
  # @returns [Array(Socket)] The bound socket.
53
92
  # @raises [Errno::EADDRINUSE] If the socket is still in use by another process.
54
93
  def bind(...)
55
- super
94
+ result = super
95
+ create_symlink_if_required!
96
+ return result
56
97
  rescue Errno::EADDRINUSE
57
98
  # 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.
58
99
  if !bound?
59
- File.unlink(@path) rescue nil
100
+ unlink_stale_paths!
60
101
  retry
61
102
  else
62
103
  raise
63
104
  end
64
105
  end
106
+
107
+ # Read a symlink, returning nil if the file does not exist.
108
+ #
109
+ # @parameter path [String] The path to the symlink.
110
+ # @returns [String | Nil] The target of the symlink, or nil if the file does not exist.
111
+ private def read_link(path)
112
+ File.readlink(path)
113
+ rescue # Errno::ENOENT, Errno::EINVAL
114
+ # The file is not a symlink, or the symlink is invalid.
115
+ nil
116
+ end
117
+
118
+ # Create a symlink to the actual socket path if required.
119
+ private def create_symlink_if_required!
120
+ # Ensure the directory exists:
121
+ FileUtils.mkdir_p(File.dirname(@path))
122
+
123
+ # This is the actual path we want to use for the socket:
124
+ target_path = @address.unix_path
125
+
126
+ # If it's the same as the original path, we are done:
127
+ return if @path == target_path
128
+
129
+ # Otherwise, we need might need to create a symlink:
130
+ if read_link(target_path) == @path
131
+ return
132
+ else
133
+ File.unlink(@path) rescue nil
134
+ end
135
+
136
+ # Create symlink at @path (original long path) pointing to target_path (short socket path)
137
+ File.symlink(target_path, @path)
138
+ end
139
+
140
+ private def unlink_stale_paths!
141
+ File.unlink(@path) rescue nil
142
+
143
+ target_path = @address.unix_path
144
+
145
+ if @path != target_path
146
+ File.unlink(target_path) rescue nil
147
+ end
148
+ end
65
149
  end
66
150
 
67
151
  # @parameter path [String]
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2025, by Samuel Williams.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
5
 
6
6
  # @namespace
7
7
  class IO
8
8
  # @namespace
9
9
  module Endpoint
10
- VERSION = "0.17.1"
10
+ VERSION = "0.17.2"
11
11
  end
12
12
  end
data/lib/io/endpoint.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2025, by Samuel Williams.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "endpoint/version"
7
7
  require_relative "endpoint/generic"
data/license.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # MIT License
2
2
 
3
3
  Copyright, 2023-2026, by Samuel Williams.
4
+ Copyright, 2026, by Delton Ding.
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -16,6 +16,10 @@ Please see the [project documentation](https://socketry.github.io/io-endpoint) f
16
16
 
17
17
  Please see the [project releases](https://socketry.github.io/io-endpointreleases/index) for all releases.
18
18
 
19
+ ### v0.17.2
20
+
21
+ - When the unix path is bigger than what can fit into `struct sockaddr_un`, a shorter temporary path will be used instead and a symlink created at the original path.
22
+
19
23
  ### v0.17.1
20
24
 
21
25
  - Add `#to_s` and `#inspect` for `IO::Endpoint::NamedEndpoints`.
@@ -54,10 +58,6 @@ Please see the [project releases](https://socketry.github.io/io-endpointreleases
54
58
 
55
59
  - Propagate options assigned to composite endpoint to nested endpoints.
56
60
 
57
- ### v0.12.0
58
-
59
- - Expose `size` and internal endpoints for composite endpoint.
60
-
61
61
  ## See Also
62
62
 
63
63
  - [async-io](https://github.com/socketry/async-io) — Where this implementation originally came from.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.17.2
4
+
5
+ - When the unix path is bigger than what can fit into `struct sockaddr_un`, a shorter temporary path will be used instead and a symlink created at the original path.
6
+
3
7
  ## v0.17.1
4
8
 
5
9
  - Add `#to_s` and `#inspect` for `IO::Endpoint::NamedEndpoints`.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,10 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-endpoint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.1
4
+ version: 0.17.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
+ - Delton Ding
8
9
  bindir: bin
9
10
  cert_chain:
10
11
  - |
metadata.gz.sig CHANGED
Binary file