@@ -16,6 +16,8 @@ public class RemoteSync : IDisposable
1616 SftpClient _sftp ;
1717 SyncDirector _director ;
1818 HashSet < string > _activeDirSync = new HashSet < string > ( ) ;
19+ readonly SemaphoreSlim _sftpLock = new SemaphoreSlim ( 1 , 1 ) ;
20+ bool _disposed ;
1921
2022
2123 public Task DoneMakingFolders { get ; }
@@ -85,7 +87,7 @@ public static void SyncFile(SftpClient sftp, string sourcePath, string destinati
8587
8688 return ;
8789 }
88- catch ( Exception ex ) when ( ex is IOException || ex is FileNotFoundException )
90+ catch ( Exception ex )
8991 {
9092 retryCount ++ ;
9193 if ( retryCount >= maxRetries )
@@ -150,6 +152,9 @@ public async Task CreateDirectories(string localPath, string remotePath)
150152
151153 try
152154 {
155+ if ( ! EnsureConnectedSafe ( ) )
156+ return ;
157+
153158 //Got local directories to sync
154159 var localDirectories = FilteredDirectories ( localPath ) ;
155160
@@ -194,6 +199,9 @@ public async Task InitialSync(string localPath, string remotePath)
194199 //Wait for the folders to be created before starting the initial sync
195200 await DoneMakingFolders ;
196201
202+ if ( ! EnsureConnectedSafe ( ) )
203+ return ;
204+
197205 //Get the local directories to sync
198206 var localDirectories = FilteredDirectories ( localPath ) ;
199207
@@ -256,65 +264,151 @@ public static bool IsFileReady(String sFilename)
256264 }
257265 }
258266
267+ private void EnsureConnected ( )
268+ {
269+ if ( _disposed )
270+ throw new ObjectDisposedException ( nameof ( RemoteSync ) ) ;
259271
260- private async void Fsw_Changed ( object ? sender , FileSystemEventArgs arg )
272+ if ( _sftp . IsConnected )
273+ return ;
274+
275+ _sftp . Connect ( ) ;
276+ }
277+
278+ private bool EnsureConnectedSafe ( )
261279 {
262- if ( arg . ChangeType == WatcherChangeTypes . Changed || arg . ChangeType == WatcherChangeTypes . Created
263- || arg . ChangeType == WatcherChangeTypes . Renamed )
280+ try
264281 {
265- var changedPath = Path . GetDirectoryName ( arg . FullPath ) ;
266- var relativePath = _localRootDirectory == changedPath ? "" : changedPath ? . Substring ( _localRootDirectory . Length ) . Replace ( '\\ ' , '/' ) ;
267- var fullRemotePath = _remoteRootDirectory + relativePath ;
268- await Task . Yield ( ) ;
269- bool makeDirectory = true ;
270- lock ( _activeDirSync )
271- {
272- if ( changedPath == null )
273- return ;
274- if ( _activeDirSync . Contains ( changedPath ) )
275- makeDirectory = false ;
276- else
277- _activeDirSync . Add ( changedPath ) ;
278- }
282+ EnsureConnected ( ) ;
283+ return true ;
284+ }
285+ catch ( Exception ex )
286+ {
287+ Logger . LogError ( $ "SFTP connection error: { ex . Message } ") ;
288+ return false ;
289+ }
290+ }
279291
280- //check if we're a new directory
281- if ( makeDirectory && Directory . Exists ( arg . FullPath ) && ! _sftp . Exists ( arg . FullPath ) )
282- {
283- _sftp . CreateDirectory ( fullRemotePath ) ;
284- }
285292
286- if ( makeDirectory )
293+ private async void Fsw_Changed ( object ? sender , FileSystemEventArgs arg )
294+ {
295+ try
296+ {
297+ if ( arg . ChangeType == WatcherChangeTypes . Changed || arg . ChangeType == WatcherChangeTypes . Created
298+ || arg . ChangeType == WatcherChangeTypes . Renamed )
287299 {
300+ var changedPath = Path . GetDirectoryName ( arg . FullPath ) ;
301+ var relativePath = _localRootDirectory == changedPath ? "" : changedPath ? . Substring ( _localRootDirectory . Length ) . Replace ( '\\ ' , '/' ) ;
302+ var fullRemotePath = _remoteRootDirectory + relativePath ;
303+ await Task . Yield ( ) ;
304+ bool makeDirectory = true ;
288305 lock ( _activeDirSync )
289306 {
290- _activeDirSync . Remove ( changedPath ) ;
307+ if ( changedPath == null )
308+ return ;
309+ if ( _activeDirSync . Contains ( changedPath ) )
310+ makeDirectory = false ;
311+ else
312+ _activeDirSync . Add ( changedPath ) ;
291313 }
292- }
293314
294- while ( ! IsFileReady ( arg . FullPath ) )
295- await Task . Delay ( 25 ) ;
315+ bool connectionOk ;
316+ await _sftpLock . WaitAsync ( ) ;
317+ try
318+ {
319+ connectionOk = EnsureConnectedSafe ( ) ;
320+ if ( connectionOk )
321+ {
322+ //check if we're a new directory
323+ if ( makeDirectory && Directory . Exists ( arg . FullPath ) && ! _sftp . Exists ( fullRemotePath ) )
324+ {
325+ _sftp . CreateDirectory ( fullRemotePath ) ;
326+ }
327+ }
328+ }
329+ finally
330+ {
331+ _sftpLock . Release ( ) ;
332+ }
296333
334+ if ( ! connectionOk )
335+ {
336+ if ( makeDirectory )
337+ {
338+ lock ( _activeDirSync )
339+ {
340+ _activeDirSync . Remove ( changedPath ) ;
341+ }
342+ }
343+ return ;
344+ }
297345
298- lock ( _activeDirSync )
299- {
300- if ( _activeDirSync . Contains ( arg . FullPath ) )
346+ if ( makeDirectory )
347+ {
348+ lock ( _activeDirSync )
349+ {
350+ _activeDirSync . Remove ( changedPath ) ;
351+ }
352+ }
353+
354+ if ( Directory . Exists ( arg . FullPath ) )
301355 return ;
302- else
303- _activeDirSync . Add ( arg . FullPath ) ;
304- }
305- SyncFile ( _sftp , arg . FullPath , fullRemotePath + "/" + Path . GetFileName ( arg . FullPath ) ) ;
306356
307- lock ( _activeDirSync )
308- {
309- _activeDirSync . Remove ( arg . FullPath ) ;
357+ var waitStart = DateTime . UtcNow ;
358+ while ( ! IsFileReady ( arg . FullPath ) )
359+ {
360+ if ( ! File . Exists ( arg . FullPath ) )
361+ return ;
362+ if ( DateTime . UtcNow - waitStart > TimeSpan . FromSeconds ( 30 ) )
363+ {
364+ Logger . LogWarnig ( $ "Timed out waiting for file to be ready: { arg . FullPath } ") ;
365+ return ;
366+ }
367+ await Task . Delay ( 25 ) ;
368+ }
369+
370+ lock ( _activeDirSync )
371+ {
372+ if ( _activeDirSync . Contains ( arg . FullPath ) )
373+ return ;
374+ else
375+ _activeDirSync . Add ( arg . FullPath ) ;
376+ }
377+
378+ bool fileConnectionOk ;
379+ await _sftpLock . WaitAsync ( ) ;
380+ try
381+ {
382+ fileConnectionOk = EnsureConnectedSafe ( ) ;
383+ if ( fileConnectionOk )
384+ {
385+ SyncFile ( _sftp , arg . FullPath , fullRemotePath + "/" + Path . GetFileName ( arg . FullPath ) ) ;
386+ }
387+ }
388+ finally
389+ {
390+ _sftpLock . Release ( ) ;
391+ lock ( _activeDirSync )
392+ {
393+ _activeDirSync . Remove ( arg . FullPath ) ;
394+ }
395+ }
396+
397+ if ( ! fileConnectionOk )
398+ return ;
310399 }
311400 }
401+ catch ( Exception ex )
402+ {
403+ Logger . LogError ( $ "Unhandled exception in file sync handler: { ex . Message } ") ;
404+ }
312405 }
313406
314407 public void Dispose ( )
315408 {
316409 if ( _sftp != null )
317410 {
411+ _disposed = true ;
318412 _sftp . Dispose ( ) ;
319413 }
320414 }
0 commit comments