private async Task <Ignores> FetchFolderIgnoresAsync(string folderId, CancellationToken cancellationToken) { // Until startup is complete, these can return a 500. // There's no sensible way to determine when startup *is* complete, so we just have to keep trying... // It's slightly evil to re-use SyncthingConnectTimeout here, but... // Again, there's the possibility that we've just abort the API... ISyncThingApiClient apiClient; lock (this.apiClientsLock) { cancellationToken.ThrowIfCancellationRequested(); apiClient = this.apiClient; if (apiClient == null) { throw new InvalidOperationException("ApiClient must not be null"); } } Ignores ignores; var startedTime = DateTime.UtcNow; while (true) { try { ignores = await apiClient.FetchIgnoresAsync(folderId); // No need to log: ApiClient did that for us break; } catch (ApiException e) { logger.Debug("Attempting to fetch folder {0}, but received status {1}", folderId, e.StatusCode); if (e.StatusCode != HttpStatusCode.InternalServerError) { throw; } } if (DateTime.UtcNow - startedTime > this.SyncthingConnectTimeout) { throw new SyncThingDidNotStartCorrectlyException(String.Format("Unable to fetch ignores for folder {0}. Syncthing returned 500 after {1}", folderId, DateTime.UtcNow - startedTime)); } await Task.Delay(1000, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); } return(ignores); }
private async Task CreateApiClientAsync(CancellationToken cancellationToken) { logger.Debug("Starting API clients"); var apiClient = await this.apiClientFactory.CreateCorrectApiClientAsync(this.Address, this.ApiKey, this.SyncthingConnectTimeout, cancellationToken); logger.Debug("Have the API client! It's {0}", apiClient.GetType().Name); lock (this.apiClientsLock) { this.apiClient = apiClient; } this.SetState(SyncThingState.Running); }
private void StopApiClients() { lock (this.apiClientsLock) { if (this.apiAbortCts != null) { this.apiAbortCts.Cancel(); } this.apiClient = null; if (this.connectionsWatcher != null) { this.connectionsWatcher.Dispose(); } this.connectionsWatcher = null; if (this.eventWatcher != null) { this.eventWatcher.Dispose(); } this.eventWatcher = null; } }
protected override void OnStop() { this.apiClient = null; }
protected override void OnStart() { this.apiClient = this.apiClientWrapper.Value; }
private async Task LoadStartupDataAsync(CancellationToken cancellationToken) { logger.Debug("Startup Complete! Loading startup data"); // There's a race where Syncthing died, and so we kill the API clients and set it to null, // but we still end up here, because threading. ISyncThingApiClient apiClient; lock (this.apiClientsLock) { cancellationToken.ThrowIfCancellationRequested(); apiClient = this.apiClient; if (apiClient == null) { throw new InvalidOperationException("ApiClient must not be null"); } } var configTask = apiClient.FetchConfigAsync(); var systemTask = apiClient.FetchSystemInfoAsync(); var versionTask = apiClient.FetchVersionAsync(); var connectionsTask = apiClient.FetchConnectionsAsync(); cancellationToken.ThrowIfCancellationRequested(); await Task.WhenAll(configTask, systemTask, versionTask, connectionsTask); // We can potentially see duplicate devices (if the user set their config file that way). Ignore them. var devices = configTask.Result.Devices.DistinctBy(x => x.DeviceID).Select(device => { var deviceObj = new Device(device.DeviceID, device.Name); ItemConnectionData connectionData; if (connectionsTask.Result.DeviceConnections.TryGetValue(device.DeviceID, out connectionData)) { deviceObj.SetConnected(connectionData.Address); } return(deviceObj); }); this.devices = new ConcurrentDictionary <string, Device>(devices.Select(x => new KeyValuePair <string, Device>(x.DeviceId, x))); var tilde = systemTask.Result.Tilde; // If the folder is invalid for any reason, we'll ignore it. // Again, there's the potential for duplicate folder IDs (if the user's been fiddling their config). // In this case, there's nothing really sensible we can do. Just pick one of them :) var folderConstructionTasks = configTask.Result.Folders .Where(x => String.IsNullOrWhiteSpace(x.Invalid)) .DistinctBy(x => x.ID) .Select(async folder => { var ignores = await this.FetchFolderIgnoresAsync(folder.ID, cancellationToken); var path = folder.Path; if (path.StartsWith("~")) { path = Path.Combine(tilde, path.Substring(1).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); } return(new Folder(folder.ID, path, new FolderIgnores(ignores.IgnorePatterns, ignores.RegexPatterns))); }); cancellationToken.ThrowIfCancellationRequested(); var folders = await Task.WhenAll(folderConstructionTasks); this.folders = new ConcurrentDictionary <string, Folder>(folders.Select(x => new KeyValuePair <string, Folder>(x.FolderId, x))); this.Version = versionTask.Result; cancellationToken.ThrowIfCancellationRequested(); this.StartedTime = DateTime.UtcNow; this.IsDataLoaded = true; this.OnDataLoaded(); }
public ISyncThingEventWatcher CreateEventWatcher(ISyncThingApiClient apiClient) { return(new SyncThingEventWatcher(apiClient)); }
public SyncThingEventWatcher(ISyncThingApiClient apiClient) : base(TimeSpan.Zero, TimeSpan.FromSeconds(10)) { this.apiClient = apiClient; }
public ISyncThingConnectionsWatcher CreateConnectionsWatcher(ISyncThingApiClient apiClient) { return(new SyncThingConnectionsWatcher(apiClient)); }
public SyncThingConnectionsWatcher(ISyncThingApiClient apiClient) : base(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10)) { this.apiClient = apiClient; }