diff --git a/gui/default/index.html b/gui/default/index.html index 81157d04d..7f26b0adf 100644 --- a/gui/default/index.html +++ b/gui/default/index.html @@ -751,12 +751,8 @@ - - - - - - + +
{{deviceName(deviceCfg)}}
@@ -849,7 +845,7 @@  Connection Type - + {{rdConnTypeString(rdConnType(deviceCfg.deviceID))}} diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js index aa301d081..f43bdea8e 100755 --- a/gui/default/syncthing/core/syncthingController.js +++ b/gui/default/syncthing/core/syncthingController.js @@ -1203,24 +1203,27 @@ angular.module('syncthing.core') $scope.rdConnType = function (deviceID) { var conn = $scope.connections[deviceID]; if (!conn) return "-1"; - if (conn.type.indexOf('relay') === 0) return "relay"; - if (conn.type.indexOf('quic') === 0) return "quic"; - if (conn.type.indexOf('tcp') === 0) return "tcp" + rdAddrType(conn.address); - return "disconnected"; - } + var type = "disconnected"; + if (conn.type.indexOf('relay') === 0) type = "relay"; + else if (conn.type.indexOf('quic') === 0) type = "quic"; + else if (conn.type.indexOf('tcp') === 0) type = "tcp"; + else return type; - function rdAddrType(address) { - var re = /(^(?:127\.|0?10\.|172\.0?1[6-9]\.|172\.0?2[0-9]\.|172\.0?3[01]\.|192\.168\.|169\.254\.|::1|[fF][cCdD][0-9a-fA-F]{2}:|[fF][eE][89aAbB][0-9a-fA-F]:))/ - if (re.test(address)) return "lan"; - return "wan"; + if (conn.isLocal) type += "lan"; + else type += "wan"; + return type; } $scope.rdConnTypeString = function (type) { switch (type) { - case "relay": - return $translate.instant('Relay'); - case "quic": - return $translate.instant('QUIC'); + case "relaywan": + return $translate.instant('Relay WAN'); + case "relaylan": + return $translate.instant('Relay LAN'); + case "quicwan": + return $translate.instant('QUIC WAN'); + case "quiclan": + return $translate.instant('QUIC LAN'); case "tcpwan": return $translate.instant('TCP WAN'); case "tcplan": @@ -1230,11 +1233,30 @@ angular.module('syncthing.core') } } + $scope.rdConnTypeIcon = function (type) { + switch (type) { + case "tcplan": + case "quiclan": + return "reception-4"; + case "tcpwan": + case "quicwan": + return "reception-3"; + case "relaylan": + return "reception-2"; + case "relaywan": + return "reception-1"; + case "disconnected": + return "reception-0"; + } + } + $scope.rdConnDetails = function (type) { switch (type) { - case "relay": + case "relaylan": + case "relaywan": return $translate.instant('Connections via relays might be rate limited by the relay'); - case "quic": + case "quiclan": + case "quicwan": return $translate.instant('QUIC connections are in most cases considered suboptimal'); case "tcpwan": return $translate.instant('Using a direct TCP connection over WAN'); diff --git a/lib/connections/service.go b/lib/connections/service.go index 127377d3b..e504c8d20 100644 --- a/lib/connections/service.go +++ b/lib/connections/service.go @@ -403,11 +403,13 @@ func (s *service) handleHellos(ctx context.Context) error { continue } + // Determine only once whether a connection is considered local + // according to our configuration, then cache the decision. + c.isLocal = s.isLAN(c.RemoteAddr()) // Wrap the connection in rate limiters. The limiter itself will // keep up with config changes to the rate and whether or not LAN // connections are limited. - isLAN := s.isLAN(c.RemoteAddr()) - rd, wr := s.limiter.getLimiters(remoteID, c, isLAN) + rd, wr := s.limiter.getLimiters(remoteID, c, c.IsLocal()) protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression, s.cfg.FolderPasswords(remoteID)) go func() { diff --git a/lib/connections/structs.go b/lib/connections/structs.go index 238ab7fb0..1a98fc720 100644 --- a/lib/connections/structs.go +++ b/lib/connections/structs.go @@ -39,6 +39,7 @@ type tlsConn interface { type internalConn struct { tlsConn connType connType + isLocal bool priority int establishedAt time.Time } @@ -107,6 +108,10 @@ func (c internalConn) Type() string { return c.connType.String() } +func (c internalConn) IsLocal() bool { + return c.isLocal +} + func (c internalConn) Priority() int { return c.priority } diff --git a/lib/model/model.go b/lib/model/model.go index 24b37ece8..33f5e61fd 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -709,6 +709,7 @@ type ConnectionInfo struct { Address string `json:"address"` ClientVersion string `json:"clientVersion"` Type string `json:"type"` + IsLocal bool `json:"isLocal"` Crypto string `json:"crypto"` } @@ -739,6 +740,7 @@ func (m *model) ConnectionStats() map[string]interface{} { } if conn, ok := m.conn[device]; ok { ci.Type = conn.Type() + ci.IsLocal = conn.IsLocal() ci.Crypto = conn.Crypto() ci.Connected = ok ci.Statistics = conn.Statistics() diff --git a/lib/protocol/mocked_connection_info_test.go b/lib/protocol/mocked_connection_info_test.go index b336a7440..aab43306f 100644 --- a/lib/protocol/mocked_connection_info_test.go +++ b/lib/protocol/mocked_connection_info_test.go @@ -28,6 +28,16 @@ type mockedConnectionInfo struct { establishedAtReturnsOnCall map[int]struct { result1 time.Time } + IsLocalStub func() bool + isLocalMutex sync.RWMutex + isLocalArgsForCall []struct { + } + isLocalReturns struct { + result1 bool + } + isLocalReturnsOnCall map[int]struct { + result1 bool + } PriorityStub func() int priorityMutex sync.RWMutex priorityArgsForCall []struct { @@ -188,6 +198,59 @@ func (fake *mockedConnectionInfo) EstablishedAtReturnsOnCall(i int, result1 time }{result1} } +func (fake *mockedConnectionInfo) IsLocal() bool { + fake.isLocalMutex.Lock() + ret, specificReturn := fake.isLocalReturnsOnCall[len(fake.isLocalArgsForCall)] + fake.isLocalArgsForCall = append(fake.isLocalArgsForCall, struct { + }{}) + stub := fake.IsLocalStub + fakeReturns := fake.isLocalReturns + fake.recordInvocation("IsLocal", []interface{}{}) + fake.isLocalMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *mockedConnectionInfo) IsLocalCallCount() int { + fake.isLocalMutex.RLock() + defer fake.isLocalMutex.RUnlock() + return len(fake.isLocalArgsForCall) +} + +func (fake *mockedConnectionInfo) IsLocalCalls(stub func() bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = stub +} + +func (fake *mockedConnectionInfo) IsLocalReturns(result1 bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = nil + fake.isLocalReturns = struct { + result1 bool + }{result1} +} + +func (fake *mockedConnectionInfo) IsLocalReturnsOnCall(i int, result1 bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = nil + if fake.isLocalReturnsOnCall == nil { + fake.isLocalReturnsOnCall = make(map[int]struct { + result1 bool + }) + } + fake.isLocalReturnsOnCall[i] = struct { + result1 bool + }{result1} +} + func (fake *mockedConnectionInfo) Priority() int { fake.priorityMutex.Lock() ret, specificReturn := fake.priorityReturnsOnCall[len(fake.priorityArgsForCall)] @@ -460,6 +523,8 @@ func (fake *mockedConnectionInfo) Invocations() map[string][][]interface{} { defer fake.cryptoMutex.RUnlock() fake.establishedAtMutex.RLock() defer fake.establishedAtMutex.RUnlock() + fake.isLocalMutex.RLock() + defer fake.isLocalMutex.RUnlock() fake.priorityMutex.RLock() defer fake.priorityMutex.RUnlock() fake.remoteAddrMutex.RLock() diff --git a/lib/protocol/mocks/connection.go b/lib/protocol/mocks/connection.go index 3aab4b8b9..3fddab239 100644 --- a/lib/protocol/mocks/connection.go +++ b/lib/protocol/mocks/connection.go @@ -94,6 +94,16 @@ type Connection struct { indexUpdateReturnsOnCall map[int]struct { result1 error } + IsLocalStub func() bool + isLocalMutex sync.RWMutex + isLocalArgsForCall []struct { + } + isLocalReturns struct { + result1 bool + } + isLocalReturnsOnCall map[int]struct { + result1 bool + } PriorityStub func() int priorityMutex sync.RWMutex priorityArgsForCall []struct { @@ -639,6 +649,59 @@ func (fake *Connection) IndexUpdateReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *Connection) IsLocal() bool { + fake.isLocalMutex.Lock() + ret, specificReturn := fake.isLocalReturnsOnCall[len(fake.isLocalArgsForCall)] + fake.isLocalArgsForCall = append(fake.isLocalArgsForCall, struct { + }{}) + stub := fake.IsLocalStub + fakeReturns := fake.isLocalReturns + fake.recordInvocation("IsLocal", []interface{}{}) + fake.isLocalMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *Connection) IsLocalCallCount() int { + fake.isLocalMutex.RLock() + defer fake.isLocalMutex.RUnlock() + return len(fake.isLocalArgsForCall) +} + +func (fake *Connection) IsLocalCalls(stub func() bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = stub +} + +func (fake *Connection) IsLocalReturns(result1 bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = nil + fake.isLocalReturns = struct { + result1 bool + }{result1} +} + +func (fake *Connection) IsLocalReturnsOnCall(i int, result1 bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = nil + if fake.isLocalReturnsOnCall == nil { + fake.isLocalReturnsOnCall = make(map[int]struct { + result1 bool + }) + } + fake.isLocalReturnsOnCall[i] = struct { + result1 bool + }{result1} +} + func (fake *Connection) Priority() int { fake.priorityMutex.Lock() ret, specificReturn := fake.priorityReturnsOnCall[len(fake.priorityArgsForCall)] @@ -1111,6 +1174,8 @@ func (fake *Connection) Invocations() map[string][][]interface{} { defer fake.indexMutex.RUnlock() fake.indexUpdateMutex.RLock() defer fake.indexUpdateMutex.RUnlock() + fake.isLocalMutex.RLock() + defer fake.isLocalMutex.RUnlock() fake.priorityMutex.RLock() defer fake.priorityMutex.RUnlock() fake.remoteAddrMutex.RLock() diff --git a/lib/protocol/mocks/connection_info.go b/lib/protocol/mocks/connection_info.go index 8612b6383..9e9dd4728 100644 --- a/lib/protocol/mocks/connection_info.go +++ b/lib/protocol/mocks/connection_info.go @@ -30,6 +30,16 @@ type ConnectionInfo struct { establishedAtReturnsOnCall map[int]struct { result1 time.Time } + IsLocalStub func() bool + isLocalMutex sync.RWMutex + isLocalArgsForCall []struct { + } + isLocalReturns struct { + result1 bool + } + isLocalReturnsOnCall map[int]struct { + result1 bool + } PriorityStub func() int priorityMutex sync.RWMutex priorityArgsForCall []struct { @@ -190,6 +200,59 @@ func (fake *ConnectionInfo) EstablishedAtReturnsOnCall(i int, result1 time.Time) }{result1} } +func (fake *ConnectionInfo) IsLocal() bool { + fake.isLocalMutex.Lock() + ret, specificReturn := fake.isLocalReturnsOnCall[len(fake.isLocalArgsForCall)] + fake.isLocalArgsForCall = append(fake.isLocalArgsForCall, struct { + }{}) + stub := fake.IsLocalStub + fakeReturns := fake.isLocalReturns + fake.recordInvocation("IsLocal", []interface{}{}) + fake.isLocalMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *ConnectionInfo) IsLocalCallCount() int { + fake.isLocalMutex.RLock() + defer fake.isLocalMutex.RUnlock() + return len(fake.isLocalArgsForCall) +} + +func (fake *ConnectionInfo) IsLocalCalls(stub func() bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = stub +} + +func (fake *ConnectionInfo) IsLocalReturns(result1 bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = nil + fake.isLocalReturns = struct { + result1 bool + }{result1} +} + +func (fake *ConnectionInfo) IsLocalReturnsOnCall(i int, result1 bool) { + fake.isLocalMutex.Lock() + defer fake.isLocalMutex.Unlock() + fake.IsLocalStub = nil + if fake.isLocalReturnsOnCall == nil { + fake.isLocalReturnsOnCall = make(map[int]struct { + result1 bool + }) + } + fake.isLocalReturnsOnCall[i] = struct { + result1 bool + }{result1} +} + func (fake *ConnectionInfo) Priority() int { fake.priorityMutex.Lock() ret, specificReturn := fake.priorityReturnsOnCall[len(fake.priorityArgsForCall)] @@ -462,6 +525,8 @@ func (fake *ConnectionInfo) Invocations() map[string][][]interface{} { defer fake.cryptoMutex.RUnlock() fake.establishedAtMutex.RLock() defer fake.establishedAtMutex.RUnlock() + fake.isLocalMutex.RLock() + defer fake.isLocalMutex.RUnlock() fake.priorityMutex.RLock() defer fake.priorityMutex.RUnlock() fake.remoteAddrMutex.RLock() diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go index 2fba29c6b..48dfde2d3 100644 --- a/lib/protocol/protocol.go +++ b/lib/protocol/protocol.go @@ -162,6 +162,7 @@ type Connection interface { type ConnectionInfo interface { Type() string Transport() string + IsLocal() bool RemoteAddr() net.Addr Priority() int String() string