<PropertyGroup>
<!-- Opt-in/out repo features -->
<UsingToolXliff>false</UsingToolXliff>
+ <AzureIdentityVersion>1.12.0</AzureIdentityVersion>
<!-- Uncomment this line to use the custom version of roslyn as needed. -->
<!-- <UsingToolMicrosoftNetCompilers Condition="'$(DotNetBuildSourceOnly)' != 'true'">true</UsingToolMicrosoftNetCompilers> -->
<!-- CoreFX -->
<SystemBuffersVersion>4.5.1</SystemBuffersVersion>
<SystemMemoryVersion>4.5.5</SystemMemoryVersion>
<SystemRuntimeLoaderVersion>4.3.0</SystemRuntimeLoaderVersion>
+ <SystemThreadingTasksExtensionsVersion>4.5.4</SystemThreadingTasksExtensionsVersion>
<SystemTextEncodingsWebVersion>8.0.0</SystemTextEncodingsWebVersion>
<SystemTextJsonVersion>8.0.4</SystemTextJsonVersion>
<XUnitAbstractionsVersion>2.0.3</XUnitAbstractionsVersion>
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
<PackageReference Include="System.Runtime.Loader" Version="$(SystemRuntimeLoaderVersion)" />
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonVersion)" />
+ <PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsVersion)" />
</ItemGroup>
<ItemGroup>
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Net.Http.Headers;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
+using System.Threading.Tasks;
+using Azure.Core;
+using Azure.Identity;
using Microsoft.FileFormats;
using Microsoft.FileFormats.ELF;
using Microsoft.FileFormats.MachO;
/// Symbol server URLs
/// </summary>
public const string MsdlSymbolServer = "https://msdl.microsoft.com/download/symbols/";
+ public const string SymwebSymbolServer = "https://symweb.azurefd.net/";
+
+ private static string _symwebHost = new Uri(SymwebSymbolServer).Host;
private readonly IHost _host;
private string _defaultSymbolCache;
}
if (symbolServerPath != null)
{
- if (!AddSymbolServer(symbolServerPath: symbolServerPath.Trim()))
+ symbolServerPath = symbolServerPath.Trim();
+ if (IsSymweb(symbolServerPath))
{
- return false;
+ if (!AddSymwebSymbolServer(includeInteractiveCredentials: false))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!AddSymbolServer(symbolServerPath))
+ {
+ return false;
+ }
}
}
foreach (string symbolCachePath in symbolCachePaths.Reverse<string>())
return true;
}
+ /// <summary>
+ /// Add the cloud symweb symbol server with authentication.
+ /// </summary>
+ /// <param name="includeInteractiveCredentials">specifies whether credentials requiring user interaction will be included in the default authentication flow</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
+ /// <returns>if false, failure</returns>
+ public bool AddSymwebSymbolServer(
+ bool includeInteractiveCredentials = false,
+ int? timeoutInMinutes = null,
+ int? retryCount = null)
+ {
+ TokenCredential tokenCredential = new DefaultAzureCredential(includeInteractiveCredentials);
+ AccessToken accessToken;
+ async ValueTask<AuthenticationHeaderValue> authenticationFunc(CancellationToken token)
+ {
+ try
+ {
+ if (accessToken.ExpiresOn <= DateTimeOffset.UtcNow.AddMinutes(2))
+ {
+ accessToken = await tokenCredential.GetTokenAsync(new TokenRequestContext(["api://af9e1c69-e5e9-4331-8cc5-cdf93d57bafa/.default"]), token).ConfigureAwait(false);
+ }
+ return new AuthenticationHeaderValue("Bearer", accessToken.Token);
+ }
+ catch (Exception ex) when (ex is CredentialUnavailableException or AuthenticationFailedException)
+ {
+ Trace.TraceError($"AddSymwebSymbolServer: {ex}");
+ return null;
+ }
+ }
+ return AddSymbolServer(SymwebSymbolServer, timeoutInMinutes, retryCount, authenticationFunc);
+ }
+
+ /// <summary>
+ /// Add symbol server to search path. The server URL can be the cloud symweb.
+ /// </summary>
+ /// <param name="accessToken">PAT or access token</param>
+ /// <param name="symbolServerPath">symbol server url (optional, uses <see cref="DefaultSymbolPath"/> if null)</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
+ /// <returns>if false, failure</returns>
+ public bool AddAuthenticatedSymbolServer(
+ string accessToken,
+ string symbolServerPath = null,
+ int? timeoutInMinutes = null,
+ int? retryCount = null)
+ {
+ if (accessToken == null)
+ {
+ throw new ArgumentNullException(nameof(accessToken));
+ }
+ AuthenticationHeaderValue authenticationValue = new("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($":{accessToken}")));
+ return AddSymbolServer(symbolServerPath, timeoutInMinutes, retryCount, (_) => new ValueTask<AuthenticationHeaderValue>(authenticationValue));
+ }
+
/// <summary>
/// Add symbol server to search path.
/// </summary>
/// <param name="symbolServerPath">symbol server url (optional, uses <see cref="DefaultSymbolPath"/> if null)</param>
- /// <param name="authToken">PAT for secure symbol server (optional)</param>
/// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
/// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
/// <returns>if false, failure</returns>
public bool AddSymbolServer(
string symbolServerPath = null,
- string authToken = null,
int? timeoutInMinutes = null,
int? retryCount = null)
+ {
+ return AddSymbolServer(symbolServerPath, timeoutInMinutes, retryCount, authenticationFunc: null);
+ }
+
+ /// <summary>
+ /// Add symbol server to search path.
+ /// </summary>
+ /// <param name="symbolServerPath">symbol server url (optional, uses <see cref="DefaultSymbolPath"/> if null)</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
+ /// <param name="authenticationFunc">function that returns the authentication value for a request</param>
+ /// <returns>if false, failure</returns>
+ public bool AddSymbolServer(
+ string symbolServerPath,
+ int? timeoutInMinutes,
+ int? retryCount,
+ Func<CancellationToken, ValueTask<AuthenticationHeaderValue>> authenticationFunc)
{
// Add symbol server URL if exists
symbolServerPath ??= DefaultSymbolPath;
if (!IsDuplicateSymbolStore<HttpSymbolStore>(store, (httpSymbolStore) => uri.Equals(httpSymbolStore.Uri)))
{
// Create http symbol server store
- HttpSymbolStore httpSymbolStore = new(Tracer.Instance, store, uri, personalAccessToken: authToken);
- httpSymbolStore.Timeout = TimeSpan.FromMinutes(timeoutInMinutes.GetValueOrDefault(DefaultTimeout));
- httpSymbolStore.RetryCount = retryCount.GetValueOrDefault(DefaultRetryCount);
+ HttpSymbolStore httpSymbolStore = new(Tracer.Instance, store, uri, authenticationFunc)
+ {
+ Timeout = TimeSpan.FromMinutes(timeoutInMinutes.GetValueOrDefault(DefaultTimeout)),
+ RetryCount = retryCount.GetValueOrDefault(DefaultRetryCount)
+ };
SetSymbolStore(httpSymbolStore);
}
}
return sb.ToString();
}
+ /// <summary>
+ /// Returns true if cloud symweb server
+ /// </summary>
+ /// <param name="server"></param>
+ private static bool IsSymweb(string server)
+ {
+ try
+ {
+ Uri uri = new(server);
+ return uri.Host.Equals(_symwebHost, StringComparison.OrdinalIgnoreCase);
+ }
+ catch (Exception ex) when (ex is UriFormatException or InvalidOperationException)
+ {
+ return false;
+ }
+ }
+
/// <summary>
/// Attempts to download/retrieve from cache the key.
/// </summary>
/// <returns>if false, error parsing symbol path</returns>
bool ParseSymbolPath(string symbolPath);
+ /// <summary>
+ /// Add the cloud symweb symbol server with authentication.
+ /// </summary>
+ /// <param name="includeInteractiveCredentials">specifies whether credentials requiring user interaction will be included in the default authentication flow</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
+ /// <returns>if false, failure</returns>
+ public bool AddSymwebSymbolServer(
+ bool includeInteractiveCredentials = false,
+ int? timeoutInMinutes = null,
+ int? retryCount = null);
+
+ /// <summary>
+ /// Add symbol server to search path with a PAT.
+ /// </summary>
+ /// <param name="accessToken">PAT or access token</param>
+ /// <param name="symbolServerPath">symbol server url (optional, uses <see cref="DefaultSymbolPath"/> if null)</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
+ /// <returns>if false, failure</returns>
+ public bool AddAuthenticatedSymbolServer(
+ string accessToken,
+ string symbolServerPath = null,
+ int? timeoutInMinutes = null,
+ int? retryCount = null);
+
/// <summary>
/// Add symbol server to search path.
/// </summary>
/// <param name="symbolServerPath">symbol server url (optional, uses <see cref="DefaultSymbolPath"/> if null)</param>
- /// <param name="authToken">PAT for secure symbol server (optional)</param>
- /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional, uses <see cref="DefaultTimeout"/> if null)</param>
- /// <param name="retryCount">number of retries (optional, uses <see cref="DefaultRetryCount"/> if null)</param>
+ /// <param name="timeoutInMinutes">symbol server timeout in minutes (optional uses <see cref="DefaultTimeout"/> if null)</param>
+ /// <param name="retryCount">number of retries (optional uses <see cref="DefaultRetryCount"/> if null)</param>
/// <returns>if false, failure</returns>
- bool AddSymbolServer(string symbolServerPath = null, string authToken = null, int? timeoutInMinutes = null, int? retryCount = null);
+ public bool AddSymbolServer(
+ string symbolServerPath = null,
+ int? timeoutInMinutes = null,
+ int? retryCount = null);
/// <summary>
/// Add cache path to symbol search path
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Net.Http.Headers;
+using System.Text;
using Microsoft.Diagnostics.DebugServices;
namespace Microsoft.Diagnostics.ExtensionCommands
[Option(Name = "--ms", Aliases = new string[] { "-ms" }, Help = "Use the public Microsoft symbol server.")]
public bool MicrosoftSymbolServer { get; set; }
- [Option(Name = "--disable", Aliases = new string[] { "-disable" }, Help = "Clear or disable symbol download support.")]
+ [Option(Name = "--mi", Aliases = new string[] { "-mi" }, Help = "Use the internal symweb symbol server.")]
+ public bool InternalSymbolServer { get; set; }
+
+ [Option(Name = "--interactive", Aliases = new string[] { "-interactive" }, Help = "Allows user interaction will be included in the authentication flow.")]
+ public bool Interactive { get; set; }
+
+ [Option(Name = "--disable", Aliases = new string[] { "-disable", "-clear" }, Help = "Clear or disable symbol download support.")]
public bool Disable { get; set; }
[Option(Name = "--reset", Aliases = new string[] { "-reset" }, Help = "Reset the HTTP symbol servers clearing any cached failures.")]
[Option(Name = "--cache", Aliases = new string[] { "-cache" }, Help = "Specify a symbol cache directory.")]
public string Cache { get; set; }
+ [Option(Name = "--nocache", Aliases = new string[] { "-nocache" }, Help = "Do not automatically add the default cache before a server.")]
+ public bool NoCache { get; set; }
+
[Option(Name = "--directory", Aliases = new string[] { "-directory" }, Help = "Specify a directory to search for symbols.")]
public string Directory { get; set; }
public override void Invoke()
{
- if (MicrosoftSymbolServer && !string.IsNullOrEmpty(SymbolServerUrl))
+ if (MicrosoftSymbolServer && InternalSymbolServer)
{
- throw new DiagnosticsException("Cannot have -ms option and a symbol server path");
+ throw new DiagnosticsException("Cannot have both -ms and -mi options");
+ }
+ if ((MicrosoftSymbolServer || InternalSymbolServer) && !string.IsNullOrEmpty(SymbolServerUrl))
+ {
+ throw new DiagnosticsException("Cannot have -ms or -mi option and a symbol server path");
}
if (Disable)
{
{
SymbolService.Reset();
}
- if (MicrosoftSymbolServer || !string.IsNullOrEmpty(SymbolServerUrl))
+ if (MicrosoftSymbolServer || InternalSymbolServer || !string.IsNullOrEmpty(SymbolServerUrl))
{
- if (string.IsNullOrEmpty(Cache))
+ if (string.IsNullOrEmpty(Cache) && !NoCache)
{
Cache = SymbolService.DefaultSymbolCache;
}
- SymbolService.AddSymbolServer(SymbolServerUrl, AccessToken, Timeout, RetryCount);
+ if (InternalSymbolServer)
+ {
+ SymbolService.AddSymwebSymbolServer(includeInteractiveCredentials: Interactive, Timeout, RetryCount);
+ }
+ else if (AccessToken is not null)
+ {
+ SymbolService.AddAuthenticatedSymbolServer(AccessToken, SymbolServerUrl, Timeout, RetryCount);
+ }
+ else
+ {
+ SymbolService.AddSymbolServer(SymbolServerUrl, Timeout, RetryCount);
+ }
}
if (!string.IsNullOrEmpty(Cache))
{
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
{
pdbs = _peFile.Pdbs.ToArray();
}
- catch (InvalidVirtualAddressException ex)
+ catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException)
{
Tracer.Error("Reading PDB records for {0}: {1}", _path, ex.Message);
}
<ItemGroup>
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" />
-<!--
- <PackageReference Condition="'$(TargetFramework)' != 'net462'" Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" />
- <PackageReference Condition="'$(TargetFramework)' == 'net462'" Include="System.Reflection.Metadata" Version="1.6.0" />
--->
+ <PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsVersion)" />
</ItemGroup>
<ItemGroup>
namespace Microsoft.SymbolStore.SymbolStores
{
/// <summary>
- /// Basic http symbol store. The request can be authentication with a PAT for VSTS symbol stores.
+ /// Basic http symbol store. The request can be authentication with a PAT or bearer token.
/// </summary>
public class HttpSymbolStore : SymbolStore
{
private readonly HttpClient _client;
- private readonly HttpClient _authenticatedClient;
+ private readonly Func<CancellationToken, ValueTask<AuthenticationHeaderValue>> _authenticationFunc;
private bool _clientFailure;
/// <summary>
set
{
_client.Timeout = value;
- if (_authenticatedClient != null)
- {
- _authenticatedClient.Timeout = value;
- }
}
}
/// <param name="tracer">logger</param>
/// <param name="backingStore">next symbol store or null</param>
/// <param name="symbolServerUri">symbol server url</param>
- /// <param name="hasPAT">flag to indicate to create an authenticatedClient if there is a PAT</param>
- private HttpSymbolStore(ITracer tracer, SymbolStore backingStore, Uri symbolServerUri, bool hasPAT)
+ /// <param name="authenticationFunc">function that returns the authentication value for a request</param>
+ public HttpSymbolStore(ITracer tracer, SymbolStore backingStore, Uri symbolServerUri, Func<CancellationToken, ValueTask<AuthenticationHeaderValue>> authenticationFunc)
: base(tracer, backingStore)
{
Uri = symbolServerUri ?? throw new ArgumentNullException(nameof(symbolServerUri));
if (!symbolServerUri.IsAbsoluteUri || symbolServerUri.IsFile)
{
- throw new ArgumentException(nameof(symbolServerUri));
+ throw new ArgumentException(null, nameof(symbolServerUri));
}
+ _authenticationFunc = authenticationFunc;
- // Normal unauthenticated client
+ // Create client
_client = new HttpClient
{
Timeout = TimeSpan.FromMinutes(4)
};
- if (hasPAT)
- {
- HttpClientHandler handler = new()
- {
- AllowAutoRedirect = false
- };
- HttpClient client = new(handler)
- {
- Timeout = TimeSpan.FromMinutes(4)
- };
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
- // Authorization is set in associated constructors.
- _authenticatedClient = client;
- }
+ // Force redirect logins to fail.
+ _client.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress");
}
/// <summary>
/// <param name="tracer">logger</param>
/// <param name="backingStore">next symbol store or null</param>
/// <param name="symbolServerUri">symbol server url</param>
- /// <param name="personalAccessToken">optional Basic Auth PAT or null if no authentication</param>
- public HttpSymbolStore(ITracer tracer, SymbolStore backingStore, Uri symbolServerUri, string personalAccessToken = null)
- : this(tracer, backingStore, symbolServerUri, !string.IsNullOrEmpty(personalAccessToken))
+ /// <param name="accessToken">optional Basic Auth PAT or null if no authentication</param>
+ public HttpSymbolStore(ITracer tracer, SymbolStore backingStore, Uri symbolServerUri, string accessToken = null)
+ : this(tracer, backingStore, symbolServerUri, string.IsNullOrEmpty(accessToken) ? null : GetAuthenticationFunc("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($":{accessToken}"))))
{
- // If PAT, create authenticated client with Basic Auth
- if (!string.IsNullOrEmpty(personalAccessToken))
- {
- _authenticatedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalAccessToken))));
- }
}
/// <summary>
/// <param name="scheme">The scheme information to use for the AuthenticationHeaderValue</param>
/// <param name="parameter">The parameter information to use for the AuthenticationHeaderValue</param>
public HttpSymbolStore(ITracer tracer, SymbolStore backingStore, Uri symbolServerUri, string scheme, string parameter)
- : this(tracer, backingStore, symbolServerUri, true)
+ : this(tracer, backingStore, symbolServerUri, GetAuthenticationFunc(scheme, parameter))
{
if (string.IsNullOrEmpty(scheme))
{
throw new ArgumentNullException(nameof(scheme));
}
-
if (string.IsNullOrEmpty(parameter))
{
throw new ArgumentNullException(nameof(parameter));
}
+ }
- // Create authenticated header with given SymbolAuthHeader
- _authenticatedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, parameter);
- // Force redirect logins to fail.
- _authenticatedClient.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress");
+ private static Func<CancellationToken, ValueTask<AuthenticationHeaderValue>> GetAuthenticationFunc(string scheme, string parameter)
+ {
+ AuthenticationHeaderValue authenticationValue = new(scheme, parameter);
+ return (_) => new ValueTask<AuthenticationHeaderValue>(authenticationValue);
}
/// <summary>
Uri uri = GetRequestUri(key.Index);
bool needsChecksumMatch = key.PdbChecksums.Any();
-
if (needsChecksumMatch)
{
string checksumHeader = string.Join(";", key.PdbChecksums);
- HttpClient client = _authenticatedClient ?? _client;
Tracer.Information($"SymbolChecksum: {checksumHeader}");
- client.DefaultRequestHeaders.Add("SymbolChecksum", checksumHeader);
+ _client.DefaultRequestHeaders.Add("SymbolChecksum", checksumHeader);
}
Stream stream = await GetFileStream(key.FullPathName, uri, token).ConfigureAwait(false);
return null;
}
string fileName = Path.GetFileName(path);
- HttpClient client = _authenticatedClient ?? _client;
int retries = 0;
while (true)
{
{
// Can not dispose the response (like via using) on success because then the content stream
// is disposed and it is returned by this function.
- HttpResponseMessage response = await client.GetAsync(requestUri, token).ConfigureAwait(false);
- if (response.StatusCode == HttpStatusCode.OK)
+ using HttpRequestMessage request = new(HttpMethod.Get, requestUri);
+ if (_authenticationFunc is not null)
{
- return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ request.Headers.Authorization = await _authenticationFunc(token).ConfigureAwait(false);
}
- if (response.StatusCode == HttpStatusCode.Found)
+ using HttpResponseMessage response = await _client.SendAsync(request, token).ConfigureAwait(false);
+ if (response.StatusCode == HttpStatusCode.OK)
{
- Uri location = response.Headers.Location;
- response.Dispose();
-
- response = await _client.GetAsync(location, token).ConfigureAwait(false);
- if (response.StatusCode == HttpStatusCode.OK)
- {
- return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- }
+ byte[] buffer = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
+ return new MemoryStream(buffer);
}
HttpStatusCode statusCode = response.StatusCode;
string reasonPhrase = response.ReasonPhrase;
- response.Dispose();
// The GET failed
public override void Dispose()
{
_client.Dispose();
- _authenticatedClient?.Dispose();
base.Dispose();
}
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
+using Azure.Core;
+using Azure.Identity;
using Microsoft.Diagnostic.Tools.Symbol.Properties;
using Microsoft.FileFormats;
using Microsoft.FileFormats.ELF;
{
public Uri Uri;
public string PersonalAccessToken;
+ public bool InternalSymwebServer;
}
private readonly List<string> InputFilePaths = new();
private readonly List<string> CacheDirectories = new();
private readonly List<ServerInfo> SymbolServers = new();
+ private TokenCredential TokenCredential = new DefaultAzureCredential(includeInteractiveCredentials: true);
private string OutputDirectory;
private TimeSpan? Timeout;
private bool Overwrite;
program.SymbolServers.Add(new ServerInfo { Uri = uri, PersonalAccessToken = null });
break;
+ case "--internal-server":
+ Uri.TryCreate("https://symweb.azurefd.net/", UriKind.Absolute, out uri);
+ program.SymbolServers.Add(new ServerInfo { Uri = uri, PersonalAccessToken = null, InternalSymwebServer = true });
+ break;
+
case "--authenticated-server-path":
if (++i < args.Length)
{
foreach (ServerInfo server in ((IEnumerable<ServerInfo>)SymbolServers).Reverse())
{
- store = new HttpSymbolStore(Tracer, store, server.Uri, server.PersonalAccessToken);
+ if (server.InternalSymwebServer)
+ {
+ store = new HttpSymbolStore(Tracer, store, server.Uri, SymwebAuthenticationFunc);
+ }
+ else
+ {
+ store = new HttpSymbolStore(Tracer, store, server.Uri, server.PersonalAccessToken);
+ }
if (Timeout.HasValue && store is HttpSymbolStore http)
{
http.Timeout = Timeout.Value;
return store;
}
+ private async ValueTask<AuthenticationHeaderValue> SymwebAuthenticationFunc(CancellationToken token)
+ {
+ try
+ {
+ AccessToken accessToken = await TokenCredential.GetTokenAsync(new TokenRequestContext(["api://af9e1c69-e5e9-4331-8cc5-cdf93d57bafa/.default"]), token).ConfigureAwait(false);
+ return new AuthenticationHeaderValue("Bearer", accessToken.Token);
+ }
+ catch (Exception ex) when (ex is CredentialUnavailableException or AuthenticationFailedException)
+ {
+ return null;
+ }
+ }
+
private sealed class SymbolStoreKeyWrapper
{
public readonly SymbolStoreKey Key;
<PrivateAssets>All</PrivateAssets>
</ProjectReference>
</ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Azure.Identity" Version="$(AzureIdentityVersion)" />
+ </ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">