mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-14 01:04:14 +00:00
This commit is contained in:
parent
2286a6ebef
commit
d0c6c18b4f
@ -83,37 +83,15 @@ func dialContextWithFallback(ctx context.Context, fallback proxy.ContextDialer,
|
|||||||
return dialerConn{conn, newDialerAddr(network, addr)}, nil
|
return dialerConn{conn, newDialerAddr(network, addr)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
proxyDialFudgeAddress := func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
defer cancel()
|
conn, err := dialer.DialContext(ctx, network, addr)
|
||||||
var proxyConn, fallbackConn net.Conn
|
if err != nil {
|
||||||
var proxyErr, fallbackErr error
|
return nil, err
|
||||||
proxyDone := make(chan struct{})
|
|
||||||
fallbackDone := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
proxyConn, proxyErr = dialer.DialContext(ctx, network, addr)
|
|
||||||
l.Debugf("Dialing proxy result %s %s: %v %v", network, addr, proxyConn, proxyErr)
|
|
||||||
if proxyErr == nil {
|
|
||||||
proxyConn = dialerConn{proxyConn, newDialerAddr(network, addr)}
|
|
||||||
}
|
}
|
||||||
close(proxyDone)
|
return dialerConn{conn, newDialerAddr(network, addr)}, err
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
fallbackConn, fallbackErr = fallback.DialContext(ctx, network, addr)
|
|
||||||
l.Debugf("Dialing fallback result %s %s: %v %v", network, addr, fallbackConn, fallbackErr)
|
|
||||||
close(fallbackDone)
|
|
||||||
}()
|
|
||||||
<-proxyDone
|
|
||||||
if proxyErr == nil {
|
|
||||||
go func() {
|
|
||||||
<-fallbackDone
|
|
||||||
if fallbackErr == nil {
|
|
||||||
_ = fallbackConn.Close()
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
return proxyConn, nil
|
return dialTwicePreferFirst(ctx, proxyDialFudgeAddress, fallback.DialContext, "proxy", "fallback", network, addr)
|
||||||
}
|
|
||||||
<-fallbackDone
|
|
||||||
return fallbackConn, fallbackErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext dials via context and/or directly, depending on how it is configured.
|
// DialContext dials via context and/or directly, depending on how it is configured.
|
||||||
@ -125,19 +103,86 @@ func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
|||||||
|
|
||||||
// DialContextReusePort tries dialing via proxy if a proxy is configured, and falls back to
|
// DialContextReusePort tries dialing via proxy if a proxy is configured, and falls back to
|
||||||
// a direct connection reusing the port from the connections registry, if no proxy is defined, or connecting via proxy
|
// a direct connection reusing the port from the connections registry, if no proxy is defined, or connecting via proxy
|
||||||
// fails. If the context has a timeout, the timeout might be applied twice.
|
// fails. It also in parallel dials without reusing the port, just in case reusing the port affects routing decisions badly.
|
||||||
func DialContextReusePort(ctx context.Context, network, addr string) (net.Conn, error) {
|
func DialContextReusePort(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
dialer := &net.Dialer{
|
// If proxy is configured, there is no point trying to reuse listen addresses.
|
||||||
Control: ReusePortControl,
|
if proxy.FromEnvironment() != proxy.Direct {
|
||||||
|
return DialContext(ctx, network, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
localAddrInterface := registry.Get(network, tcpAddrLess)
|
localAddrInterface := registry.Get(network, tcpAddrLess)
|
||||||
if localAddrInterface != nil {
|
if localAddrInterface == nil {
|
||||||
if addr, ok := localAddrInterface.(*net.TCPAddr); !ok {
|
// Nothing listening, nothing to reuse.
|
||||||
|
return DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
laddr, ok := localAddrInterface.(*net.TCPAddr)
|
||||||
|
if !ok {
|
||||||
return nil, errUnexpectedInterfaceType
|
return nil, errUnexpectedInterfaceType
|
||||||
} else {
|
}
|
||||||
dialer.LocalAddr = addr
|
|
||||||
|
// Dial twice, once reusing the listen address, another time not reusing it, just in case reusing the address
|
||||||
|
// influences routing and we fail to reach our destination.
|
||||||
|
dialer := net.Dialer{
|
||||||
|
Control: ReusePortControl,
|
||||||
|
LocalAddr: laddr,
|
||||||
|
}
|
||||||
|
return dialTwicePreferFirst(ctx, dialer.DialContext, (&net.Dialer{}).DialContext, "reuse", "non-reuse", network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dialFunc func(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
|
||||||
|
func dialTwicePreferFirst(ctx context.Context, first, second dialFunc, firstName, secondName, network, address string) (net.Conn, error) {
|
||||||
|
// Delay second dial by some time.
|
||||||
|
sleep := time.Second
|
||||||
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
timeout := time.Until(deadline)
|
||||||
|
if timeout > 0 {
|
||||||
|
sleep = timeout / 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dialContextWithFallback(ctx, dialer, network, addr)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var firstConn, secondConn net.Conn
|
||||||
|
var firstErr, secondErr error
|
||||||
|
firstDone := make(chan struct{})
|
||||||
|
secondDone := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
firstConn, firstErr = first(ctx, network, address)
|
||||||
|
l.Debugf("Dialing %s result %s %s: %v %v", firstName, network, address, firstConn, firstErr)
|
||||||
|
close(firstDone)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-firstDone:
|
||||||
|
if firstErr == nil {
|
||||||
|
// First succeeded, no point doing anything in second
|
||||||
|
secondErr = errors.New("didn't dial")
|
||||||
|
close(secondDone)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
secondErr = ctx.Err()
|
||||||
|
close(secondDone)
|
||||||
|
return
|
||||||
|
case <-time.After(sleep):
|
||||||
|
}
|
||||||
|
secondConn, secondErr = second(ctx, network, address)
|
||||||
|
l.Debugf("Dialing %s result %s %s: %v %v", secondName, network, address, secondConn, secondErr)
|
||||||
|
close(secondDone)
|
||||||
|
}()
|
||||||
|
<-firstDone
|
||||||
|
if firstErr == nil {
|
||||||
|
go func() {
|
||||||
|
<-secondDone
|
||||||
|
if secondConn != nil {
|
||||||
|
_ = secondConn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return firstConn, firstErr
|
||||||
|
}
|
||||||
|
<-secondDone
|
||||||
|
return secondConn, secondErr
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user