package swarmimport (mamanet)// The 250ms value is from happy eyeballs RFC 8305. This is a rough estimate of 1 RTTconst (// duration by which TCP dials are delayed relative to the last QUIC dialPublicTCPDelay = 250 * time.MillisecondPrivateTCPDelay = 30 * time.Millisecond// duration by which QUIC dials are delayed relative to previous QUIC dialPublicQUICDelay = 250 * time.MillisecondPrivateQUICDelay = 30 * time.Millisecond// RelayDelay is the duration by which relay dials are delayed relative to direct addressesRelayDelay = 500 * time.Millisecond// delay for other transport addresses. This will apply to /webrtc-direct.PublicOtherDelay = 1 * time.SecondPrivateOtherDelay = 100 * time.Millisecond)// NoDelayDialRanker ranks addresses with no delay. This is useful for simultaneous connect requests.func ( []ma.Multiaddr) []network.AddrDelay {returngetAddrDelay(, 0, 0, 0, 0)}// DefaultDialRanker determines the ranking of outgoing connection attempts.//// Addresses are grouped into three distinct groups://// - private addresses (localhost and local networks (RFC 1918))// - public addresses// - relay addresses//// Within each group, the addresses are ranked according to the ranking logic described below.// We then dial addresses according to this ranking, with short timeouts applied between dial attempts.// This ranking logic dramatically reduces the number of simultaneous dial attempts, while introducing// no additional latency in the vast majority of cases.//// Private and public address groups are dialed in parallel.// Dialing relay addresses is delayed by 500 ms, if we have any non-relay alternatives.//// Within each group (private, public, relay addresses) we apply the following ranking logic://// 1. If both IPv6 QUIC and IPv4 QUIC addresses are present, we do a Happy Eyeballs RFC 8305 style ranking.// First dial the IPv6 QUIC address with the lowest port. After this we dial the IPv4 QUIC address with// the lowest port delayed by 250ms (PublicQUICDelay) for public addresses, and 30ms (PrivateQUICDelay)// for local addresses. After this we dial all the rest of the addresses delayed by 250ms (PublicQUICDelay)// for public addresses, and 30ms (PrivateQUICDelay) for local addresses.// 2. If only one of QUIC IPv6 or QUIC IPv4 addresses are present, dial the QUIC address with the lowest port// first. After this we dial the rest of the QUIC addresses delayed by 250ms (PublicQUICDelay) for public// addresses, and 30ms (PrivateQUICDelay) for local addresses.// 3. If a QUIC or WebTransport address is present, TCP addresses dials are delayed relative to the last QUIC dial:// We prefer to end up with a QUIC connection. For public addresses, the delay introduced is 250ms (PublicTCPDelay),// and for private addresses 30ms (PrivateTCPDelay).// 4. For the TCP addresses we follow a strategy similar to QUIC with an optimisation for handling the long TCP// handshake time described in 6. If both IPv6 TCP and IPv4 TCP addresses are present, we do a Happy Eyeballs// style ranking. First dial the IPv6 TCP address with the lowest port. After this, dial the IPv4 TCP address// with the lowest port delayed by 250ms (PublicTCPDelay) for public addresses, and 30ms (PrivateTCPDelay)// for local addresses. After this we dial all the rest of the addresses delayed by 250ms (PublicTCPDelay) for// public addresses, and 30ms (PrivateTCPDelay) for local addresses.// 5. If only one of TCP IPv6 or TCP IPv4 addresses are present, dial the TCP address with the lowest port// first. After this we dial the rest of the TCP addresses delayed by 250ms (PublicTCPDelay) for public// addresses, and 30ms (PrivateTCPDelay) for local addresses.// 6. When a TCP socket is connected and awaiting security and muxer upgrade, we stop new dials for 2*PublicTCPDelay// to allow for the upgrade to complete.// 7. WebRTC Direct, and other IP transport addresses are dialed 1 second after the last QUIC or TCP dial.// We only ever need to dial these if the peer doesn't have any other transport available, in which// case these are dialed immediately.//// We dial lowest ports first as they are more likely to be the listen port.func ( []ma.Multiaddr) []network.AddrDelay { , := filterAddrs(, isRelayAddr) , := filterAddrs(, manet.IsPrivateAddr) , := filterAddrs(, func( ma.Multiaddr) bool { returnisProtocolAddr(, ma.P_IP4) || isProtocolAddr(, ma.P_IP6) })vartime.Durationiflen() > 0 {// if there is a public direct address available delay relay dials = RelayDelay } := make([]network.AddrDelay, 0, len()) = append(, getAddrDelay(, PrivateTCPDelay, PrivateQUICDelay, PrivateOtherDelay, 0)...) = append(, getAddrDelay(, PublicTCPDelay, PublicQUICDelay, PublicOtherDelay, 0)...) = append(, getAddrDelay(, PublicTCPDelay, PublicQUICDelay, PublicOtherDelay, )...)vartime.Durationiflen() > 0 { = [len()-1].Delay }for := 0; < len(); ++ { = append(, network.AddrDelay{Addr: [], Delay: + PublicOtherDelay}) }return}// getAddrDelay ranks a group of addresses according to the ranking logic explained in// documentation for defaultDialRanker.// offset is used to delay all addresses by a fixed duration. This is useful for delaying all relay// addresses relative to direct addresses.func getAddrDelay( []ma.Multiaddr, time.Duration, time.Duration,time.Duration, time.Duration) []network.AddrDelay {iflen() == 0 {returnnil }sort.Slice(, func(, int) bool { returnscore([]) < score([]) })// addrs is now sorted by (Transport, IPVersion). Reorder addrs for happy eyeballs dialing. // For QUIC and TCP, if we have both IPv6 and IPv4 addresses, move the // highest priority IPv4 address to the second position. := false := false// tcpStartIdx is the index of the first TCP Addressvarint { := 0// If the first QUIC address is IPv6 move the first QUIC IPv4 address to second positionifisQUICAddr([0]) && isProtocolAddr([0], ma.P_IP6) {for := 1; < len(); ++ {ifisQUICAddr([]) && isProtocolAddr([], ma.P_IP4) {// The first IPv4 address is at position j // Move the jth element at position 1 shifting the affected elementsif > 1 { := []copy([2:], [1:]) [1] = } = true = + 1break } } }for = ; < len(); ++ {ifisProtocolAddr([], ma.P_TCP) {break } }// If the first TCP address is IPv6 move the first TCP IPv4 address to second positionif < len() && isProtocolAddr([], ma.P_IP6) {for := + 1; < len(); ++ {ifisProtocolAddr([], ma.P_TCP) && isProtocolAddr([], ma.P_IP4) {// First TCP IPv4 address is at position j, move it to position tcpStartIdx+1 // which is the second priority TCP addressif > +1 { := []copy([+2:], [+1:]) [+1] = } = truebreak } } } } := make([]network.AddrDelay, 0, len())vartime.Durationvartime.Durationfor , := range {vartime.Durationswitch {caseisQUICAddr():// We dial an IPv6 address, then after quicDelay an IPv4 // address, then after a further quicDelay we dial the rest of the addresses.if == 1 { = }if > 1 {// If we have happy eyeballs for QUIC, dials after the second position // will be delayed by 2*quicDelayif { = 2 * } else { = } } = = + caseisProtocolAddr(, ma.P_TCP):// We dial an IPv6 address, then after tcpDelay an IPv4 // address, then after a further tcpDelay we dial the rest of the addresses.if == +1 { = }if > +1 {// If we have happy eyeballs for TCP, dials after the second position // will be delayed by 2*tcpDelayif { = 2 * } else { = } } += = // if it's neither quic, webtransport, tcp, or websocket addressdefault: = + } = append(, network.AddrDelay{Addr: , Delay: + }) }return}// score scores a multiaddress for dialing delay. Lower is better.// The lower 16 bits of the result are the port. Low ports are ranked higher because they're// more likely to be listen addresses.// The addresses are ranked as:// QUICv1 IPv6 > QUICdraft29 IPv6 > QUICv1 IPv4 > QUICdraft29 IPv4 >// WebTransport IPv6 > WebTransport IPv4 > TCP IPv6 > TCP IPv4func score( ma.Multiaddr) int { := 0ifisProtocolAddr(, ma.P_IP4) { = 1 << 18 }if , := .ValueForProtocol(ma.P_WEBTRANSPORT); == nil { , := .ValueForProtocol(ma.P_UDP) , := strconv.Atoi()return + (1 << 19) + }if , := .ValueForProtocol(ma.P_QUIC); == nil { , := .ValueForProtocol(ma.P_UDP) , := strconv.Atoi()return + + (1 << 17) }if , := .ValueForProtocol(ma.P_QUIC_V1); == nil { , := .ValueForProtocol(ma.P_UDP) , := strconv.Atoi()return + }if , := .ValueForProtocol(ma.P_TCP); == nil { , := strconv.Atoi()return + + (1 << 20) }if , := .ValueForProtocol(ma.P_WEBRTC_DIRECT); == nil {return1 << 21 }return (1 << 30)}func isProtocolAddr( ma.Multiaddr, int) bool { := falsema.ForEach(, func( ma.Component) bool {if .Protocol().Code == { = truereturnfalse }returntrue })return}func isQUICAddr( ma.Multiaddr) bool {returnisProtocolAddr(, ma.P_QUIC) || isProtocolAddr(, ma.P_QUIC_V1)}// filterAddrs filters an address slice in placefunc filterAddrs( []ma.Multiaddr, func( ma.Multiaddr) bool) (, []ma.Multiaddr) { := 0for := 0; < len(); ++ {if ([]) { [], [] = [], [] ++ } }return [:], [:]}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.