mt-libuv 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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