/
unix_socket.cr
106 lines (93 loc) · 2.43 KB
/
unix_socket.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# A local interprocess communication clientsocket.
#
# Only available on UNIX and UNIX-like operating systems.
#
# Example usage:
# ```
# require "socket"
#
# sock = UNIXSocket.new("/tmp/myapp.sock")
# sock.puts "message"
# response = sock.gets
# sock.close
# ```
class UNIXSocket < Socket
getter path : String?
# Connects a named UNIX socket, bound to a filesystem pathname.
def initialize(@path : String, type : Type = Type::STREAM)
super(Family::UNIX, type, Protocol::IP)
connect(UNIXAddress.new(path)) do |error|
close
raise error
end
end
protected def initialize(family : Family, type : Type)
super family, type, Protocol::IP
end
# Creates a UNIXSocket from an already configured raw file descriptor
def initialize(*, fd : Int32, type : Type = Type::STREAM, @path : String? = nil)
super fd, Family::UNIX, type, Protocol::IP
end
# Opens an UNIX socket to a filesystem pathname, yields it to the block, then
# eventually closes the socket when the block returns.
#
# Returns the value of the block.
def self.open(path, type : Type = Type::STREAM)
sock = new(path, type)
begin
yield sock
ensure
sock.close
end
end
# Returns a pair of unamed UNIX sockets.
#
# ```
# require "socket"
#
# left, right = UNIXSocket.pair
#
# spawn do
# # echo server
# message = right.gets
# right.puts message
# end
#
# left.puts "message"
# left.gets # => "message"
# ```
def self.pair(type : Type = Type::STREAM)
fds = uninitialized Int32[2]
socktype = type.value
{% if LibC.has_constant?(:SOCK_CLOEXEC) %}
socktype |= LibC::SOCK_CLOEXEC
{% end %}
if LibC.socketpair(Family::UNIX, socktype, 0, fds) != 0
raise Socket::Error.new("socketpair:")
end
{UNIXSocket.new(fd: fds[0], type: type), UNIXSocket.new(fd: fds[1], type: type)}
end
# Creates a pair of unamed UNIX sockets (see `pair`) and yields them to the
# block. Eventually closes both sockets when the block returns.
#
# Returns the value of the block.
def self.pair(type : Type = Type::STREAM)
left, right = pair(type)
begin
yield left, right
ensure
left.close
right.close
end
end
def local_address
UNIXAddress.new(path.to_s)
end
def remote_address
UNIXAddress.new(path.to_s)
end
def receive
bytes_read, sockaddr, addrlen = recvfrom
{bytes_read, UNIXAddress.from(sockaddr, addrlen)}
end
end