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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +24 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +24 -0
  8. data/README.md +195 -0
  9. data/Rakefile +31 -0
  10. data/ext/README.md +6 -0
  11. data/ext/Rakefile +28 -0
  12. data/lib/mt-libuv/async.rb +51 -0
  13. data/lib/mt-libuv/check.rb +59 -0
  14. data/lib/mt-libuv/coroutines.rb +79 -0
  15. data/lib/mt-libuv/dns.rb +98 -0
  16. data/lib/mt-libuv/error.rb +88 -0
  17. data/lib/mt-libuv/ext/ext.rb +322 -0
  18. data/lib/mt-libuv/ext/platform/darwin_x64.rb +61 -0
  19. data/lib/mt-libuv/ext/platform/unix.rb +69 -0
  20. data/lib/mt-libuv/ext/platform/windows.rb +83 -0
  21. data/lib/mt-libuv/ext/tasks/mac.rb +24 -0
  22. data/lib/mt-libuv/ext/tasks/unix.rb +42 -0
  23. data/lib/mt-libuv/ext/tasks/win.rb +29 -0
  24. data/lib/mt-libuv/ext/tasks.rb +27 -0
  25. data/lib/mt-libuv/ext/types.rb +253 -0
  26. data/lib/mt-libuv/fiber_pool.rb +83 -0
  27. data/lib/mt-libuv/file.rb +309 -0
  28. data/lib/mt-libuv/filesystem.rb +263 -0
  29. data/lib/mt-libuv/fs_event.rb +37 -0
  30. data/lib/mt-libuv/handle.rb +108 -0
  31. data/lib/mt-libuv/idle.rb +59 -0
  32. data/lib/mt-libuv/mixins/accessors.rb +41 -0
  33. data/lib/mt-libuv/mixins/assertions.rb +25 -0
  34. data/lib/mt-libuv/mixins/fs_checks.rb +96 -0
  35. data/lib/mt-libuv/mixins/listener.rb +69 -0
  36. data/lib/mt-libuv/mixins/net.rb +42 -0
  37. data/lib/mt-libuv/mixins/resource.rb +30 -0
  38. data/lib/mt-libuv/mixins/stream.rb +276 -0
  39. data/lib/mt-libuv/pipe.rb +217 -0
  40. data/lib/mt-libuv/prepare.rb +59 -0
  41. data/lib/mt-libuv/q.rb +475 -0
  42. data/lib/mt-libuv/reactor.rb +567 -0
  43. data/lib/mt-libuv/signal.rb +62 -0
  44. data/lib/mt-libuv/spawn.rb +113 -0
  45. data/lib/mt-libuv/tcp.rb +465 -0
  46. data/lib/mt-libuv/timer.rb +107 -0
  47. data/lib/mt-libuv/tty.rb +42 -0
  48. data/lib/mt-libuv/udp.rb +302 -0
  49. data/lib/mt-libuv/version.rb +5 -0
  50. data/lib/mt-libuv/work.rb +86 -0
  51. data/lib/mt-libuv.rb +80 -0
  52. data/mt-libuv.gemspec +62 -0
  53. data/spec/async_spec.rb +67 -0
  54. data/spec/coroutines_spec.rb +121 -0
  55. data/spec/cpu_spec.rb +10 -0
  56. data/spec/defer_spec.rb +906 -0
  57. data/spec/dns_spec.rb +110 -0
  58. data/spec/dsl_spec.rb +43 -0
  59. data/spec/filesystem_spec.rb +270 -0
  60. data/spec/idle_spec.rb +44 -0
  61. data/spec/pipe_spec.rb +151 -0
  62. data/spec/spawn_spec.rb +119 -0
  63. data/spec/tcp_spec.rb +272 -0
  64. data/spec/test.sh +4 -0
  65. data/spec/test_fail.sh +3 -0
  66. data/spec/test_read.sh +3 -0
  67. data/spec/timer_spec.rb +14 -0
  68. data/spec/udp_spec.rb +73 -0
  69. data/spec/zen_spec.rb +34 -0
  70. metadata +196 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 55ed231748185b35f9e88cb285439d40bc75172b4c0133badf659b6a70be6969
