C#: Make FeedManager unit-testable. · github/codeql@0f2a7cf · GitHub
Skip to content

Commit 0f2a7cf

Browse files
committed
C#: Make FeedManager unit-testable.
1 parent 29e1392 commit 0f2a7cf

3 files changed

Lines changed: 134 additions & 94 deletions

File tree

‎csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs‎

Lines changed: 9 additions & 94 deletions
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
2+
using System;
3+
using System.IO;
4+
using Semmle.Util.Logging;
5+
using System.Net.Http;
6+
using System.Net;
7+
using System.Security.Cryptography.X509Certificates;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace Semmle.Extraction.CSharp.DependencyFetching
12+
{
13+
public class FeedManagerIO : IFeedManagerIO
14+
{
15+
private readonly ILogger logger;
16+
private readonly IDependabotProxy? dependabotProxy;
17+
18+
public FeedManagerIO(ILogger logger, IDependabotProxy? dependabotProxy)
19+
{
20+
this.logger = logger;
21+
this.dependabotProxy = dependabotProxy;
22+
}
23+
24+
public string? GetDirectoryName(string path)
25+
{
26+
try
27+
{
28+
return new FileInfo(path).Directory?.FullName;
29+
}
30+
catch (Exception exc)
31+
{
32+
logger.LogWarning($"Failed to get directory of '{path}': {exc}");
33+
}
34+
return null;
35+
}
36+
37+
private static async Task<HttpResponseMessage> ExecuteGetRequest(string address, HttpClient httpClient, CancellationToken cancellationToken)
38+
{
39+
return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
40+
}
41+
42+
public bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount)
43+
{
44+
logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable...");
45+
46+
// Configure the HttpClient to be aware of the Dependabot Proxy, if used.
47+
HttpClientHandler httpClientHandler = new();
48+
if (dependabotProxy != null)
49+
{
50+
httpClientHandler.Proxy = new WebProxy(dependabotProxy.Address);
51+
52+
if (dependabotProxy.Certificate != null)
53+
{
54+
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) =>
55+
{
56+
if (chain is null || cert is null)
57+
{
58+
var msg = cert is null && chain is null
59+
? "certificate and chain"
60+
: chain is null
61+
? "chain"
62+
: "certificate";
63+
logger.LogWarning($"Dependabot proxy certificate validation failed due to missing {msg}");
64+
return false;
65+
}
66+
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
67+
chain.ChainPolicy.CustomTrustStore.Add(dependabotProxy.Certificate);
68+
return chain.Build(cert);
69+
};
70+
}
71+
}
72+
73+
using HttpClient client = new(httpClientHandler);
74+
75+
for (var i = 0; i < tryCount; i++)
76+
{
77+
using var cts = new CancellationTokenSource();
78+
cts.CancelAfter(timeoutMilliSeconds);
79+
try
80+
{
81+
logger.LogInfo($"Attempt {i + 1}/{tryCount} to reach NuGet feed '{feed}'.");
82+
using var response = ExecuteGetRequest(feed, client, cts.Token).GetAwaiter().GetResult();
83+
response.EnsureSuccessStatusCode();
84+
logger.LogInfo($"Querying NuGet feed '{feed}' succeeded.");
85+
return true;
86+
}
87+
catch (Exception exc)
88+
{
89+
if (exc is TaskCanceledException tce &&
90+
tce.CancellationToken == cts.Token &&
91+
cts.Token.IsCancellationRequested)
92+
{
93+
logger.LogInfo($"Didn't receive answer from NuGet feed '{feed}' in {timeoutMilliSeconds}ms.");
94+
timeoutMilliSeconds *= 2;
95+
continue;
96+
}
97+
98+
logger.LogInfo($"Querying NuGet feed '{feed}' failed. The reason for the failure: {exc.Message}");
99+
return false;
100+
}
101+
}
102+
103+
logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times.");
104+
return false;
105+
}
106+
107+
108+
}
109+
}
Lines changed: 16 additions & 0 deletions

0 commit comments

Comments
 (0)