diff --git a/src/Shared/Contracts/IBaseProjectManager.cs b/src/Shared/Contracts/IBaseProjectManager.cs
index d2745264..d2eaf23f 100644
--- a/src/Shared/Contracts/IBaseProjectManager.cs
+++ b/src/Shared/Contracts/IBaseProjectManager.cs
@@ -20,6 +20,8 @@ public interface IBaseProjectManager
///
public string ManagerType { get; }
+ public TimeSpan? Timeout { get; }
+
///
/// Per-object option container.
///
diff --git a/src/Shared/Contracts/IProjectManagerFactory.cs b/src/Shared/Contracts/IProjectManagerFactory.cs
index 2fff38e5..67f1b3a7 100644
--- a/src/Shared/Contracts/IProjectManagerFactory.cs
+++ b/src/Shared/Contracts/IProjectManagerFactory.cs
@@ -4,6 +4,7 @@ namespace Microsoft.CST.OpenSource.Contracts;
using PackageManagers;
using PackageUrl;
+using System;
public interface IProjectManagerFactory
{
@@ -12,6 +13,7 @@ public interface IProjectManagerFactory
///
/// The for the package to create the project manager for.
/// The new destination directory, if provided.
+ /// The to wait before the request times out.
/// The implementation of for this 's type.
- IBaseProjectManager? CreateProjectManager(PackageURL purl, string destinationDirectory = ".");
+ IBaseProjectManager? CreateProjectManager(PackageURL purl, string destinationDirectory = ".", TimeSpan? timeout = null);
}
\ No newline at end of file
diff --git a/src/Shared/PackageManagers/BaseProjectManager.cs b/src/Shared/PackageManagers/BaseProjectManager.cs
index 280aa375..666fd175 100644
--- a/src/Shared/PackageManagers/BaseProjectManager.cs
+++ b/src/Shared/PackageManagers/BaseProjectManager.cs
@@ -25,18 +25,22 @@ public abstract class BaseProjectManager : IBaseProjectManager
///
public abstract string ManagerType { get; }
+ ///
+ public TimeSpan? Timeout { get; } = null;
+
///
/// Initializes a new instance of the class.
///
- public BaseProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory = ".")
+ public BaseProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory = ".", TimeSpan? timeout = null)
{
EnvironmentHelper.OverrideEnvironmentVariables(this);
Options = new Dictionary();
TopLevelExtractionDirectory = destinationDirectory;
HttpClientFactory = httpClientFactory;
+ Timeout = timeout;
}
- public BaseProjectManager(string destinationDirectory = ".") : this(new DefaultHttpClientFactory(), destinationDirectory)
+ public BaseProjectManager(string destinationDirectory = ".", TimeSpan? timeout = null) : this(new DefaultHttpClientFactory(), destinationDirectory, timeout)
{
}
@@ -524,7 +528,12 @@ protected virtual Task> SearchRepoUrlsInPackageMe
protected HttpClient CreateHttpClient()
{
- return HttpClientFactory.CreateClient(GetType().Name);
+ HttpClient client = HttpClientFactory.CreateClient(GetType().Name);
+ if (Timeout.HasValue)
+ {
+ client.Timeout = Timeout.Value;
+ }
+ return client;
}
}
}
diff --git a/src/Shared/PackageManagers/CPANProjectManager.cs b/src/Shared/PackageManagers/CPANProjectManager.cs
index 4513e049..db243eec 100644
--- a/src/Shared/PackageManagers/CPANProjectManager.cs
+++ b/src/Shared/PackageManagers/CPANProjectManager.cs
@@ -34,11 +34,11 @@ internal class CPANProjectManager : BaseProjectManager
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Modified through reflection.")]
public string ENV_CPAN_API_ENDPOINT { get; set; } = "https://fastapi.metacpan.org";
- public CPANProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public CPANProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public CPANProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public CPANProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/CRANProjectManager.cs b/src/Shared/PackageManagers/CRANProjectManager.cs
index a0c59c67..98d24b5b 100644
--- a/src/Shared/PackageManagers/CRANProjectManager.cs
+++ b/src/Shared/PackageManagers/CRANProjectManager.cs
@@ -26,11 +26,11 @@ internal class CRANProjectManager : BaseProjectManager
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Modified through reflection.")]
public string ENV_CRAN_ENDPOINT { get; set; } = "https://cran.r-project.org";
- public CRANProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public CRANProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public CRANProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public CRANProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/CargoProjectManager.cs b/src/Shared/PackageManagers/CargoProjectManager.cs
index 513de0a3..1361db3d 100644
--- a/src/Shared/PackageManagers/CargoProjectManager.cs
+++ b/src/Shared/PackageManagers/CargoProjectManager.cs
@@ -42,8 +42,9 @@ public class CargoProjectManager : TypedManager? actions = null,
- IHttpClientFactory? httpClientFactory = null)
- : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory)
+ IHttpClientFactory? httpClientFactory = null,
+ TimeSpan? timeout = null)
+ : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/CocoapodsProjectManager.cs b/src/Shared/PackageManagers/CocoapodsProjectManager.cs
index b12e82bf..29093360 100644
--- a/src/Shared/PackageManagers/CocoapodsProjectManager.cs
+++ b/src/Shared/PackageManagers/CocoapodsProjectManager.cs
@@ -17,6 +17,7 @@ namespace Microsoft.CST.OpenSource.PackageManagers
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
+ using System.Threading;
using System.Threading.Tasks;
internal class CocoapodsProjectManager : BaseProjectManager
@@ -38,11 +39,11 @@ internal class CocoapodsProjectManager : BaseProjectManager
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Modified through reflection.")]
public string ENV_COCOAPODS_METADATA_ENDPOINT { get; set; } = "https://cocoapods.org";
- public CocoapodsProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public CocoapodsProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public CocoapodsProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public CocoapodsProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/ComposerProjectManager.cs b/src/Shared/PackageManagers/ComposerProjectManager.cs
index e4e200c1..5d13b157 100644
--- a/src/Shared/PackageManagers/ComposerProjectManager.cs
+++ b/src/Shared/PackageManagers/ComposerProjectManager.cs
@@ -26,11 +26,11 @@ internal class ComposerProjectManager : BaseProjectManager
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Modified through reflection.")]
public string ENV_COMPOSER_ENDPOINT { get; set; } = "https://repo.packagist.org";
- public ComposerProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public ComposerProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public ComposerProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public ComposerProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/CondaProjectManager.cs b/src/Shared/PackageManagers/CondaProjectManager.cs
index a299e3e4..95abe991 100644
--- a/src/Shared/PackageManagers/CondaProjectManager.cs
+++ b/src/Shared/PackageManagers/CondaProjectManager.cs
@@ -33,8 +33,9 @@ public class CondaProjectManager : TypedManager? actions = null,
- IHttpClientFactory? httpClientFactory = null)
- : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory)
+ IHttpClientFactory? httpClientFactory = null,
+ TimeSpan? timeout = null)
+ : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/GemProjectManager.cs b/src/Shared/PackageManagers/GemProjectManager.cs
index b457b408..85a0ba0a 100644
--- a/src/Shared/PackageManagers/GemProjectManager.cs
+++ b/src/Shared/PackageManagers/GemProjectManager.cs
@@ -27,11 +27,11 @@ internal class GemProjectManager : BaseProjectManager
public string ENV_RUBYGEMS_ENDPOINT { get; set; } = "https://rubygems.org";
public string ENV_RUBYGEMS_ENDPOINT_API { get; set; } = "https://api.rubygems.org";
- public GemProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public GemProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public GemProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public GemProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/GitHubProjectManager.cs b/src/Shared/PackageManagers/GitHubProjectManager.cs
index 95c5de73..5e968460 100644
--- a/src/Shared/PackageManagers/GitHubProjectManager.cs
+++ b/src/Shared/PackageManagers/GitHubProjectManager.cs
@@ -28,11 +28,11 @@ internal class GitHubProjectManager : BaseProjectManager
private const string DEFAULT_GITHUB_ENDPOINT = "https://github.com";
public string ENV_GITHUB_ENDPOINT { get; set; } = DEFAULT_GITHUB_ENDPOINT;
- public GitHubProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public GitHubProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public GitHubProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public GitHubProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/GolangProjectManager.cs b/src/Shared/PackageManagers/GolangProjectManager.cs
index 380aa4ef..7067adb9 100644
--- a/src/Shared/PackageManagers/GolangProjectManager.cs
+++ b/src/Shared/PackageManagers/GolangProjectManager.cs
@@ -37,8 +37,9 @@ public class GolangProjectManager : TypedManager? actions = null,
- IHttpClientFactory? httpClientFactory = null)
- : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory)
+ IHttpClientFactory? httpClientFactory = null,
+ TimeSpan? timeout = null)
+ : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/HackageProjectManager.cs b/src/Shared/PackageManagers/HackageProjectManager.cs
index e339014f..9eda543e 100644
--- a/src/Shared/PackageManagers/HackageProjectManager.cs
+++ b/src/Shared/PackageManagers/HackageProjectManager.cs
@@ -26,11 +26,11 @@ internal class HackageProjectManager : BaseProjectManager
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Modified through reflection.")]
public string ENV_HACKAGE_ENDPOINT { get; set; } = "https://hackage.haskell.org";
- public HackageProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public HackageProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public HackageProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public HackageProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/MavenProjectManager.cs b/src/Shared/PackageManagers/MavenProjectManager.cs
index 75bb6844..fca738b7 100644
--- a/src/Shared/PackageManagers/MavenProjectManager.cs
+++ b/src/Shared/PackageManagers/MavenProjectManager.cs
@@ -33,8 +33,9 @@ public class MavenProjectManager : TypedManager? actions = null,
- IHttpClientFactory? httpClientFactory = null)
- : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory)
+ IHttpClientFactory? httpClientFactory = null,
+ TimeSpan? timeout = null)
+ : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/NPMProjectManager.cs b/src/Shared/PackageManagers/NPMProjectManager.cs
index 4c2541e8..c8fb0369 100644
--- a/src/Shared/PackageManagers/NPMProjectManager.cs
+++ b/src/Shared/PackageManagers/NPMProjectManager.cs
@@ -43,8 +43,9 @@ public class NPMProjectManager : TypedManager? actions = null,
- IHttpClientFactory? httpClientFactory = null)
- : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory)
+ IHttpClientFactory? httpClientFactory = null,
+ TimeSpan? timeout = null)
+ : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory, timeout)
{
}
@@ -129,6 +130,11 @@ public override async Task> DownloadVersionAsync(PackageURL
downloadedPaths.Add(extractionPath);
}
}
+ catch (TaskCanceledException ex)
+ {
+ Logger.Debug(ex, "Downloading NPM package timed out: {0}", ex.Message);
+ throw;
+ }
catch (Exception ex)
{
Logger.Debug(ex, "Error downloading NPM package: {0}", ex.Message);
diff --git a/src/Shared/PackageManagers/NuGetProjectManager.cs b/src/Shared/PackageManagers/NuGetProjectManager.cs
index 9518e8ec..077413ea 100644
--- a/src/Shared/PackageManagers/NuGetProjectManager.cs
+++ b/src/Shared/PackageManagers/NuGetProjectManager.cs
@@ -46,8 +46,9 @@ public class NuGetProjectManager : TypedManager? actions = null,
- IHttpClientFactory? httpClientFactory = null)
- : base(actions ?? new NuGetPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory)
+ IHttpClientFactory? httpClientFactory = null,
+ TimeSpan? timeout = null)
+ : base(actions ?? new NuGetPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory, timeout)
{
GetRegistrationEndpointAsync().Wait();
}
diff --git a/src/Shared/PackageManagers/ProjectManagerFactory.cs b/src/Shared/PackageManagers/ProjectManagerFactory.cs
index ee766fe5..d7acffa1 100644
--- a/src/Shared/PackageManagers/ProjectManagerFactory.cs
+++ b/src/Shared/PackageManagers/ProjectManagerFactory.cs
@@ -17,13 +17,14 @@ public class ProjectManagerFactory : IProjectManagerFactory
///
/// The only runtime parameter we need is the destination directory. Everything else can be defined in the constructor call itself.
/// The destination that any files should be saved to when downloading from this ProjectManager, defaults to the runtime directory.
+ /// The to wait before the request times out.
/// An implementation of the class, or null if unable to construct.
///
/// destinationDirectory =>
/// new NPMProjectManager(httpClientFactory, destinationDirectory)
///
/// Example implementations in GetDefaultManagers(IHttpClientFactory?)
- public delegate BaseProjectManager? ConstructProjectManager(string destinationDirectory = ".");
+ public delegate BaseProjectManager? ConstructProjectManager(string destinationDirectory = ".", TimeSpan? timeout = null);
///
/// The dictionary of project managers.
@@ -61,82 +62,82 @@ public static Dictionary GetDefaultManagers(IHt
return new Dictionary(StringComparer.InvariantCultureIgnoreCase)
{
{
- CargoProjectManager.Type, destinationDirectory =>
- new CargoProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory)
+ CargoProjectManager.Type, (destinationDirectory, timeout) =>
+ new CargoProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory, timeout)
},
{
- CocoapodsProjectManager.Type, destinationDirectory =>
- new CocoapodsProjectManager(httpClientFactory, destinationDirectory)
+ CocoapodsProjectManager.Type, (destinationDirectory, timeout) =>
+ new CocoapodsProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- ComposerProjectManager.Type, destinationDirectory =>
- new ComposerProjectManager(httpClientFactory, destinationDirectory)
+ ComposerProjectManager.Type, (destinationDirectory, timeout) =>
+ new ComposerProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- CondaProjectManager.Type, destinationDirectory =>
- new CondaProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory)
+ CondaProjectManager.Type, (destinationDirectory, timeout) =>
+ new CondaProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory, timeout)
},
{
- CPANProjectManager.Type, destinationDirectory =>
- new CPANProjectManager(httpClientFactory, destinationDirectory)
+ CPANProjectManager.Type, (destinationDirectory, timeout) =>
+ new CPANProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- CRANProjectManager.Type, destinationDirectory =>
- new CRANProjectManager(httpClientFactory, destinationDirectory)
+ CRANProjectManager.Type, (destinationDirectory, timeout) =>
+ new CRANProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- GemProjectManager.Type, destinationDirectory =>
- new GemProjectManager(httpClientFactory, destinationDirectory)
+ GemProjectManager.Type, (destinationDirectory, timeout) =>
+ new GemProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- GitHubProjectManager.Type, destinationDirectory =>
- new GitHubProjectManager(httpClientFactory, destinationDirectory)
+ GitHubProjectManager.Type, (destinationDirectory, timeout) =>
+ new GitHubProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- GolangProjectManager.Type, destinationDirectory =>
- new GolangProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory)
+ GolangProjectManager.Type, (destinationDirectory, timeout) =>
+ new GolangProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory, timeout)
},
{
- HackageProjectManager.Type, destinationDirectory =>
- new HackageProjectManager(httpClientFactory, destinationDirectory)
+ HackageProjectManager.Type, (destinationDirectory, timeout) =>
+ new HackageProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- MavenProjectManager.Type, destinationDirectory =>
- new MavenProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory)
+ MavenProjectManager.Type, (destinationDirectory, timeout) =>
+ new MavenProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory, timeout)
},
{
- NPMProjectManager.Type, destinationDirectory =>
- new NPMProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory)
+ NPMProjectManager.Type, (destinationDirectory, timeout) =>
+ new NPMProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory, timeout)
},
{
- NuGetProjectManager.Type, destinationDirectory =>
- new NuGetProjectManager(destinationDirectory, new NuGetPackageActions(), httpClientFactory) // Add the NuGetPackageActions to the NuGetProjectManager.
+ NuGetProjectManager.Type, (destinationDirectory, timeout) =>
+ new NuGetProjectManager(destinationDirectory, new NuGetPackageActions(), httpClientFactory, timeout) // Add the NuGetPackageActions to the NuGetProjectManager.
},
{
- PyPIProjectManager.Type, destinationDirectory =>
- new PyPIProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory)
+ PyPIProjectManager.Type, (destinationDirectory, timeout) =>
+ new PyPIProjectManager(destinationDirectory, new NoOpPackageActions(), httpClientFactory, timeout)
},
{
- UbuntuProjectManager.Type, destinationDirectory =>
- new UbuntuProjectManager(httpClientFactory, destinationDirectory)
+ UbuntuProjectManager.Type, (destinationDirectory, timeout) =>
+ new UbuntuProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- URLProjectManager.Type, destinationDirectory =>
- new URLProjectManager(httpClientFactory, destinationDirectory)
+ URLProjectManager.Type, (destinationDirectory, timeout) =>
+ new URLProjectManager(httpClientFactory, destinationDirectory, timeout)
},
{
- VSMProjectManager.Type, destinationDirectory =>
- new VSMProjectManager(httpClientFactory, destinationDirectory)
+ VSMProjectManager.Type, (destinationDirectory, timeout) =>
+ new VSMProjectManager(httpClientFactory, destinationDirectory, timeout)
},
};
}
///
- public IBaseProjectManager? CreateProjectManager(PackageURL purl, string destinationDirectory = ".")
+ public IBaseProjectManager? CreateProjectManager(PackageURL purl, string destinationDirectory = ".", TimeSpan? timeout = null)
{
ConstructProjectManager? projectManager = _projectManagers.GetValueOrDefault(purl.Type);
- return projectManager?.Invoke(destinationDirectory);
+ return projectManager?.Invoke(destinationDirectory, timeout);
}
///
diff --git a/src/Shared/PackageManagers/PyPIProjectManager.cs b/src/Shared/PackageManagers/PyPIProjectManager.cs
index 37656938..4178b447 100644
--- a/src/Shared/PackageManagers/PyPIProjectManager.cs
+++ b/src/Shared/PackageManagers/PyPIProjectManager.cs
@@ -37,8 +37,9 @@ public class PyPIProjectManager : TypedManager? actions = null,
- IHttpClientFactory? httpClientFactory = null)
- : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory)
+ IHttpClientFactory? httpClientFactory = null,
+ TimeSpan? timeout = null)
+ : base(actions ?? new NoOpPackageActions(), httpClientFactory ?? new DefaultHttpClientFactory(), directory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/TypedManager.cs b/src/Shared/PackageManagers/TypedManager.cs
index 6f7db4ec..bed2be4a 100644
--- a/src/Shared/PackageManagers/TypedManager.cs
+++ b/src/Shared/PackageManagers/TypedManager.cs
@@ -35,7 +35,7 @@ public abstract class TypedManager : BaseProjectManager, IT
///
protected readonly IManagerPackageActions Actions;
- protected TypedManager(IManagerPackageActions actions, IHttpClientFactory httpClientFactory, string directory) : base(httpClientFactory, directory)
+ protected TypedManager(IManagerPackageActions actions, IHttpClientFactory httpClientFactory, string directory, TimeSpan? timeout = null) : base(httpClientFactory, directory, timeout)
{
Actions = actions;
}
diff --git a/src/Shared/PackageManagers/URLProjectManager.cs b/src/Shared/PackageManagers/URLProjectManager.cs
index 1bf6af1a..32a1b196 100644
--- a/src/Shared/PackageManagers/URLProjectManager.cs
+++ b/src/Shared/PackageManagers/URLProjectManager.cs
@@ -20,11 +20,11 @@ internal class URLProjectManager : BaseProjectManager
public override string ManagerType => Type;
- public URLProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public URLProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public URLProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public URLProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/UbuntuProjectManager.cs b/src/Shared/PackageManagers/UbuntuProjectManager.cs
index 2a0bbd20..ed013d42 100644
--- a/src/Shared/PackageManagers/UbuntuProjectManager.cs
+++ b/src/Shared/PackageManagers/UbuntuProjectManager.cs
@@ -34,11 +34,11 @@ internal class UbuntuProjectManager : BaseProjectManager
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Modified through reflection.")]
public string ENV_UBUNTU_POOL_NAMES { get; set; } = "main,universe,multiverse,restricted";
- public UbuntuProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public UbuntuProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public UbuntuProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public UbuntuProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/Shared/PackageManagers/VSMProjectManager.cs b/src/Shared/PackageManagers/VSMProjectManager.cs
index b30cd5e0..96638244 100644
--- a/src/Shared/PackageManagers/VSMProjectManager.cs
+++ b/src/Shared/PackageManagers/VSMProjectManager.cs
@@ -28,11 +28,11 @@ internal class VSMProjectManager : BaseProjectManager
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Modified through reflection.")]
public string ENV_VS_MARKETPLACE_ENDPOINT { get; set; } = "https://marketplace.visualstudio.com";
- public VSMProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory) : base(httpClientFactory, destinationDirectory)
+ public VSMProjectManager(IHttpClientFactory httpClientFactory, string destinationDirectory, TimeSpan? timeout = null) : base(httpClientFactory, destinationDirectory, timeout)
{
}
- public VSMProjectManager(string destinationDirectory) : base(destinationDirectory)
+ public VSMProjectManager(string destinationDirectory, TimeSpan? timeout = null) : base(destinationDirectory, timeout)
{
}
diff --git a/src/oss-tests/FindSquatsTests.cs b/src/oss-tests/FindSquatsTests.cs
index f0204376..ca845dfe 100644
--- a/src/oss-tests/FindSquatsTests.cs
+++ b/src/oss-tests/FindSquatsTests.cs
@@ -50,7 +50,7 @@ public async Task DetectSquats(string packageUrl, bool generateSquats, bool expe
// Override the NuGet constructor to add the mocked NuGetPackageActions.
managerOverrides[NuGetProjectManager.Type] =
- _ => new NuGetProjectManager(".", nugetPackageActions, httpClientFactory);
+ (destinationDirectory, timeout) => new NuGetProjectManager(".", nugetPackageActions, httpClientFactory);
ProjectManagerFactory projectManagerFactory = new(managerOverrides);
FindSquatsTool fst = new(projectManagerFactory);
@@ -554,8 +554,8 @@ public async Task NewtonsoftMutations_Succeeds_Async()
IManagerPackageActions packageActions = PackageActionsHelper.SetupPackageActions(newtonsoft, validSquats: squattingPackages) ?? throw new InvalidOperationException();
Dictionary overrideDict = ProjectManagerFactory.GetDefaultManagers(httpClientFactory);
- overrideDict[NuGetProjectManager.Type] = directory =>
- new NuGetProjectManager(directory, packageActions, httpClientFactory);
+ overrideDict[NuGetProjectManager.Type] = (destinationDirectory, timeout) =>
+ new NuGetProjectManager(destinationDirectory, packageActions, httpClientFactory);
FindPackageSquats findPackageSquats = new(new ProjectManagerFactory(overrideDict), newtonsoft);
@@ -589,8 +589,8 @@ public async Task RequestsMutations_Succeeds_Async()
IManagerPackageActions packageActions = PackageActionsHelper.SetupPackageActions(requests, validSquats: squattingPackages) ?? throw new InvalidOperationException();
Dictionary overrideDict = ProjectManagerFactory.GetDefaultManagers(httpClientFactory);
- overrideDict[NuGetProjectManager.Type] = directory =>
- new NuGetProjectManager(directory, packageActions, httpClientFactory);
+ overrideDict[NuGetProjectManager.Type] = (destinationDirectory, timeout) =>
+ new NuGetProjectManager(destinationDirectory, packageActions, httpClientFactory);
FindPackageSquats findPackageSquats = new(new ProjectManagerFactory(overrideDict), requests);
diff --git a/src/oss-tests/ProjectManagerTests/CondaProjectManagerTests.cs b/src/oss-tests/ProjectManagerTests/CondaProjectManagerTests.cs
index bdf7141b..1dd0ee26 100644
--- a/src/oss-tests/ProjectManagerTests/CondaProjectManagerTests.cs
+++ b/src/oss-tests/ProjectManagerTests/CondaProjectManagerTests.cs
@@ -25,7 +25,7 @@ public CondaProjectManagerTests()
Mock mockFactory = new();
_httpFactory = mockFactory.Object;
- _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory) { CallBase = true };
+ _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory, null) { CallBase = true };
}
[DataTestMethod]
diff --git a/src/oss-tests/ProjectManagerTests/GolangProjectManagerTests.cs b/src/oss-tests/ProjectManagerTests/GolangProjectManagerTests.cs
index 0786846d..2d828edb 100644
--- a/src/oss-tests/ProjectManagerTests/GolangProjectManagerTests.cs
+++ b/src/oss-tests/ProjectManagerTests/GolangProjectManagerTests.cs
@@ -44,7 +44,7 @@ public GolangProjectManagerTests()
mockFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(mockHttp.ToHttpClient());
_httpFactory = mockFactory.Object;
- _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory) { CallBase = true };
+ _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory, null) { CallBase = true };
}
[DataTestMethod]
diff --git a/src/oss-tests/ProjectManagerTests/MavenProjectManagerTests.cs b/src/oss-tests/ProjectManagerTests/MavenProjectManagerTests.cs
index 33b937ba..64ac2b1b 100644
--- a/src/oss-tests/ProjectManagerTests/MavenProjectManagerTests.cs
+++ b/src/oss-tests/ProjectManagerTests/MavenProjectManagerTests.cs
@@ -51,7 +51,7 @@ public MavenProjectManagerTests()
mockFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(mockHttp.ToHttpClient());
_httpFactory = mockFactory.Object;
- _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory) { CallBase = true };
+ _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory, null) { CallBase = true };
}
[DataTestMethod]
diff --git a/src/oss-tests/ProjectManagerTests/NPMProjectManagerTests.cs b/src/oss-tests/ProjectManagerTests/NPMProjectManagerTests.cs
index 04f5a91e..65b9a5d7 100644
--- a/src/oss-tests/ProjectManagerTests/NPMProjectManagerTests.cs
+++ b/src/oss-tests/ProjectManagerTests/NPMProjectManagerTests.cs
@@ -127,7 +127,7 @@ public NPMProjectManagerTests()
mockFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(mockHttp.ToHttpClient());
_httpFactory = mockFactory.Object;
- _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory) { CallBase = true };
+ _projectManager = new Mock(".", new NoOpPackageActions(), _httpFactory, null) { CallBase = true };
}
[DataTestMethod]
@@ -290,6 +290,8 @@ await _projectManager.Object.DetailedPackageExistsAsync(new PackageURL("pkg:npm/
public async Task PackageVersionPulledAsync(string purlString, bool expectedPulled = true)
{
PackageURL purl = new(purlString);
+
+ var project = _projectManager;
string? content = await _projectManager.Object.GetMetadataAsync(purl);
diff --git a/src/oss-tests/ProjectManagerTests/ProjectManagerFactoryTests.cs b/src/oss-tests/ProjectManagerTests/ProjectManagerFactoryTests.cs
index a67b7275..96a8f320 100644
--- a/src/oss-tests/ProjectManagerTests/ProjectManagerFactoryTests.cs
+++ b/src/oss-tests/ProjectManagerTests/ProjectManagerFactoryTests.cs
@@ -3,13 +3,17 @@
namespace Microsoft.CST.OpenSource.Tests.ProjectManagerTests;
using Contracts;
+using Moq;
using PackageActions;
using PackageManagers;
using PackageUrl;
+using RichardSzalay.MockHttp;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Net.Http;
+using System.Threading.Tasks;
using VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -53,20 +57,20 @@ public void DefaultSucceeds()
AssertFactoryCreatesCorrect(projectManagerFactory);
}
-
+
///
/// Test adding a new manager to the dictionary, not overriding an existing one.
///
[TestMethod]
public void AddTestManagerSucceeds()
{
- _managerOverrides["test"] = directory => new NuGetProjectManager(directory, null, _httpClientFactory); // Create a test type with the NuGetProjectManager.
+ _managerOverrides["test"] = (destinationDirectory, timeout) => new NuGetProjectManager(destinationDirectory, null, _httpClientFactory); // Create a test type with the NuGetProjectManager.
ProjectManagerFactory projectManagerFactory = new(_managerOverrides);
AssertFactoryCreatesCorrect(projectManagerFactory);
}
-
+
///
/// Test overriding the nuget and npm entries as well as their destination directories in the dictionary.
///
@@ -74,9 +78,9 @@ public void AddTestManagerSucceeds()
public void OverrideManagerSucceeds()
{
_managerOverrides[NuGetProjectManager.Type] =
- _ => new NuGetProjectManager("nugetTestDirectory", null, _httpClientFactory); // Override the default entry for nuget, and override the destinationDirectory.
+ (destinationDirectory, timeout) => new NuGetProjectManager("nugetTestDirectory", null, _httpClientFactory); // Override the default entry for nuget, and override the destinationDirectory.
_managerOverrides[NPMProjectManager.Type] =
- _ => new NPMProjectManager("npmTestDirectory", null, _httpClientFactory); // Override the default entry for npm, and override the destinationDirectory.
+ (destinationDirectory, timeout) => new NPMProjectManager("npmTestDirectory", null, _httpClientFactory); // Override the default entry for npm, and override the destinationDirectory.
ProjectManagerFactory projectManagerFactory = new(_managerOverrides);
@@ -85,24 +89,24 @@ public void OverrideManagerSucceeds()
// Assert that the overrides worked by checking the TopLevelExtractionDirectory was changed.
IBaseProjectManager? nuGetProjectManager = projectManagerFactory.CreateProjectManager(new PackageURL("pkg:nuget/foo"));
Assert.AreEqual("nugetTestDirectory", nuGetProjectManager?.TopLevelExtractionDirectory);
-
+
IBaseProjectManager? npmProjectManager = projectManagerFactory.CreateProjectManager(new PackageURL("pkg:npm/foo"));
Assert.AreEqual("npmTestDirectory", npmProjectManager?.TopLevelExtractionDirectory);
}
-
+
///
/// Test changing an entry in the dictionary of constructors to construct a manager of a different type.
///
[TestMethod]
public void ChangeProjectManagerSucceeds()
{
- _managerOverrides[NuGetProjectManager.Type] = directory => new NPMProjectManager(directory, null, _httpClientFactory); // Override the default entry for nuget and set it as another NPMProjectManager.
-
+ _managerOverrides[NuGetProjectManager.Type] = (destinationDirectory, timeout) => new NPMProjectManager(destinationDirectory, null, _httpClientFactory); // Override the default entry for nuget and set it as another NPMProjectManager.
+
ProjectManagerFactory projectManagerFactory = new(_managerOverrides);
AssertFactoryCreatesCorrect(projectManagerFactory);
}
-
+
///
/// Test removing an entry from the default dictionary of project manager constructors.
///
@@ -112,11 +116,11 @@ public void RemoveProjectManagerSucceeds()
Assert.IsTrue(_managerOverrides.Remove(NuGetProjectManager.Type));
ProjectManagerFactory projectManagerFactory = new(_managerOverrides);
-
+
PackageURL packageUrl = new("pkg:nuget/foo");
Assert.IsNull(projectManagerFactory.CreateProjectManager(packageUrl));
}
-
+
///
/// Test removing all project managers from the dictionary of project manager constructors.
///
@@ -127,9 +131,9 @@ public void RemoveAllProjectManagersSucceeds()
_managerOverrides.Clear();
ProjectManagerFactory projectManagerFactory = new(_managerOverrides);
-
+
AssertFactoryCreatesCorrect(projectManagerFactory);
-
+
foreach (PackageURL packageUrl in ProjectManagerFactory.GetDefaultManagers(_httpClientFactory).Keys
.Select(purlType => new PackageURL($"pkg:{purlType}/foo")))
{
@@ -137,6 +141,61 @@ public void RemoveAllProjectManagersSucceeds()
}
}
+ ///
+ /// Test that timeout is set if a value is passed.
+ ///
+ [TestMethod]
+ public void CreateProjectManagerSetsTimeOutCorrectly()
+ {
+ // Arrange
+ ProjectManagerFactory projectManagerFactory = new();
+ TimeSpan testTimeout = TimeSpan.FromMilliseconds(100);
+ PackageURL testPackageUrl = new("pkg:npm/foo");
+
+ // Act
+ IBaseProjectManager? testProjectManager = projectManagerFactory.CreateProjectManager(testPackageUrl, ".", testTimeout);
+ IBaseProjectManager? testProjectManagerWithoutTimeout = projectManagerFactory.CreateProjectManager(testPackageUrl, ".");
+
+ // Assert
+ Assert.AreEqual(testTimeout, testProjectManager?.Timeout);
+ Assert.IsNull(testProjectManagerWithoutTimeout?.Timeout);
+ }
+
+ ///
+ /// Test that requests time out after the timespan if specified.
+ ///
+ [TestMethod]
+ public async Task PackageManagerRequestsTimeOutCorrectly()
+ {
+ // Arrange
+ Mock mockFactory = new();
+ MockHttpMessageHandler mockHttp = new();
+ mockHttp
+ .When(HttpMethod.Get, "*")
+ .Respond(async () =>
+ {
+ await Task.Delay(120); // simulate a delay slightly longer than the timeout set
+ return new HttpResponseMessage(System.Net.HttpStatusCode.OK)
+ {
+ Content = new StringContent("This is a delayed response")
+ };
+ });
+ HttpClient testClient = mockHttp.ToHttpClient();
+ testClient.Timeout = TimeSpan.FromMilliseconds(100);
+ mockFactory.Setup(_ => _.CreateClient(It.IsAny())).Returns(testClient);
+ IHttpClientFactory httpFactory = mockFactory.Object;
+ PackageURL testPackageUrl = new("pkg:npm/foo@0.1");
+
+ Mock testProjectManager = new Mock(".", new NoOpPackageActions(), httpFactory, null) { CallBase = true };
+
+ //Act
+ Exception exception = await Assert.ThrowsExceptionAsync(() => testProjectManager.Object.DownloadVersionAsync(testPackageUrl, false, false));
+
+ //Assert
+ Assert.IsNotNull(exception.InnerException);
+ Assert.IsInstanceOfType(exception.InnerException, typeof(TimeoutException));
+ }
+
///
/// Helper method to assert that the creates the expected implementation of .
///