IO::EndpointSourceIOEndpointUNIXEndpoint

class UNIXEndpoint

This class doesn't exert ownership over the specified unix socket and ensures exclusive access by using flock where possible.

Definitions

def self.short_path_for(path)

Compute a stable temporary UNIX socket path for an overlong path.

Signature

parameter path String

The original (possibly overlong) path.

returns String

A short, stable path suitable for Address.unix.

Implementation

def self.short_path_for(path)
	# We need to ensure the path is absolute and canonical, otherwise the SHA1 hash will not be consistent:
	path = File.expand_path(path)
	
	# We then use the SHA1 hash of the path to create a short, stable path:
	File.join(Dir.tmpdir, Digest::SHA1.hexdigest(path) + ".ipc")
end

def initialize(path, type = Socket::SOCK_STREAM, **options)

Initialize a new UNIX domain socket endpoint.

Signature

parameter path String

The path to the UNIX socket.

parameter type Integer

The socket type (defaults to Socket::SOCK_STREAM).

parameter options Hash

Additional options to pass to the parent class.

Implementation

def initialize(path, type = Socket::SOCK_STREAM, **options)
	@path = path
	
	begin
		address = Address.unix(path, type)
	rescue ArgumentError
		path = self.class.short_path_for(path)
		address = Address.unix(path, type)
	end
	
	super(address, **options)
end

def to_s

Get a string representation of the UNIX endpoint.

Signature

returns String

A string representation showing the socket path.

Implementation

def to_s
	"unix:#{@path}"
end

def inspect

Get a detailed string representation of the UNIX endpoint.

Signature

returns String

A detailed string representation including the path.

Implementation

def inspect
	target_path = @address.unix_path
	
	if @path == target_path
		"\#<#{self.class} path=#{@path.inspect}>"
	else
		"\#<#{self.class} path=#{@path.inspect} target=#{target_path.inspect}>"
	end
end

def path

Signature

attribute String

The path to the UNIX socket.

Implementation

def path
	@path
end

def symlink?

Check if a symlink is used for this endpoint.

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.

Signature

returns Boolean

True if the original path differs from the socket path, indicating a symlink is required.

Implementation

def symlink?
	File.symlink?(@path)
end

def bound?

Check if the socket is currently bound and accepting connections.

Signature

returns Boolean

True if the socket is bound and accepting connections, false otherwise.

Implementation

def bound?
	self.connect do
		return true
	end
rescue Errno::ECONNREFUSED
	return false
rescue Errno::ENOENT
	return false
end

def bind(...)

Bind the UNIX socket, handling stale socket files.

Signature

yields {|socket| ...}

If a block is given, yields the bound socket.

parameter socket Socket

The bound socket.

returns Array(Socket)

The bound socket.

raises Errno::EADDRINUSE

If the socket is still in use by another process.

Implementation

def bind(...)
	result = super
	create_symlink_if_required!
	return result
rescue Errno::EADDRINUSE
	# 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.
	if !bound?
		unlink_stale_paths!
		retry
	else
		raise
	end
end