Skip to content

Commit

Permalink
Try all ip addresses when establishing a connection
Browse files Browse the repository at this point in the history
Fixes JuliaWeb#672
Reenables JuliaWeb#428
  • Loading branch information
fredrikekre committed Feb 19, 2021
1 parent c0b9b5c commit 1a26ed6
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 27 deletions.
69 changes: 42 additions & 27 deletions src/ConnectionPool.jl
Original file line number Diff line number Diff line change
Expand Up @@ -648,37 +648,52 @@ function getconnection(::Type{TCPSocket},

@debug 2 "TCP connect: $host:$p..."

addrs = Sockets.getalladdrinfo(host)

connect_timeout = connect_timeout == 0 && readtimeout > 0 ? readtimeout : connect_timeout
if connect_timeout == 0
tcp = Sockets.connect(host == "localhost" ? ip"127.0.0.1" : Sockets.getalladdrinfo(host)[1], p)
keepalive && keepalive!(tcp)
return tcp
end

tcp = Sockets.TCPSocket()
Sockets.connect!(tcp, Sockets.getalladdrinfo(host)[1], p)

timeout = Ref{Bool}(false)
@async begin
sleep(connect_timeout)
if tcp.status == Base.StatusConnecting
timeout[] = true
tcp.status = Base.StatusClosing
ccall(:jl_forceclose_uv, Nothing, (Ptr{Nothing},), tcp.handle)
#close(tcp)
end
end
try
Sockets.wait_connected(tcp)
catch e
if timeout[]
throw(ConnectTimeout(host, port))
lasterr = ErrorException("unknown connection error")

for addr in addrs
if connect_timeout == 0
try
tcp = Sockets.connect(addr, p)
keepalive && keepalive!(tcp)
return tcp
catch err
lasterr = err
continue # to next ip addr
end
else
tcp = Sockets.TCPSocket()
Sockets.connect!(tcp, addr, p)

timeout = Ref{Bool}(false)
@async begin
sleep(connect_timeout)
if tcp.status == Base.StatusConnecting
timeout[] = true
tcp.status = Base.StatusClosing
ccall(:jl_forceclose_uv, Nothing, (Ptr{Nothing},), tcp.handle)
#close(tcp)
end
end
try
Sockets.wait_connected(tcp)
keepalive && keepalive!(tcp)
return tcp
catch err
if timeout[]
lasterr = ConnectTimeout(host, port)
else
lasterr = err
end
continue # to next ip addr
end
end
rethrow(e)
end

keepalive && keepalive!(tcp)
return tcp
# If no connetion could be set up, to any address, throw last error
throw(lasterr)
end

const nosslconfig = SSLConfig()
Expand Down
23 changes: 23 additions & 0 deletions test/client.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using ..TestRequest
using HTTP
using Sockets
using JSON
using Test

Expand Down Expand Up @@ -215,6 +216,28 @@ end
@test_throws HTTP.IOError HTTP.get("http://httpbin.org/delay/5"; readtimeout=1, retry=false)
end

@testset "Retry all resolved IP addresses" begin
# See issue https://github.com/JuliaWeb/HTTP.jl/issues/672
# Bit tricky to test, but can at least be tested if localhost
# resolves to both IPv4 and IPv6 by listening to the respective
# interface
alladdrs = getalladdrinfo("localhost")
if ip"127.0.0.1" in alladdrs && ip"::1" in alladdrs
for interface in (IPv4(0), IPv6(0))
server = listen(interface, 8080)
@async HTTP.listen(string(interface), 8080; server=server) do http
HTTP.setstatus(http, 200)
HTTP.startwrite(http)
HTTP.write(http, "hello, world")
end
req = HTTP.get("http://localhost:8080")
close(server)
@test req.status == 200
@test String(req.body) == "hello, world"
end
end
end

@testset "Public entry point of HTTP.request and friends (e.g. issue #463)" begin
headers = Dict("User-Agent" => "HTTP.jl")
query = Dict("hello" => "world")
Expand Down

0 comments on commit 1a26ed6

Please sign in to comment.