11using System ;
22using System . Collections . Generic ;
33using System . Collections . Immutable ;
4- using System . IO ;
54using System . Linq ;
6- using System . Net ;
7- using System . Net . Http ;
8- using System . Security . Cryptography . X509Certificates ;
9- using System . Text ;
105using System . Text . RegularExpressions ;
11- using System . Threading ;
12- using System . Threading . Tasks ;
136using Semmle . Util ;
147using Semmle . Util . Logging ;
158
@@ -22,9 +15,9 @@ internal sealed partial class FeedManager : IDisposable
2215 private readonly ILogger logger ;
2316 private readonly IDotNet dotnet ;
2417 private readonly IFileProvider fileProvider ;
25- private readonly IDependabotProxy ? dependabotProxy ;
2618 private readonly DependencyDirectory emptyPackageDirectory ;
2719 private readonly ImmutableHashSet < string > privateRegistryFeeds ;
20+ private readonly IFeedManagerIO feedManagerIo ;
2821
2922 /// <summary>
3023 /// Gets whether there are private package registries configured for C#.
@@ -79,12 +72,12 @@ internal sealed partial class FeedManager : IDisposable
7972 /// </summary>
8073 public ImmutableHashSet < string > ReachableFallbackFeeds => lazyReachableFallbackFeeds . Value ;
8174
82- public FeedManager ( ILogger logger , IDotNet dotnet , IDependabotProxy ? dependabotProxy , IFileProvider fileProvider )
75+ public FeedManager ( ILogger logger , IDotNet dotnet , IDependabotProxy ? dependabotProxy , IFileProvider fileProvider , IFeedManagerIO feedManagerIo )
8376 {
8477 this . logger = logger ;
8578 this . dotnet = dotnet ;
86- this . dependabotProxy = dependabotProxy ;
8779 this . fileProvider = fileProvider ;
80+ this . feedManagerIo = feedManagerIo ;
8881 privateRegistryFeeds = dependabotProxy ? . RegistryURLs . ToImmutableHashSet ( ) ?? [ ] ;
8982 HasPrivateRegistryFeeds = privateRegistryFeeds . Count > 0 ;
9083 emptyPackageDirectory = new DependencyDirectory ( "empty" , "empty package" , logger ) ;
@@ -105,17 +98,9 @@ public FeedManager(ILogger logger, IDotNet dotnet, IDependabotProxy? dependabotP
10598 } ) ;
10699 }
107100
108- private string ? GetDirectoryName ( string path )
101+ public FeedManager ( ILogger logger , IDotNet dotnet , IDependabotProxy ? dependabotProxy , IFileProvider fileProvider )
102+ : this ( logger , dotnet , dependabotProxy , fileProvider , new FeedManagerIO ( logger , dependabotProxy ) )
109103 {
110- try
111- {
112- return new FileInfo ( path ) . Directory ? . FullName ;
113- }
114- catch ( Exception exc )
115- {
116- logger . LogWarning ( $ "Failed to get directory of '{ path } ': { exc } ") ;
117- }
118- return null ;
119104 }
120105
121106 private IEnumerable < string > GetFeeds ( Func < IList < string > > getNugetFeeds )
@@ -193,7 +178,7 @@ private IEnumerable<string> FeedsToUseAux(HashSet<string> feedsToConsider)
193178 public IEnumerable < string > FeedsToUse ( string path )
194179 {
195180 // Find the path specific feeds.
196- var folder = GetDirectoryName ( path ) ;
181+ var folder = feedManagerIo . GetDirectoryName ( path ) ;
197182 var feedsToConsider = folder is not null ? GetFeedsFromFolder ( folder ) . ToHashSet ( ) : new HashSet < string > ( ) ;
198183
199184 return FeedsToUseAux ( feedsToConsider ) ;
@@ -238,76 +223,6 @@ public List<string> MakeRestoreFeeds(string path)
238223 return ( timeoutMilliSeconds , tryCount ) ;
239224 }
240225
241- private static async Task < HttpResponseMessage > ExecuteGetRequest ( string address , HttpClient httpClient , CancellationToken cancellationToken )
242- {
243- return await httpClient . GetAsync ( address , HttpCompletionOption . ResponseHeadersRead , cancellationToken ) ;
244- }
245-
246- private bool IsFeedReachable ( string feed , int timeoutMilliSeconds , int tryCount )
247- {
248- logger . LogInfo ( $ "Checking if NuGet feed '{ feed } ' is reachable...") ;
249-
250- // Configure the HttpClient to be aware of the Dependabot Proxy, if used.
251- HttpClientHandler httpClientHandler = new ( ) ;
252- if ( dependabotProxy != null )
253- {
254- httpClientHandler . Proxy = new WebProxy ( dependabotProxy . Address ) ;
255-
256- if ( dependabotProxy . Certificate != null )
257- {
258- httpClientHandler . ServerCertificateCustomValidationCallback = ( message , cert , chain , _ ) =>
259- {
260- if ( chain is null || cert is null )
261- {
262- var msg = cert is null && chain is null
263- ? "certificate and chain"
264- : chain is null
265- ? "chain"
266- : "certificate" ;
267- logger . LogWarning ( $ "Dependabot proxy certificate validation failed due to missing { msg } ") ;
268- return false ;
269- }
270- chain . ChainPolicy . TrustMode = X509ChainTrustMode . CustomRootTrust ;
271- chain . ChainPolicy . CustomTrustStore . Add ( dependabotProxy . Certificate ) ;
272- return chain . Build ( cert ) ;
273- } ;
274- }
275- }
276-
277- using HttpClient client = new ( httpClientHandler ) ;
278-
279- for ( var i = 0 ; i < tryCount ; i ++ )
280- {
281- using var cts = new CancellationTokenSource ( ) ;
282- cts . CancelAfter ( timeoutMilliSeconds ) ;
283- try
284- {
285- logger . LogInfo ( $ "Attempt { i + 1 } /{ tryCount } to reach NuGet feed '{ feed } '.") ;
286- using var response = ExecuteGetRequest ( feed , client , cts . Token ) . GetAwaiter ( ) . GetResult ( ) ;
287- response . EnsureSuccessStatusCode ( ) ;
288- logger . LogInfo ( $ "Querying NuGet feed '{ feed } ' succeeded.") ;
289- return true ;
290- }
291- catch ( Exception exc )
292- {
293- if ( exc is TaskCanceledException tce &&
294- tce . CancellationToken == cts . Token &&
295- cts . Token . IsCancellationRequested )
296- {
297- logger . LogInfo ( $ "Didn't receive answer from NuGet feed '{ feed } ' in { timeoutMilliSeconds } ms.") ;
298- timeoutMilliSeconds *= 2 ;
299- continue ;
300- }
301-
302- logger . LogInfo ( $ "Querying NuGet feed '{ feed } ' failed. The reason for the failure: { exc . Message } ") ;
303- return false ;
304- }
305- }
306-
307- logger . LogWarning ( $ "Didn't receive answer from NuGet feed '{ feed } '. Tried it { tryCount } times.") ;
308- return false ;
309- }
310-
311226 /// <summary>
312227 /// Retrieves a list of excluded NuGet feeds from the corresponding environment variable.
313228 /// </summary>
@@ -361,7 +276,7 @@ public bool IsDefaultFeedReachable()
361276 if ( CheckNugetFeedResponsiveness )
362277 {
363278 var ( initialTimeout , tryCount ) = GetFeedRequestSettings ( isFallback : false ) ;
364- return IsFeedReachable ( PublicNugetOrgFeed , initialTimeout , tryCount ) ;
279+ return feedManagerIo . IsFeedReachable ( PublicNugetOrgFeed , initialTimeout , tryCount ) ;
365280 }
366281
367282 return true ;
@@ -380,7 +295,7 @@ private List<string> GetReachableNuGetFeeds(HashSet<string> feedsToCheck, bool i
380295
381296 var ( initialTimeout , tryCount ) = GetFeedRequestSettings ( isFallback ) ;
382297 var reachableFeeds = feedsToCheck
383- . Where ( feed => IsFeedReachable ( feed , initialTimeout , tryCount ) )
298+ . Where ( feed => feedManagerIo . IsFeedReachable ( feed , initialTimeout , tryCount ) )
384299 . ToList ( ) ;
385300
386301 if ( reachableFeeds . Count == 0 )
@@ -464,7 +379,7 @@ private ImmutableHashSet<string> GetAllFeeds()
464379 if ( nugetConfigs . Count > 0 )
465380 {
466381 var nugetConfigFeeds = nugetConfigs
467- . Select ( GetDirectoryName )
382+ . Select ( feedManagerIo . GetDirectoryName )
468383 . Where ( folder => folder != null )
469384 . SelectMany ( folder => GetFeedsFromFolder ( folder ! ) )
470385 . ToHashSet ( ) ;
0 commit comments