Skip to content

Commit

Permalink
Merge pull request #8219 from DustinCampbell/handle-io-exceptions
Browse files Browse the repository at this point in the history
Handle UnauthorizedAccessExceptions in DirectoryHelper
  • Loading branch information
DustinCampbell authored Feb 8, 2023
2 parents 88aca56 + 3fc8c66 commit e92db20
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ internal static IEnumerable<string> GetFilteredFiles(
// This can also happen if the directory is too long for windows.
files = Array.Empty<string>();
}
catch (UnauthorizedAccessException ex)
{
logger?.LogWarning("UnauthorizedAccess: {exception}", ex.Message);
yield break;
}
catch (PathTooLongException ex)
{
logger?.LogWarning("PathTooLong: {exception}", ex.Message);
Expand Down Expand Up @@ -91,6 +96,11 @@ internal static IEnumerable<string> GetFilteredFiles(
// This can also happen if the directory is too long for windows.
directories = Array.Empty<string>();
}
catch (UnauthorizedAccessException ex)
{
logger?.LogWarning("UnauthorizedAccess: {exception}", ex.Message);
yield break;
}
catch (PathTooLongException ex)
{
logger?.LogWarning("PathTooLong: {exception}", ex.Message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer;

internal class MonitorProjectConfigurationFilePathEndpoint : IMonitorProjectConfigurationFilePathHandler, IDisposable
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly WorkspaceDirectoryPathResolver _workspaceDirectoryPathResolver;
private readonly IEnumerable<IProjectConfigurationFileChangeListener> _listeners;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
private readonly LanguageServerFeatureOptions _options;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger _logger;
private readonly ConcurrentDictionary<string, (string ConfigurationDirectory, IFileChangeDetector Detector)> _outputPathMonitors;
private readonly object _disposeLock;
Expand All @@ -29,21 +30,22 @@ internal class MonitorProjectConfigurationFilePathEndpoint : IMonitorProjectConf
public bool MutatesSolutionState => false;

public MonitorProjectConfigurationFilePathEndpoint(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
ProjectSnapshotManagerDispatcher dispatcher,
WorkspaceDirectoryPathResolver workspaceDirectoryPathResolver,
IEnumerable<IProjectConfigurationFileChangeListener> listeners,
LanguageServerFeatureOptions languageServerFeatureOptions,
LanguageServerFeatureOptions options,
ILoggerFactory loggerFactory)
{
if (languageServerFeatureOptions is null)
if (options is null)
{
throw new ArgumentNullException(nameof(languageServerFeatureOptions));
throw new ArgumentNullException(nameof(options));
}

_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_dispatcher = dispatcher;
_workspaceDirectoryPathResolver = workspaceDirectoryPathResolver;
_listeners = listeners;
_languageServerFeatureOptions = languageServerFeatureOptions;
_options = options;
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
_logger = loggerFactory.CreateLogger<MonitorProjectConfigurationFilePathEndpoint>();
_outputPathMonitors = new ConcurrentDictionary<string, (string, IFileChangeDetector)>(FilePathComparer.Instance);
_disposeLock = new object();
Expand Down Expand Up @@ -72,7 +74,7 @@ public async Task HandleNotificationAsync(MonitorProjectConfigurationFilePathPar
return;
}

if (!request.ConfigurationFilePath.EndsWith(_languageServerFeatureOptions.ProjectConfigurationFileName, StringComparison.Ordinal))
if (!request.ConfigurationFilePath.EndsWith(_options.ProjectConfigurationFileName, StringComparison.Ordinal))
{
_logger.LogError("Invalid configuration file path provided for project '{0}': '{1}'", request.ProjectFilePath, request.ConfigurationFilePath);
return;
Expand Down Expand Up @@ -188,8 +190,10 @@ public void Dispose()
}

// Protected virtual for testing
protected virtual IFileChangeDetector CreateFileChangeDetector() => new ProjectConfigurationFileChangeDetector(
_projectSnapshotManagerDispatcher,
_listeners,
_languageServerFeatureOptions);
protected virtual IFileChangeDetector CreateFileChangeDetector()
=> new ProjectConfigurationFileChangeDetector(
_dispatcher,
_listeners,
_options,
_loggerFactory);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer;

internal class ProjectConfigurationFileChangeDetector : IFileChangeDetector
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly IEnumerable<IProjectConfigurationFileChangeListener> _listeners;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
private readonly ILogger? _logger;
private readonly LanguageServerFeatureOptions _options;
private readonly ILogger _logger;
private FileSystemWatcher? _watcher;

private static readonly IReadOnlyCollection<string> s_ignoredDirectories = new string[]
Expand All @@ -29,30 +29,20 @@ internal class ProjectConfigurationFileChangeDetector : IFileChangeDetector
};

public ProjectConfigurationFileChangeDetector(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
ProjectSnapshotManagerDispatcher dispatcher,
IEnumerable<IProjectConfigurationFileChangeListener> listeners,
LanguageServerFeatureOptions languageServerFeatureOptions,
ILoggerFactory? loggerFactory = null)
LanguageServerFeatureOptions options,
ILoggerFactory loggerFactory)
{
if (projectSnapshotManagerDispatcher is null)
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
throw new ArgumentNullException(nameof(loggerFactory));
}

if (listeners is null)
{
throw new ArgumentNullException(nameof(listeners));
}

if (languageServerFeatureOptions is null)
{
throw new ArgumentNullException(nameof(languageServerFeatureOptions));
}

_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_listeners = listeners;
_languageServerFeatureOptions = languageServerFeatureOptions;
_logger = loggerFactory?.CreateLogger<ProjectConfigurationFileChangeDetector>();
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
_listeners = listeners ?? throw new ArgumentNullException(nameof(listeners));
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = loggerFactory.CreateLogger<ProjectConfigurationFileChangeDetector>();
}

public async Task StartAsync(string workspaceDirectory, CancellationToken cancellationToken)
Expand All @@ -67,7 +57,7 @@ public async Task StartAsync(string workspaceDirectory, CancellationToken cancel
workspaceDirectory = FilePathNormalizer.Normalize(workspaceDirectory);
var existingConfigurationFiles = GetExistingConfigurationFiles(workspaceDirectory);

await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(() =>
await _dispatcher.RunOnDispatcherThreadAsync(() =>
{
foreach (var configurationFilePath in existingConfigurationFiles)
{
Expand All @@ -84,7 +74,7 @@ await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(() =>
return;
}

_watcher = new RazorFileSystemWatcher(workspaceDirectory, _languageServerFeatureOptions.ProjectConfigurationFileName)
_watcher = new RazorFileSystemWatcher(workspaceDirectory, _options.ProjectConfigurationFileName)
{
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime,
IncludeSubdirectories = true,
Expand All @@ -97,12 +87,12 @@ await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(() =>
{
// Translate file renames into remove / add

if (args.OldFullPath.EndsWith(_languageServerFeatureOptions.ProjectConfigurationFileName, FilePathComparison.Instance))
if (args.OldFullPath.EndsWith(_options.ProjectConfigurationFileName, FilePathComparison.Instance))
{
// Renaming from project configuration file to something else. Just remove the configuration file.
FileSystemWatcher_ProjectConfigurationFileEvent_Background(args.OldFullPath, RazorFileChangeKind.Removed);
}
else if (args.FullPath.EndsWith(_languageServerFeatureOptions.ProjectConfigurationFileName, FilePathComparison.Instance))
else if (args.FullPath.EndsWith(_options.ProjectConfigurationFileName, FilePathComparison.Instance))
{
// Renaming from a non-project configuration file file to a real one. Just add the configuration file.
FileSystemWatcher_ProjectConfigurationFileEvent_Background(args.FullPath, RazorFileChangeKind.Added);
Expand All @@ -128,14 +118,18 @@ protected virtual void OnInitializationFinished()
// Protected virtual for testing
protected virtual IEnumerable<string> GetExistingConfigurationFiles(string workspaceDirectory)
{
var files = DirectoryHelper.GetFilteredFiles(workspaceDirectory, _languageServerFeatureOptions.ProjectConfigurationFileName, s_ignoredDirectories, logger: _logger);
using var _ = _logger.BeginScope("Searching for existing project configuration files");

return files;
return DirectoryHelper.GetFilteredFiles(
workspaceDirectory,
_options.ProjectConfigurationFileName,
s_ignoredDirectories,
logger: _logger);
}

private void FileSystemWatcher_ProjectConfigurationFileEvent_Background(string physicalFilePath, RazorFileChangeKind kind)
{
_ = _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
_ = _dispatcher.RunOnDispatcherThreadAsync(
() => FileSystemWatcher_ProjectConfigurationFileEvent(physicalFilePath, kind),
CancellationToken.None);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -40,7 +41,8 @@ public async Task StartAsync_NotifiesListenersOfExistingConfigurationFiles()
cts,
Dispatcher,
new[] { listener1.Object, listener2.Object },
existingConfigurationFiles);
existingConfigurationFiles,
LoggerFactory);

// Act
await detector.StartAsync("/some/workspace+directory", cts.Token);
Expand Down Expand Up @@ -77,10 +79,11 @@ private class TestProjectConfigurationFileChangeDetector : ProjectConfigurationF

public TestProjectConfigurationFileChangeDetector(
CancellationTokenSource cancellationTokenSource,
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
ProjectSnapshotManagerDispatcher dispatcher,
IEnumerable<IProjectConfigurationFileChangeListener> listeners,
IReadOnlyList<string> existingConfigurationFiles)
: base(projectSnapshotManagerDispatcher, listeners, TestLanguageServerFeatureOptions.Instance)
IReadOnlyList<string> existingConfigurationFiles,
ILoggerFactory loggerFactory)
: base(dispatcher, listeners, TestLanguageServerFeatureOptions.Instance, loggerFactory)
{
_cancellationTokenSource = cancellationTokenSource;
_existingConfigurationFiles = existingConfigurationFiles;
Expand Down

0 comments on commit e92db20

Please # to comment.