4
+ data.tar.gz: 51622861571ac5be5a93e06ee86e64f5c92ed3b4bbd37236717999694c60b5cc
5
+ SHA512:
6
+ metadata.gz: 3e1d126ffe76a1eb330bdb3203293088d0826270151c7e15cd6db1c09874304a2d745f5ba5909ab48268d9775a909da75f920e799a2c291229a280c1463099da
7
+ data.tar.gz: c30c8f7243fc969d1ae70b1e587a89f31ec49f552a085eccdf45ab031989f27731423373eb633a4e11f7def2d7ab4f2b3874ba2599d73f77c35a94a4f950494a
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ html
6
+ pkg
7
+ doc
8
+ tmp
9
+ rerun.txt
10
+ Gemfile.lock
11
+ .bundle
12
+ .idea
13
+ *.rbc
14
+ .yardoc
15
+ bin
16
+ Gemfile-custom
17
+ ext/*
18
+ vendor
19
+
20
+ *.gem
21
+
22
+ *.rdb
data/.gitmodules ADDED
@@ -0,0 +1,6 @@
1
+ [submodule "ext/libuv"]
2
+ path = ext/libuv
3
+ url = https://github.com/libuv/libuv.git
4
+ [submodule "ext/gyp"]
5
+ path = ext/gyp
6
+ url = https://chromium.googlesource.com/external/gyp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,24 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-2.3.5
4
+ - ruby-2.4.2
5
+ - ruby-head
6
+ - rubinius
7
+ - jruby-9.1.13.0
8
+ - jruby-head
9
+ branches:
10
+ only:
11
+ - master
12
+ before_install:
13
+ - git submodule update --init --recursive
14
+ - gem install ffi
15
+ before_script:
16
+ - rake compile
17
+ sudo: false
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: jruby-head
21
+ - rvm: ruby-head
22
+ - rvm: rubinius
23
+ sudo: required
24
+ dist: trusty
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ git_source(:github) do |repo_name|
2
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
3
+ "https://github.com/#{repo_name}.git"
4
+ end
5
+
6
+ source "https://rubygems.org"
7
+ gemspec
8
+
9
+ gem "rubysl", :platform => :rbx
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2004-2013 Cotag Media
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
21
+ ===
22
+
23
+ This license applies to all parts of uvrb (Ruby FFI bindings for libuv only)
24
+ Libuv itself [is using Node license](https://github.com/joyent/libuv/blob/master/LICENSE)
data/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # Libuv FFI bindings for Ruby
2
+
3
+ [![Build Status](https://travis-ci.org/cotag/libuv.svg?branch=master)](https://travis-ci.org/cotag/libuv)
4
+
5
+ [Libuv](https://github.com/libuv/libuv) is a cross platform asynchronous IO implementation that powers NodeJS. It supports sockets, both UDP and TCP, filesystem watch, TTY, Pipes and other asynchronous primitives like timer, check, prepare and idle.
6
+
7
+ The Libuv gem contains Libuv and a Ruby wrapper that implements [pipelined promises](http://en.wikipedia.org/wiki/Futures_and_promises#Promise_pipelining) for asynchronous flow control and [coroutines](http://en.wikipedia.org/wiki/Coroutine) / [futures](https://en.wikipedia.org/wiki/Futures_and_promises) for untangling evented code
8
+
9
+ ## Usage
10
+
11
+ Libuv supports multiple reactors that can run on different threads.
12
+
13
+ For convenience the thread local or default reactor can be accessed via the `reactor` method
14
+ You can pass a block to be executed on the reactor and the reactor will run until there is nothing left to do.
15
+
16
+ ```ruby
17
+ require 'libuv'
18
+
19
+ reactor do |reactor|
20
+ reactor.timer {
21
+ puts "5 seconds passed"
22
+ }.start(5000)
23
+ end
24
+
25
+ puts "reactor stopped. No more IO to process"
26
+ ```
27
+
28
+ Promises are used to simplify code flow.
29
+
30
+ ```ruby
31
+ require 'libuv'
32
+
33
+ reactor do |reactor|
34
+ reactor.tcp { |data, socket|
35
+ puts "received: #{data}"
36
+ socket.close
37
+ }
38
+ .connect('127.0.0.1', 3000) { |socket|
39
+ socket.start_read
40
+ .write("GET / HTTP/1.1\r\n\r\n")
41
+ }
42
+ .catch { |error|
43
+ puts "error: #{error}"
44
+ }
45
+ .finally {
46
+ puts "socket closed"
47
+ }
48
+ end
49
+ ```
50
+
51
+ Continuations are used if callbacks are not defined
52
+
53
+ ```ruby
54
+ require 'libuv'
55
+
56
+ reactor do |reactor|
57
+ begin
58
+ reactor.tcp { |data, socket|
59
+ puts "received: #{data}"
60
+ socket.close
61
+ }
62
+ .connect('127.0.0.1', 3000)
63
+ .start_read
64
+ .write("GET / HTTP/1.1\r\n\r\n")
65
+ rescue => error
66
+ puts "error: #{error}"
67
+ end
68
+ end
69
+ ```
70
+
71
+ Any promise can be converted into a continuation
72
+
73
+ ```ruby
74
+ require 'libuv'
75
+
76
+ reactor do |reactor|
77
+ # Perform work on the thread pool with promises
78
+ reactor.work {
79
+ 10 * 2
80
+ }.then { |result|
81
+ puts "result using a promise #{result}"
82
+ }
83
+
84
+ # Use the coroutine helper to obtain the result without a callback
85
+ result = reactor.work {
86
+ 10 * 3
87
+ }.value
88
+ puts "no additional callbacks here #{result}"
89
+ end
90
+ ```
91
+
92
+
93
+ Check out the [yard documentation](http://rubydoc.info/gems/libuv/Libuv/Reactor)
94
+
95
+
96
+ ## Installation
97
+
98
+ ```shell
99
+ gem install libuv
100
+ ```
101
+
102
+ or
103
+
104
+ ```shell
105
+ git clone https://github.com/cotag/libuv.git
106
+ cd libuv
107
+ bundle install
108
+ rake compile
109
+ ```
110
+
111
+ ### Prerequisites
112
+
113
+ * The installation on BSD/Linux requires [python 2.x](http://www.python.org/getit/) to be installed and available on the PATH
114
+ * setting the environmental variable `USE_GLOBAL_LIBUV` will prevent compiling the packaged version.
115
+ * if you have a compatible `libuv.(so | dylib | dll)` on the PATH already
116
+
117
+ On Windows the GEM ships with a pre-compiled binary. If you would like to build yourself:
118
+
119
+ - A copy of Visual Studio 2017. [Visual Studio Build Tools](https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017) works fine.
120
+ - Windows 10 SDK
121
+ - C++/CLI Support
122
+ - C++ tools for CMake
123
+ - A copy of [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) x64 - ~30MB installs
124
+ - ruby 2.4+ x64 with MSYS2 is preferred
125
+ - Add the env var `set GYP_MSVS_VERSION=2017`
126
+ - If using jRuby then [GCC](http://win-builds.org/stable/) is also required
127
+ - Setup the paths as described on the gcc page
128
+ - Add required environmental variable `set LIBRARY_PATH=X:\win-builds-64\lib;X:\win-builds-64\x86_64-w64-mingw32\lib`
129
+ - `rake compile`
130
+
131
+
132
+
133
+ ## Features
134
+
135
+ * TCP (with TLS support)
136
+ * UDP
137
+ * TTY
138
+ * Pipes
139
+ * Timer
140
+ * Prepare
141
+ * Check
142
+ * Idle
143
+ * Signals
144
+ * Async callbacks
145
+ * Async DNS Resolution
146
+ * Filesystem Events
147
+ * Filesystem manipulation
148
+ * File manipulation
149
+ * Errors (with a catch-all fallback for anything unhandled on the event reactor)
150
+ * Work queue (thread pool)
151
+ * Coroutines / futures (makes use of Fibers)
152
+
153
+ ### Server Name Indication
154
+
155
+ You can host a TLS enabled server with multiple hostnames using SNI.
156
+
157
+ ```ruby
158
+ server = reactor.tcp
159
+ server.bind('0.0.0.0', 3000, **{
160
+ hosts: [{
161
+ private_key: '/blah.key',
162
+ cert_chain: '/blah.crt',
163
+ host_name: 'somehost.com',
164
+ },
165
+ {
166
+ private_key: '/blah2.key',
167
+ cert_chain: '/blah2.crt',
168
+ host_name: 'somehost2.com'
169
+ },
170
+ {
171
+ private_key: '/blah3.key',
172
+ cert_chain: '/blah3.crt',
173
+ host_name: 'somehost3.com'
174
+ }]
175
+ }) do |client|
176
+ client.start_tls
177
+ client.start_read
178
+ end
179
+
180
+ # at some point later
181
+ server.add_host(private_key: '/blah4.key', cert_chain: '/blah4.crt', host_name: 'somehost4.com')
182
+ server.remove_host('somehost2.com')
183
+ ```
184
+
185
+ You don't have to specify any hosts at binding time.
186
+
187
+
188
+ ## Protocols and 3rd party plugins
189
+
190
+ * [HTTP](https://github.com/cotag/uv-rays - with SNI [server name indication] support)
191
+ * [Faraday plugin](https://github.com/cotag/uv-rays/blob/master/lib/faraday/adapter/libuv.rb)
192
+ * [HTTPI plugin](https://github.com/cotag/uv-rays/blob/master/lib/httpi/adapter/libuv.rb)
193
+ * [HTTP2](https://github.com/igrigorik/http-2)
194
+ * [SOAP](https://github.com/savonrb/savon) (using HTTPI plugin)
195
+ * [SNMP](https://github.com/acaprojects/ruby-engine/blob/master/lib/protocols/snmp.rb)
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'rspec/core/rake_task' # testing framework
3
+ require 'yard' # yard documentation
4
+ require 'ffi' # loads the extension
5
+ require 'rake/clean' # for the :clobber rake task
6
+ require File.expand_path('../lib/mt-libuv/ext/tasks', __FILE__) # platform specific rake tasks used by compile
7
+
8
+
9
+
10
+ # By default we don't run network tests
11
+ task :default => :limited_spec
12
+ RSpec::Core::RakeTask.new(:limited_spec) do |t|
13
+ # Exclude network tests
14
+ t.rspec_opts = "--tag ~network"
15
+ end
16
+ RSpec::Core::RakeTask.new(:spec)
17
+
18
+
19
+ desc "Run all tests"
20
+ task :test => [:spec]
21
+
22
+
23
+ YARD::Rake::YardocTask.new do |t|
24
+ t.files = ['lib/**/*.rb', '-', 'ext/README.md', 'README.md']
25
+ end
26
+
27
+
28
+ desc "Compile libuv from submodule"
29
+ task :compile => ["ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}"]
30
+
31
+ CLOBBER.include("ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}")
data/ext/README.md ADDED
@@ -0,0 +1,6 @@
1
+ To compile a libuv.(so|dll|dylib) go to root directory of libuv project and run:
2
+
3
+ ```shell
4
+ git submodule update --init
5
+ rake compile
6
+ ```
data/ext/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+
2
+ require 'fileutils'
3
+
4
+ if ENV.has_key?('USE_GLOBAL_LIBUV')
5
+ exit(0)
6
+ else
7
+ require 'rubygems'
8
+ require 'ffi'
9
+ require 'rake/clean'
10
+ require '../lib/libuv/ext/tasks'
11
+
12
+ Dir.chdir File.expand_path("../", __FILE__)
13
+ Dir.chdir '..'
14
+
15
+ task :default => :libuv
16
+
17
+ if FFI::Platform.windows?
18
+ task :libuv do
19
+ FileUtils.mkdir('ext/libuv/lib')
20
+ FileUtils.cp 'ext/libuv.dll', 'ext/libuv/lib/libuv.dll'
21
+ end
22
+ else
23
+ desc "Compile libuv from submodule"
24
+ task :libuv => ["ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}"]
25
+
26
+ CLOBBER.include("ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}")
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTLibuv
4
+ class Async < Handle
5
+
6
+
7
+ define_callback function: :on_async
8
+
9
+
10
+ # @param reactor [::MTLibuv::Reactor] reactor this async callback will be associated
11
+ def initialize(reactor)
12
+ @reactor = reactor
13
+
14
+ async_ptr = ::MTLibuv::Ext.allocate_handle_async
15
+ on_async = callback(:on_async, async_ptr.address)
16
+ error = check_result(::MTLibuv::Ext.async_init(reactor.handle, async_ptr, on_async))
17
+
18
+ super(async_ptr, error)
19
+ end
20
+
21
+ # Triggers a notify event, calling everything in the notify chain
22
+ def call
23
+ return if @closed
24
+ error = check_result ::MTLibuv::Ext.async_send(handle)
25
+ reject(error) if error
26
+ self
27
+ end
28
+
29
+ # Used to update the callback that will be triggered when async is called
30
+ #
31
+ # @param callback [Proc] the callback to be called on reactor prepare
32
+ def progress(&callback)
33
+ @callback = callback
34
+ self
35
+ end
36
+
37
+
38
+ private
39
+
40
+
41
+ def on_async(handle)
42
+ @reactor.exec do
43
+ begin
44
+ @callback.call
45
+ rescue Exception => e
46
+ @reactor.log e, 'performing async callback'
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTLibuv
4
+ class Check < Handle
5
+
6
+
7
+ define_callback function: :on_check
8
+
9
+
10
+ # @param reactor [::MTLibuv::Reactor] reactor this check will be associated
11
+ # @param callback [Proc] callback to be called on reactor check
12
+ def initialize(reactor)
13
+ @reactor = reactor
14
+
15
+ check_ptr = ::MTLibuv::Ext.allocate_handle_check
16
+ error = check_result(::MTLibuv::Ext.check_init(reactor.handle, check_ptr))
17
+
18
+ super(check_ptr, error)
19
+ end
20
+
21
+ # Enables the check handler.
22
+ def start
23
+ return if @closed
24
+ error = check_result ::MTLibuv::Ext.check_start(handle, callback(:on_check))
25
+ reject(error) if error
26
+ self
27
+ end
28
+
29
+ # Disables the check handler.
30
+ def stop
31
+ return if @closed
32
+ error = check_result ::MTLibuv::Ext.check_stop(handle)
33
+ reject(error) if error
34
+ self
35
+ end
36
+
37
+ # Used to update the callback that will be triggered on reactor check
38
+ #
39
+ # @param callback [Proc] the callback to be called on reactor check
40
+ def progress(&callback)
41
+ @callback = callback
42
+ self
43
+ end
44
+
45
+
46
+ private
47
+
48
+
49
+ def on_check(handle)
50
+ @reactor.exec do
51
+ begin
52
+ @callback.call
53
+ rescue Exception => e
54
+ @reactor.log e, 'performing check callback'
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fiber'
4
+
5
+ class CoroutineRejection < RuntimeError
6
+ attr_accessor :value
7
+ end
8
+
9
+ module MTLibuv
10
+ # Takes a Promise response and turns it into a co-routine
11
+ # for code execution without using callbacks
12
+ #
13
+ # @param *promises [::MTLibuv::Q::Promise] a number of promises that will be combined into a single promise
14
+ # @return [Object] Returns the result of a single promise or an array of results if provided multiple promises
15
+ # @raise [Exception] if the promise is rejected
16
+ def co(*yieldable)
17
+ on_reactor = MTLibuv::Reactor.current
18
+ raise 'must be running on a reactor thread to use coroutines' unless on_reactor
19
+
20
+ f = Fiber.current
21
+ wasError = false
22
+
23
+ # Convert the input into a promise on the current reactor
24
+ if yieldable.length == 1
25
+ promise = yieldable[0]
26
+ # Passed independently as this is often overwritten for performance
27
+ promise.progress &Proc.new if block_given?
28
+ else
29
+ promise = on_reactor.all(*yieldable)
30
+ end
31
+
32
+ # Use the promise to resume the Fiber
33
+ promise.then(proc { |res|
34
+ if MTLibuv::Reactor.current == on_reactor
35
+ f.resume res
36
+ else
37
+ on_reactor.schedule { f.resume(res) }
38
+ end
39
+ }, proc { |err|
40
+ wasError = true
41
+ if MTLibuv::Reactor.current == on_reactor
42
+ f.resume err
43
+ else
44
+ on_reactor.schedule { f.resume(err) }
45
+ end
46
+ })
47
+
48
+ # We want to prevent the reactor from stopping while we are waiting on a response
49
+ on_reactor.ref
50
+ result = Fiber.yield # Assign the result from the resume
51
+ on_reactor.unref
52
+
53
+ # Either return the result or raise an error
54
+ if wasError
55
+ if result.is_a?(Exception)
56
+ backtrace = caller
57
+ backtrace.shift
58
+ if result.respond_to?(:backtrace) && result.backtrace
59
+ backtrace << '---- continuation ----'
60
+ backtrace.concat(result.backtrace)
61
+ end
62
+ result.set_backtrace(backtrace)
63
+ raise result
64
+ else
65
+ e = case result
66
+ when String, Symbol
67
+ CoroutineRejection.new(result.to_s)
68
+ else
69
+ CoroutineRejection.new
70
+ end
71
+ e.value = result
72
+ raise e
73
+ end
74
+ end
75
+ result
76
+ end
77
+
78
+ module_function :co
79
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTLibuv
4
+ class Dns < Q::DeferredPromise
5
+ include Resource, Listener, Net
6
+
7
+
8
+ define_callback function: :on_complete, params: [:pointer, :int, Ext::UvAddrinfo.by_ref]
9
+
10
+
11
+ attr_reader :results
12
+ attr_reader :domain
13
+ attr_reader :port
14
+ attr_reader :hint
15
+
16
+
17
+ HINTS = {
18
+ :IPv4 => ::MTLibuv::Ext::UvAddrinfo.new,
19
+ :IPv6 => ::MTLibuv::Ext::UvAddrinfo.new
20
+ }
21
+ HINTS[:IPv4].tap do |hint|
22
+ hint[:family] = FFI::Platform.windows? ? 2 : Socket::Constants::AF_INET
23
+ hint[:socktype] = Socket::Constants::SOCK_STREAM
24
+ hint[:protocol] = Socket::Constants::IPPROTO_TCP
25
+ end
26
+ HINTS[:IPv6].tap do |hint|
27
+ hint[:family] = FFI::Platform.windows? ? 23 : Socket::Constants::AF_INET6
28
+ hint[:socktype] = Socket::Constants::SOCK_STREAM
29
+ hint[:protocol] = Socket::Constants::IPPROTO_TCP
30
+ end
31
+
32
+
33
+ # @param reactor [::MTLibuv::Reactor] reactor this work request will be associated
34
+ # @param domain [String] the domain name to resolve
35
+ # @param port [Integer, String] the port we wish to use
36
+ def initialize(reactor, domain, port, hint = :IPv4, wait: true)
37
+ super(reactor, reactor.defer)
38
+
39
+ @domain = domain
40
+ @port = port
41
+ @hint = hint
42
+ @complete = false
43
+ @pointer = ::MTLibuv::Ext.allocate_request_getaddrinfo
44
+ @error = nil # error in callback
45
+
46
+ @instance_id = @pointer.address
47
+ error = check_result ::MTLibuv::Ext.getaddrinfo(@reactor, @pointer, callback(:on_complete), domain, port.to_s, HINTS[hint])
48
+
49
+ if error
50
+ ::MTLibuv::Ext.free(@pointer)
51
+ @complete = true
52
+ @defer.reject(error)
53
+ end
54
+
55
+ @defer.promise.value if wait
56
+ end
57
+
58
+ # Indicates if the lookup has completed yet or not.
59
+ #
60
+ # @return [true, false]
61
+ def completed?
62
+ return @complete
63
+ end
64
+
65
+
66
+ private
67
+
68
+
69
+ def on_complete(req, status, addrinfo)
70
+ @complete = true
71
+ ::MTLibuv::Ext.free(req)
72
+
73
+ e = check_result(status)
74
+
75
+ @reactor.exec do
76
+ if e
77
+ @defer.reject(e)
78
+ else
79
+ begin
80
+ current = addrinfo
81
+ @results = []
82
+ while !current.null?
83
+ @results << get_ip_and_port(current[:addr])
84
+ current = current[:next]
85
+ end
86
+ @defer.resolve(@results)
87
+ rescue Exception => e
88
+ @defer.reject(e)
89
+ end
90
+ ::MTLibuv::Ext.freeaddrinfo(addrinfo)
91
+ end
92
+ end
93
+
94
+ # Clean up references
95
+ cleanup_callbacks
96
+ end
97
+ end
98
+ end