async-io 1.4.0 → 1.5.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 +4 -4
- data/examples/broken_ssl.rb +14 -0
- data/lib/async/io/{wrap/tcp.rb → address.rb} +2 -37
- data/lib/async/io/endpoint.rb +126 -101
- data/lib/async/io/generic.rb +30 -2
- data/lib/async/io/protocol/line.rb +4 -0
- data/lib/async/io/socket.rb +44 -31
- data/lib/async/io/ssl_socket.rb +40 -8
- data/lib/async/io/standard.rb +29 -0
- data/lib/async/io/tcp_socket.rb +16 -1
- data/lib/async/io/unix_socket.rb +6 -5
- data/lib/async/io/version.rb +1 -1
- data/spec/async/io/c10k_spec.rb +1 -1
- data/spec/async/io/endpoint_spec.rb +3 -3
- data/spec/async/io/generic_examples.rb +24 -0
- data/spec/async/io/generic_spec.rb +6 -0
- data/spec/async/io/socket/tcp_spec.rb +17 -48
- data/spec/async/io/socket/udp_spec.rb +14 -24
- data/spec/async/io/socket_spec.rb +26 -0
- data/spec/async/io/ssl_server_spec.rb +60 -0
- data/spec/async/io/ssl_socket_spec.rb +22 -18
- data/spec/async/io/standard_spec.rb +45 -0
- data/spec/async/io/tcp_socket_spec.rb +8 -1
- data/spec/async/io/udp_socket_spec.rb +23 -21
- data/spec/async/io/unix_socket_spec.rb +23 -18
- data/spec/async/io/wrap/tcp_spec.rb +49 -13
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96b8fa83be7fa357a0aa41a783fb890389940717f110a67df2bcac406c0283ad
|
4
|
+
data.tar.gz: cf5fde9ff0c2b09f84b00cb2732606c4fd622f6819024fe2a1c10a48fa6564bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1efa54e086e3f3753a224bb1e7e4cb6a5fa59d15f570af9c1d2f3969eef5dae5698c657c7976ead21070ddfb6b9a87c5fe88f10c6625bd469cf422e05a1c2247
|
7
|
+
data.tar.gz: 3fc8f33825173f68d076ac6246e5a570db472613dfec9c3f2f30edd6f0d6719dd303138b802300a2d155c4cf423550a7286596aa26112d2415f05d7c881ef8d5
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
server = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
|
7
|
+
server.bind(Addrinfo.tcp('127.0.0.1', 4433))
|
8
|
+
server.listen(128)
|
9
|
+
|
10
|
+
ssl_server = OpenSSL::SSL::SSLServer.new(server, OpenSSL::SSL::SSLContext.new)
|
11
|
+
|
12
|
+
puts ssl_server.addr
|
13
|
+
|
14
|
+
# openssl/ssl.rb:234:in `addr': undefined method `addr' for #<Socket:fd 8> (NoMethodError)
|
@@ -18,45 +18,10 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
|
21
|
+
require 'socket'
|
22
22
|
|
23
23
|
module Async
|
24
24
|
module IO
|
25
|
-
|
26
|
-
module TCPServer
|
27
|
-
def self.new(*args)
|
28
|
-
return ::TCPServer.new(*args) unless Task.current?
|
29
|
-
|
30
|
-
case args.size
|
31
|
-
when 2
|
32
|
-
local_address = Async::IO::Address.tcp(*args)
|
33
|
-
when 1
|
34
|
-
local_address = Async::IO::Address.tcp("0.0.0.0", *args)
|
35
|
-
else
|
36
|
-
raise ArgumentError, "TCPServer.new([hostname], port)"
|
37
|
-
end
|
38
|
-
|
39
|
-
return Async::IO::Socket.bind(local_address)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
module TCPSocket
|
44
|
-
def self.new(remote_host, remote_port, local_host=nil, local_port=nil)
|
45
|
-
return ::TCPSocket.new(remote_host, remote_port, local_host, local_port) unless Task.current?
|
46
|
-
|
47
|
-
remote_address = Async::IO::Address.tcp(remote_host, remote_port)
|
48
|
-
|
49
|
-
if local_host && local_port
|
50
|
-
local_address = Async::IO::Address.tcp(local_host, local_port)
|
51
|
-
end
|
52
|
-
|
53
|
-
return Async::IO::Socket.connect(remote_address, local_address)
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.open(*args)
|
57
|
-
self.new(*args)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
25
|
+
Address = Addrinfo
|
61
26
|
end
|
62
27
|
end
|
data/lib/async/io/endpoint.rb
CHANGED
@@ -18,109 +18,92 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require_relative 'address'
|
21
22
|
require_relative 'socket'
|
22
23
|
require 'uri'
|
23
24
|
|
24
25
|
module Async
|
25
26
|
module IO
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
include Comparable
|
30
|
-
|
31
|
-
class << self
|
32
|
-
def parse(string, **options)
|
33
|
-
uri = URI.parse(string)
|
34
|
-
self.send(uri.scheme, uri.host, uri.port, **options)
|
35
|
-
end
|
36
|
-
|
37
|
-
# args: nodename, service, family, socktype, protocol, flags
|
38
|
-
def tcp(*args, **options)
|
39
|
-
args[3] = ::Socket::SOCK_STREAM
|
40
|
-
|
41
|
-
HostEndpoint.new(args, **options)
|
42
|
-
end
|
27
|
+
class Endpoint
|
28
|
+
def self.parse(string, **options)
|
29
|
+
uri = URI.parse(string)
|
43
30
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def unix(*args, **options)
|
51
|
-
AddressEndpoint.new(Address.unix(*args), **options)
|
52
|
-
end
|
31
|
+
self.send(uri.scheme, uri.host, uri.port, **options)
|
32
|
+
end
|
33
|
+
|
34
|
+
# args: nodename, service, family, socktype, protocol, flags
|
35
|
+
def self.tcp(*args, **options)
|
36
|
+
args[3] = ::Socket::SOCK_STREAM
|
53
37
|
|
54
|
-
|
55
|
-
|
56
|
-
|
38
|
+
HostEndpoint.new(args, **options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.udp(*args, **options)
|
42
|
+
args[3] = ::Socket::SOCK_DGRAM
|
57
43
|
|
58
|
-
|
59
|
-
def each(specifications, &block)
|
60
|
-
return to_enum(:each, specifications) unless block_given?
|
61
|
-
|
62
|
-
specifications.each do |specification|
|
63
|
-
if specification.is_a? self
|
64
|
-
yield specification
|
65
|
-
elsif specification.is_a? Array
|
66
|
-
yield self.send(*specification)
|
67
|
-
elsif specification.is_a? String
|
68
|
-
yield self.parse(specification)
|
69
|
-
elsif specification.is_a? ::BasicSocket
|
70
|
-
yield SocketEndpoint.new(specification)
|
71
|
-
elsif specification.is_a? Generic
|
72
|
-
yield Endpoint.new(specification)
|
73
|
-
else
|
74
|
-
raise ArgumentError.new("Not sure how to convert #{specification} to endpoint!")
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
44
|
+
HostEndpoint.new(args, **options)
|
78
45
|
end
|
79
46
|
|
80
|
-
def
|
81
|
-
|
47
|
+
def self.unix(*args, **options)
|
48
|
+
AddressEndpoint.new(Address.unix(*args), **options)
|
82
49
|
end
|
83
50
|
|
84
|
-
def
|
85
|
-
|
51
|
+
def self.ssl(*args, **options)
|
52
|
+
SecureEndpoint.new(self.tcp(*args, **options), **options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.try_convert(specification)
|
56
|
+
if specification.is_a? self
|
57
|
+
specification
|
58
|
+
elsif specification.is_a? Array
|
59
|
+
self.send(*specification)
|
60
|
+
elsif specification.is_a? String
|
61
|
+
self.parse(specification)
|
62
|
+
elsif specification.is_a? ::BasicSocket
|
63
|
+
SocketEndpoint.new(specification)
|
64
|
+
elsif specification.is_a? Generic
|
65
|
+
Endpoint.new(specification)
|
66
|
+
else
|
67
|
+
raise ArgumentError.new("Not sure how to convert #{specification} to endpoint!")
|
68
|
+
end
|
86
69
|
end
|
87
70
|
|
88
|
-
|
89
|
-
|
71
|
+
# Generate a list of endpoint from an array.
|
72
|
+
def self.each(specifications, &block)
|
73
|
+
return to_enum(:each, specifications) unless block_given?
|
90
74
|
|
75
|
+
specifications.each do |specification|
|
76
|
+
yield try_convert(specification)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def each
|
81
|
+
return to_enum unless block_given?
|
82
|
+
|
83
|
+
yield self
|
84
|
+
end
|
85
|
+
|
86
|
+
def accept(backlog = Socket::SOMAXCONN, &block)
|
91
87
|
bind do |server|
|
92
88
|
server.listen(backlog)
|
89
|
+
|
93
90
|
server.accept_each(&block)
|
94
91
|
end
|
95
92
|
end
|
96
|
-
|
97
|
-
def connect
|
98
|
-
yield specification
|
99
|
-
end
|
100
93
|
end
|
101
94
|
|
102
95
|
class HostEndpoint < Endpoint
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
task.annotate("binding to #{specification.inspect}")
|
108
|
-
|
109
|
-
Addrinfo.foreach(*specification).each do |address|
|
110
|
-
tasks << task.async do
|
111
|
-
Socket.bind(address, **options, &block)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
tasks.each(&:wait)
|
96
|
+
def initialize(specification, **options)
|
97
|
+
@specification = specification
|
98
|
+
@options = options
|
116
99
|
end
|
117
100
|
|
118
101
|
def connect(&block)
|
119
102
|
last_error = nil
|
120
103
|
|
121
|
-
Addrinfo.foreach(
|
104
|
+
Addrinfo.foreach(*@specification).each do |address|
|
122
105
|
begin
|
123
|
-
return Socket.connect(address, &block)
|
106
|
+
return Socket.connect(address, **@options, &block)
|
124
107
|
rescue
|
125
108
|
last_error = $!
|
126
109
|
end
|
@@ -128,41 +111,60 @@ module Async
|
|
128
111
|
|
129
112
|
raise last_error
|
130
113
|
end
|
131
|
-
|
132
|
-
|
133
|
-
# This class will open and close the socket automatically.
|
134
|
-
class AddressEndpoint < Endpoint
|
114
|
+
|
135
115
|
def bind(&block)
|
136
|
-
|
116
|
+
Addrinfo.foreach(*@specification) do |address|
|
117
|
+
Socket.bind(address, **@options, &block)
|
118
|
+
end
|
137
119
|
end
|
138
120
|
|
139
|
-
def
|
140
|
-
|
121
|
+
def each
|
122
|
+
return to_enum unless block_given?
|
123
|
+
|
124
|
+
Addrinfo.foreach(*@specification).each do |address|
|
125
|
+
yield AddressEndpoint.new(address, **@options)
|
126
|
+
end
|
141
127
|
end
|
142
128
|
end
|
143
129
|
|
144
|
-
# This class
|
145
|
-
class
|
130
|
+
# This class will open and close the socket automatically.
|
131
|
+
class AddressEndpoint < Endpoint
|
132
|
+
def initialize(address, **options)
|
133
|
+
@address = address
|
134
|
+
@options = options
|
135
|
+
end
|
136
|
+
|
137
|
+
attr :address
|
138
|
+
attr :options
|
139
|
+
|
146
140
|
def bind(&block)
|
147
|
-
|
141
|
+
Socket.bind(@address, **@options, &block)
|
148
142
|
end
|
149
143
|
|
150
144
|
def connect(&block)
|
151
|
-
|
145
|
+
Socket.connect(@address, **@options, &block)
|
152
146
|
end
|
153
147
|
end
|
154
148
|
|
155
149
|
class SecureEndpoint < Endpoint
|
150
|
+
def initialize(endpoint, **options)
|
151
|
+
@endpoint = endpoint
|
152
|
+
@options = options
|
153
|
+
end
|
154
|
+
|
155
|
+
attr :endpoint
|
156
|
+
attr :options
|
157
|
+
|
156
158
|
def hostname
|
157
|
-
options[:hostname]
|
159
|
+
@options[:hostname]
|
158
160
|
end
|
159
161
|
|
160
162
|
def params
|
161
|
-
options[:ssl_params]
|
163
|
+
@options[:ssl_params]
|
162
164
|
end
|
163
165
|
|
164
166
|
def context
|
165
|
-
if context = options[:ssl_context]
|
167
|
+
if context = @options[:ssl_context]
|
166
168
|
if params = self.params
|
167
169
|
context = context.dup
|
168
170
|
context.set_params(params)
|
@@ -178,24 +180,47 @@ module Async
|
|
178
180
|
return context
|
179
181
|
end
|
180
182
|
|
181
|
-
def bind
|
182
|
-
|
183
|
+
def bind
|
184
|
+
@endpoint.bind do |server|
|
183
185
|
yield SSLServer.new(server, context)
|
184
186
|
end
|
185
187
|
end
|
186
188
|
|
187
189
|
def connect(&block)
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
190
|
+
SSLSocket.connect(@endpoint.connect, context, hostname, &block)
|
191
|
+
end
|
192
|
+
|
193
|
+
def each
|
194
|
+
return to_enum unless block_given?
|
195
|
+
|
196
|
+
@endpoint.each do |endpoint|
|
197
|
+
yield self.class.new(endpoint, @options)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# This class doesn't exert ownership over the specified socket, wraps a native ::IO.
|
203
|
+
class SocketEndpoint < Endpoint
|
204
|
+
def initialize(socket)
|
205
|
+
# This socket should already be in the required state.
|
206
|
+
@socket = Async::IO.try_convert(socket)
|
207
|
+
end
|
208
|
+
|
209
|
+
attr :socket
|
210
|
+
|
211
|
+
def bind(&block)
|
212
|
+
if block_given?
|
213
|
+
yield @socket
|
214
|
+
else
|
215
|
+
return @socket
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def connect(&block)
|
220
|
+
if block_given?
|
221
|
+
yield @socket
|
222
|
+
else
|
223
|
+
return @socket
|
199
224
|
end
|
200
225
|
end
|
201
226
|
end
|
data/lib/async/io/generic.rb
CHANGED
@@ -25,7 +25,11 @@ module Async
|
|
25
25
|
module IO
|
26
26
|
# Convert a Ruby ::IO object to a wrapped instance:
|
27
27
|
def self.try_convert(io, &block)
|
28
|
-
Generic::WRAPPERS[io.class]
|
28
|
+
if wrapper_class = Generic::WRAPPERS[io.class]
|
29
|
+
wrapper_class.new(io, &block)
|
30
|
+
else
|
31
|
+
raise ArgumentError.new("Unsure how to wrap #{io.class}!")
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
# Represents an asynchronous IO within a reactor.
|
@@ -79,15 +83,39 @@ module Async
|
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
82
|
-
wraps ::IO, :external_encoding, :internal_encoding, :autoclose?, :autoclose=, :pid, :stat, :binmode, :flush, :set_encoding, :to_io, :to_i, :reopen, :fileno, :fsync, :fdatasync, :sync, :sync=, :tell, :seek, :rewind, :pos, :pos=, :eof, :eof?, :close_on_exec?, :close_on_exec=, :closed?, :close_read, :close_write, :isatty, :tty?, :binmode?, :sysseek, :advise, :ioctl, :fcntl
|
86
|
+
wraps ::IO, :external_encoding, :internal_encoding, :autoclose?, :autoclose=, :pid, :stat, :binmode, :flush, :set_encoding, :to_io, :to_i, :reopen, :fileno, :fsync, :fdatasync, :sync, :sync=, :tell, :seek, :rewind, :pos, :pos=, :eof, :eof?, :close_on_exec?, :close_on_exec=, :closed?, :close_read, :close_write, :isatty, :tty?, :binmode?, :sysseek, :advise, :ioctl, :fcntl, :nread, :ready?, :pread, :pwrite, :pathconf
|
83
87
|
|
84
88
|
# @example
|
85
89
|
# data = io.read(512)
|
86
90
|
wrap_blocking_method :read, :read_nonblock
|
91
|
+
alias sysread read
|
92
|
+
alias readpartial read
|
87
93
|
|
88
94
|
# @example
|
89
95
|
# io.write("Hello World")
|
90
96
|
wrap_blocking_method :write, :write_nonblock
|
97
|
+
alias syswrite write
|
98
|
+
alias << write
|
99
|
+
|
100
|
+
def wait(timeout = nil, mode = :read)
|
101
|
+
case mode
|
102
|
+
when :read
|
103
|
+
wait_readable(timeout)
|
104
|
+
when :write
|
105
|
+
wait_writable(timeout)
|
106
|
+
else
|
107
|
+
wait_any(:rw, timeout)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def nonblock
|
112
|
+
true
|
113
|
+
end
|
114
|
+
alias nonblock= nonblock
|
115
|
+
|
116
|
+
def nonblock?
|
117
|
+
true
|
118
|
+
end
|
91
119
|
|
92
120
|
protected
|
93
121
|
|
data/lib/async/io/socket.rb
CHANGED
@@ -24,13 +24,11 @@ require_relative 'generic'
|
|
24
24
|
module Async
|
25
25
|
module IO
|
26
26
|
class BasicSocket < Generic
|
27
|
-
wraps ::BasicSocket, :setsockopt, :connect_address, :local_address, :remote_address, :do_not_reverse_lookup, :do_not_reverse_lookup=, :shutdown, :getsockopt, :getsockname, :getpeername, :getpeereid
|
27
|
+
wraps ::BasicSocket, :setsockopt, :connect_address, :close_read, :close_write, :local_address, :remote_address, :do_not_reverse_lookup, :do_not_reverse_lookup=, :shutdown, :getsockopt, :getsockname, :getpeername, :getpeereid
|
28
28
|
|
29
29
|
wrap_blocking_method :recv, :recv_nonblock
|
30
30
|
wrap_blocking_method :recvmsg, :recvmsg_nonblock
|
31
31
|
|
32
|
-
wrap_blocking_method :recvfrom, :recvfrom_nonblock
|
33
|
-
|
34
32
|
wrap_blocking_method :sendmsg, :sendmsg_nonblock
|
35
33
|
wrap_blocking_method :send, :sendmsg_nonblock, invert: false
|
36
34
|
|
@@ -39,25 +37,7 @@ module Async
|
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
42
|
-
module
|
43
|
-
def accept(task: Task.current)
|
44
|
-
peer, address = async_send(:accept_nonblock)
|
45
|
-
|
46
|
-
wrapper = Socket.new(peer, self.reactor)
|
47
|
-
|
48
|
-
return wrapper, address unless block_given?
|
49
|
-
|
50
|
-
task.async do |task|
|
51
|
-
task.annotate "incoming connection #{address.inspect}"
|
52
|
-
|
53
|
-
begin
|
54
|
-
yield wrapper, address
|
55
|
-
ensure
|
56
|
-
wrapper.close
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
40
|
+
module Server
|
61
41
|
def accept_each(task: Task.current)
|
62
42
|
task.annotate "accepting connections #{self.local_address.inspect}"
|
63
43
|
|
@@ -72,8 +52,9 @@ module Async
|
|
72
52
|
class Socket < BasicSocket
|
73
53
|
wraps ::Socket, :bind, :ipv6only!, :listen
|
74
54
|
|
55
|
+
wrap_blocking_method :recvfrom, :recvfrom_nonblock
|
56
|
+
|
75
57
|
include ::Socket::Constants
|
76
|
-
include ServerSocket
|
77
58
|
|
78
59
|
def connect(*args)
|
79
60
|
begin
|
@@ -83,6 +64,28 @@ module Async
|
|
83
64
|
end
|
84
65
|
end
|
85
66
|
|
67
|
+
alias connect_nonblock connect
|
68
|
+
|
69
|
+
def accept(task: Task.current)
|
70
|
+
peer, address = async_send(:accept_nonblock)
|
71
|
+
wrapper = Socket.new(peer, task.reactor)
|
72
|
+
|
73
|
+
return wrapper, address unless block_given?
|
74
|
+
|
75
|
+
task.async do |task|
|
76
|
+
task.annotate "incoming connection #{address.inspect}"
|
77
|
+
|
78
|
+
begin
|
79
|
+
yield wrapper, address
|
80
|
+
ensure
|
81
|
+
wrapper.close
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
alias accept_nonblock accept
|
87
|
+
alias sysaccept accept
|
88
|
+
|
86
89
|
def self.build(*args, task: Task.current)
|
87
90
|
socket = wrapped_klass.new(*args)
|
88
91
|
|
@@ -101,11 +104,11 @@ module Async
|
|
101
104
|
# @param remote_address [Addrinfo] The remote address to connect to.
|
102
105
|
# @param local_address [Addrinfo] The local address to bind to before connecting.
|
103
106
|
# @option protcol [Integer] The socket protocol to use.
|
104
|
-
def self.connect(remote_address, local_address = nil, task: Task.current, **options)
|
107
|
+
def self.connect(remote_address, local_address = nil, reuse_port: false, task: Task.current, **options)
|
105
108
|
task.annotate "connecting to #{remote_address.inspect}"
|
106
109
|
|
107
110
|
wrapper = build(remote_address.afamily, remote_address.socktype, remote_address.protocol, **options) do |socket|
|
108
|
-
socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR,
|
111
|
+
socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, reuse_port)
|
109
112
|
|
110
113
|
if local_address
|
111
114
|
socket.bind(local_address.to_sockaddr)
|
@@ -138,8 +141,6 @@ module Async
|
|
138
141
|
# @option protocol [Integer] The socket protocol to use.
|
139
142
|
# @option reuse_port [Boolean] Allow this port to be bound in multiple processes.
|
140
143
|
def self.bind(local_address, protocol: 0, reuse_port: false, task: Task.current, **options, &block)
|
141
|
-
task.annotate "binding to #{local_address.inspect}"
|
142
|
-
|
143
144
|
wrapper = build(local_address.afamily, local_address.socktype, protocol, **options) do |socket|
|
144
145
|
socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true)
|
145
146
|
socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, true) if reuse_port
|
@@ -148,10 +149,14 @@ module Async
|
|
148
149
|
|
149
150
|
return wrapper unless block_given?
|
150
151
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
152
|
+
task.async do
|
153
|
+
task.annotate "binding to #{local_address.inspect}"
|
154
|
+
|
155
|
+
begin
|
156
|
+
yield wrapper, task
|
157
|
+
ensure
|
158
|
+
wrapper.close
|
159
|
+
end
|
155
160
|
end
|
156
161
|
end
|
157
162
|
|
@@ -163,10 +168,18 @@ module Async
|
|
163
168
|
server.accept_each(task: task, &block)
|
164
169
|
end
|
165
170
|
end
|
171
|
+
|
172
|
+
include Server
|
173
|
+
|
174
|
+
def self.pair(*args)
|
175
|
+
::Socket.pair(*args).map(&self.method(:new))
|
176
|
+
end
|
166
177
|
end
|
167
178
|
|
168
179
|
class IPSocket < BasicSocket
|
169
180
|
wraps ::IPSocket, :addr, :peeraddr
|
181
|
+
|
182
|
+
wrap_blocking_method :recvfrom, :recvfrom_nonblock
|
170
183
|
end
|
171
184
|
end
|
172
185
|
end
|