mt-libuv 4.1.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 +7 -0
- data/.gitignore +22 -0
- data/.gitmodules +6 -0
- data/.rspec +1 -0
- data/.travis.yml +24 -0
- data/Gemfile +9 -0
- data/LICENSE +24 -0
- data/README.md +195 -0
- data/Rakefile +31 -0
- data/ext/README.md +6 -0
- data/ext/Rakefile +28 -0
- data/lib/mt-libuv/async.rb +51 -0
- data/lib/mt-libuv/check.rb +59 -0
- data/lib/mt-libuv/coroutines.rb +79 -0
- data/lib/mt-libuv/dns.rb +98 -0
- data/lib/mt-libuv/error.rb +88 -0
- data/lib/mt-libuv/ext/ext.rb +322 -0
- data/lib/mt-libuv/ext/platform/darwin_x64.rb +61 -0
- data/lib/mt-libuv/ext/platform/unix.rb +69 -0
- data/lib/mt-libuv/ext/platform/windows.rb +83 -0
- data/lib/mt-libuv/ext/tasks/mac.rb +24 -0
- data/lib/mt-libuv/ext/tasks/unix.rb +42 -0
- data/lib/mt-libuv/ext/tasks/win.rb +29 -0
- data/lib/mt-libuv/ext/tasks.rb +27 -0
- data/lib/mt-libuv/ext/types.rb +253 -0
- data/lib/mt-libuv/fiber_pool.rb +83 -0
- data/lib/mt-libuv/file.rb +309 -0
- data/lib/mt-libuv/filesystem.rb +263 -0
- data/lib/mt-libuv/fs_event.rb +37 -0
- data/lib/mt-libuv/handle.rb +108 -0
- data/lib/mt-libuv/idle.rb +59 -0
- data/lib/mt-libuv/mixins/accessors.rb +41 -0
- data/lib/mt-libuv/mixins/assertions.rb +25 -0
- data/lib/mt-libuv/mixins/fs_checks.rb +96 -0
- data/lib/mt-libuv/mixins/listener.rb +69 -0
- data/lib/mt-libuv/mixins/net.rb +42 -0
- data/lib/mt-libuv/mixins/resource.rb +30 -0
- data/lib/mt-libuv/mixins/stream.rb +276 -0
- data/lib/mt-libuv/pipe.rb +217 -0
- data/lib/mt-libuv/prepare.rb +59 -0
- data/lib/mt-libuv/q.rb +475 -0
- data/lib/mt-libuv/reactor.rb +567 -0
- data/lib/mt-libuv/signal.rb +62 -0
- data/lib/mt-libuv/spawn.rb +113 -0
- data/lib/mt-libuv/tcp.rb +465 -0
- data/lib/mt-libuv/timer.rb +107 -0
- data/lib/mt-libuv/tty.rb +42 -0
- data/lib/mt-libuv/udp.rb +302 -0
- data/lib/mt-libuv/version.rb +5 -0
- data/lib/mt-libuv/work.rb +86 -0
- data/lib/mt-libuv.rb +80 -0
- data/mt-libuv.gemspec +62 -0
- data/spec/async_spec.rb +67 -0
- data/spec/coroutines_spec.rb +121 -0
- data/spec/cpu_spec.rb +10 -0
- data/spec/defer_spec.rb +906 -0
- data/spec/dns_spec.rb +110 -0
- data/spec/dsl_spec.rb +43 -0
- data/spec/filesystem_spec.rb +270 -0
- data/spec/idle_spec.rb +44 -0
- data/spec/pipe_spec.rb +151 -0
- data/spec/spawn_spec.rb +119 -0
- data/spec/tcp_spec.rb +272 -0
- data/spec/test.sh +4 -0
- data/spec/test_fail.sh +3 -0
- data/spec/test_read.sh +3 -0
- data/spec/timer_spec.rb +14 -0
- data/spec/udp_spec.rb +73 -0
- data/spec/zen_spec.rb +34 -0
- metadata +196 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Spawn < Handle
|
5
|
+
define_callback function: :on_exit, params: [:pointer, :int64, :int]
|
6
|
+
|
7
|
+
attr_reader :stdin, :stdout, :stderr
|
8
|
+
|
9
|
+
# @param reactor [::MTLibuv::Reactor] reactor this timer will be associated
|
10
|
+
# @param callback [Proc] callback to be called when the timer is triggered
|
11
|
+
def initialize(reactor, cmd, working_dir: '.', args: [], env: nil, flags: 0, mode: :capture)
|
12
|
+
@reactor = reactor
|
13
|
+
|
14
|
+
process_ptr = ::MTLibuv::Ext.allocate_handle_process
|
15
|
+
@options = Ext::UvProcessOptions.new
|
16
|
+
|
17
|
+
# Configure IO objects
|
18
|
+
@io_obj = Ext::StdioObjs.new
|
19
|
+
case mode.to_sym
|
20
|
+
when :capture
|
21
|
+
@stdin = @reactor.pipe
|
22
|
+
@stdout = @reactor.pipe
|
23
|
+
@stderr = @reactor.pipe
|
24
|
+
@io_obj[:stdin] = build_stdio(:CREATE_READABLE_PIPE, pipe: @stdin)
|
25
|
+
@io_obj[:stdout] = build_stdio(:CREATE_WRITABLE_PIPE, pipe: @stdout)
|
26
|
+
@io_obj[:stderr] = build_stdio(:CREATE_WRITABLE_PIPE, pipe: @stderr)
|
27
|
+
when :ignore
|
28
|
+
@io_obj[:stdin] = build_stdio(:UV_IGNORE)
|
29
|
+
@io_obj[:stdout] = build_stdio(:UV_IGNORE)
|
30
|
+
@io_obj[:stderr] = build_stdio(:UV_IGNORE)
|
31
|
+
when :inherit
|
32
|
+
@io_obj[:stdin] = build_stdio(:UV_INHERIT_FD, fd: ::STDIN.fileno)
|
33
|
+
@io_obj[:stdout] = build_stdio(:UV_INHERIT_FD, fd: ::STDOUT.fileno)
|
34
|
+
@io_obj[:stderr] = build_stdio(:UV_INHERIT_FD, fd: ::STDERR.fileno)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Configure arguments
|
38
|
+
@cmd = FFI::MemoryPointer.from_string(cmd)
|
39
|
+
@args = Array(args).map { |arg| FFI::MemoryPointer.from_string(arg) }
|
40
|
+
@args.unshift(@cmd)
|
41
|
+
@args_ptr = FFI::MemoryPointer.new(:pointer, @args.length + 1)
|
42
|
+
@args_ptr.write_array_of_pointer(@args)
|
43
|
+
|
44
|
+
# Configure environment
|
45
|
+
if env
|
46
|
+
@env = Array(env).map { |e| FFI::MemoryPointer.from_string(e) }
|
47
|
+
@env_ptr = FFI::MemoryPointer.new(:pointer, @env.length + 1)
|
48
|
+
@env_ptr.write_array_of_pointer(@env)
|
49
|
+
end
|
50
|
+
|
51
|
+
@working_dir = FFI::MemoryPointer.from_string(working_dir)
|
52
|
+
|
53
|
+
# Apply the options
|
54
|
+
@options[:exit_cb] = callback(:on_exit, process_ptr.address)
|
55
|
+
@options[:file] = @cmd
|
56
|
+
@options[:args] = @args_ptr
|
57
|
+
@options[:env] = @env_ptr
|
58
|
+
@options[:cwd] = @working_dir
|
59
|
+
@options[:flags] = 0
|
60
|
+
@options[:stdio_count] = 3
|
61
|
+
@options[:stdio] = @io_obj
|
62
|
+
|
63
|
+
error = check_result(::MTLibuv::Ext.spawn(reactor.handle, process_ptr, @options))
|
64
|
+
super(process_ptr, error)
|
65
|
+
end
|
66
|
+
|
67
|
+
def kill(signal = 2)
|
68
|
+
return self if @closed
|
69
|
+
::MTLibuv::Ext.process_kill(handle, signal)
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
|
77
|
+
def build_stdio(flags, pipe: nil, fd: nil)
|
78
|
+
io = Ext::UvStdioContainer.new
|
79
|
+
io[:flags] = flags
|
80
|
+
if pipe
|
81
|
+
io[:data][:pipe_handle] = pipe.handle
|
82
|
+
elsif fd
|
83
|
+
io[:data][:fd] = fd
|
84
|
+
end
|
85
|
+
io
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_exit(handle, exit_status, term_signal)
|
89
|
+
@reactor.exec do
|
90
|
+
if exit_status == 0
|
91
|
+
@defer.resolve(term_signal)
|
92
|
+
else
|
93
|
+
err = ::MTLibuv::Error::ProcessExitCode.new "Non-zero exit code returned #{exit_status}"
|
94
|
+
err.exit_status = exit_status
|
95
|
+
err.term_signal = term_signal
|
96
|
+
@defer.reject(err)
|
97
|
+
end
|
98
|
+
|
99
|
+
if @stdin
|
100
|
+
@reactor.next_tick do
|
101
|
+
@stdin.close
|
102
|
+
@stdout.close
|
103
|
+
@stderr.close
|
104
|
+
|
105
|
+
close
|
106
|
+
end
|
107
|
+
else
|
108
|
+
close
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/mt-libuv/tcp.rb
ADDED
@@ -0,0 +1,465 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ipaddr'
|
4
|
+
require 'mt-ruby-tls'
|
5
|
+
|
6
|
+
|
7
|
+
module MTLibuv
|
8
|
+
class TCP < Handle
|
9
|
+
include Stream, Net
|
10
|
+
|
11
|
+
|
12
|
+
define_callback function: :on_connect, params: [:pointer, :int]
|
13
|
+
|
14
|
+
|
15
|
+
TLS_ERROR = "TLS write failed"
|
16
|
+
|
17
|
+
|
18
|
+
attr_reader :connected
|
19
|
+
attr_reader :protocol
|
20
|
+
attr_reader :tls
|
21
|
+
|
22
|
+
# Check if tls active on the socket
|
23
|
+
def tls?; !@tls.nil?; end
|
24
|
+
|
25
|
+
|
26
|
+
def initialize(reactor, acceptor = nil, progress: nil, flags: nil, **tls_options)
|
27
|
+
@reactor = reactor
|
28
|
+
@progress = progress
|
29
|
+
@tls_options = tls_options
|
30
|
+
|
31
|
+
tcp_ptr = ::MTLibuv::Ext.allocate_handle_tcp
|
32
|
+
error = if flags
|
33
|
+
check_result(::MTLibuv::Ext.tcp_init_ex(reactor.handle, tcp_ptr, flags))
|
34
|
+
else
|
35
|
+
check_result(::MTLibuv::Ext.tcp_init(reactor.handle, tcp_ptr))
|
36
|
+
end
|
37
|
+
|
38
|
+
if acceptor && error.nil?
|
39
|
+
error = check_result(::MTLibuv::Ext.accept(acceptor, tcp_ptr))
|
40
|
+
@connected = true
|
41
|
+
else
|
42
|
+
@connected = false
|
43
|
+
end
|
44
|
+
|
45
|
+
super(tcp_ptr, error)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
#
|
50
|
+
# TLS Abstraction ----------------------
|
51
|
+
# --------------------------------------
|
52
|
+
#
|
53
|
+
def start_tls(args = {})
|
54
|
+
return self unless @connected && @tls.nil?
|
55
|
+
|
56
|
+
args[:verify_peer] = true if @on_verify
|
57
|
+
|
58
|
+
@handshake = false
|
59
|
+
@pending_writes = []
|
60
|
+
@tls_options.merge!(args)
|
61
|
+
|
62
|
+
hosts = @tls_options[:hosts]
|
63
|
+
if hosts && hosts[0]
|
64
|
+
opts = @tls_options.merge(hosts[0])
|
65
|
+
@tls = ::RubyTls::SSL::Box.new(opts[:server], self, opts)
|
66
|
+
hosts[1..-1].each do |host_opts|
|
67
|
+
@tls.add_host(**host_opts)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
@tls = ::RubyTls::SSL::Box.new(@tls_options[:server], self, @tls_options)
|
71
|
+
end
|
72
|
+
@tls.start
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Push through any pending writes when handshake has completed
|
77
|
+
def handshake_cb(protocol = nil)
|
78
|
+
@handshake = true
|
79
|
+
@protocol = protocol
|
80
|
+
|
81
|
+
writes = @pending_writes
|
82
|
+
@pending_writes = nil
|
83
|
+
writes.each do |deferred, data|
|
84
|
+
@pending_write = deferred
|
85
|
+
@tls.encrypt(data)
|
86
|
+
end
|
87
|
+
|
88
|
+
begin
|
89
|
+
@on_handshake.call(self, protocol) if @on_handshake
|
90
|
+
rescue => e
|
91
|
+
@reactor.log e, 'performing TLS handshake callback'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Provide a callback once the TLS handshake has completed
|
96
|
+
def on_handshake(&blk)
|
97
|
+
@on_handshake = blk
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
# This is clear text data that has been decrypted
|
102
|
+
# Same as stream.rb on_read for clear text
|
103
|
+
def dispatch_cb(data)
|
104
|
+
begin
|
105
|
+
@progress.call data, self
|
106
|
+
rescue Exception => e
|
107
|
+
@reactor.log e, 'performing TLS read data callback'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# We resolve the existing tls write promise with a the
|
112
|
+
# real writes promise (a close may have occurred)
|
113
|
+
def transmit_cb(data)
|
114
|
+
if @pending_write
|
115
|
+
@pending_write.resolve(direct_write(data))
|
116
|
+
@pending_write = nil
|
117
|
+
else
|
118
|
+
direct_write(data)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Close can be called multiple times
|
123
|
+
def close_cb
|
124
|
+
if @pending_write
|
125
|
+
@pending_write.reject(TLS_ERROR)
|
126
|
+
@pending_write = nil
|
127
|
+
end
|
128
|
+
|
129
|
+
# Shutdown the stream
|
130
|
+
close
|
131
|
+
end
|
132
|
+
|
133
|
+
def verify_cb(cert)
|
134
|
+
if @on_verify
|
135
|
+
begin
|
136
|
+
return @on_verify.call cert
|
137
|
+
rescue => e
|
138
|
+
@reactor.log e, 'performing TLS verify callback'
|
139
|
+
return false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
# overwrite the default close to ensure
|
147
|
+
# pending writes are rejected
|
148
|
+
def close
|
149
|
+
return self if @closed
|
150
|
+
|
151
|
+
# Free tls memory
|
152
|
+
# Next tick as may recieve data after closing
|
153
|
+
if @tls
|
154
|
+
@reactor.next_tick do
|
155
|
+
@tls.cleanup
|
156
|
+
end
|
157
|
+
end
|
158
|
+
@connected = false
|
159
|
+
|
160
|
+
if @pending_writes
|
161
|
+
@pending_writes.each do |deferred, data|
|
162
|
+
deferred.reject(TLS_ERROR)
|
163
|
+
end
|
164
|
+
@pending_writes = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
super
|
168
|
+
end
|
169
|
+
|
170
|
+
# Verify peers will be called for each cert in the chain
|
171
|
+
def verify_peer(&blk)
|
172
|
+
@on_verify = blk
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
alias_method :direct_write, :write
|
177
|
+
def write(data, wait: false)
|
178
|
+
if @tls
|
179
|
+
deferred = @reactor.defer
|
180
|
+
|
181
|
+
if @handshake
|
182
|
+
@pending_write = deferred
|
183
|
+
@tls.encrypt(data)
|
184
|
+
else
|
185
|
+
@pending_writes << [deferred, data]
|
186
|
+
end
|
187
|
+
|
188
|
+
if wait
|
189
|
+
return deferred.promise if wait == :promise
|
190
|
+
deferred.promise.value
|
191
|
+
end
|
192
|
+
|
193
|
+
self
|
194
|
+
else
|
195
|
+
direct_write(data, wait: wait)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
alias_method :do_shutdown, :shutdown
|
200
|
+
def shutdown
|
201
|
+
if @pending_writes && @pending_writes.length > 0
|
202
|
+
@pending_writes[-1][0].promise.finally { do_shutdown }
|
203
|
+
else
|
204
|
+
do_shutdown
|
205
|
+
end
|
206
|
+
self
|
207
|
+
end
|
208
|
+
|
209
|
+
def add_host(**host_opts)
|
210
|
+
@tls_options[:hosts] ||= []
|
211
|
+
@tls_options[:hosts] << host_opts
|
212
|
+
end
|
213
|
+
|
214
|
+
def remove_host(name)
|
215
|
+
if @tls_options[:hosts]
|
216
|
+
found = nil
|
217
|
+
@tls_options[:hosts].each do |host|
|
218
|
+
if host[:host_name] == name
|
219
|
+
found = host
|
220
|
+
break
|
221
|
+
end
|
222
|
+
end
|
223
|
+
@tls_options[:hosts].delete(found) if found
|
224
|
+
end
|
225
|
+
end
|
226
|
+
#
|
227
|
+
# END TLS Abstraction ------------------
|
228
|
+
# --------------------------------------
|
229
|
+
#
|
230
|
+
|
231
|
+
def bind(ip, port, **tls_options, &blk)
|
232
|
+
return self if @closed
|
233
|
+
|
234
|
+
@on_accept = blk
|
235
|
+
@on_listen = proc { accept }
|
236
|
+
|
237
|
+
assert_type(String, ip, IP_ARGUMENT_ERROR)
|
238
|
+
assert_type(Integer, port, PORT_ARGUMENT_ERROR)
|
239
|
+
|
240
|
+
begin
|
241
|
+
@tcp_socket = create_socket(IPAddr.new(ip), port)
|
242
|
+
@tcp_socket.bind
|
243
|
+
@tls_options.merge!(tls_options)
|
244
|
+
@tls_options[:server] = true
|
245
|
+
rescue Exception => e
|
246
|
+
reject(e)
|
247
|
+
end
|
248
|
+
|
249
|
+
self
|
250
|
+
end
|
251
|
+
|
252
|
+
def open(fd, binding = true)
|
253
|
+
return self if @closed
|
254
|
+
|
255
|
+
if binding
|
256
|
+
@on_listen = proc { accept }
|
257
|
+
@on_accept = Proc.new
|
258
|
+
elsif block_given?
|
259
|
+
@callback = Proc.new
|
260
|
+
else
|
261
|
+
@coroutine = @reactor.defer
|
262
|
+
end
|
263
|
+
error = check_result ::MTLibuv::Ext.tcp_open(handle, fd)
|
264
|
+
reject(error) if error
|
265
|
+
@coroutine.promise.value if @coroutine
|
266
|
+
|
267
|
+
self
|
268
|
+
end
|
269
|
+
|
270
|
+
def connect(ip, port)
|
271
|
+
return self if @closed
|
272
|
+
|
273
|
+
assert_type(String, ip, IP_ARGUMENT_ERROR)
|
274
|
+
assert_type(Integer, port, PORT_ARGUMENT_ERROR)
|
275
|
+
|
276
|
+
begin
|
277
|
+
@tcp_socket = create_socket(IPAddr.new(ip), port)
|
278
|
+
@tcp_socket.connect(callback(:on_connect, @tcp_socket.connect_req.address))
|
279
|
+
rescue Exception => e
|
280
|
+
reject(e)
|
281
|
+
end
|
282
|
+
|
283
|
+
if block_given?
|
284
|
+
@callback = Proc.new
|
285
|
+
else
|
286
|
+
@coroutine = @reactor.defer
|
287
|
+
@coroutine.promise.value
|
288
|
+
end
|
289
|
+
|
290
|
+
self
|
291
|
+
end
|
292
|
+
|
293
|
+
# The name of the client (local) end of the socket
|
294
|
+
def sockname
|
295
|
+
return [] if @closed
|
296
|
+
sockaddr, len = get_sockaddr_and_len
|
297
|
+
check_result! ::MTLibuv::Ext.tcp_getsockname(handle, sockaddr, len)
|
298
|
+
get_ip_and_port(::MTLibuv::Ext::Sockaddr.new(sockaddr), len.get_int(0))
|
299
|
+
end
|
300
|
+
|
301
|
+
# The IP address of the peer (remote) end of the socket
|
302
|
+
def peername
|
303
|
+
return [] if @closed
|
304
|
+
sockaddr, len = get_sockaddr_and_len
|
305
|
+
check_result! ::MTLibuv::Ext.tcp_getpeername(handle, sockaddr, len)
|
306
|
+
get_ip_and_port(::MTLibuv::Ext::Sockaddr.new(sockaddr), len.get_int(0))
|
307
|
+
end
|
308
|
+
|
309
|
+
def enable_nodelay
|
310
|
+
return self if @closed
|
311
|
+
check_result ::MTLibuv::Ext.tcp_nodelay(handle, 1)
|
312
|
+
self
|
313
|
+
end
|
314
|
+
|
315
|
+
def disable_nodelay
|
316
|
+
return self if @closed
|
317
|
+
check_result ::MTLibuv::Ext.tcp_nodelay(handle, 0)
|
318
|
+
self
|
319
|
+
end
|
320
|
+
|
321
|
+
def enable_keepalive(delay)
|
322
|
+
return self if @closed # The to_i asserts integer
|
323
|
+
check_result ::MTLibuv::Ext.tcp_keepalive(handle, 1, delay.to_i)
|
324
|
+
self
|
325
|
+
end
|
326
|
+
|
327
|
+
def disable_keepalive
|
328
|
+
return self if @closed
|
329
|
+
check_result ::MTLibuv::Ext.tcp_keepalive(handle, 0, 0)
|
330
|
+
self
|
331
|
+
end
|
332
|
+
|
333
|
+
def enable_simultaneous_accepts
|
334
|
+
return self if @closed
|
335
|
+
check_result ::MTLibuv::Ext.tcp_simultaneous_accepts(handle, 1)
|
336
|
+
self
|
337
|
+
end
|
338
|
+
|
339
|
+
def disable_simultaneous_accepts
|
340
|
+
return self if @closed
|
341
|
+
check_result ::MTLibuv::Ext.tcp_simultaneous_accepts(handle, 0)
|
342
|
+
self
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
private
|
347
|
+
|
348
|
+
|
349
|
+
def create_socket(ip, port)
|
350
|
+
if ip.ipv4?
|
351
|
+
Socket4.new(reactor, handle, ip.to_s, port)
|
352
|
+
else
|
353
|
+
Socket6.new(reactor, handle, ip.to_s, port)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def on_connect(req, status)
|
358
|
+
cleanup_callbacks req.address
|
359
|
+
::MTLibuv::Ext.free(req)
|
360
|
+
e = check_result(status)
|
361
|
+
|
362
|
+
@reactor.exec do
|
363
|
+
if e
|
364
|
+
reject(e)
|
365
|
+
else
|
366
|
+
@connected = true
|
367
|
+
|
368
|
+
begin
|
369
|
+
if @callback
|
370
|
+
@callback.call(self)
|
371
|
+
@callback = nil
|
372
|
+
elsif @coroutine
|
373
|
+
@coroutine.resolve(nil)
|
374
|
+
@coroutine = nil
|
375
|
+
else
|
376
|
+
raise ArgumentError, 'no callback provided'
|
377
|
+
end
|
378
|
+
rescue Exception => e
|
379
|
+
@reactor.log e, 'performing TCP connection callback'
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def accept
|
386
|
+
begin
|
387
|
+
raise RuntimeError, CLOSED_HANDLE_ERROR if @closed
|
388
|
+
tcp = TCP.new(reactor, handle, **@tls_options)
|
389
|
+
|
390
|
+
@reactor.exec do
|
391
|
+
begin
|
392
|
+
@on_accept.call(tcp)
|
393
|
+
rescue Exception => e
|
394
|
+
@reactor.log e, 'performing TCP accept callback'
|
395
|
+
end
|
396
|
+
end
|
397
|
+
rescue Exception => e
|
398
|
+
@reactor.log e, 'failed to accept TCP connection'
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
|
403
|
+
class SocketBase
|
404
|
+
include Resource
|
405
|
+
|
406
|
+
def initialize(reactor, tcp, ip, port)
|
407
|
+
@ip = ip
|
408
|
+
@port = port
|
409
|
+
@tcp = tcp
|
410
|
+
@reactor = reactor
|
411
|
+
@req = ::MTLibuv::Ext.allocate_request_connect
|
412
|
+
end
|
413
|
+
|
414
|
+
def bind
|
415
|
+
check_result! ::MTLibuv::Ext.tcp_bind(@tcp, ip_addr, 0)
|
416
|
+
end
|
417
|
+
|
418
|
+
def connect(callback)
|
419
|
+
@callback = callback
|
420
|
+
check_result!(tcp_connect)
|
421
|
+
end
|
422
|
+
|
423
|
+
def connect_req
|
424
|
+
@req
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
protected
|
429
|
+
|
430
|
+
|
431
|
+
def tcp_connect
|
432
|
+
::MTLibuv::Ext.tcp_connect(
|
433
|
+
@req,
|
434
|
+
@tcp,
|
435
|
+
ip_addr,
|
436
|
+
@callback
|
437
|
+
)
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
|
442
|
+
class Socket4 < SocketBase
|
443
|
+
protected
|
444
|
+
|
445
|
+
|
446
|
+
def ip_addr
|
447
|
+
@sockaddr = Ext::SockaddrIn.new
|
448
|
+
check_result! ::MTLibuv::Ext.ip4_addr(@ip, @port, @sockaddr)
|
449
|
+
@sockaddr
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
class Socket6 < SocketBase
|
455
|
+
protected
|
456
|
+
|
457
|
+
|
458
|
+
def ip_addr
|
459
|
+
@sockaddr = Ext::SockaddrIn6.new
|
460
|
+
check_result! ::MTLibuv::Ext.ip6_addr(@ip, @port, @sockaddr)
|
461
|
+
@sockaddr
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Timer < Handle
|
5
|
+
|
6
|
+
|
7
|
+
define_callback function: :on_timer
|
8
|
+
|
9
|
+
|
10
|
+
# @param reactor [::MTLibuv::Reactor] reactor this timer will be associated
|
11
|
+
# @param callback [Proc] callback to be called when the timer is triggered
|
12
|
+
def initialize(reactor)
|
13
|
+
@reactor = reactor
|
14
|
+
|
15
|
+
timer_ptr = ::MTLibuv::Ext.allocate_handle_timer
|
16
|
+
error = check_result(::MTLibuv::Ext.timer_init(reactor.handle, timer_ptr))
|
17
|
+
@stopped = true
|
18
|
+
|
19
|
+
super(timer_ptr, error)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Enables the timer. A repeat of 0 means no repeat
|
23
|
+
#
|
24
|
+
# @param timeout [Integer] time in milliseconds before the timer callback is triggered the first time
|
25
|
+
# @param repeat [Integer] time in milliseconds between repeated callbacks after the first
|
26
|
+
def start(timeout, repeat = 0)
|
27
|
+
return if @closed
|
28
|
+
@stopped = false
|
29
|
+
|
30
|
+
# prevent timeouts less than 0 (very long time otherwise as cast to an unsigned)
|
31
|
+
# and you probably don't want to wait a few lifetimes
|
32
|
+
timeout = timeout.to_i
|
33
|
+
timeout = 0 if timeout < 0
|
34
|
+
|
35
|
+
error = check_result ::MTLibuv::Ext.timer_start(handle, callback(:on_timer), timeout, repeat.to_i)
|
36
|
+
reject(error) if error
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Disables the timer.
|
42
|
+
def stop
|
43
|
+
return if @stopped || @closed
|
44
|
+
@stopped = true
|
45
|
+
error = check_result ::MTLibuv::Ext.timer_stop(handle)
|
46
|
+
reject(error) if error
|
47
|
+
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
# Resets the current repeat
|
52
|
+
def again
|
53
|
+
return if @closed
|
54
|
+
error = check_result ::MTLibuv::Ext.timer_again(handle)
|
55
|
+
reject(error) if error
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Set the current repeat timeout
|
61
|
+
# Repeat is the time in milliseconds between repeated callbacks after the initial timeout fires
|
62
|
+
#
|
63
|
+
# @param time [Integer] time in milliseconds between repeated callbacks after the first
|
64
|
+
def repeat=(time)
|
65
|
+
return if @closed
|
66
|
+
error = check_result ::MTLibuv::Ext.timer_set_repeat(handle, time.to_i)
|
67
|
+
reject(error) if error
|
68
|
+
time
|
69
|
+
end
|
70
|
+
|
71
|
+
# Set or gets the current repeat timeout
|
72
|
+
# Repeat is the time in milliseconds between repeated callbacks after the initial timeout fires
|
73
|
+
#
|
74
|
+
# @param times [Integer] time in milliseconds between repeated callbacks after the first
|
75
|
+
def repeat(time = nil)
|
76
|
+
return if @closed
|
77
|
+
if time.nil?
|
78
|
+
::MTLibuv::Ext.timer_get_repeat(handle)
|
79
|
+
else
|
80
|
+
self.repeat = time
|
81
|
+
self
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Used to update the callback to be triggered by the timer
|
86
|
+
#
|
87
|
+
# @param callback [Proc] the callback to be called by the timer
|
88
|
+
def progress(&callback)
|
89
|
+
@callback = callback
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
|
97
|
+
def on_timer(handle)
|
98
|
+
@reactor.exec do
|
99
|
+
begin
|
100
|
+
@callback.call
|
101
|
+
rescue Exception => e
|
102
|
+
@reactor.log e, 'performing timer callback'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|