Skip to content

Commit

Permalink
Move punycode to uri namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
epergo authored and makenowjust committed Mar 4, 2018
1 parent 64a7fda commit 86ac0d7
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 285 deletions.
10 changes: 5 additions & 5 deletions spec/std/punycode_spec.cr → spec/std/uri/punycode_spec.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require "spec"
require "punycode"
require "uri/punycode"

describe Punycode do
describe URI::Punycode do
[
{"3年B組金八先生", "3B-ww4c5e180e575a65lsy2b"},
{"安室奈美恵-with-SUPER-MONKEYS", "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n"},
Expand All @@ -15,15 +15,15 @@ describe Punycode do
dec, enc = example

it "encodes #{dec} to #{enc}" do
Punycode.encode(dec).should eq enc
URI::Punycode.encode(dec).should eq enc
end

it "decodes #{enc} to #{dec}" do
Punycode.decode(enc).should eq dec
URI::Punycode.decode(enc).should eq dec
end
end

it "translate to ascii only host name" do
Punycode.to_ascii("test.テスト.テスト").should eq "test.xn--zckzah.xn--zckzah"
URI::Punycode.to_ascii("test.テスト.テスト").should eq "test.xn--zckzah.xn--zckzah"
end
end
186 changes: 0 additions & 186 deletions src/punycode.cr

This file was deleted.

8 changes: 8 additions & 0 deletions src/socket/addrinfo.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "uri/punycode"

class Socket
# Domain name resolver.
struct Addrinfo
Expand Down Expand Up @@ -76,6 +78,12 @@ class Socket
end

private def self.getaddrinfo(domain, service, family, type, protocol, timeout)
# RFC 3986 says:
# > When a non-ASCII registered name represents an internationalized domain name
# > intended for resolution via the DNS, the name must be transformed to the IDNA
# > encoding [RFC3490] prior to name lookup.
domain = URI::Punycode.to_ascii domain

hints = LibC::Addrinfo.new
hints.ai_family = (family || Family::UNSPEC).to_i32
hints.ai_socktype = type
Expand Down
95 changes: 1 addition & 94 deletions src/socket/ip_socket.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require "punycode"

class IPSocket < Socket
# Returns the `IPAddress` for the local end of the IP socket.
def local_address
Expand All @@ -24,97 +22,6 @@ class IPSocket < Socket
raise Errno.new("getpeername")
end

IPAddress.new(sockaddr, addrlen)
end

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

# Yields LibC::Addrinfo to the block while the block returns false and there are more LibC::Addrinfo results.
#
# The block must return true if it succeeded using that addressinfo
# (to connect or bind, for example), and false otherwise. If it returns false and
# the LibC::Addrinfo has a next LibC::Addrinfo, it is yielded to the block, and so on.
private def getaddrinfo(host, port, family, socktype, protocol = LibC::IPPROTO_IP, timeout = nil)
IPSocket.getaddrinfo(host, port, family, socktype, protocol, timeout) { |ai| yield ai }
end

def self.getaddrinfo(host, port, family, socktype, protocol = LibC::IPPROTO_IP, timeout = nil)
# RFC 3986 says:
# > When a non-ASCII registered name represents an internationalized domain name
# > intended for resolution via the DNS, the name must be transformed to the IDNA
# > encoding [RFC3490] prior to name lookup.
host = Punycode.to_ascii host

hints = LibC::Addrinfo.new
hints.family = (family || LibC::AF_UNSPEC).to_i32
hints.socktype = socktype
hints.protocol = protocol
hints.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.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
IPAddress.from(sockaddr, addrlen)
end
end
Loading

0 comments on commit 86ac0d7

Please sign in to comment.