From 4a54342982c98d517a906b96f728ade2411be885 Mon Sep 17 00:00:00 2001 From: Daniel Klecha Date: Sun, 17 Mar 2024 22:10:57 +0100 Subject: [PATCH] support cancellation token, pass resolver options and cancellation token to all resolvers --- .../Commands/ScanCommand.cs | 24 ++++-- .../DotnetThirdPartyNotices.csproj | 2 +- ...{DynamicSettings.cs => ResolverOptions.cs} | 2 +- DotnetThirdPartyNotices/Program.cs | 1 - .../Services/GithubRawLicenseResolver.cs | 14 ++-- .../GithubRepositoryLicenseResolver.cs | 14 ++-- .../IFileVersionInfoLicenseResolver.cs | 5 +- .../Services/ILicenseService.cs | 2 +- .../Services/IUriLicenseResolver.cs | 8 +- .../Services/LicenseService.cs | 78 ++++++++++--------- .../Services/NetFrameworkLicenseResolver.cs | 11 +-- .../Services/OpenSourceOrgLicenseResolver.cs | 14 ++-- 12 files changed, 97 insertions(+), 78 deletions(-) rename DotnetThirdPartyNotices/Models/{DynamicSettings.cs => ResolverOptions.cs} (87%) diff --git a/DotnetThirdPartyNotices/Commands/ScanCommand.cs b/DotnetThirdPartyNotices/Commands/ScanCommand.cs index 0fec57a..369da83 100644 --- a/DotnetThirdPartyNotices/Commands/ScanCommand.cs +++ b/DotnetThirdPartyNotices/Commands/ScanCommand.cs @@ -28,7 +28,7 @@ internal class ScanCommand : Command AddOption(new Option("--configuration", () => "Release", "Project configuration to use")); } - internal new class Handler(ILogger logger, IProjectService projectService, ILicenseService licenseService, DynamicSettings dynamicSettings) : ICommandHandler + internal new class Handler(ILogger logger, IProjectService projectService, ILicenseService licenseService) : ICommandHandler { public string? ScanDir { get; set; } public string? OutputFilename { get; set; } @@ -47,9 +47,9 @@ public int Invoke(InvocationContext context) public async Task InvokeAsync(InvocationContext context) { + var cancellationToken = context.GetCancellationToken(); MSBuildLocator.RegisterDefaults(); ScanDir ??= Directory.GetCurrentDirectory(); - dynamicSettings.GitHubToken = GithubToken; var projectFilePaths = projectService.GetProjectFilePaths(ScanDir); projectFilePaths = GetFilteredProjectPathes(projectFilePaths); if (projectFilePaths.Length == 0) @@ -58,9 +58,9 @@ public async Task InvokeAsync(InvocationContext context) return 0; } foreach (var projectFilePath in projectFilePaths) - await ScanProjectAsync(projectFilePath); + await ScanProjectAsync(projectFilePath, cancellationToken); if (!CopyToOutDir) - await GenerateOutputFileAsync(OutputFilename); + await GenerateOutputFileAsync(OutputFilename, cancellationToken); return 0; } @@ -72,8 +72,9 @@ private string[] GetFilteredProjectPathes(string[] projectPathes) return projectPathes.Where(x => filterRegex.IsMatch(x)).ToArray(); } - private async Task ScanProjectAsync(string projectFilePath) + private async Task ScanProjectAsync(string projectFilePath, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); var stopWatch = new Stopwatch(); stopWatch.Start(); logger.LogInformation("Resolving files for {ProjectName} using {configuration} configuration...", Path.GetFileName(projectFilePath), Configuration ?? "Release"); @@ -84,6 +85,7 @@ private async Task ScanProjectAsync(string projectFilePath) logger.LogInformation("Resolved files count: {ResolvedFilesCount}", resolvedFiles.Count); foreach (var resolvedFileInfo in resolvedFiles) { + cancellationToken.ThrowIfCancellationRequested(); logger.LogInformation("Resolving license for {RelativeOutputPath}", resolvedFileInfo.RelativeOutputPath); if (resolvedFileInfo.NuSpec != null) { @@ -93,7 +95,11 @@ private async Task ScanProjectAsync(string projectFilePath) { logger.LogWarning("Package not found"); } - var licenseContent = await licenseService.ResolveFromResolvedFileInfo(resolvedFileInfo); + var resolverOptions = new ResolverOptions + { + GitHubToken = GithubToken + }; + var licenseContent = await licenseService.ResolveFromResolvedFileInfoAsync(resolvedFileInfo, resolverOptions, cancellationToken); if (licenseContent == null) { _unresolvedFiles.Add(resolvedFileInfo); @@ -107,11 +113,12 @@ private async Task ScanProjectAsync(string projectFilePath) stopWatch.Stop(); logger.LogInformation("Project {ProjectName} resolved in {StopwatchElapsedMilliseconds}ms", Path.GetFileName(projectFilePath), stopWatch.ElapsedMilliseconds); if (CopyToOutDir && !string.IsNullOrEmpty(ScanDir) && !string.IsNullOrEmpty(OutputFilename)) - await GenerateOutputFileAsync(Path.Combine(ScanDir, project.GetPropertyValue("OutDir"), Path.GetFileName(OutputFilename))); + await GenerateOutputFileAsync(Path.Combine(ScanDir, project.GetPropertyValue("OutDir"), Path.GetFileName(OutputFilename)), cancellationToken); } - private async Task GenerateOutputFileAsync(string? outputFilePath) + private async Task GenerateOutputFileAsync(string? outputFilePath, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); if (outputFilePath == null) return; var uniqueResolvedFilesCount = _licenseContents.Values.Sum(v => v.GroupBy(x => x.SourcePath).Count()); @@ -139,6 +146,7 @@ private async Task GenerateOutputFileAsync(string? outputFilePath) logger.LogInformation("Generate licenses in {StopwatchElapsedMilliseconds}ms", stopWatch.ElapsedMilliseconds); if (stringBuilder.Length == 0) return; + cancellationToken.ThrowIfCancellationRequested(); logger.LogInformation("Writing to {OutputFilename}...", outputFilePath); await System.IO.File.WriteAllTextAsync(outputFilePath, stringBuilder.ToString()); _licenseContents.Clear(); diff --git a/DotnetThirdPartyNotices/DotnetThirdPartyNotices.csproj b/DotnetThirdPartyNotices/DotnetThirdPartyNotices.csproj index 8a4b9a5..c2f12dc 100644 --- a/DotnetThirdPartyNotices/DotnetThirdPartyNotices.csproj +++ b/DotnetThirdPartyNotices/DotnetThirdPartyNotices.csproj @@ -10,7 +10,7 @@ dotnet-thirdpartynotices DotnetThirdPartyNotices A .NET tool to generate file with third party legal notices - 0.3.2 + 0.3.3 MIT git https://github.com/bugproof/DotnetThirdPartyNotices diff --git a/DotnetThirdPartyNotices/Models/DynamicSettings.cs b/DotnetThirdPartyNotices/Models/ResolverOptions.cs similarity index 87% rename from DotnetThirdPartyNotices/Models/DynamicSettings.cs rename to DotnetThirdPartyNotices/Models/ResolverOptions.cs index 2ac18ff..6238810 100644 --- a/DotnetThirdPartyNotices/Models/DynamicSettings.cs +++ b/DotnetThirdPartyNotices/Models/ResolverOptions.cs @@ -6,7 +6,7 @@ namespace DotnetThirdPartyNotices.Models; -internal class DynamicSettings +internal class ResolverOptions { public string? GitHubToken { get; set; } } diff --git a/DotnetThirdPartyNotices/Program.cs b/DotnetThirdPartyNotices/Program.cs index 279691f..1caf525 100644 --- a/DotnetThirdPartyNotices/Program.cs +++ b/DotnetThirdPartyNotices/Program.cs @@ -35,7 +35,6 @@ x.AddSingleton(); x.AddSingleton(); x.AddSingleton(); - x.AddSingleton(); x.AddHttpClient(); var assembly = Assembly.GetExecutingAssembly(); var types = assembly.GetTypes(); diff --git a/DotnetThirdPartyNotices/Services/GithubRawLicenseResolver.cs b/DotnetThirdPartyNotices/Services/GithubRawLicenseResolver.cs index 4f9fbdf..0f89d3f 100644 --- a/DotnetThirdPartyNotices/Services/GithubRawLicenseResolver.cs +++ b/DotnetThirdPartyNotices/Services/GithubRawLicenseResolver.cs @@ -4,23 +4,23 @@ namespace DotnetThirdPartyNotices.Services; -internal class GithubRawLicenseResolver(IHttpClientFactory httpClientFactory, DynamicSettings dynamicSettings) : ILicenseUriLicenseResolver +internal class GithubRawLicenseResolver(IHttpClientFactory httpClientFactory) : ILicenseUriLicenseResolver { - public bool CanResolve(Uri uri) => uri.Host == "github.com"; + public Task CanResolveAsync(Uri uri, ResolverOptions resolverOptions, CancellationToken cancellationToken) => Task.FromResult(uri.Host == "github.com"); - public async Task Resolve(Uri uri) + public async Task ResolveAsync(Uri uri, ResolverOptions resolverOptions, CancellationToken cancellationToken) { var uriBuilder = new UriBuilder(uri) { Host = "raw.githubusercontent.com" }; uriBuilder.Path = uriBuilder.Path.Replace("/blob", string.Empty); var httpClient = httpClientFactory.CreateClient(); // https://developer.github.com/v3/#user-agent-required httpClient.DefaultRequestHeaders.Add("User-Agent", "DotnetLicense"); - if (!string.IsNullOrEmpty(dynamicSettings.GitHubToken)) - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", dynamicSettings.GitHubToken); - var httpResponseMessage = await httpClient.GetAsync(uriBuilder.Uri); + if (!string.IsNullOrEmpty(resolverOptions.GitHubToken)) + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", resolverOptions.GitHubToken); + var httpResponseMessage = await httpClient.GetAsync(uriBuilder.Uri, cancellationToken); if (!httpResponseMessage.IsSuccessStatusCode || httpResponseMessage.Content.Headers.ContentType?.MediaType != "text/plain") return null; - return await httpResponseMessage.Content.ReadAsStringAsync(); + return await httpResponseMessage.Content.ReadAsStringAsync(cancellationToken); } } diff --git a/DotnetThirdPartyNotices/Services/GithubRepositoryLicenseResolver.cs b/DotnetThirdPartyNotices/Services/GithubRepositoryLicenseResolver.cs index 06fff25..cd33e23 100644 --- a/DotnetThirdPartyNotices/Services/GithubRepositoryLicenseResolver.cs +++ b/DotnetThirdPartyNotices/Services/GithubRepositoryLicenseResolver.cs @@ -8,11 +8,11 @@ namespace DotnetThirdPartyNotices.Services; -internal class GithubRepositoryLicenseResolver(IHttpClientFactory httpClientFactory, DynamicSettings dynamicSettings) : IProjectUriLicenseResolver, IRepositoryUriLicenseResolver +internal class GithubRepositoryLicenseResolver(IHttpClientFactory httpClientFactory) : IProjectUriLicenseResolver, IRepositoryUriLicenseResolver { - public bool CanResolve(Uri uri) => uri.Host == "github.com"; + public Task CanResolveAsync(Uri uri, ResolverOptions resolverOptions, CancellationToken cancellationToken) => Task.FromResult(uri.Host == "github.com"); - public async Task Resolve(Uri licenseUri) + public async Task ResolveAsync(Uri licenseUri, ResolverOptions resolverOptions, CancellationToken cancellationToken) { var repositoryPath = licenseUri.AbsolutePath.TrimEnd('/'); if (repositoryPath.EndsWith(".git")) @@ -24,12 +24,12 @@ internal class GithubRepositoryLicenseResolver(IHttpClientFactory httpClientFact }; // https://developer.github.com/v3/#user-agent-required httpClient.DefaultRequestHeaders.Add("User-Agent", "DotnetLicense"); - if (!string.IsNullOrEmpty(dynamicSettings.GitHubToken)) - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", dynamicSettings.GitHubToken); - var response = await httpClient.GetAsync(uriBuilder.Uri); + if (!string.IsNullOrEmpty(resolverOptions.GitHubToken)) + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", resolverOptions.GitHubToken); + var response = await httpClient.GetAsync(uriBuilder.Uri, cancellationToken); if (!response.IsSuccessStatusCode) return null; - var json = await response.Content.ReadAsStringAsync(); + var json = await response.Content.ReadAsStringAsync(cancellationToken); var jsonDocument = JsonDocument.Parse(json); var rootElement = jsonDocument.RootElement; var encoding = rootElement.GetProperty("encoding").GetString(); diff --git a/DotnetThirdPartyNotices/Services/IFileVersionInfoLicenseResolver.cs b/DotnetThirdPartyNotices/Services/IFileVersionInfoLicenseResolver.cs index 01ae395..e1dc371 100644 --- a/DotnetThirdPartyNotices/Services/IFileVersionInfoLicenseResolver.cs +++ b/DotnetThirdPartyNotices/Services/IFileVersionInfoLicenseResolver.cs @@ -1,3 +1,4 @@ +using DotnetThirdPartyNotices.Models; using System.Diagnostics; using System.Threading.Tasks; @@ -5,6 +6,6 @@ namespace DotnetThirdPartyNotices.Services; internal interface IFileVersionInfoLicenseResolver : ILicenseResolver { - bool CanResolve(FileVersionInfo fileVersionInfo); - Task Resolve(FileVersionInfo fileVersionInfo); + Task CanResolveAsync(FileVersionInfo fileVersionInfo, CancellationToken cancellationToken); + Task ResolveAsync(FileVersionInfo fileVersionInfo, ResolverOptions resolverOptions, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/DotnetThirdPartyNotices/Services/ILicenseService.cs b/DotnetThirdPartyNotices/Services/ILicenseService.cs index db30bf8..82e7a73 100644 --- a/DotnetThirdPartyNotices/Services/ILicenseService.cs +++ b/DotnetThirdPartyNotices/Services/ILicenseService.cs @@ -4,5 +4,5 @@ namespace DotnetThirdPartyNotices.Services; internal interface ILicenseService { - Task ResolveFromResolvedFileInfo(ResolvedFileInfo resolvedFileInfo); + Task ResolveFromResolvedFileInfoAsync(ResolvedFileInfo resolvedFileInfo, ResolverOptions resolverOptions, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/DotnetThirdPartyNotices/Services/IUriLicenseResolver.cs b/DotnetThirdPartyNotices/Services/IUriLicenseResolver.cs index f816542..d66efda 100644 --- a/DotnetThirdPartyNotices/Services/IUriLicenseResolver.cs +++ b/DotnetThirdPartyNotices/Services/IUriLicenseResolver.cs @@ -1,7 +1,9 @@ -namespace DotnetThirdPartyNotices.Services; +using DotnetThirdPartyNotices.Models; + +namespace DotnetThirdPartyNotices.Services; internal interface IUriLicenseResolver { - bool CanResolve(Uri licenseUri); - Task Resolve(Uri licenseUri); + Task CanResolveAsync(Uri licenseUri, ResolverOptions resolverOptions, CancellationToken cancellationToken); + Task ResolveAsync(Uri licenseUri, ResolverOptions resolverOptions, CancellationToken cancellationToken); } diff --git a/DotnetThirdPartyNotices/Services/LicenseService.cs b/DotnetThirdPartyNotices/Services/LicenseService.cs index c534dc4..2cfb683 100644 --- a/DotnetThirdPartyNotices/Services/LicenseService.cs +++ b/DotnetThirdPartyNotices/Services/LicenseService.cs @@ -8,21 +8,21 @@ internal partial class LicenseService(ILogger logger, IEnumerabl { private static readonly Dictionary LicenseCache = []; - public async Task ResolveFromResolvedFileInfo(ResolvedFileInfo resolvedFileInfo) + public async Task ResolveFromResolvedFileInfoAsync(ResolvedFileInfo resolvedFileInfo, ResolverOptions resolverOptions, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(resolvedFileInfo); if (resolvedFileInfo.NuSpec != null && LicenseCache.TryGetValue(resolvedFileInfo.NuSpec.Id, out string? value)) return value; - return (await ResolveFromLicenseRelativePathAsync(resolvedFileInfo)) - ?? (await ResolveFromLicenseUrlAsync(resolvedFileInfo, false)) - ?? (await ResolveFromRepositoryUrlAsync(resolvedFileInfo, false)) - ?? (await ResolveFromProjectUrlAsync(resolvedFileInfo, false)) - ?? (await ResolveFromPackagePathAsync(resolvedFileInfo)) - ?? (await ResolveFromSourcePathAsync(resolvedFileInfo)) - ?? (await ResolveFromFileVersionInfoAsync(resolvedFileInfo)) - ?? (await ResolveFromLicenseUrlAsync(resolvedFileInfo, true)) - ?? (await ResolveFromRepositoryUrlAsync(resolvedFileInfo, true)) - ?? (await ResolveFromProjectUrlAsync(resolvedFileInfo, true)); + return (await ResolveFromLicenseRelativePathAsync(resolvedFileInfo, cancellationToken)) + ?? (await ResolveFromLicenseUrlAsync(resolvedFileInfo, false, resolverOptions, cancellationToken)) + ?? (await ResolveFromRepositoryUrlAsync(resolvedFileInfo, false, resolverOptions, cancellationToken)) + ?? (await ResolveFromProjectUrlAsync(resolvedFileInfo, false, resolverOptions, cancellationToken)) + ?? (await ResolveFromPackagePathAsync(resolvedFileInfo, cancellationToken)) + ?? (await ResolveFromSourcePathAsync(resolvedFileInfo, cancellationToken)) + ?? (await ResolveFromFileVersionInfoAsync(resolvedFileInfo, resolverOptions, cancellationToken)) + ?? (await ResolveFromLicenseUrlAsync(resolvedFileInfo, true, resolverOptions, cancellationToken)) + ?? (await ResolveFromRepositoryUrlAsync(resolvedFileInfo, true, resolverOptions, cancellationToken)) + ?? (await ResolveFromProjectUrlAsync(resolvedFileInfo, true, resolverOptions, cancellationToken)); } private static string? UnifyLicense(string? license) @@ -50,8 +50,9 @@ internal partial class LicenseService(ILogger logger, IEnumerabl return license; } - private async Task ResolveFromPackagePathAsync(ResolvedFileInfo resolvedFileInfo) + private async Task ResolveFromPackagePathAsync(ResolvedFileInfo resolvedFileInfo, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(resolvedFileInfo.PackagePath)) return null; if (LicenseCache.TryGetValue(resolvedFileInfo.PackagePath, out string? value)) @@ -73,8 +74,9 @@ internal partial class LicenseService(ILogger logger, IEnumerabl return license; } - private async Task ResolveFromSourcePathAsync(ResolvedFileInfo resolvedFileInfo) + private async Task ResolveFromSourcePathAsync(ResolvedFileInfo resolvedFileInfo, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(resolvedFileInfo.SourcePath)) return null; if (LicenseCache.TryGetValue(resolvedFileInfo.SourcePath, out string? value)) @@ -107,7 +109,7 @@ internal partial class LicenseService(ILogger logger, IEnumerabl } } - private async Task ResolveFromFileVersionInfoAsync(ResolvedFileInfo resolvedFileInfo) + private async Task ResolveFromFileVersionInfoAsync(ResolvedFileInfo resolvedFileInfo, ResolverOptions resolverOptions, CancellationToken cancellationToken) { if(resolvedFileInfo.VersionInfo == null) return null; @@ -117,9 +119,9 @@ internal partial class LicenseService(ILogger logger, IEnumerabl { foreach (var resolver in fileVersionInfoLicenseResolvers) { - if (!resolver.CanResolve(resolvedFileInfo.VersionInfo)) + if (!await resolver.CanResolveAsync(resolvedFileInfo.VersionInfo, cancellationToken)) continue; - var license = await resolver.Resolve(resolvedFileInfo.VersionInfo); + var license = await resolver.ResolveAsync(resolvedFileInfo.VersionInfo, resolverOptions, cancellationToken); license = UnifyLicense(license); if (license != null) { @@ -136,8 +138,9 @@ internal partial class LicenseService(ILogger logger, IEnumerabl } } - private async Task ResolveFromLicenseRelativePathAsync(ResolvedFileInfo resolvedFileInfo) + private async Task ResolveFromLicenseRelativePathAsync(ResolvedFileInfo resolvedFileInfo, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(resolvedFileInfo.PackagePath) || string.IsNullOrEmpty(resolvedFileInfo.NuSpec?.LicenseRelativePath)) return null; var licenseFullPath = Path.Combine(resolvedFileInfo.PackagePath, resolvedFileInfo.NuSpec.LicenseRelativePath); @@ -163,7 +166,7 @@ internal partial class LicenseService(ILogger logger, IEnumerabl } } - private async Task ResolveFromProjectUrlAsync(ResolvedFileInfo resolvedFileInfo, bool useFinalUrl) + private async Task ResolveFromProjectUrlAsync(ResolvedFileInfo resolvedFileInfo, bool useFinalUrl, ResolverOptions resolverOptions, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(resolvedFileInfo.NuSpec?.ProjectUrl)) return null; @@ -174,8 +177,8 @@ internal partial class LicenseService(ILogger logger, IEnumerabl try { var license = useFinalUrl - ? await ResolveFromFinalUrlAsync(uri, projectUriLicenseResolvers) - : await ResolveFromUrlAsync(uri, projectUriLicenseResolvers); + ? await ResolveFromFinalUrlAsync(uri, projectUriLicenseResolvers, resolverOptions, cancellationToken) + : await ResolveFromUrlAsync(uri, projectUriLicenseResolvers, resolverOptions, cancellationToken); license = UnifyLicense(license); if (license == null) return null; @@ -190,30 +193,32 @@ internal partial class LicenseService(ILogger logger, IEnumerabl } } - private async Task ResolveFromUrlAsync(Uri uri, IEnumerable urlLicenseResolvers) + private async Task ResolveFromUrlAsync(Uri uri, IEnumerable urlLicenseResolvers, ResolverOptions resolverOptions, CancellationToken cancellationToken) { foreach (var resolver in urlLicenseResolvers) { - if (!resolver.CanResolve(uri)) + if (!await resolver.CanResolveAsync(uri, resolverOptions, cancellationToken)) continue; - var license = await resolver.Resolve(uri); + var license = await resolver.ResolveAsync(uri, resolverOptions, cancellationToken); license = UnifyLicense(license); if (license != null) return license; + cancellationToken.ThrowIfCancellationRequested(); } return null; } - private async Task ResolveFromFinalUrlAsync(Uri uri, IEnumerable urlLicenseResolvers) + private async Task ResolveFromFinalUrlAsync(Uri uri, IEnumerable urlLicenseResolvers, ResolverOptions resolverOptions, CancellationToken cancellationToken) { using var httpClient = httpClientFactory.CreateClient(); - var httpResponseMessage = await httpClient.GetAsync(uri); + var httpResponseMessage = await httpClient.GetAsync(uri, cancellationToken); if (!httpResponseMessage.IsSuccessStatusCode && uri.AbsolutePath.EndsWith(".txt")) { + cancellationToken.ThrowIfCancellationRequested(); // try without .txt extension var fixedUri = new UriBuilder(uri); fixedUri.Path = fixedUri.Path.Remove(fixedUri.Path.Length - 4); - httpResponseMessage = await httpClient.GetAsync(fixedUri.Uri); + httpResponseMessage = await httpClient.GetAsync(fixedUri.Uri, cancellationToken); if (!httpResponseMessage.IsSuccessStatusCode) return null; } @@ -221,22 +226,24 @@ internal partial class LicenseService(ILogger logger, IEnumerabl { foreach (var resolver in urlLicenseResolvers) { - if (!resolver.CanResolve(httpResponseMessage.RequestMessage.RequestUri)) + if (!await resolver.CanResolveAsync(httpResponseMessage.RequestMessage.RequestUri, resolverOptions, cancellationToken)) continue; - var license = await resolver.Resolve(httpResponseMessage.RequestMessage.RequestUri); + var license = await resolver.ResolveAsync(httpResponseMessage.RequestMessage.RequestUri, resolverOptions, cancellationToken); license = UnifyLicense(license); if (license != null) return license; + cancellationToken.ThrowIfCancellationRequested(); } } // Finally, if no license uri can be found despite all the redirects, try to blindly get it if (httpResponseMessage.Content.Headers.ContentType?.MediaType != "text/plain") return null; - var license2 = await httpResponseMessage.Content.ReadAsStringAsync(); + var license2 = await httpResponseMessage.Content.ReadAsStringAsync(cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); return UnifyLicense(license2); } - private async Task ResolveFromRepositoryUrlAsync(ResolvedFileInfo resolvedFileInfo, bool useFinalUrl) + private async Task ResolveFromRepositoryUrlAsync(ResolvedFileInfo resolvedFileInfo, bool useFinalUrl, ResolverOptions resolverOptions, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(resolvedFileInfo.NuSpec?.RepositoryUrl)) return null; @@ -247,8 +254,8 @@ internal partial class LicenseService(ILogger logger, IEnumerabl try { var license = useFinalUrl - ? await ResolveFromFinalUrlAsync(uri, repositoryUriLicenseResolvers) - : await ResolveFromUrlAsync(uri, repositoryUriLicenseResolvers); + ? await ResolveFromFinalUrlAsync(uri, repositoryUriLicenseResolvers, resolverOptions, cancellationToken) + : await ResolveFromUrlAsync(uri, repositoryUriLicenseResolvers, resolverOptions, cancellationToken); license = UnifyLicense(license); if (license == null) return null; @@ -263,8 +270,9 @@ internal partial class LicenseService(ILogger logger, IEnumerabl } } - private async Task ResolveFromLicenseUrlAsync(ResolvedFileInfo resolvedFileInfo, bool useFinalUrl) + private async Task ResolveFromLicenseUrlAsync(ResolvedFileInfo resolvedFileInfo, bool useFinalUrl, ResolverOptions resolverOptions, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(resolvedFileInfo.NuSpec?.LicenseUrl)) return null; if (LicenseCache.TryGetValue(resolvedFileInfo.NuSpec.LicenseUrl, out string? value)) @@ -274,8 +282,8 @@ internal partial class LicenseService(ILogger logger, IEnumerabl try { var license = useFinalUrl - ? await ResolveFromFinalUrlAsync(uri, licenseUriLicenseResolvers) - : await ResolveFromUrlAsync(uri, licenseUriLicenseResolvers); + ? await ResolveFromFinalUrlAsync(uri, licenseUriLicenseResolvers, resolverOptions, cancellationToken) + : await ResolveFromUrlAsync(uri, licenseUriLicenseResolvers, resolverOptions, cancellationToken); license = UnifyLicense(license); if (license == null) return null; diff --git a/DotnetThirdPartyNotices/Services/NetFrameworkLicenseResolver.cs b/DotnetThirdPartyNotices/Services/NetFrameworkLicenseResolver.cs index f5ad7d3..2ea1f55 100644 --- a/DotnetThirdPartyNotices/Services/NetFrameworkLicenseResolver.cs +++ b/DotnetThirdPartyNotices/Services/NetFrameworkLicenseResolver.cs @@ -1,4 +1,5 @@ -using System; +using DotnetThirdPartyNotices.Models; +using System; using System.Diagnostics; using System.IO; using System.Reflection; @@ -10,11 +11,11 @@ internal class NetFrameworkLicenseResolver : ILicenseUriLicenseResolver, IFileVe { private static string? _licenseContent; - public bool CanResolve(Uri licenseUri) => licenseUri.ToString().Contains("LinkId=529443"); - public bool CanResolve(FileVersionInfo fileVersionInfo) => fileVersionInfo.ProductName == "Microsoft® .NET Framework"; + public Task CanResolveAsync(Uri licenseUri, ResolverOptions resolverOptions, CancellationToken cancellationToken) => Task.FromResult(licenseUri.ToString().Contains("LinkId=529443")); + public Task CanResolveAsync(FileVersionInfo fileVersionInfo, CancellationToken cancellationToken) => Task.FromResult(fileVersionInfo.ProductName == "Microsoft® .NET Framework"); - public Task Resolve(Uri licenseUri) => GetLicenseContent(); - public Task Resolve(FileVersionInfo fileVersionInfo) => GetLicenseContent(); + public Task ResolveAsync(Uri licenseUri, ResolverOptions resolverOptions, CancellationToken cancellationToken) => GetLicenseContent(); + public Task ResolveAsync(FileVersionInfo fileVersionInfo, ResolverOptions resolverOptions, CancellationToken cancellationToken) => GetLicenseContent(); public async Task GetLicenseContent() { diff --git a/DotnetThirdPartyNotices/Services/OpenSourceOrgLicenseResolver.cs b/DotnetThirdPartyNotices/Services/OpenSourceOrgLicenseResolver.cs index af96c9b..b7e4134 100644 --- a/DotnetThirdPartyNotices/Services/OpenSourceOrgLicenseResolver.cs +++ b/DotnetThirdPartyNotices/Services/OpenSourceOrgLicenseResolver.cs @@ -7,13 +7,13 @@ namespace DotnetThirdPartyNotices.Services; -internal class OpenSourceOrgLicenseResolver(DynamicSettings dynamicSettings) : ILicenseUriLicenseResolver +internal class OpenSourceOrgLicenseResolver : ILicenseUriLicenseResolver { - public bool CanResolve(Uri licenseUri) => licenseUri.Host == "opensource.org"; + public Task CanResolveAsync(Uri licenseUri, ResolverOptions resolverOptions, CancellationToken cancellationToken) => Task.FromResult(licenseUri.Host == "opensource.org"); internal static readonly char[] separator = ['/']; - public async Task Resolve(Uri licenseUri) + public async Task ResolveAsync(Uri licenseUri, ResolverOptions resolverOptions, CancellationToken cancellationToken) { var s = licenseUri.AbsolutePath.Split(separator, StringSplitOptions.RemoveEmptyEntries); if (s[0] != "licenses") @@ -22,12 +22,12 @@ internal class OpenSourceOrgLicenseResolver(DynamicSettings dynamicSettings) : I HttpClient httpClient = new() { BaseAddress = new Uri("https://api.github.com") }; // https://developer.github.com/v3/#user-agent-required httpClient.DefaultRequestHeaders.Add("User-Agent", "DotnetLicense"); - if (!string.IsNullOrEmpty(dynamicSettings.GitHubToken)) - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", dynamicSettings.GitHubToken); - var httpResponseMessage = await httpClient.GetAsync($"licenses/{licenseId}"); + if (!string.IsNullOrEmpty(resolverOptions.GitHubToken)) + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", resolverOptions.GitHubToken); + var httpResponseMessage = await httpClient.GetAsync($"licenses/{licenseId}", cancellationToken); if (!httpResponseMessage.IsSuccessStatusCode) return null; - var content = await httpResponseMessage.Content.ReadAsStringAsync(); + var content = await httpResponseMessage.Content.ReadAsStringAsync(cancellationToken); var jsonDocument = JsonDocument.Parse(content); return jsonDocument.RootElement.GetProperty("body").GetString(); }