@@ -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
|
||||
}
|
||||
|
||||
+10
-10
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+79
-29
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user