From 9d21b91124d6465e77542768317e2b0a764fffa5 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sat, 29 Jul 2023 10:24:44 +0200 Subject: [PATCH] all: Refactor the protocol/model interface a bit (ref #8981) (#9007) --- lib/api/api_test.go | 54 +++++----- lib/model/fakeconns_test.go | 8 +- lib/model/folder_recvonly_test.go | 26 ++--- lib/model/folder_sendrecv_test.go | 4 +- lib/model/indexhandler.go | 60 +++++------ lib/model/mocks/model.go | 72 ++++++------- lib/model/model.go | 30 +++--- lib/model/model_test.go | 155 +++++++++++++++------------- lib/model/progressemitter.go | 8 +- lib/model/progressemitter_test.go | 2 +- lib/model/requests_test.go | 24 ++--- lib/model/testutils_test.go | 5 + lib/protocol/benchmark_test.go | 12 +-- lib/protocol/common_test.go | 20 ++-- lib/protocol/encryption.go | 34 +++--- lib/protocol/mocks/connection.go | 130 +++++++++++------------ lib/protocol/nativemodel_darwin.go | 16 +-- lib/protocol/nativemodel_unix.go | 2 +- lib/protocol/nativemodel_windows.go | 16 +-- lib/protocol/protocol.go | 108 +++++++++++++------ lib/protocol/protocol_test.go | 4 +- 21 files changed, 431 insertions(+), 359 deletions(-) diff --git a/lib/api/api_test.go b/lib/api/api_test.go index 78e3795ce..6d1abfecb 100644 --- a/lib/api/api_test.go +++ b/lib/api/api_test.go @@ -58,7 +58,7 @@ var ( func init() { dev1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR") - apiCfg.GUIReturns(config.GUIConfiguration{APIKey: testAPIKey}) + apiCfg.GUIReturns(config.GUIConfiguration{APIKey: testAPIKey, RawAddress: "127.0.0.1:0"}) } func TestMain(m *testing.M) { @@ -496,7 +496,9 @@ func TestAPIServiceRequests(t *testing.T) { } for _, tc := range cases { + tc := tc t.Run(cases[0].URL, func(t *testing.T) { + t.Parallel() testHTTPRequest(t, baseURL, tc, testAPIKey) }) } @@ -557,9 +559,10 @@ func TestHTTPLogin(t *testing.T) { cfg := newMockedConfig() cfg.GUIReturns(config.GUIConfiguration{ - User: "üser", - Password: "$2a$10$IdIZTxTg/dCNuNEGlmLynOjqg4B1FvDKuIV5e0BB3pnWVHNb8.GSq", // bcrypt of "räksmörgås" in UTF-8 - APIKey: testAPIKey, + User: "üser", + Password: "$2a$10$IdIZTxTg/dCNuNEGlmLynOjqg4B1FvDKuIV5e0BB3pnWVHNb8.GSq", // bcrypt of "räksmörgås" in UTF-8 + RawAddress: "127.0.0.1:0", + APIKey: testAPIKey, }) baseURL, cancel, err := startHTTP(cfg) if err != nil { @@ -1050,30 +1053,31 @@ func TestHostCheck(t *testing.T) { t.Error("Incorrect host header, check disabled: expected 200 OK, not", resp.Status) } - // A server bound to a wildcard address also doesn't do the check + if !testing.Short() { + // A server bound to a wildcard address also doesn't do the check - cfg = newMockedConfig() - cfg.GUIReturns(config.GUIConfiguration{ - RawAddress: "0.0.0.0:0", - InsecureSkipHostCheck: true, - }) - baseURL, cancel, err = startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + cfg = newMockedConfig() + cfg.GUIReturns(config.GUIConfiguration{ + RawAddress: "0.0.0.0:0", + }) + baseURL, cancel, err = startHTTP(cfg) + if err != nil { + t.Fatal(err) + } + defer cancel() - // A request with a suspicious Host header should be allowed + // A request with a suspicious Host header should be allowed - req, _ = http.NewRequest("GET", baseURL, nil) - req.Host = "example.com" - resp, err = http.DefaultClient.Do(req) - if err != nil { - t.Fatal(err) - } - resp.Body.Close() - if resp.StatusCode != http.StatusOK { - t.Error("Incorrect host header, wildcard bound: expected 200 OK, not", resp.Status) + req, _ = http.NewRequest("GET", baseURL, nil) + req.Host = "example.com" + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + if resp.StatusCode != http.StatusOK { + t.Error("Incorrect host header, wildcard bound: expected 200 OK, not", resp.Status) + } } // This should all work over IPv6 as well diff --git a/lib/model/fakeconns_test.go b/lib/model/fakeconns_test.go index 9b5495b5f..8b51741a1 100644 --- a/lib/model/fakeconns_test.go +++ b/lib/model/fakeconns_test.go @@ -32,12 +32,12 @@ func newFakeConnection(id protocol.DeviceID, model Model) *fakeConnection { f.RequestCalls(func(ctx context.Context, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) { return f.fileData[name], nil }) - f.IDReturns(id) + f.DeviceIDReturns(id) f.CloseCalls(func(err error) { f.closeOnce.Do(func() { close(f.closed) }) - model.Closed(id, err) + model.Closed(f, err) f.ClosedReturns(f.closed) }) return f @@ -157,7 +157,7 @@ func (f *fakeConnection) sendIndexUpdate() { for i := range f.files { toSend[i] = prepareFileInfoForIndex(f.files[i]) } - f.model.IndexUpdate(f.id, f.folder, toSend) + f.model.IndexUpdate(f, f.folder, toSend) } func addFakeConn(m *testModel, dev protocol.DeviceID, folderID string) *fakeConnection { @@ -165,7 +165,7 @@ func addFakeConn(m *testModel, dev protocol.DeviceID, folderID string) *fakeConn fc.folder = folderID m.AddConnection(fc, protocol.Hello{}) - m.ClusterConfig(dev, protocol.ClusterConfig{ + m.ClusterConfig(fc, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: folderID, diff --git a/lib/model/folder_recvonly_test.go b/lib/model/folder_recvonly_test.go index 918c3a6b1..9a583e28f 100644 --- a/lib/model/folder_recvonly_test.go +++ b/lib/model/folder_recvonly_test.go @@ -30,7 +30,7 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { defer wcfgCancel() ffs := f.Filesystem(nil) defer cleanupModel(m) - addFakeConn(m, device1, f.ID) + conn := addFakeConn(m, device1, f.ID) // Create some test data @@ -45,7 +45,7 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { // Send and index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) size := globalSize(t, m, "ro") @@ -112,7 +112,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { defer wcfgCancel() ffs := f.Filesystem(nil) defer cleanupModel(m) - addFakeConn(m, device1, f.ID) + conn := addFakeConn(m, device1, f.ID) // Create some test data @@ -122,7 +122,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { // Send and index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -202,7 +202,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) { defer wcfgCancel() ffs := f.Filesystem(nil) defer cleanupModel(m) - addFakeConn(m, device1, f.ID) + conn := addFakeConn(m, device1, f.ID) // Create some test data @@ -212,7 +212,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) { // Send an index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -272,7 +272,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) { defer wcfgCancel() ffs := f.Filesystem(nil) defer cleanupModel(m) - addFakeConn(m, device1, f.ID) + conn := addFakeConn(m, device1, f.ID) // Create some test data @@ -282,7 +282,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) { // Send an index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -337,7 +337,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { defer wcfgCancel() ffs := f.Filesystem(nil) defer cleanupModel(m) - addFakeConn(m, device1, f.ID) + conn := addFakeConn(m, device1, f.ID) // Create some test data @@ -347,7 +347,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { // Send an index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -402,7 +402,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { return true }) snap.Release() - must(t, m.IndexUpdate(device1, "ro", files)) + must(t, m.IndexUpdate(conn, "ro", files)) // Ensure the pull to resolve conflicts (content identical) happened must(t, f.doInSync(func() error { @@ -427,7 +427,7 @@ func TestRecvOnlyRevertOwnID(t *testing.T) { defer wcfgCancel() ffs := f.Filesystem(nil) defer cleanupModel(m) - addFakeConn(m, device1, f.ID) + conn := addFakeConn(m, device1, f.ID) // Create some test data @@ -470,7 +470,7 @@ func TestRecvOnlyRevertOwnID(t *testing.T) { }() // Receive an index update with an older version, but valid and then revert - must(t, m.Index(device1, f.ID, []protocol.FileInfo{fi})) + must(t, m.Index(conn, f.ID, []protocol.FileInfo{fi})) f.Revert() select { diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index 36764560f..02a9e6b0b 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -1278,7 +1278,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - addFakeConn(m, device1, f.ID) + conn := addFakeConn(m, device1, f.ID) name := "foo" if fd, err := f.mtimefs.Create(name); err != nil { @@ -1296,7 +1296,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) { if !ok { t.Fatal("file missing") } - must(t, m.Index(device1, f.ID, []protocol.FileInfo{{Name: name, Type: protocol.FileInfoTypeSymlink, Version: file.Version.Update(device1.Short())}})) + must(t, m.Index(conn, f.ID, []protocol.FileInfo{{Name: name, Type: protocol.FileInfoTypeSymlink, Version: file.Version.Update(device1.Short())}})) scanChan := make(chan string) diff --git a/lib/model/indexhandler.go b/lib/model/indexhandler.go index 2b0e55a3b..a227e7f41 100644 --- a/lib/model/indexhandler.go +++ b/lib/model/indexhandler.go @@ -56,10 +56,10 @@ func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, f // the IndexID, or something else weird has // happened. We send a full index to reset the // situation. - l.Infof("Device %v folder %s is delta index compatible, but seems out of sync with reality", conn.ID().Short(), folder.Description()) + l.Infof("Device %v folder %s is delta index compatible, but seems out of sync with reality", conn.DeviceID().Short(), folder.Description()) startSequence = 0 } else { - l.Debugf("Device %v folder %s is delta index compatible (mlv=%d)", conn.ID().Short(), folder.Description(), startInfo.local.MaxSequence) + l.Debugf("Device %v folder %s is delta index compatible (mlv=%d)", conn.DeviceID().Short(), folder.Description(), startInfo.local.MaxSequence) startSequence = startInfo.local.MaxSequence } } else if startInfo.local.IndexID != 0 { @@ -67,10 +67,10 @@ func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, f // not the right one. Either they are confused or we // must have reset our database since last talking to // them. We'll start with a full index transfer. - l.Infof("Device %v folder %s has mismatching index ID for us (%v != %v)", conn.ID().Short(), folder.Description(), startInfo.local.IndexID, myIndexID) + l.Infof("Device %v folder %s has mismatching index ID for us (%v != %v)", conn.DeviceID().Short(), folder.Description(), startInfo.local.IndexID, myIndexID) startSequence = 0 } else { - l.Debugf("Device %v folder %s has no index ID for us", conn.ID().Short(), folder.Description()) + l.Debugf("Device %v folder %s has no index ID for us", conn.DeviceID().Short(), folder.Description()) } // This is the other side's description of themselves. We @@ -78,23 +78,23 @@ func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, f // otherwise we drop our old index data and expect to get a // completely new set. - theirIndexID := fset.IndexID(conn.ID()) + theirIndexID := fset.IndexID(conn.DeviceID()) if startInfo.remote.IndexID == 0 { // They're not announcing an index ID. This means they // do not support delta indexes and we should clear any // information we have from them before accepting their // index, which will presumably be a full index. - l.Debugf("Device %v folder %s does not announce an index ID", conn.ID().Short(), folder.Description()) - fset.Drop(conn.ID()) + l.Debugf("Device %v folder %s does not announce an index ID", conn.DeviceID().Short(), folder.Description()) + fset.Drop(conn.DeviceID()) } else if startInfo.remote.IndexID != theirIndexID { // The index ID we have on file is not what they're // announcing. They must have reset their database and // will probably send us a full index. We drop any // information we have and remember this new index ID // instead. - l.Infof("Device %v folder %s has a new index ID (%v)", conn.ID().Short(), folder.Description(), startInfo.remote.IndexID) - fset.Drop(conn.ID()) - fset.SetIndexID(conn.ID(), startInfo.remote.IndexID) + l.Infof("Device %v folder %s has a new index ID (%v)", conn.DeviceID().Short(), folder.Description(), startInfo.remote.IndexID) + fset.Drop(conn.DeviceID()) + fset.SetIndexID(conn.DeviceID(), startInfo.remote.IndexID) } return &indexHandler{ @@ -129,12 +129,12 @@ func (s *indexHandler) waitForFileset(ctx context.Context) (*db.FileSet, error) } func (s *indexHandler) Serve(ctx context.Context) (err error) { - l.Debugf("Starting index handler for %s to %s at %s (slv=%d)", s.folder, s.conn.ID(), s.conn, s.prevSequence) + l.Debugf("Starting index handler for %s to %s at %s (slv=%d)", s.folder, s.conn.DeviceID().Short(), s.conn, s.prevSequence) stop := make(chan struct{}) defer func() { err = svcutil.NoRestartErr(err) - l.Debugf("Exiting index handler for %s to %s at %s: %v", s.folder, s.conn.ID(), s.conn, err) + l.Debugf("Exiting index handler for %s to %s at %s: %v", s.folder, s.conn.DeviceID().Short(), s.conn, err) close(stop) }() @@ -308,7 +308,7 @@ func (s *indexHandler) sendIndexTo(ctx context.Context, fset *db.FileSet) error } func (s *indexHandler) receive(fs []protocol.FileInfo, update bool, op string) error { - deviceID := s.conn.ID() + deviceID := s.conn.DeviceID() s.cond.L.Lock() paused := s.paused @@ -369,7 +369,7 @@ func prepareFileInfoForIndex(f protocol.FileInfo) protocol.FileInfo { } func (s *indexHandler) String() string { - return fmt.Sprintf("indexHandler@%p for %s to %s at %s", s, s.folder, s.conn.ID().Short(), s.conn) + return fmt.Sprintf("indexHandler@%p for %s to %s at %s", s, s.folder, s.conn.DeviceID().Short(), s.conn) } type indexHandlerRegistry struct { @@ -414,7 +414,7 @@ func newIndexHandlerRegistry(conn protocol.Connection, downloads *deviceDownload } func (r *indexHandlerRegistry) String() string { - return fmt.Sprintf("indexHandlerRegistry/%v", r.conn.ID().Short()) + return fmt.Sprintf("indexHandlerRegistry/%v", r.conn.DeviceID().Short()) } func (r *indexHandlerRegistry) GetSupervisor() *suture.Supervisor { @@ -447,11 +447,11 @@ func (r *indexHandlerRegistry) AddIndexInfo(folder string, startInfo *clusterCon if is, ok := r.indexHandlers[folder]; ok { r.sup.RemoveAndWait(is.token, 0) delete(r.indexHandlers, folder) - l.Debugf("Removed index sender for device %v and folder %v due to added pending", r.conn.ID().Short(), folder) + l.Debugf("Removed index sender for device %v and folder %v due to added pending", r.conn.DeviceID().Short(), folder) } folderState, ok := r.folderStates[folder] if !ok { - l.Debugf("Pending index handler for device %v and folder %v", r.conn.ID().Short(), folder) + l.Debugf("Pending index handler for device %v and folder %v", r.conn.DeviceID().Short(), folder) r.startInfos[folder] = startInfo return } @@ -464,13 +464,13 @@ func (r *indexHandlerRegistry) Remove(folder string) { r.mut.Lock() defer r.mut.Unlock() - l.Debugf("Removing index handler for device %v and folder %v", r.conn.ID().Short(), folder) + l.Debugf("Removing index handler for device %v and folder %v", r.conn.DeviceID().Short(), folder) if is, ok := r.indexHandlers[folder]; ok { r.sup.RemoveAndWait(is.token, 0) delete(r.indexHandlers, folder) } delete(r.startInfos, folder) - l.Debugf("Removed index handler for device %v and folder %v", r.conn.ID().Short(), folder) + l.Debugf("Removed index handler for device %v and folder %v", r.conn.DeviceID().Short(), folder) } // RemoveAllExcept stops all running index handlers and removes those pending to be started, @@ -484,13 +484,13 @@ func (r *indexHandlerRegistry) RemoveAllExcept(except map[string]remoteFolderSta if _, ok := except[folder]; !ok { r.sup.RemoveAndWait(is.token, 0) delete(r.indexHandlers, folder) - l.Debugf("Removed index handler for device %v and folder %v (removeAllExcept)", r.conn.ID().Short(), folder) + l.Debugf("Removed index handler for device %v and folder %v (removeAllExcept)", r.conn.DeviceID().Short(), folder) } } for folder := range r.startInfos { if _, ok := except[folder]; !ok { delete(r.startInfos, folder) - l.Debugf("Removed pending index handler for device %v and folder %v (removeAllExcept)", r.conn.ID().Short(), folder) + l.Debugf("Removed pending index handler for device %v and folder %v (removeAllExcept)", r.conn.DeviceID().Short(), folder) } } } @@ -499,7 +499,7 @@ func (r *indexHandlerRegistry) RemoveAllExcept(except map[string]remoteFolderSta // changes. The exception being if the folder is removed entirely, then call // Remove. The fset and runner arguments may be nil, if given folder is paused. func (r *indexHandlerRegistry) RegisterFolderState(folder config.FolderConfiguration, fset *db.FileSet, runner service) { - if !folder.SharedWith(r.conn.ID()) { + if !folder.SharedWith(r.conn.DeviceID()) { r.Remove(folder.ID) return } @@ -516,13 +516,13 @@ func (r *indexHandlerRegistry) RegisterFolderState(folder config.FolderConfigura // folderPausedLocked stops a running index handler. // It is a noop if the folder isn't known or has not been started yet. func (r *indexHandlerRegistry) folderPausedLocked(folder string) { - l.Debugf("Pausing index handler for device %v and folder %v", r.conn.ID().Short(), folder) + l.Debugf("Pausing index handler for device %v and folder %v", r.conn.DeviceID().Short(), folder) delete(r.folderStates, folder) if is, ok := r.indexHandlers[folder]; ok { is.pause() - l.Debugf("Paused index handler for device %v and folder %v", r.conn.ID().Short(), folder) + l.Debugf("Paused index handler for device %v and folder %v", r.conn.DeviceID().Short(), folder) } else { - l.Debugf("No index handler for device %v and folder %v to pause", r.conn.ID().Short(), folder) + l.Debugf("No index handler for device %v and folder %v to pause", r.conn.DeviceID().Short(), folder) } } @@ -541,16 +541,16 @@ func (r *indexHandlerRegistry) folderRunningLocked(folder config.FolderConfigura if isOk { r.sup.RemoveAndWait(is.token, 0) delete(r.indexHandlers, folder.ID) - l.Debugf("Removed index handler for device %v and folder %v in resume", r.conn.ID().Short(), folder.ID) + l.Debugf("Removed index handler for device %v and folder %v in resume", r.conn.DeviceID().Short(), folder.ID) } r.startLocked(folder, fset, runner, info) delete(r.startInfos, folder.ID) - l.Debugf("Started index handler for device %v and folder %v in resume", r.conn.ID().Short(), folder.ID) + l.Debugf("Started index handler for device %v and folder %v in resume", r.conn.DeviceID().Short(), folder.ID) } else if isOk { - l.Debugf("Resuming index handler for device %v and folder %v", r.conn.ID().Short(), folder) + l.Debugf("Resuming index handler for device %v and folder %v", r.conn.DeviceID().Short(), folder) is.resume(fset, runner) } else { - l.Debugf("Not resuming index handler for device %v and folder %v as none is paused and there is no start info", r.conn.ID().Short(), folder.ID) + l.Debugf("Not resuming index handler for device %v and folder %v as none is paused and there is no start info", r.conn.DeviceID().Short(), folder.ID) } } @@ -560,7 +560,7 @@ func (r *indexHandlerRegistry) ReceiveIndex(folder string, fs []protocol.FileInf is, isOk := r.indexHandlers[folder] if !isOk { l.Infof("%v for nonexistent or paused folder %q", op, folder) - return ErrFolderMissing + return fmt.Errorf("%s: %w", folder, ErrFolderMissing) } return is.receive(fs, update, op) } diff --git a/lib/model/mocks/model.go b/lib/model/mocks/model.go index db19252c7..c0f8c95b8 100644 --- a/lib/model/mocks/model.go +++ b/lib/model/mocks/model.go @@ -44,16 +44,16 @@ type Model struct { arg1 string arg2 string } - ClosedStub func(protocol.DeviceID, error) + ClosedStub func(protocol.Connection, error) closedMutex sync.RWMutex closedArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 error } - ClusterConfigStub func(protocol.DeviceID, protocol.ClusterConfig) error + ClusterConfigStub func(protocol.Connection, protocol.ClusterConfig) error clusterConfigMutex sync.RWMutex clusterConfigArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 protocol.ClusterConfig } clusterConfigReturns struct { @@ -200,10 +200,10 @@ type Model struct { dismissPendingFolderReturnsOnCall map[int]struct { result1 error } - DownloadProgressStub func(protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) error + DownloadProgressStub func(protocol.Connection, string, []protocol.FileDownloadProgressUpdate) error downloadProgressMutex sync.RWMutex downloadProgressArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileDownloadProgressUpdate } @@ -303,10 +303,10 @@ type Model struct { result1 []*model.TreeEntry result2 error } - IndexStub func(protocol.DeviceID, string, []protocol.FileInfo) error + IndexStub func(protocol.Connection, string, []protocol.FileInfo) error indexMutex sync.RWMutex indexArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo } @@ -316,10 +316,10 @@ type Model struct { indexReturnsOnCall map[int]struct { result1 error } - IndexUpdateStub func(protocol.DeviceID, string, []protocol.FileInfo) error + IndexUpdateStub func(protocol.Connection, string, []protocol.FileInfo) error indexUpdateMutex sync.RWMutex indexUpdateArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo } @@ -447,10 +447,10 @@ type Model struct { result1 []db.FileInfoTruncated result2 error } - RequestStub func(protocol.DeviceID, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error) + RequestStub func(protocol.Connection, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error) requestMutex sync.RWMutex requestArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 string arg4 int32 @@ -728,10 +728,10 @@ func (fake *Model) BringToFrontArgsForCall(i int) (string, string) { return argsForCall.arg1, argsForCall.arg2 } -func (fake *Model) Closed(arg1 protocol.DeviceID, arg2 error) { +func (fake *Model) Closed(arg1 protocol.Connection, arg2 error) { fake.closedMutex.Lock() fake.closedArgsForCall = append(fake.closedArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 error }{arg1, arg2}) stub := fake.ClosedStub @@ -748,24 +748,24 @@ func (fake *Model) ClosedCallCount() int { return len(fake.closedArgsForCall) } -func (fake *Model) ClosedCalls(stub func(protocol.DeviceID, error)) { +func (fake *Model) ClosedCalls(stub func(protocol.Connection, error)) { fake.closedMutex.Lock() defer fake.closedMutex.Unlock() fake.ClosedStub = stub } -func (fake *Model) ClosedArgsForCall(i int) (protocol.DeviceID, error) { +func (fake *Model) ClosedArgsForCall(i int) (protocol.Connection, error) { fake.closedMutex.RLock() defer fake.closedMutex.RUnlock() argsForCall := fake.closedArgsForCall[i] return argsForCall.arg1, argsForCall.arg2 } -func (fake *Model) ClusterConfig(arg1 protocol.DeviceID, arg2 protocol.ClusterConfig) error { +func (fake *Model) ClusterConfig(arg1 protocol.Connection, arg2 protocol.ClusterConfig) error { fake.clusterConfigMutex.Lock() ret, specificReturn := fake.clusterConfigReturnsOnCall[len(fake.clusterConfigArgsForCall)] fake.clusterConfigArgsForCall = append(fake.clusterConfigArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 protocol.ClusterConfig }{arg1, arg2}) stub := fake.ClusterConfigStub @@ -787,13 +787,13 @@ func (fake *Model) ClusterConfigCallCount() int { return len(fake.clusterConfigArgsForCall) } -func (fake *Model) ClusterConfigCalls(stub func(protocol.DeviceID, protocol.ClusterConfig) error) { +func (fake *Model) ClusterConfigCalls(stub func(protocol.Connection, protocol.ClusterConfig) error) { fake.clusterConfigMutex.Lock() defer fake.clusterConfigMutex.Unlock() fake.ClusterConfigStub = stub } -func (fake *Model) ClusterConfigArgsForCall(i int) (protocol.DeviceID, protocol.ClusterConfig) { +func (fake *Model) ClusterConfigArgsForCall(i int) (protocol.Connection, protocol.ClusterConfig) { fake.clusterConfigMutex.RLock() defer fake.clusterConfigMutex.RUnlock() argsForCall := fake.clusterConfigArgsForCall[i] @@ -1484,7 +1484,7 @@ func (fake *Model) DismissPendingFolderReturnsOnCall(i int, result1 error) { }{result1} } -func (fake *Model) DownloadProgress(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileDownloadProgressUpdate) error { +func (fake *Model) DownloadProgress(arg1 protocol.Connection, arg2 string, arg3 []protocol.FileDownloadProgressUpdate) error { var arg3Copy []protocol.FileDownloadProgressUpdate if arg3 != nil { arg3Copy = make([]protocol.FileDownloadProgressUpdate, len(arg3)) @@ -1493,7 +1493,7 @@ func (fake *Model) DownloadProgress(arg1 protocol.DeviceID, arg2 string, arg3 [] fake.downloadProgressMutex.Lock() ret, specificReturn := fake.downloadProgressReturnsOnCall[len(fake.downloadProgressArgsForCall)] fake.downloadProgressArgsForCall = append(fake.downloadProgressArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileDownloadProgressUpdate }{arg1, arg2, arg3Copy}) @@ -1516,13 +1516,13 @@ func (fake *Model) DownloadProgressCallCount() int { return len(fake.downloadProgressArgsForCall) } -func (fake *Model) DownloadProgressCalls(stub func(protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) error) { +func (fake *Model) DownloadProgressCalls(stub func(protocol.Connection, string, []protocol.FileDownloadProgressUpdate) error) { fake.downloadProgressMutex.Lock() defer fake.downloadProgressMutex.Unlock() fake.DownloadProgressStub = stub } -func (fake *Model) DownloadProgressArgsForCall(i int) (protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) { +func (fake *Model) DownloadProgressArgsForCall(i int) (protocol.Connection, string, []protocol.FileDownloadProgressUpdate) { fake.downloadProgressMutex.RLock() defer fake.downloadProgressMutex.RUnlock() argsForCall := fake.downloadProgressArgsForCall[i] @@ -1990,7 +1990,7 @@ func (fake *Model) GlobalDirectoryTreeReturnsOnCall(i int, result1 []*model.Tree }{result1, result2} } -func (fake *Model) Index(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileInfo) error { +func (fake *Model) Index(arg1 protocol.Connection, arg2 string, arg3 []protocol.FileInfo) error { var arg3Copy []protocol.FileInfo if arg3 != nil { arg3Copy = make([]protocol.FileInfo, len(arg3)) @@ -1999,7 +1999,7 @@ func (fake *Model) Index(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.Fi fake.indexMutex.Lock() ret, specificReturn := fake.indexReturnsOnCall[len(fake.indexArgsForCall)] fake.indexArgsForCall = append(fake.indexArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo }{arg1, arg2, arg3Copy}) @@ -2022,13 +2022,13 @@ func (fake *Model) IndexCallCount() int { return len(fake.indexArgsForCall) } -func (fake *Model) IndexCalls(stub func(protocol.DeviceID, string, []protocol.FileInfo) error) { +func (fake *Model) IndexCalls(stub func(protocol.Connection, string, []protocol.FileInfo) error) { fake.indexMutex.Lock() defer fake.indexMutex.Unlock() fake.IndexStub = stub } -func (fake *Model) IndexArgsForCall(i int) (protocol.DeviceID, string, []protocol.FileInfo) { +func (fake *Model) IndexArgsForCall(i int) (protocol.Connection, string, []protocol.FileInfo) { fake.indexMutex.RLock() defer fake.indexMutex.RUnlock() argsForCall := fake.indexArgsForCall[i] @@ -2058,7 +2058,7 @@ func (fake *Model) IndexReturnsOnCall(i int, result1 error) { }{result1} } -func (fake *Model) IndexUpdate(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileInfo) error { +func (fake *Model) IndexUpdate(arg1 protocol.Connection, arg2 string, arg3 []protocol.FileInfo) error { var arg3Copy []protocol.FileInfo if arg3 != nil { arg3Copy = make([]protocol.FileInfo, len(arg3)) @@ -2067,7 +2067,7 @@ func (fake *Model) IndexUpdate(arg1 protocol.DeviceID, arg2 string, arg3 []proto fake.indexUpdateMutex.Lock() ret, specificReturn := fake.indexUpdateReturnsOnCall[len(fake.indexUpdateArgsForCall)] fake.indexUpdateArgsForCall = append(fake.indexUpdateArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo }{arg1, arg2, arg3Copy}) @@ -2090,13 +2090,13 @@ func (fake *Model) IndexUpdateCallCount() int { return len(fake.indexUpdateArgsForCall) } -func (fake *Model) IndexUpdateCalls(stub func(protocol.DeviceID, string, []protocol.FileInfo) error) { +func (fake *Model) IndexUpdateCalls(stub func(protocol.Connection, string, []protocol.FileInfo) error) { fake.indexUpdateMutex.Lock() defer fake.indexUpdateMutex.Unlock() fake.IndexUpdateStub = stub } -func (fake *Model) IndexUpdateArgsForCall(i int) (protocol.DeviceID, string, []protocol.FileInfo) { +func (fake *Model) IndexUpdateArgsForCall(i int) (protocol.Connection, string, []protocol.FileInfo) { fake.indexUpdateMutex.RLock() defer fake.indexUpdateMutex.RUnlock() argsForCall := fake.indexUpdateArgsForCall[i] @@ -2666,7 +2666,7 @@ func (fake *Model) RemoteNeedFolderFilesReturnsOnCall(i int, result1 []db.FileIn }{result1, result2} } -func (fake *Model) Request(arg1 protocol.DeviceID, arg2 string, arg3 string, arg4 int32, arg5 int32, arg6 int64, arg7 []byte, arg8 uint32, arg9 bool) (protocol.RequestResponse, error) { +func (fake *Model) Request(arg1 protocol.Connection, arg2 string, arg3 string, arg4 int32, arg5 int32, arg6 int64, arg7 []byte, arg8 uint32, arg9 bool) (protocol.RequestResponse, error) { var arg7Copy []byte if arg7 != nil { arg7Copy = make([]byte, len(arg7)) @@ -2675,7 +2675,7 @@ func (fake *Model) Request(arg1 protocol.DeviceID, arg2 string, arg3 string, arg fake.requestMutex.Lock() ret, specificReturn := fake.requestReturnsOnCall[len(fake.requestArgsForCall)] fake.requestArgsForCall = append(fake.requestArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 string arg4 int32 @@ -2704,13 +2704,13 @@ func (fake *Model) RequestCallCount() int { return len(fake.requestArgsForCall) } -func (fake *Model) RequestCalls(stub func(protocol.DeviceID, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error)) { +func (fake *Model) RequestCalls(stub func(protocol.Connection, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error)) { fake.requestMutex.Lock() defer fake.requestMutex.Unlock() fake.RequestStub = stub } -func (fake *Model) RequestArgsForCall(i int) (protocol.DeviceID, string, string, int32, int32, int64, []byte, uint32, bool) { +func (fake *Model) RequestArgsForCall(i int) (protocol.Connection, string, string, int32, int32, int64, []byte, uint32, bool) { fake.requestMutex.RLock() defer fake.requestMutex.RUnlock() argsForCall := fake.requestArgsForCall[i] diff --git a/lib/model/model.go b/lib/model/model.go index 5830b4267..6de44b520 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -847,7 +847,7 @@ func (comp *FolderCompletion) setComplectionPct() { } // Map returns the members as a map, e.g. used in api to serialize as JSON. -func (comp FolderCompletion) Map() map[string]interface{} { +func (comp *FolderCompletion) Map() map[string]interface{} { return map[string]interface{}{ "completion": comp.CompletionPct, "globalBytes": comp.GlobalBytes, @@ -1110,22 +1110,23 @@ func (p *pager) done() bool { // Index is called when a new device is connected and we receive their full index. // Implements the protocol.Model interface. -func (m *model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) error { - return m.handleIndex(deviceID, folder, fs, false) +func (m *model) Index(conn protocol.Connection, folder string, fs []protocol.FileInfo) error { + return m.handleIndex(conn, folder, fs, false) } // IndexUpdate is called for incremental updates to connected devices' indexes. // Implements the protocol.Model interface. -func (m *model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) error { - return m.handleIndex(deviceID, folder, fs, true) +func (m *model) IndexUpdate(conn protocol.Connection, folder string, fs []protocol.FileInfo) error { + return m.handleIndex(conn, folder, fs, true) } -func (m *model) handleIndex(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, update bool) error { +func (m *model) handleIndex(conn protocol.Connection, folder string, fs []protocol.FileInfo, update bool) error { op := "Index" if update { op += " update" } + deviceID := conn.DeviceID() l.Debugf("%v (in): %s / %q: %d files", op, deviceID, folder, len(fs)) if cfg, ok := m.cfg.Folder(folder); !ok || !cfg.SharedWith(deviceID) { @@ -1159,12 +1160,13 @@ type ClusterConfigReceivedEventData struct { Device protocol.DeviceID `json:"device"` } -func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) error { +func (m *model) ClusterConfig(conn protocol.Connection, cm protocol.ClusterConfig) error { // Check the peer device's announced folders against our own. Emits events // for folders that we don't expect (unknown or not shared). // Also, collect a list of folders we do share, and if he's interested in // temporary indexes, subscribe the connection. + deviceID := conn.DeviceID() l.Debugf("Handling ClusterConfig from %v", deviceID.Short()) m.pmut.RLock() @@ -1544,7 +1546,7 @@ func (m *model) sendClusterConfig(ids []protocol.DeviceID) { m.pmut.RUnlock() // Generating cluster-configs acquires fmut -> must happen outside of pmut. for _, conn := range ccConns { - cm, passwords := m.generateClusterConfig(conn.ID()) + cm, passwords := m.generateClusterConfig(conn.DeviceID()) conn.SetFolderPasswords(passwords) go conn.ClusterConfig(cm) } @@ -1774,7 +1776,8 @@ func (m *model) introduceDevice(device protocol.Device, introducerCfg config.Dev } // Closed is called when a connection has been closed -func (m *model) Closed(device protocol.DeviceID, err error) { +func (m *model) Closed(conn protocol.Connection, err error) { + device := conn.DeviceID() m.pmut.Lock() conn, ok := m.conn[device] if !ok { @@ -1834,11 +1837,13 @@ func (r *requestResponse) Wait() { // Request returns the specified data segment by reading it from local disk. // Implements the protocol.Model interface. -func (m *model) Request(deviceID protocol.DeviceID, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) { +func (m *model) Request(conn protocol.Connection, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) { if size < 0 || offset < 0 { return nil, protocol.ErrInvalid } + deviceID := conn.DeviceID() + m.fmut.RLock() folderCfg, ok := m.folderCfgs[folder] folderIgnores := m.folderIgnores[folder] @@ -2214,7 +2219,7 @@ func (m *model) GetHello(id protocol.DeviceID) protocol.HelloIntf { // be sent to the connected peer, thereafter index updates whenever the local // folder changes. func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) { - deviceID := conn.ID() + deviceID := conn.DeviceID() device, ok := m.cfg.Device(deviceID) if !ok { l.Infoln("Trying to add connection to unknown device") @@ -2303,11 +2308,12 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) { m.deviceWasSeen(deviceID) } -func (m *model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) error { +func (m *model) DownloadProgress(conn protocol.Connection, folder string, updates []protocol.FileDownloadProgressUpdate) error { m.fmut.RLock() cfg, ok := m.folderCfgs[folder] m.fmut.RUnlock() + device := conn.DeviceID() if !ok || cfg.DisableTempIndexes || !cfg.SharedWith(device) { return nil } diff --git a/lib/model/model_test.go b/lib/model/model_test.go index 568342f3d..2a8623909 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -94,7 +94,7 @@ func TestRequest(t *testing.T) { m.ScanFolder("default") // Existing, shared file - res, err := m.Request(device1, "default", "foo", 0, 6, 0, nil, 0, false) + res, err := m.Request(device1Conn, "default", "foo", 0, 6, 0, nil, 0, false) if err != nil { t.Fatal(err) } @@ -104,35 +104,35 @@ func TestRequest(t *testing.T) { } // Existing, nonshared file - _, err = m.Request(device2, "default", "foo", 0, 6, 0, nil, 0, false) + _, err = m.Request(device2Conn, "default", "foo", 0, 6, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Nonexistent file - _, err = m.Request(device1, "default", "nonexistent", 0, 6, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "nonexistent", 0, 6, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Shared folder, but disallowed file name - _, err = m.Request(device1, "default", "../walk.go", 0, 6, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "../walk.go", 0, 6, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Negative offset - _, err = m.Request(device1, "default", "foo", 0, -4, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "foo", 0, -4, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Larger block than available - _, err = m.Request(device1, "default", "foo", 0, 42, 0, []byte("hash necessary but not checked"), 0, false) + _, err = m.Request(device1Conn, "default", "foo", 0, 42, 0, []byte("hash necessary but not checked"), 0, false) if err == nil { t.Error("Unexpected nil error on read past end of file") } - _, err = m.Request(device1, "default", "foo", 0, 42, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "foo", 0, 42, 0, nil, 0, false) if err != nil { t.Error("Unexpected error when large read should be permitted") } @@ -168,11 +168,11 @@ func benchmarkIndex(b *testing.B, nfiles int) { defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) files := genFiles(nfiles) - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) b.ResetTimer() for i := 0; i < b.N; i++ { - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) } b.ReportAllocs() } @@ -197,11 +197,11 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) { files := genFiles(nfiles) ufiles := genFiles(nufiles) - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) b.ResetTimer() for i := 0; i < b.N; i++ { - must(b, m.IndexUpdate(device1, fcfg.ID, ufiles)) + must(b, m.IndexUpdate(device1Conn, fcfg.ID, ufiles)) } b.ReportAllocs() } @@ -218,7 +218,7 @@ func BenchmarkRequestOut(b *testing.B) { fc.addFile(f.Name, 0o644, protocol.FileInfoTypeFile, []byte("some data to return")) } m.AddConnection(fc, protocol.Hello{}) - must(b, m.Index(device1, "default", files)) + must(b, m.Index(device1Conn, "default", files)) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -247,7 +247,7 @@ func BenchmarkRequestInSingleFile(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := m.Request(device1, "default", "request/for/a/file/in/a/couple/of/dirs/128k", 0, 128<<10, 0, nil, 0, false); err != nil { + if _, err := m.Request(device1Conn, "default", "request/for/a/file/in/a/couple/of/dirs/128k", 0, 128<<10, 0, nil, 0, false); err != nil { b.Error(err) } } @@ -287,7 +287,7 @@ func TestDeviceRename(t *testing.T) { t.Errorf("Device already has a name") } - m.Closed(conn.ID(), protocol.ErrTimeout) + m.Closed(conn, protocol.ErrTimeout) hello.DeviceName = "tester" m.AddConnection(conn, hello) @@ -295,7 +295,7 @@ func TestDeviceRename(t *testing.T) { t.Errorf("Device did not get a name") } - m.Closed(conn.ID(), protocol.ErrTimeout) + m.Closed(conn, protocol.ErrTimeout) hello.DeviceName = "tester2" m.AddConnection(conn, hello) @@ -317,7 +317,7 @@ func TestDeviceRename(t *testing.T) { t.Errorf("Device name not saved in config") } - m.Closed(conn.ID(), protocol.ErrTimeout) + m.Closed(conn, protocol.ErrTimeout) waiter, err := cfg.Modify(func(cfg *config.Configuration) { cfg.Options.OverwriteRemoteDevNames = true @@ -528,7 +528,7 @@ func TestIntroducer(t *testing.T) { SkipIntroductionRemovals: true, EncryptionPasswordToken: []byte("faketoken"), }) - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) if newDev, ok := m.cfg.Device(device2); !ok || !newDev.Introducer || !newDev.SkipIntroductionRemovals { t.Error("device 2 missing or wrong flags") @@ -584,7 +584,7 @@ func TestIntroducer(t *testing.T) { Introducer: true, SkipIntroductionRemovals: true, }) - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) // Should not get introducer, as it's already unset, and it's an existing device. if newDev, ok := m.cfg.Device(device2); !ok || newDev.Introducer || newDev.SkipIntroductionRemovals { @@ -634,7 +634,7 @@ func TestIntroducer(t *testing.T) { }, }, }) - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); ok { t.Error("device 2 should have been removed") @@ -686,7 +686,7 @@ func TestIntroducer(t *testing.T) { }, }, }) - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -743,7 +743,7 @@ func TestIntroducer(t *testing.T) { Introducer: true, SkipIntroductionRemovals: true, }) - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -794,7 +794,7 @@ func TestIntroducer(t *testing.T) { }, }, }) - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -847,7 +847,7 @@ func TestIntroducer(t *testing.T) { }) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -906,14 +906,14 @@ func TestIssue5063(t *testing.T) { for _, c := range m.conn { conn := c.(*fakeConnection) conn.CloseCalls(func(_ error) {}) - defer m.Closed(c.ID(), errStopped) // to unblock deferred m.Stop() + defer m.Closed(c, errStopped) // to unblock deferred m.Stop() } m.pmut.Unlock() wg := sync.WaitGroup{} addAndVerify := func(id string) { - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } @@ -951,7 +951,7 @@ func TestAutoAcceptRejected(t *testing.T) { // defer cleanupModel(m) defer cancel() id := srand.String(8) - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if cfg, ok := m.cfg.Folder(id); ok && cfg.SharedWith(device1) { t.Error("unexpected shared", id) @@ -964,7 +964,7 @@ func TestAutoAcceptNewFolder(t *testing.T) { defer cleanupModel(m) defer cancel() id := srand.String(8) - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } @@ -976,14 +976,14 @@ func TestAutoAcceptNewFolderFromTwoDevices(t *testing.T) { defer cancel() id := srand.String(8) defer os.RemoveAll(id) - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) { t.Error("unexpected expected shared", id) } - m.ClusterConfig(device2, createClusterConfig(device2, id)) + m.ClusterConfig(device2Conn, createClusterConfig(device2, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device2) { t.Error("expected shared", id) } @@ -997,14 +997,14 @@ func TestAutoAcceptNewFolderFromOnlyOneDevice(t *testing.T) { defer os.RemoveAll(id) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) { t.Error("unexpected expected shared", id) } - m.ClusterConfig(device2, createClusterConfig(device2, id)) + m.ClusterConfig(device2Conn, createClusterConfig(device2, id)) if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) { t.Error("unexpected shared", id) } @@ -1035,10 +1035,10 @@ func TestAutoAcceptNewFolderPremutationsNoPanic(t *testing.T) { cfg.Folders = append(cfg.Folders, fcfg) } m, cancel := newState(t, cfg) - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{dev1folder}, }) - m.ClusterConfig(device2, protocol.ClusterConfig{ + m.ClusterConfig(device2Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{dev2folder}, }) cleanupModel(m) @@ -1058,7 +1058,7 @@ func TestAutoAcceptMultipleFolders(t *testing.T) { m, cancel := newState(t, defaultAutoAcceptCfg) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, createClusterConfig(device1, id1, id2)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id1, id2)) if fcfg, ok := m.cfg.Folder(id1); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id1) } @@ -1086,7 +1086,7 @@ func TestAutoAcceptExistingFolder(t *testing.T) { if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device1) { t.Error("missing folder, or shared", id) } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) || fcfg.Path != idOther { t.Error("missing folder, or unshared, or path changed", id) @@ -1112,7 +1112,7 @@ func TestAutoAcceptNewAndExistingFolder(t *testing.T) { if fcfg, ok := m.cfg.Folder(id1); !ok || fcfg.SharedWith(device1) { t.Error("missing folder, or shared", id1) } - m.ClusterConfig(device1, createClusterConfig(device1, id1, id2)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id1, id2)) for i, id := range []string{id1, id2} { if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { @@ -1143,7 +1143,7 @@ func TestAutoAcceptAlreadyShared(t *testing.T) { if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("missing folder, or not shared", id) } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("missing folder, or not shared", id) @@ -1159,7 +1159,7 @@ func TestAutoAcceptNameConflict(t *testing.T) { m, cancel := newState(t, defaultAutoAcceptCfg) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: id, @@ -1179,7 +1179,7 @@ func TestAutoAcceptPrefersLabel(t *testing.T) { label := srand.String(8) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: id, @@ -1203,7 +1203,7 @@ func TestAutoAcceptFallsBackToID(t *testing.T) { } defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: id, @@ -1245,7 +1245,7 @@ func TestAutoAcceptPausedWhenFolderConfigChanged(t *testing.T) { t.Fatal("folder running?") } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) m.generateClusterConfig(device1) if fcfg, ok := m.cfg.Folder(id); !ok { @@ -1294,7 +1294,7 @@ func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) { t.Fatal("folder running?") } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) m.generateClusterConfig(device1) if fcfg, ok := m.cfg.Folder(id); !ok { @@ -1338,7 +1338,7 @@ func TestAutoAcceptEnc(t *testing.T) { // would panic. clusterConfig := func(deviceID protocol.DeviceID, cm protocol.ClusterConfig) { m.AddConnection(newFakeConnection(deviceID, m), protocol.Hello{}) - m.ClusterConfig(deviceID, cm) + m.ClusterConfig(&protocolmocks.Connection{DeviceIDStub: func() protocol.DeviceID { return deviceID }}, cm) } clusterConfig(device1, basicCC()) @@ -1703,7 +1703,7 @@ func TestRWScanRecovery(t *testing.T) { } func TestGlobalDirectoryTree(t *testing.T) { - m, _, fcfg, wCancel := setupModelWithConnection(t) + m, conn, fcfg, wCancel := setupModelWithConnection(t) defer wCancel() defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) @@ -1807,7 +1807,7 @@ func TestGlobalDirectoryTree(t *testing.T) { return string(bytes) } - must(t, m.Index(device1, "default", testdata)) + must(t, m.Index(conn, "default", testdata)) result, _ := m.GlobalDirectoryTree("default", "", -1, false) @@ -2014,7 +2014,7 @@ func benchmarkTree(b *testing.B, n1, n2 int) { m.ScanFolder(fcfg.ID) files := genDeepFiles(n1, n2) - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -2160,7 +2160,7 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) { conn2 := newFakeConnection(device2, m) m.AddConnection(conn2, protocol.Hello{}) - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(conn1, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: "default", @@ -2172,7 +2172,7 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) { }, }, }) - m.ClusterConfig(device2, protocol.ClusterConfig{ + m.ClusterConfig(conn2, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: "default", @@ -2398,7 +2398,7 @@ func TestCustomMarkerName(t *testing.T) { } func TestRemoveDirWithContent(t *testing.T) { - m, _, fcfg, wcfgCancel := setupModelWithConnection(t) + m, conn, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) @@ -2425,7 +2425,7 @@ func TestRemoveDirWithContent(t *testing.T) { file.Deleted = true file.Version = file.Version.Update(device1.Short()).Update(device1.Short()) - must(t, m.IndexUpdate(device1, fcfg.ID, []protocol.FileInfo{dir, file})) + must(t, m.IndexUpdate(conn, fcfg.ID, []protocol.FileInfo{dir, file})) // Is there something we could trigger on instead of just waiting? timeout := time.NewTimer(5 * time.Second) @@ -2919,19 +2919,19 @@ func TestRequestLimit(t *testing.T) { }) must(t, err) waiter.Wait() - m, _ := setupModelWithConnectionFromWrapper(t, wrapper) + m, conn := setupModelWithConnectionFromWrapper(t, wrapper) defer cleanupModel(m) m.ScanFolder("default") befReq := time.Now() - first, err := m.Request(device1, "default", file, 0, 2000, 0, nil, 0, false) + first, err := m.Request(conn, "default", file, 0, 2000, 0, nil, 0, false) if err != nil { t.Fatalf("First request failed: %v", err) } reqDur := time.Since(befReq) returned := make(chan struct{}) go func() { - second, err := m.Request(device1, "default", file, 0, 2000, 0, nil, 0, false) + second, err := m.Request(conn, "default", file, 0, 2000, 0, nil, 0, false) if err != nil { t.Errorf("Second request failed: %v", err) } @@ -2969,12 +2969,16 @@ func TestConnCloseOnRestart(t *testing.T) { br := &testutils.BlockingRW{} nw := &testutils.NoopRW{} - m.AddConnection(protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, new(protocolmocks.ConnectionInfo), protocol.CompressionNever, nil, m.keyGen), protocol.Hello{}) + ci := &protocolmocks.ConnectionInfo{} + m.AddConnection(protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, ci, protocol.CompressionNever, nil, m.keyGen), protocol.Hello{}) m.pmut.RLock() if len(m.closed) != 1 { - t.Fatalf("Expected just one conn (len(m.conn) == %v)", len(m.conn)) + t.Fatalf("Expected just one conn (len(m.closed) == %v)", len(m.closed)) + } + var closed chan struct{} + for _, c := range m.closed { + closed = c } - closed := m.closed[device1] m.pmut.RUnlock() waiter, err := w.RemoveDevice(device1) @@ -3069,7 +3073,10 @@ func TestDevicePause(t *testing.T) { defer sub.Unsubscribe() m.pmut.RLock() - closed := m.closed[device1] + var closed chan struct{} + for _, c := range m.closed { + closed = c + } m.pmut.RUnlock() pauseDevice(t, m.cfg, device1, true) @@ -3567,7 +3574,7 @@ func TestAddFolderCompletion(t *testing.T) { } func TestScanDeletedROChangedOnSR(t *testing.T) { - m, _, fcfg, wCancel := setupModelWithConnection(t) + m, conn, fcfg, wCancel := setupModelWithConnection(t) ffs := fcfg.Filesystem(nil) defer wCancel() defer cleanupModelAndRemoveDir(m, ffs.URI()) @@ -3585,7 +3592,7 @@ func TestScanDeletedROChangedOnSR(t *testing.T) { } // A remote must have the file, otherwise the deletion below is // automatically resolved as not a ro-changed item. - must(t, m.IndexUpdate(device1, fcfg.ID, []protocol.FileInfo{file})) + must(t, m.IndexUpdate(conn, fcfg.ID, []protocol.FileInfo{file})) must(t, ffs.Remove(name)) m.ScanFolders() @@ -3690,17 +3697,17 @@ func TestIssue6961(t *testing.T) { } m.ServeBackground() defer cleanupModelAndRemoveDir(m, tfs.URI()) - addFakeConn(m, device1, fcfg.ID) - addFakeConn(m, device2, fcfg.ID) + conn1 := addFakeConn(m, device1, fcfg.ID) + conn2 := addFakeConn(m, device2, fcfg.ID) m.ScanFolders() name := "foo" version := protocol.Vector{}.Update(device1.Short()) // Remote, valid and existing file - must(t, m.Index(device1, fcfg.ID, []protocol.FileInfo{{Name: name, Version: version, Sequence: 1}})) + must(t, m.Index(conn1, fcfg.ID, []protocol.FileInfo{{Name: name, Version: version, Sequence: 1}})) // Remote, invalid (receive-only) and existing file - must(t, m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}})) + must(t, m.Index(conn2, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}})) // Create a local file if fd, err := tfs.OpenFile(name, fs.OptCreate, 0o666); err != nil { t.Fatal(err) @@ -3726,7 +3733,7 @@ func TestIssue6961(t *testing.T) { m.ScanFolders() // Drop the remote index, add some other file. - must(t, m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: "bar", RawInvalid: true, Sequence: 1}})) + must(t, m.Index(conn2, fcfg.ID, []protocol.FileInfo{{Name: "bar", RawInvalid: true, Sequence: 1}})) // Pause and unpause folder to create new db.FileSet and thus recalculate everything pauseFolder(t, wcfg, fcfg.ID, true) @@ -3740,7 +3747,7 @@ func TestIssue6961(t *testing.T) { } func TestCompletionEmptyGlobal(t *testing.T) { - m, _, fcfg, wcfgCancel := setupModelWithConnection(t) + m, conn, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) files := []protocol.FileInfo{{Name: "foo", Version: protocol.Vector{}.Update(myID.Short()), Sequence: 1}} @@ -3749,7 +3756,7 @@ func TestCompletionEmptyGlobal(t *testing.T) { m.fmut.Unlock() files[0].Deleted = true files[0].Version = files[0].Version.Update(device1.Short()) - must(t, m.IndexUpdate(device1, fcfg.ID, files)) + must(t, m.IndexUpdate(conn, fcfg.ID, files)) comp := m.testCompletion(protocol.LocalDeviceID, fcfg.ID) if comp.CompletionPct != 95 { t.Error("Expected completion of 95%, got", comp.CompletionPct) @@ -3762,34 +3769,34 @@ func TestNeedMetaAfterIndexReset(t *testing.T) { addDevice2(t, w, fcfg) m := setupModel(t, w) defer cleanupModelAndRemoveDir(m, fcfg.Path) - addFakeConn(m, device1, fcfg.ID) - addFakeConn(m, device2, fcfg.ID) + conn1 := addFakeConn(m, device1, fcfg.ID) + conn2 := addFakeConn(m, device2, fcfg.ID) var seq int64 = 1 files := []protocol.FileInfo{{Name: "foo", Size: 10, Version: protocol.Vector{}.Update(device1.Short()), Sequence: seq}} // Start with two remotes having one file, then both deleting it, then // only one adding it again. - must(t, m.Index(device1, fcfg.ID, files)) - must(t, m.Index(device2, fcfg.ID, files)) + must(t, m.Index(conn1, fcfg.ID, files)) + must(t, m.Index(conn2, fcfg.ID, files)) seq++ files[0].SetDeleted(device2.Short()) files[0].Sequence = seq - must(t, m.IndexUpdate(device2, fcfg.ID, files)) - must(t, m.IndexUpdate(device1, fcfg.ID, files)) + must(t, m.IndexUpdate(conn1, fcfg.ID, files)) + must(t, m.IndexUpdate(conn2, fcfg.ID, files)) seq++ files[0].Deleted = false files[0].Size = 20 files[0].Version = files[0].Version.Update(device1.Short()) files[0].Sequence = seq - must(t, m.IndexUpdate(device1, fcfg.ID, files)) + must(t, m.IndexUpdate(conn1, fcfg.ID, files)) if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 { t.Error("Expected one needed item for device2, got", comp.NeedItems) } // Pretend we had an index reset on device 1 - must(t, m.Index(device1, fcfg.ID, files)) + must(t, m.Index(conn1, fcfg.ID, files)) if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 { t.Error("Expected one needed item for device2, got", comp.NeedItems) } diff --git a/lib/model/progressemitter.go b/lib/model/progressemitter.go index fab9e72c9..537f6b561 100644 --- a/lib/model/progressemitter.go +++ b/lib/model/progressemitter.go @@ -315,15 +315,15 @@ func (t *ProgressEmitter) emptyLocked() bool { func (t *ProgressEmitter) temporaryIndexSubscribe(conn protocol.Connection, folders []string) { t.mut.Lock() defer t.mut.Unlock() - t.connections[conn.ID()] = conn - t.foldersByConns[conn.ID()] = folders + t.connections[conn.DeviceID()] = conn + t.foldersByConns[conn.DeviceID()] = folders } func (t *ProgressEmitter) temporaryIndexUnsubscribe(conn protocol.Connection) { t.mut.Lock() defer t.mut.Unlock() - delete(t.connections, conn.ID()) - delete(t.foldersByConns, conn.ID()) + delete(t.connections, conn.DeviceID()) + delete(t.foldersByConns, conn.DeviceID()) } func (t *ProgressEmitter) clearLocked() { diff --git a/lib/model/progressemitter_test.go b/lib/model/progressemitter_test.go index 24eb70158..e103a69ae 100644 --- a/lib/model/progressemitter_test.go +++ b/lib/model/progressemitter_test.go @@ -463,7 +463,7 @@ func TestSendDownloadProgressMessages(t *testing.T) { p.temporaryIndexUnsubscribe(fc) sendMsgs(p) - _, ok := p.sentDownloadStates[fc.ID()] + _, ok := p.sentDownloadStates[fc.DeviceID()] if ok { t.Error("Should not be there") } diff --git a/lib/model/requests_test.go b/lib/model/requests_test.go index 1c728a869..08001c9a2 100644 --- a/lib/model/requests_test.go +++ b/lib/model/requests_test.go @@ -107,7 +107,7 @@ func TestSymlinkTraversalRead(t *testing.T) { <-done // Request a file by traversing the symlink - res, err := m.Request(device1, "default", "symlink/requests_test.go", 0, 10, 0, nil, 0, false) + res, err := m.Request(device1Conn, "default", "symlink/requests_test.go", 0, 10, 0, nil, 0, false) if err == nil || res != nil { t.Error("Managed to traverse symlink") } @@ -439,7 +439,7 @@ func TestRescanIfHaveInvalidContent(t *testing.T) { t.Fatalf("unexpected weak hash: %d != 103547413", f.Blocks[0].WeakHash) } - res, err := m.Request(device1, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) + res, err := m.Request(device1Conn, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) if err != nil { t.Fatal(err) } @@ -453,7 +453,7 @@ func TestRescanIfHaveInvalidContent(t *testing.T) { writeFile(t, tfs, "foo", payload) - _, err = m.Request(device1, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) + _, err = m.Request(device1Conn, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) if err == nil { t.Fatalf("expected failure") } @@ -1122,7 +1122,7 @@ func TestRequestIndexSenderPause(t *testing.T) { cc := basicClusterConfig(device1, myID, fcfg.ID) cc.Folders[0].Paused = true - m.ClusterConfig(device1, cc) + m.ClusterConfig(fc, cc) seq++ files[0].Sequence = seq @@ -1143,7 +1143,7 @@ func TestRequestIndexSenderPause(t *testing.T) { // Remote unpaused cc.Folders[0].Paused = false - m.ClusterConfig(device1, cc) + m.ClusterConfig(fc, cc) select { case <-time.After(5 * time.Second): t.Fatal("timed out before receiving index") @@ -1168,12 +1168,12 @@ func TestRequestIndexSenderPause(t *testing.T) { // Local and remote paused, then first resume remote, then local cc.Folders[0].Paused = true - m.ClusterConfig(device1, cc) + m.ClusterConfig(fc, cc) pauseFolder(t, m.cfg, fcfg.ID, true) cc.Folders[0].Paused = false - m.ClusterConfig(device1, cc) + m.ClusterConfig(fc, cc) pauseFolder(t, m.cfg, fcfg.ID, false) @@ -1190,7 +1190,7 @@ func TestRequestIndexSenderPause(t *testing.T) { // Folder removed on remote cc = protocol.ClusterConfig{} - m.ClusterConfig(device1, cc) + m.ClusterConfig(fc, cc) seq++ files[0].Sequence = seq @@ -1304,7 +1304,7 @@ func TestRequestReceiveEncrypted(t *testing.T) { return nil }) m.AddConnection(fc, protocol.Hello{}) - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(fc, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: "default", @@ -1354,7 +1354,7 @@ func TestRequestReceiveEncrypted(t *testing.T) { } // Simulate request from device that is untrusted too, i.e. with non-empty, but garbage hash - _, err := m.Request(device1, fcfg.ID, name, 0, 1064, 0, []byte("garbage"), 0, false) + _, err := m.Request(fc, fcfg.ID, name, 0, 1064, 0, []byte("garbage"), 0, false) must(t, err) changed, err := m.LocalChangedFolderFiles(fcfg.ID, 1, 10) @@ -1380,7 +1380,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) { }) must(t, err) waiter.Wait() - addFakeConn(m, device2, fcfg.ID) + conn := addFakeConn(m, device2, fcfg.ID) tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) @@ -1405,7 +1405,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) { file := fc.files[0] fc.mut.Unlock() file.SetIgnored() - m.IndexUpdate(device2, fcfg.ID, []protocol.FileInfo{prepareFileInfoForIndex(file)}) + m.IndexUpdate(conn, fcfg.ID, []protocol.FileInfo{prepareFileInfoForIndex(file)}) // Wait for the ignored file to be received and possible pulled timeout := time.After(10 * time.Second) diff --git a/lib/model/testutils_test.go b/lib/model/testutils_test.go index 0ec044eca..a558d9a9d 100644 --- a/lib/model/testutils_test.go +++ b/lib/model/testutils_test.go @@ -19,6 +19,7 @@ import ( "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/protocol/mocks" "github.com/syncthing/syncthing/lib/rand" ) @@ -29,12 +30,16 @@ var ( defaultFolderConfig config.FolderConfiguration defaultCfg config.Configuration defaultAutoAcceptCfg config.Configuration + device1Conn = &mocks.Connection{} + device2Conn = &mocks.Connection{} ) func init() { myID, _ = protocol.DeviceIDFromString("ZNWFSWE-RWRV2BD-45BLMCV-LTDE2UR-4LJDW6J-R5BPWEB-TXD27XJ-IZF5RA4") device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR") device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY") + device1Conn.DeviceIDReturns(device1) + device2Conn.DeviceIDReturns(device2) cfg := config.New(myID) cfg.Options.MinHomeDiskFree.Value = 0 // avoids unnecessary free space checks diff --git a/lib/protocol/benchmark_test.go b/lib/protocol/benchmark_test.go index c740e0725..4fd07c407 100644 --- a/lib/protocol/benchmark_test.go +++ b/lib/protocol/benchmark_test.go @@ -167,15 +167,15 @@ func negotiateTLS(cert tls.Certificate, conn0, conn1 net.Conn) (net.Conn, net.Co type fakeModel struct{} -func (*fakeModel) Index(_ DeviceID, _ string, _ []FileInfo) error { +func (*fakeModel) Index(Connection, string, []FileInfo) error { return nil } -func (*fakeModel) IndexUpdate(_ DeviceID, _ string, _ []FileInfo) error { +func (*fakeModel) IndexUpdate(Connection, string, []FileInfo) error { return nil } -func (*fakeModel) Request(_ DeviceID, _, _ string, _, size int32, offset int64, _ []byte, _ uint32, _ bool) (RequestResponse, error) { +func (*fakeModel) Request(_ Connection, _, _ string, _, size int32, offset int64, _ []byte, _ uint32, _ bool) (RequestResponse, error) { // We write the offset to the end of the buffer, so the receiver // can verify that it did in fact get some data back over the // connection. @@ -184,13 +184,13 @@ func (*fakeModel) Request(_ DeviceID, _, _ string, _, size int32, offset int64, return &fakeRequestResponse{buf}, nil } -func (*fakeModel) ClusterConfig(_ DeviceID, _ ClusterConfig) error { +func (*fakeModel) ClusterConfig(Connection, ClusterConfig) error { return nil } -func (*fakeModel) Closed(DeviceID, error) { +func (*fakeModel) Closed(Connection, error) { } -func (*fakeModel) DownloadProgress(_ DeviceID, _ string, _ []FileDownloadProgressUpdate) error { +func (*fakeModel) DownloadProgress(Connection, string, []FileDownloadProgressUpdate) error { return nil } diff --git a/lib/protocol/common_test.go b/lib/protocol/common_test.go index 8299bb970..a8ddec8d7 100644 --- a/lib/protocol/common_test.go +++ b/lib/protocol/common_test.go @@ -13,8 +13,8 @@ type TestModel struct { hash []byte weakHash uint32 fromTemporary bool - indexFn func(DeviceID, string, []FileInfo) - ccFn func(DeviceID, ClusterConfig) + indexFn func(string, []FileInfo) + ccFn func(ClusterConfig) closedCh chan struct{} closedErr error } @@ -25,18 +25,18 @@ func newTestModel() *TestModel { } } -func (t *TestModel) Index(deviceID DeviceID, folder string, files []FileInfo) error { +func (t *TestModel) Index(_ Connection, folder string, files []FileInfo) error { if t.indexFn != nil { - t.indexFn(deviceID, folder, files) + t.indexFn(folder, files) } return nil } -func (*TestModel) IndexUpdate(_ DeviceID, _ string, _ []FileInfo) error { +func (*TestModel) IndexUpdate(Connection, string, []FileInfo) error { return nil } -func (t *TestModel) Request(_ DeviceID, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { +func (t *TestModel) Request(_ Connection, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { t.folder = folder t.name = name t.offset = offset @@ -49,19 +49,19 @@ func (t *TestModel) Request(_ DeviceID, folder, name string, _, size int32, offs return &fakeRequestResponse{buf}, nil } -func (t *TestModel) Closed(_ DeviceID, err error) { +func (t *TestModel) Closed(_ Connection, err error) { t.closedErr = err close(t.closedCh) } -func (t *TestModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) error { +func (t *TestModel) ClusterConfig(_ Connection, config ClusterConfig) error { if t.ccFn != nil { - t.ccFn(deviceID, config) + t.ccFn(config) } return nil } -func (*TestModel) DownloadProgress(DeviceID, string, []FileDownloadProgressUpdate) error { +func (*TestModel) DownloadProgress(Connection, string, []FileDownloadProgressUpdate) error { return nil } diff --git a/lib/protocol/encryption.go b/lib/protocol/encryption.go index 322dfedcf..1149aeb28 100644 --- a/lib/protocol/encryption.go +++ b/lib/protocol/encryption.go @@ -43,12 +43,12 @@ const ( // receives encrypted metadata and requests from the untrusted device, so it // must decrypt those and answer requests by encrypting the data. type encryptedModel struct { - model Model + model contextLessModel folderKeys *folderKeyRegistry keyGen *KeyGenerator } -func newEncryptedModel(model Model, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel { +func newEncryptedModel(model contextLessModel, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel { return encryptedModel{ model: model, folderKeys: folderKeys, @@ -56,30 +56,30 @@ func newEncryptedModel(model Model, folderKeys *folderKeyRegistry, keyGen *KeyGe } } -func (e encryptedModel) Index(deviceID DeviceID, folder string, files []FileInfo) error { +func (e encryptedModel) Index(folder string, files []FileInfo) error { if folderKey, ok := e.folderKeys.get(folder); ok { // incoming index data to be decrypted if err := decryptFileInfos(e.keyGen, files, folderKey); err != nil { return err } } - return e.model.Index(deviceID, folder, files) + return e.model.Index(folder, files) } -func (e encryptedModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error { +func (e encryptedModel) IndexUpdate(folder string, files []FileInfo) error { if folderKey, ok := e.folderKeys.get(folder); ok { // incoming index data to be decrypted if err := decryptFileInfos(e.keyGen, files, folderKey); err != nil { return err } } - return e.model.IndexUpdate(deviceID, folder, files) + return e.model.IndexUpdate(folder, files) } -func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { +func (e encryptedModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { folderKey, ok := e.folderKeys.get(folder) if !ok { - return e.model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) + return e.model.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) } // Figure out the real file name, offset and size from the encrypted / @@ -120,7 +120,7 @@ func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, // Perform that request and grab the data. - resp, err := e.model.Request(deviceID, folder, realName, blockNo, realSize, realOffset, realHash, 0, false) + resp, err := e.model.Request(folder, realName, blockNo, realSize, realOffset, realHash, 0, false) if err != nil { return nil, err } @@ -142,21 +142,21 @@ func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, return rawResponse{enc}, nil } -func (e encryptedModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error { +func (e encryptedModel) DownloadProgress(folder string, updates []FileDownloadProgressUpdate) error { if _, ok := e.folderKeys.get(folder); !ok { - return e.model.DownloadProgress(deviceID, folder, updates) + return e.model.DownloadProgress(folder, updates) } // Encrypted devices shouldn't send these - ignore them. return nil } -func (e encryptedModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) error { - return e.model.ClusterConfig(deviceID, config) +func (e encryptedModel) ClusterConfig(config ClusterConfig) error { + return e.model.ClusterConfig(config) } -func (e encryptedModel) Closed(device DeviceID, err error) { - e.model.Closed(device, err) +func (e encryptedModel) Closed(err error) { + e.model.Closed(err) } // The encryptedConnection sits between the model and the encrypted device. It @@ -185,8 +185,8 @@ func (e encryptedConnection) SetFolderPasswords(passwords map[string]string) { e.folderKeys.setPasswords(passwords) } -func (e encryptedConnection) ID() DeviceID { - return e.conn.ID() +func (e encryptedConnection) DeviceID() DeviceID { + return e.conn.DeviceID() } func (e encryptedConnection) Index(ctx context.Context, folder string, files []FileInfo) error { diff --git a/lib/protocol/mocks/connection.go b/lib/protocol/mocks/connection.go index 3fddab239..3c4f60b2c 100644 --- a/lib/protocol/mocks/connection.go +++ b/lib/protocol/mocks/connection.go @@ -41,6 +41,16 @@ type Connection struct { cryptoReturnsOnCall map[int]struct { result1 string } + DeviceIDStub func() protocol.DeviceID + deviceIDMutex sync.RWMutex + deviceIDArgsForCall []struct { + } + deviceIDReturns struct { + result1 protocol.DeviceID + } + deviceIDReturnsOnCall map[int]struct { + result1 protocol.DeviceID + } DownloadProgressStub func(context.Context, string, []protocol.FileDownloadProgressUpdate) downloadProgressMutex sync.RWMutex downloadProgressArgsForCall []struct { @@ -58,16 +68,6 @@ type Connection struct { establishedAtReturnsOnCall map[int]struct { result1 time.Time } - IDStub func() protocol.DeviceID - iDMutex sync.RWMutex - iDArgsForCall []struct { - } - iDReturns struct { - result1 protocol.DeviceID - } - iDReturnsOnCall map[int]struct { - result1 protocol.DeviceID - } IndexStub func(context.Context, string, []protocol.FileInfo) error indexMutex sync.RWMutex indexArgsForCall []struct { @@ -368,6 +368,59 @@ func (fake *Connection) CryptoReturnsOnCall(i int, result1 string) { }{result1} } +func (fake *Connection) DeviceID() protocol.DeviceID { + fake.deviceIDMutex.Lock() + ret, specificReturn := fake.deviceIDReturnsOnCall[len(fake.deviceIDArgsForCall)] + fake.deviceIDArgsForCall = append(fake.deviceIDArgsForCall, struct { + }{}) + stub := fake.DeviceIDStub + fakeReturns := fake.deviceIDReturns + fake.recordInvocation("DeviceID", []interface{}{}) + fake.deviceIDMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *Connection) DeviceIDCallCount() int { + fake.deviceIDMutex.RLock() + defer fake.deviceIDMutex.RUnlock() + return len(fake.deviceIDArgsForCall) +} + +func (fake *Connection) DeviceIDCalls(stub func() protocol.DeviceID) { + fake.deviceIDMutex.Lock() + defer fake.deviceIDMutex.Unlock() + fake.DeviceIDStub = stub +} + +func (fake *Connection) DeviceIDReturns(result1 protocol.DeviceID) { + fake.deviceIDMutex.Lock() + defer fake.deviceIDMutex.Unlock() + fake.DeviceIDStub = nil + fake.deviceIDReturns = struct { + result1 protocol.DeviceID + }{result1} +} + +func (fake *Connection) DeviceIDReturnsOnCall(i int, result1 protocol.DeviceID) { + fake.deviceIDMutex.Lock() + defer fake.deviceIDMutex.Unlock() + fake.DeviceIDStub = nil + if fake.deviceIDReturnsOnCall == nil { + fake.deviceIDReturnsOnCall = make(map[int]struct { + result1 protocol.DeviceID + }) + } + fake.deviceIDReturnsOnCall[i] = struct { + result1 protocol.DeviceID + }{result1} +} + func (fake *Connection) DownloadProgress(arg1 context.Context, arg2 string, arg3 []protocol.FileDownloadProgressUpdate) { var arg3Copy []protocol.FileDownloadProgressUpdate if arg3 != nil { @@ -460,59 +513,6 @@ func (fake *Connection) EstablishedAtReturnsOnCall(i int, result1 time.Time) { }{result1} } -func (fake *Connection) ID() protocol.DeviceID { - fake.iDMutex.Lock() - ret, specificReturn := fake.iDReturnsOnCall[len(fake.iDArgsForCall)] - fake.iDArgsForCall = append(fake.iDArgsForCall, struct { - }{}) - stub := fake.IDStub - fakeReturns := fake.iDReturns - fake.recordInvocation("ID", []interface{}{}) - fake.iDMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *Connection) IDCallCount() int { - fake.iDMutex.RLock() - defer fake.iDMutex.RUnlock() - return len(fake.iDArgsForCall) -} - -func (fake *Connection) IDCalls(stub func() protocol.DeviceID) { - fake.iDMutex.Lock() - defer fake.iDMutex.Unlock() - fake.IDStub = stub -} - -func (fake *Connection) IDReturns(result1 protocol.DeviceID) { - fake.iDMutex.Lock() - defer fake.iDMutex.Unlock() - fake.IDStub = nil - fake.iDReturns = struct { - result1 protocol.DeviceID - }{result1} -} - -func (fake *Connection) IDReturnsOnCall(i int, result1 protocol.DeviceID) { - fake.iDMutex.Lock() - defer fake.iDMutex.Unlock() - fake.IDStub = nil - if fake.iDReturnsOnCall == nil { - fake.iDReturnsOnCall = make(map[int]struct { - result1 protocol.DeviceID - }) - } - fake.iDReturnsOnCall[i] = struct { - result1 protocol.DeviceID - }{result1} -} - func (fake *Connection) Index(arg1 context.Context, arg2 string, arg3 []protocol.FileInfo) error { var arg3Copy []protocol.FileInfo if arg3 != nil { @@ -1164,12 +1164,12 @@ func (fake *Connection) Invocations() map[string][][]interface{} { defer fake.clusterConfigMutex.RUnlock() fake.cryptoMutex.RLock() defer fake.cryptoMutex.RUnlock() + fake.deviceIDMutex.RLock() + defer fake.deviceIDMutex.RUnlock() fake.downloadProgressMutex.RLock() defer fake.downloadProgressMutex.RUnlock() fake.establishedAtMutex.RLock() defer fake.establishedAtMutex.RUnlock() - fake.iDMutex.RLock() - defer fake.iDMutex.RUnlock() fake.indexMutex.RLock() defer fake.indexMutex.RUnlock() fake.indexUpdateMutex.RLock() diff --git a/lib/protocol/nativemodel_darwin.go b/lib/protocol/nativemodel_darwin.go index ef5d15b35..b51513df7 100644 --- a/lib/protocol/nativemodel_darwin.go +++ b/lib/protocol/nativemodel_darwin.go @@ -9,27 +9,27 @@ package protocol import "golang.org/x/text/unicode/norm" -func makeNative(m Model) Model { return nativeModel{m} } +func makeNative(m contextLessModel) contextLessModel { return nativeModel{m} } type nativeModel struct { - Model + contextLessModel } -func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo) error { +func (m nativeModel) Index(folder string, files []FileInfo) error { for i := range files { files[i].Name = norm.NFD.String(files[i].Name) } - return m.Model.Index(deviceID, folder, files) + return m.contextLessModel.Index(folder, files) } -func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error { +func (m nativeModel) IndexUpdate(folder string, files []FileInfo) error { for i := range files { files[i].Name = norm.NFD.String(files[i].Name) } - return m.Model.IndexUpdate(deviceID, folder, files) + return m.contextLessModel.IndexUpdate(folder, files) } -func (m nativeModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { +func (m nativeModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { name = norm.NFD.String(name) - return m.Model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) + return m.contextLessModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) } diff --git a/lib/protocol/nativemodel_unix.go b/lib/protocol/nativemodel_unix.go index 90e50e394..c714309c6 100644 --- a/lib/protocol/nativemodel_unix.go +++ b/lib/protocol/nativemodel_unix.go @@ -7,4 +7,4 @@ package protocol // Normal Unixes uses NFC and slashes, which is the wire format. -func makeNative(m Model) Model { return m } +func makeNative(m contextLessModel) contextLessModel { return m } diff --git a/lib/protocol/nativemodel_windows.go b/lib/protocol/nativemodel_windows.go index c6e8bf124..efc47d172 100644 --- a/lib/protocol/nativemodel_windows.go +++ b/lib/protocol/nativemodel_windows.go @@ -13,30 +13,30 @@ import ( "strings" ) -func makeNative(m Model) Model { return nativeModel{m} } +func makeNative(m contextLessModel) contextLessModel { return nativeModel{m} } type nativeModel struct { - Model + contextLessModel } -func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo) error { +func (m nativeModel) Index(folder string, files []FileInfo) error { files = fixupFiles(files) - return m.Model.Index(deviceID, folder, files) + return m.contextLessModel.Index(folder, files) } -func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error { +func (m nativeModel) IndexUpdate(folder string, files []FileInfo) error { files = fixupFiles(files) - return m.Model.IndexUpdate(deviceID, folder, files) + return m.contextLessModel.IndexUpdate(folder, files) } -func (m nativeModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { +func (m nativeModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { if strings.Contains(name, `\`) { l.Warnf("Dropping request for %s, contains invalid path separator", name) return nil, ErrNoSuchFile } name = filepath.FromSlash(name) - return m.Model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) + return m.contextLessModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) } func fixupFiles(files []FileInfo) []FileInfo { diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go index 044ca47b7..6c6581c1a 100644 --- a/lib/protocol/protocol.go +++ b/lib/protocol/protocol.go @@ -123,17 +123,28 @@ var ( type Model interface { // An index was received from the peer device - Index(deviceID DeviceID, folder string, files []FileInfo) error + Index(conn Connection, folder string, files []FileInfo) error // An index update was received from the peer device - IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error + IndexUpdate(conn Connection, folder string, files []FileInfo) error // A request was made by the peer device - Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) + Request(conn Connection, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) // A cluster configuration message was received - ClusterConfig(deviceID DeviceID, config ClusterConfig) error + ClusterConfig(conn Connection, config ClusterConfig) error // The peer device closed the connection or an error occurred - Closed(device DeviceID, err error) + Closed(conn Connection, err error) // The peer device sent progress updates for the files it is currently downloading - DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error + DownloadProgress(conn Connection, folder string, updates []FileDownloadProgressUpdate) error +} + +// contextLessModel is the Model interface, but without the initial +// Connection parameter. Internal use only. +type contextLessModel interface { + Index(folder string, files []FileInfo) error + IndexUpdate(folder string, files []FileInfo) error + Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) + ClusterConfig(config ClusterConfig) error + Closed(err error) + DownloadProgress(folder string, updates []FileDownloadProgressUpdate) error } type RequestResponse interface { @@ -146,7 +157,7 @@ type Connection interface { Start() SetFolderPasswords(passwords map[string]string) Close(err error) - ID() DeviceID + DeviceID() DeviceID Index(ctx context.Context, folder string, files []FileInfo) error IndexUpdate(ctx context.Context, folder string, files []FileInfo) error Request(ctx context.Context, folder string, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) @@ -171,8 +182,8 @@ type ConnectionInfo interface { type rawConnection struct { ConnectionInfo - id DeviceID - receiver Model + deviceID DeviceID + model contextLessModel startTime time.Time cr *countingReader @@ -229,10 +240,16 @@ const ( // Should not be modified in production code, just for testing. var CloseTimeout = 10 * time.Second -func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string, keyGen *KeyGenerator) Connection { +func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, model Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string, keyGen *KeyGenerator) Connection { + // We create the wrapper for the model first, as it needs to be passed + // in at the lowest level in the stack. At the end of construction, + // before returning, we add the connection to cwm so that it can be used + // by the model. + cwm := &connectionWrappingModel{model: model} + // Encryption / decryption is first (outermost) before conversion to // native path formats. - nm := makeNative(receiver) + nm := makeNative(cwm) em := newEncryptedModel(nm, newFolderKeyRegistry(keyGen, passwords), keyGen) // We do the wire format conversion first (outermost) so that the @@ -241,17 +258,18 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer ec := newEncryptedConnection(rc, rc, em.folderKeys, keyGen) wc := wireFormatConnection{ec} + cwm.conn = wc return wc } -func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression) *rawConnection { +func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver contextLessModel, connInfo ConnectionInfo, compress Compression) *rawConnection { cr := &countingReader{Reader: reader} cw := &countingWriter{Writer: writer} return &rawConnection{ ConnectionInfo: connInfo, - id: deviceID, - receiver: receiver, + deviceID: deviceID, + model: receiver, cr: cr, cw: cw, closer: closer, @@ -295,8 +313,8 @@ func (c *rawConnection) Start() { c.startTime = time.Now().Truncate(time.Second) } -func (c *rawConnection) ID() DeviceID { - return c.id +func (c *rawConnection) DeviceID() DeviceID { + return c.deviceID } // Index writes the list of file information to the connected peer device @@ -462,7 +480,7 @@ func (c *rawConnection) dispatcherLoop() (err error) { switch msg := msg.(type) { case *ClusterConfig: - err = c.receiver.ClusterConfig(c.id, *msg) + err = c.model.ClusterConfig(*msg) case *Index: err = c.handleIndex(*msg) @@ -477,7 +495,7 @@ func (c *rawConnection) dispatcherLoop() (err error) { c.handleResponse(*msg) case *DownloadProgress: - err = c.receiver.DownloadProgress(c.id, msg.Folder, msg.Updates) + err = c.model.DownloadProgress(msg.Folder, msg.Updates) } if err != nil { return newHandleError(err, msgContext) @@ -579,13 +597,13 @@ func (c *rawConnection) readHeader(fourByteBuf []byte) (Header, error) { } func (c *rawConnection) handleIndex(im Index) error { - l.Debugf("Index(%v, %v, %d file)", c.id, im.Folder, len(im.Files)) - return c.receiver.Index(c.id, im.Folder, im.Files) + l.Debugf("Index(%v, %v, %d file)", c.deviceID, im.Folder, len(im.Files)) + return c.model.Index(im.Folder, im.Files) } func (c *rawConnection) handleIndexUpdate(im IndexUpdate) error { - l.Debugf("queueing IndexUpdate(%v, %v, %d files)", c.id, im.Folder, len(im.Files)) - return c.receiver.IndexUpdate(c.id, im.Folder, im.Files) + l.Debugf("queueing IndexUpdate(%v, %v, %d files)", c.deviceID, im.Folder, len(im.Files)) + return c.model.IndexUpdate(im.Folder, im.Files) } // checkIndexConsistency verifies a number of invariants on FileInfos received in @@ -651,7 +669,7 @@ func checkFilename(name string) error { } func (c *rawConnection) handleRequest(req Request) { - res, err := c.receiver.Request(c.id, req.Folder, req.Name, int32(req.BlockNo), int32(req.Size), req.Offset, req.Hash, req.WeakHash, req.FromTemporary) + res, err := c.model.Request(req.Folder, req.Name, int32(req.BlockNo), int32(req.Size), req.Offset, req.Hash, req.WeakHash, req.FromTemporary) if err != nil { c.send(context.Background(), &Response{ ID: req.ID, @@ -925,7 +943,7 @@ func (c *rawConnection) internalClose(err error) { c.closeOnce.Do(func() { l.Debugln("close due to", err) if cerr := c.closer.Close(); cerr != nil { - l.Debugln(c.id, "failed to close underlying conn:", cerr) + l.Debugln(c.deviceID, "failed to close underlying conn:", cerr) } close(c.closed) @@ -940,7 +958,7 @@ func (c *rawConnection) internalClose(err error) { <-c.dispatcherLoopStopped - c.receiver.Closed(c.ID(), err) + c.model.Closed(err) }) } @@ -958,11 +976,11 @@ func (c *rawConnection) pingSender() { case <-ticker.C: d := time.Since(c.cw.Last()) if d < PingSendInterval/2 { - l.Debugln(c.id, "ping skipped after wr", d) + l.Debugln(c.deviceID, "ping skipped after wr", d) continue } - l.Debugln(c.id, "ping -> after", d) + l.Debugln(c.deviceID, "ping -> after", d) c.ping() case <-c.closed: @@ -983,11 +1001,11 @@ func (c *rawConnection) pingReceiver() { case <-ticker.C: d := time.Since(c.cr.Last()) if d > ReceiveTimeout { - l.Debugln(c.id, "ping timeout", d) + l.Debugln(c.deviceID, "ping timeout", d) c.internalClose(ErrTimeout) } - l.Debugln(c.id, "last read within", d) + l.Debugln(c.deviceID, "last read within", d) case <-c.closed: return @@ -1068,3 +1086,35 @@ func messageContext(msg message) (string, error) { return "", errors.New("unknown or empty message") } } + +// connectionWrappingModel takes the Model interface from the model package, +// which expects the Connection as the first parameter in all methods, and +// wraps it to conform to the protocol.contextLessModel interface. +type connectionWrappingModel struct { + conn Connection + model Model +} + +func (c *connectionWrappingModel) Index(folder string, files []FileInfo) error { + return c.model.Index(c.conn, folder, files) +} + +func (c *connectionWrappingModel) IndexUpdate(folder string, files []FileInfo) error { + return c.model.IndexUpdate(c.conn, folder, files) +} + +func (c *connectionWrappingModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { + return c.model.Request(c.conn, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) +} + +func (c *connectionWrappingModel) ClusterConfig(config ClusterConfig) error { + return c.model.ClusterConfig(c.conn, config) +} + +func (c *connectionWrappingModel) Closed(err error) { + c.model.Closed(c.conn, err) +} + +func (c *connectionWrappingModel) DownloadProgress(folder string, updates []FileDownloadProgressUpdate) error { + return c.model.DownloadProgress(c.conn, folder, updates) +} diff --git a/lib/protocol/protocol_test.go b/lib/protocol/protocol_test.go index 90916f760..69f954aa3 100644 --- a/lib/protocol/protocol_test.go +++ b/lib/protocol/protocol_test.go @@ -145,7 +145,7 @@ func TestCloseRace(t *testing.T) { indexReceived := make(chan struct{}) unblockIndex := make(chan struct{}) m0 := newTestModel() - m0.indexFn = func(_ DeviceID, _ string, _ []FileInfo) { + m0.indexFn = func(string, []FileInfo) { close(indexReceived) <-unblockIndex } @@ -924,7 +924,7 @@ func TestDispatcherToCloseDeadlock(t *testing.T) { m := newTestModel() rw := testutils.NewBlockingRW() c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen)) - m.ccFn = func(devID DeviceID, cc ClusterConfig) { + m.ccFn = func(ClusterConfig) { c.Close(errManual) } c.Start()