Created
November 20, 2019 21:49
-
-
Save rdp/c776c1ea95dfd666d1d828eba4caabc3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "socket" # works with 0.31.1 FWIW | |
struct Socket::Addrinfo | |
class DnsRequestCbArg | |
getter value : Int32 | Pointer(LibC::Addrinfo) | Nil | |
@fiber : Fiber | |
def initialize | |
@fiber = Fiber.current | |
end | |
def value=(val) | |
@value = val | |
end | |
end | |
def self.getaddrinfo(host, port, family, socktype, protocol = Protocol::IP, timeout = nil) | |
hints = LibC::Addrinfo.new | |
hints.ai_family = (family || Family::UNSPEC).to_i32 | |
hints.ai_socktype = socktype | |
hints.ai_protocol = protocol | |
hints.ai_flags = 0 | |
dns_req = DnsRequestCbArg.new | |
# may fire immediately or on the next event loop | |
req = create_dns_request(host, port.to_s, pointerof(hints), dns_req) do |err, addr, data| | |
dreq = data.as(DnsRequestCbArg) | |
if err == 0 | |
dreq.value = addr | |
else | |
dreq.value = err | |
end | |
end | |
if timeout && req | |
spawn do | |
sleep timeout.not_nil! | |
req.not_nil!.cancel unless dns_req.value | |
end | |
end | |
success = false | |
value = dns_req.value | |
# BUG: not thread safe. change when threads are implemented | |
unless value | |
Fiber.yield | |
value = dns_req.value | |
end | |
if value.is_a?(LibC::Addrinfo*) | |
begin | |
cur_addr = value | |
while cur_addr | |
success = yield new(cur_addr) | |
break if success | |
cur_addr = cur_addr.value.ai_next | |
end | |
ensure | |
LibEvent2.evutil_freeaddrinfo value | |
end | |
elsif value.is_a?(Int) | |
if value == LibEvent2::EVUTIL_EAI_CANCEL | |
raise IO::Timeout.new("Failed to resolve #{host} in #{timeout} seconds") | |
end | |
error_message = String.new(LibC.gai_strerror(value)) | |
raise Socket::Error.new("getaddrinfo: #{error_message}") | |
else | |
raise "unknown type #{value.inspect}" | |
end | |
# shouldn't raise | |
raise Socket::Error.new("getaddrinfo: unspecified error") unless success | |
end | |
def self.create_dns_request(nodename, servname, hints, data, &callback : LibEvent2::DnsGetAddrinfoCallback) | |
Thread.current.event_base.new_dns_base.getaddrinfo(nodename, servname, hints, data, &callback) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment