Skip to content

Commit

Permalink
Merge pull request #23596 from JuliaLang/jn/21555
Browse files Browse the repository at this point in the history
provide improved getaddrinfo, getnameinfo
  • Loading branch information
vtjnash authored Sep 19, 2017
2 parents 417331c + ebe4436 commit d989d3e
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 64 deletions.
9 changes: 9 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,15 @@ end
@deprecate get_creds!(cache::CachedCredentials, credid, default) get!(cache, credid, default)
end

@noinline function getaddrinfo(callback::Function, host::AbstractString)
depwarn("getaddrinfo with a callback function is deprecated, wrap code in @async instead for deferred execution", :getaddrinfo)
@async begin
r = getaddrinfo(host)
callback(r)
end
nothing
end

# END 0.7 deprecations

# BEGIN 1.0 deprecations
Expand Down
2 changes: 2 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,8 @@ export
fdio,
flush,
getaddrinfo,
getalladdrinfo,
getnameinfo,
gethostname,
getipaddr,
getpeername,
Expand Down
1 change: 1 addition & 0 deletions base/libuv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function reinit_stdio()
global uv_jl_connectcb = cfunction(uv_connectcb, Void, Tuple{Ptr{Void}, Cint})
global uv_jl_writecb_task = cfunction(uv_writecb_task, Void, Tuple{Ptr{Void}, Cint})
global uv_jl_getaddrinfocb = cfunction(uv_getaddrinfocb, Void, Tuple{Ptr{Void}, Cint, Ptr{Void}})
global uv_jl_getnameinfocb = cfunction(uv_getnameinfocb, Void, Tuple{Ptr{Void}, Cint, Cstring, Cstring})
global uv_jl_recvcb = cfunction(uv_recvcb, Void, Tuple{Ptr{Void}, Cssize_t, Ptr{Void}, Ptr{Void}, Cuint})
global uv_jl_sendcb = cfunction(uv_sendcb, Void, Tuple{Ptr{Void}, Cint})
global uv_jl_return_spawn = cfunction(uv_return_spawn, Void, Tuple{Ptr{Void}, Int64, Int32})
Expand Down
208 changes: 160 additions & 48 deletions base/socket.jl
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ end
##

struct DNSError <: Exception
host::AbstractString
host::String
code::Int32
end

Expand All @@ -592,81 +592,193 @@ function show(io::IO, err::DNSError)
" (", uverrorname(err.code), ")")
end

callback_dict = ObjectIdDict()

function uv_getaddrinfocb(req::Ptr{Void}, status::Cint, addrinfo::Ptr{Void})
data = ccall(:jl_uv_getaddrinfo_data, Ptr{Void}, (Ptr{Void},), req)
data == C_NULL && return
cb = unsafe_pointer_to_objref(data)::Function
pop!(callback_dict,cb) # using pop forces an error if cb not in callback_dict
if status != 0 || addrinfo == C_NULL
invokelatest(cb, UVError("uv_getaddrinfocb received an unexpected status code", status))
else
freeaddrinfo = addrinfo
while addrinfo != C_NULL
sockaddr = ccall(:jl_sockaddr_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
if ccall(:jl_sockaddr_is_ip4, Int32, (Ptr{Void},), sockaddr) == 1
invokelatest(cb, IPv4(ntoh(ccall(:jl_sockaddr_host4, UInt32, (Ptr{Void},), sockaddr))))
break
#elseif ccall(:jl_sockaddr_is_ip6, Int32, (Ptr{Void},), sockaddr) == 1
# host = Vector{UInt128}(1)
# scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Void}, Ptr{UInt128}), sockaddr, host)
# invokelatest(cb, IPv6(ntoh(host[1])))
# break
data = uv_req_data(req)
if data != C_NULL
t = unsafe_pointer_to_objref(data)::Task
uv_req_set_data(req, C_NULL)
if status != 0 || addrinfo == C_NULL
schedule(t, UVError("getaddrinfocb", status))
else
freeaddrinfo = addrinfo
addrs = IPAddr[]
while addrinfo != C_NULL
sockaddr = ccall(:jl_sockaddr_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
if ccall(:jl_sockaddr_is_ip4, Int32, (Ptr{Void},), sockaddr) == 1
ip4addr = ccall(:jl_sockaddr_host4, UInt32, (Ptr{Void},), sockaddr)
push!(addrs, IPv4(ntoh(ip4addr)))
elseif ccall(:jl_sockaddr_is_ip6, Int32, (Ptr{Void},), sockaddr) == 1
ip6addr = Ref{UInt128}()
scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Void}, Ptr{UInt128}), sockaddr, ip6addr)
push!(addrs, IPv6(ntoh(ip6addr[])))
end
addrinfo = ccall(:jl_next_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
end
addrinfo = ccall(:jl_next_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
ccall(:uv_freeaddrinfo, Void, (Ptr{Void},), freeaddrinfo)
schedule(t, addrs)
end
ccall(:uv_freeaddrinfo, Void, (Ptr{Void},), freeaddrinfo)
else
# no owner for this req, safe to just free it
Libc.free(req)
end
Libc.free(req)
nothing
end

function getaddrinfo(cb::Function, host::String)
"""
getalladdrinfo(host::AbstractString) -> Vector{IPAddr}
Gets all of the IP addresses of the `host`.
Uses the operating system's underlying getaddrinfo implementation, which may do a DNS lookup.
"""
function getalladdrinfo(host::String)
isascii(host) || error("non-ASCII hostname: $host")
callback_dict[cb] = cb
status = ccall(:jl_getaddrinfo, Int32, (Ptr{Void}, Cstring, Ptr{UInt8}, Any, Ptr{Void}),
eventloop(), host, C_NULL, cb, uv_jl_getaddrinfocb::Ptr{Void})
if status == UV_EINVAL
throw(ArgumentError("Invalid uv_getaddrinfo() agument"))
elseif status in [UV_ENOMEM, UV_ENOBUFS]
throw(OutOfMemoryError())
elseif status < 0
throw(UVError("uv_getaddrinfo returned an unexpected error code", status))
req = Libc.malloc(_sizeof_uv_getaddrinfo)
uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call
status = ccall(:jl_getaddrinfo, Int32, (Ptr{Void}, Ptr{Void}, Cstring, Ptr{Void}, Ptr{Void}),
eventloop(), req, host, #=service=#C_NULL, uv_jl_getaddrinfocb::Ptr{Void})
if status < 0
Libc.free(req)
if status == UV_EINVAL
throw(ArgumentError("Invalid getaddrinfo argument"))
elseif status == UV_ENOMEM || status == UV_ENOBUFS
throw(OutOfMemoryError())
end
uv_error("getaddrinfo", status)
end
ct = current_task()
preserve_handle(ct)
r = try
uv_req_set_data(req, ct)
wait()
finally
if uv_req_data(req) != C_NULL
# req is still alive,
# so make sure we don't get spurious notifications later
uv_req_set_data(req, C_NULL)
ccall(:uv_cancel, Int32, (Ptr{Void},), req) # try to let libuv know we don't care anymore
else
# done with req
Libc.free(req)
end
unpreserve_handle(ct)
end
return nothing
if isa(r, UVError)
code = r.code
if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS,
UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL,
UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME,
UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE,
UV_EAI_SOCKTYPE)
throw(DNSError(host, code))
elseif code == UV_EAI_MEMORY
throw(OutOfMemoryError())
else
throw(UVError("getaddrinfo", code))
end
end
return r::Vector{IPAddr}
end
getaddrinfo(cb::Function, host::AbstractString) = getaddrinfo(cb, String(host))
getalladdrinfo(host::AbstractString) = getalladdrinfo(String(host))

"""
getaddrinfo(host::AbstractString) -> IPAddr
getalladdrinfo(host::AbstractString, IPAddr=IPv4) -> IPAddr
Gets the IP address of the `host` (may have to do a DNS lookup)
Gets the first IP address of the `host` of the specified IPAddr type.
Uses the operating system's underlying getaddrinfo implementation, which may do a DNS lookup.
"""
function getaddrinfo(host::String)
c = Condition()
getaddrinfo(host) do IP
notify(c,IP)
function getaddrinfo(host::String, T::Type{<:IPAddr})
addrs = getalladdrinfo(host)
for addr in addrs
if addr isa T
return addr
end
end
throw(DNSError(host, UV_EAI_NONAME))
end
getaddrinfo(host::AbstractString, T::Type{<:IPAddr}) = getaddrinfo(String(host), T)
getaddrinfo(host::AbstractString) = getaddrinfo(String(host), IPv4)

function uv_getnameinfocb(req::Ptr{Void}, status::Cint, hostname::Cstring, service::Cstring)
data = uv_req_data(req)
if data != C_NULL
t = unsafe_pointer_to_objref(data)::Task
uv_req_set_data(req, C_NULL)
if status != 0
schedule(t, UVError("getnameinfocb", status))
else
schedule(t, unsafe_string(hostname))
end
else
# no owner for this req, safe to just free it
Libc.free(req)
end
nothing
end

"""
getnameinfo(host::IPAddr) -> String
Performs a reverse-lookup for IP address to return a hostname and service
using the operating system's underlying getnameinfo implementation.
"""
function getnameinfo(address::Union{IPv4, IPv6})
req = Libc.malloc(_sizeof_uv_getnameinfo)
uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call
ev = eventloop()
port = hton(UInt16(0))
flags = 0
uvcb = uv_jl_getnameinfocb::Ptr{Void}
status = UV_EINVAL
if address isa IPv4
status = ccall(:jl_getnameinfo, Int32, (Ptr{Void}, Ptr{Void}, UInt32, UInt16, Cint, Ptr{Void}),
ev, req, hton(address.host), port, flags, uvcb)
elseif address isa IPv6
status = ccall(:jl_getnameinfo6, Int32, (Ptr{Void}, Ptr{Void}, Ref{UInt128}, UInt16, Cint, Ptr{Void}),
ev, req, hton(address.host), port, flags, uvcb)
end
if status < 0
Libc.free(req)
if status == UV_EINVAL
throw(ArgumentError("Invalid getnameinfo argument"))
elseif status == UV_ENOMEM || status == UV_ENOBUFS
throw(OutOfMemoryError())
end
uv_error("getnameinfo", status)
end
ct = current_task()
preserve_handle(ct)
r = try
uv_req_set_data(req, ct)
wait()
finally
if uv_req_data(req) != C_NULL
# req is still alive,
# so make sure we don't get spurious notifications later
uv_req_set_data(req, C_NULL)
ccall(:uv_cancel, Int32, (Ptr{Void},), req) # try to let libuv know we don't care anymore
else
# done with req
Libc.free(req)
end
unpreserve_handle(ct)
end
r = wait(c)
if isa(r, UVError)
r = r::UVError
code = r.code
if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS,
UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL,
UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME,
UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE,
UV_EAI_SOCKTYPE)
throw(DNSError(host, code))
throw(DNSError(repr(address), code))
elseif code == UV_EAI_MEMORY
throw(OutOfMemoryError())
else
throw(SystemError("uv_getaddrinfocb", -code))
throw(UVError("getnameinfo", code))
end
end
return r::IPAddr
return r::String
end
getaddrinfo(host::AbstractString) = getaddrinfo(String(host))


const _sizeof_uv_interface_address = ccall(:jl_uv_sizeof_interface_address,Int32,())

Expand Down
2 changes: 2 additions & 0 deletions doc/src/stdlib/io-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ Base.connect(::AbstractString)
Base.listen(::Any)
Base.listen(::AbstractString)
Base.getaddrinfo
Base.getalladdrinfo
Base.getnameinfo
Base.getsockname
Base.getpeername
Base.IPv4
Expand Down
40 changes: 31 additions & 9 deletions src/jl_uv.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ JL_DLLEXPORT size_t jl_uv_buf_len(const uv_buf_t *buf) { return buf->len; }
JL_DLLEXPORT void jl_uv_buf_set_base(uv_buf_t *buf, char *b) { buf->base = b; }
JL_DLLEXPORT void jl_uv_buf_set_len(uv_buf_t *buf, size_t n) { buf->len = n; }
JL_DLLEXPORT void *jl_uv_connect_handle(uv_connect_t *connect) { return connect->handle; }
JL_DLLEXPORT void *jl_uv_getaddrinfo_data(uv_getaddrinfo_t *req) { return req->data; }
JL_DLLEXPORT uv_file jl_uv_file_handle(jl_uv_file_t *f) { return f->file; }
JL_DLLEXPORT void *jl_uv_req_data(uv_req_t *req) { return req->data; }
JL_DLLEXPORT void jl_uv_req_set_data(uv_req_t *req, void *data) { req->data = data; }
Expand Down Expand Up @@ -712,23 +711,46 @@ JL_DLLEXPORT struct sockaddr_in *jl_uv_interface_address_sockaddr(uv_interface_a
return &ifa->address.address4;
}

JL_DLLEXPORT int jl_getaddrinfo(uv_loop_t *loop, const char *host,
const char *service, jl_function_t *cb,
uv_getaddrinfo_cb uvcb)
JL_DLLEXPORT int jl_getaddrinfo(uv_loop_t *loop, uv_getaddrinfo_t *req,
const char *host, const char *service, uv_getaddrinfo_cb uvcb)
{
uv_getaddrinfo_t *req = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t));
struct addrinfo hints;

memset (&hints, 0, sizeof (hints));
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags |= AI_CANONNAME;

req->data = cb;
req->data = NULL;
return uv_getaddrinfo(loop, req, uvcb, host, service, &hints);
}

return uv_getaddrinfo(loop,req,uvcb,host,service,&hints);
JL_DLLEXPORT int jl_getnameinfo(uv_loop_t *loop, uv_getnameinfo_t *req,
uint32_t host, uint16_t port, int flags, uv_getnameinfo_cb uvcb)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = host;
addr.sin_port = port;

req->data = NULL;
return uv_getnameinfo(loop, req, uvcb, (struct sockaddr*)&addr, flags);
}

JL_DLLEXPORT int jl_getnameinfo6(uv_loop_t *loop, uv_getnameinfo_t *req,
void *host, uint16_t port, int flags, uv_getnameinfo_cb uvcb)
{
struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, host, 16);
addr.sin6_port = port;

req->data = NULL;
return uv_getnameinfo(loop, req, uvcb, (struct sockaddr*)&addr, flags);
}


JL_DLLEXPORT struct sockaddr *jl_sockaddr_from_addrinfo(struct addrinfo *addrinfo)
{
return addrinfo->ai_addr;
Expand Down
Loading

2 comments on commit d989d3e

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily benchmark build, I will reply here when finished:

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan

Please sign in to comment.