Created
November 20, 2019 21:32
-
-
Save rdp/2ebd7fbed16dd5e2bb0ff5151b3b644b to your computer and use it in GitHub Desktop.
This file contains hidden or 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" | |
| 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 | |
| @fiber.resume | |
| end | |
| end | |
| # close but broken TCPSocket somehow? :| | |
| 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 = Scheduler.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 | |
| Scheduler.reschedule | |
| value = dns_req.value | |
| end | |
| if value.is_a?(LibC::Addrinfo*) | |
| begin | |
| cur_addr = value | |
| while cur_addr | |
| success = yield cur_addr.value | |
| 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 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