Skip to content

Commit

Permalink
Tor: Introduce --UseOnlyRunningTor command line option
Browse files Browse the repository at this point in the history
  • Loading branch information
kiminuo committed May 6, 2024
1 parent bbb6860 commit 2ecc6c3
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 44 deletions.
4 changes: 4 additions & 0 deletions WalletWasabi.Daemon/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ [ nameof(RegTestCoordinatorUri)] = (
[ nameof(UseTor)] = (
"All the communications go through the Tor network",
GetBoolValue("UseTor", PersistentConfig.UseTor, cliArgs)),
[ nameof(UseOnlyRunningTor)] = (
"Connect to an already running Tor instance without attempting to run the bundled Tor",
GetBoolValue("UseOnlyRunningTor", false, cliArgs)),

Check warning on line 64 in WalletWasabi.Daemon/Config.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ Getting worse: Large Method

Config increases from 116 to 119 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
[ nameof(TorFolder)] = (
"Folder where Tor binary is located",
GetNullableStringValue("TorFolder", null, cliArgs)),
Expand Down Expand Up @@ -155,6 +158,7 @@ [ nameof(CoordinatorIdentifier)] = (
public string? TestNetCoordinatorUri => GetEffectiveValue<NullableStringValue, string?>(nameof(TestNetCoordinatorUri));
public string? RegTestCoordinatorUri => GetEffectiveValue<NullableStringValue, string?>(nameof(RegTestCoordinatorUri));
public bool UseTor => GetEffectiveValue<BoolValue, bool>(nameof(UseTor)) && Network != Network.RegTest;
public bool UseOnlyRunningTor => GetEffectiveValue<BoolValue, bool>(nameof(UseOnlyRunningTor));
public string? TorFolder => GetEffectiveValue<NullableStringValue, string?>(nameof(TorFolder));
public int TorSocksPort => GetEffectiveValue<IntValue, int>(nameof(TorSocksPort));
public int TorControlPort => GetEffectiveValue<IntValue, int>(nameof(TorControlPort));
Expand Down
13 changes: 10 additions & 3 deletions WalletWasabi.Daemon/Global.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public Global(string dataDir, string configFilePath, Config config)
TorSettings = new TorSettings(
DataDir,
distributionFolderPath: EnvironmentHelpers.GetFullBaseDirectory(),
Config.TerminateTorOnExit,
terminateOnExit: Config.TerminateTorOnExit,
useOnlyRunningTor: Config.UseOnlyRunningTor,

Check warning on line 58 in WalletWasabi.Daemon/Global.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ Getting worse: Large Method

Global increases from 73 to 74 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
socksPort: config.TorSocksPort,
controlPort: config.TorControlPort,
torFolder: config.TorFolder,
Expand Down Expand Up @@ -186,7 +187,8 @@ public Global(string dataDir, string configFilePath, Config config)
private WasabiHttpClientFactory BuildHttpClientFactory(Func<Uri> backendUriGetter) =>
new(
Config.UseTor ? TorSettings.SocksEndpoint : null,
backendUriGetter);
backendUriGetter,
torControlAvailable: !TorSettings.UseOnlyRunningTor);

public async Task InitializeNoWalletAsync(bool initializeSleepInhibitor, TerminateService terminateService, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -336,7 +338,12 @@ private async Task StartTorProcessManagerAsync(CancellationToken cancellationTok
}
}

HostedServices.Register<TorMonitor>(() => new TorMonitor(period: TimeSpan.FromMinutes(1), torProcessManager: TorManager, httpClientFactory: HttpClientFactory), nameof(TorMonitor));
// Do not monitor Tor when Tor is an already running service.
if (!TorSettings.UseOnlyRunningTor)
{
HostedServices.Register<TorMonitor>(() => new TorMonitor(period: TimeSpan.FromMinutes(1), torProcessManager: TorManager, httpClientFactory: HttpClientFactory), nameof(TorMonitor));
}

HostedServices.Register<TorStatusChecker>(() => TorStatusChecker, "Tor Network Checker");
}
}
Expand Down
31 changes: 22 additions & 9 deletions WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,16 @@ public class TorHttpPool : IAsyncDisposable
SingleWriter = false,
};

public TorHttpPool(EndPoint endpoint)
: this(new TorTcpConnectionFactory(endpoint))
public TorHttpPool(EndPoint endpoint, bool torControlAvailable = true)
: this(new TorTcpConnectionFactory(endpoint), torControlAvailable)
{
}

/// <summary>Constructor that helps in tests.</summary>
internal TorHttpPool(TorTcpConnectionFactory tcpConnectionFactory)
internal TorHttpPool(TorTcpConnectionFactory tcpConnectionFactory, bool torControlAvailable = true)
{
TcpConnectionFactory = tcpConnectionFactory;
TorControlAvailable = torControlAvailable;
PreBuildingRequestChannel = Channel.CreateUnbounded<TorPrebuildCircuitRequest>(Options);
PreBuildingLoopTask = Task.Run(PreBuildingLoopAsync);
}
Expand All @@ -72,7 +73,7 @@ internal TorHttpPool(TorTcpConnectionFactory tcpConnectionFactory)
private object ConnectionsLock { get; } = new();

private TorTcpConnectionFactory TcpConnectionFactory { get; }

public bool TorControlAvailable { get; }
public static DateTimeOffset? TorDoesntWorkSince { get; private set; }

public Task<bool> IsTorRunningAsync(CancellationToken cancel)
Expand Down Expand Up @@ -407,9 +408,12 @@ private async Task<TorTcpConnection> ObtainFreeConnectionAsync(Uri requestUri, I

private async Task<TorTcpConnection?> CreateNewConnectionAsync(Uri requestUri, INamedCircuit circuit, CancellationToken cancellationToken)
{
lock (ConnectionsLock)
if (TorControlAvailable)
{
TorStreamsBeingBuilt[circuit.Name] = null;
lock (ConnectionsLock)
{
TorStreamsBeingBuilt[circuit.Name] = null;
}
}

TorTcpConnection? connection = null;
Expand Down Expand Up @@ -443,11 +447,14 @@ private async Task<TorTcpConnection> ObtainFreeConnectionAsync(Uri requestUri, I
{
lock (ConnectionsLock)
{
if (TorStreamsBeingBuilt.Remove(circuit.Name, out TorStreamInfo? streamInfo))
if (TorControlAvailable)
{
if (connection is not null && streamInfo is not null)
if (TorStreamsBeingBuilt.Remove(circuit.Name, out TorStreamInfo? streamInfo))
{
connection.SetLatestStreamInfo(streamInfo, ifEmpty: true);
if (connection is not null && streamInfo is not null)
{
connection.SetLatestStreamInfo(streamInfo, ifEmpty: true);
}

Check warning on line 457 in WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ Getting worse: Complex Method

CreateNewConnectionAsync increases in cyclomatic complexity from 10 to 12, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
}
}

Expand Down Expand Up @@ -524,8 +531,14 @@ internal virtual async Task<HttpResponseMessage> SendCoreAsync(TorTcpConnection
/// </remarks>
public void ReportStreamStatus(string streamUsername, StreamStatusFlag streamStatus, string circuitID)
{
if (TorControlAvailable)
{
throw new UnreachableException("This method can be called only if Wasabi was run with Tor Control support.");
}

lock (ConnectionsLock)
{

// If the key is not present, then are not the ones starting that Tor stream.
if (TorStreamsBeingBuilt.ContainsKey(streamUsername))
{
Expand Down
3 changes: 2 additions & 1 deletion WalletWasabi/Tor/TorMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
namespace WalletWasabi.Tor;

/// <summary>Monitors Tor process bootstrap and reachability of Wasabi Backend.</summary>
/// <remarks>Tor Monitor can only work if Tor Control is available.</remarks>
public class TorMonitor : PeriodicRunner
{
public static readonly TimeSpan CheckIfRunningAfterTorMisbehavedFor = TimeSpan.FromMinutes(10);
Expand Down Expand Up @@ -107,7 +108,7 @@ private async Task MonitorEventsAsync(CancellationToken cancellationToken)
using CancellationTokenSource linkedCts2 = CancellationTokenSource.CreateLinkedTokenSource(linkedCts.Token, torTerminatedCancellationToken);

Logger.LogInfo("Starting Tor bootstrap monitor…");
await torControlClient.SubscribeEventsAsync(EventNames, linkedCts2.Token).ConfigureAwait(false);
await torControlClient!.SubscribeEventsAsync(EventNames, linkedCts2.Token).ConfigureAwait(false);
bool circuitEstablished = false;

await foreach (TorControlReply reply in torControlClient.ReadEventsAsync(linkedCts2.Token).ConfigureAwait(false))
Expand Down
117 changes: 89 additions & 28 deletions WalletWasabi/Tor/TorProcessManager.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;

Check notice on line 1 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

✅ Getting better: Overall Code Complexity

The mean cyclomatic complexity decreases from 4.55 to 4.43, threshold = 4. This file has many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more conditionals.
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand All @@ -21,7 +22,7 @@ public class TorProcessManager : IAsyncDisposable
internal const string TorProcessStartedByDifferentUser = "Tor was started by another user and we can't use it nor kill it.";

/// <summary>Task completion source returning a cancellation token which is canceled when Tor process is terminated.</summary>
private volatile TaskCompletionSource<(CancellationToken, TorControlClient)> _tcs = new();
private volatile TaskCompletionSource<(CancellationToken, TorControlClient?)> _tcs = new();

public TorProcessManager(TorSettings settings) :
this(settings, new(settings.SocksEndpoint))
Expand Down Expand Up @@ -50,14 +51,20 @@ internal TorProcessManager(TorSettings settings, TorTcpConnectionFactory tcpConn
private TorSettings Settings { get; }
private TorTcpConnectionFactory TcpConnectionFactory { get; }

/// <remarks>Guarded by <see cref="StateLock"/>.</remarks>
/// <remarks>
/// Only set if <see cref="TorSettings.UseOnlyRunningTor"/> is not on.
/// <para>Guarded by <see cref="StateLock"/>.</para>
/// </remarks>
private ProcessAsync? TorProcess { get; set; }

/// <remarks>Guarded by <see cref="StateLock"/>.</remarks>
/// <remarks>
/// Only set if <see cref="TorSettings.UseOnlyRunningTor"/> is not on.
/// <para>Guarded by <see cref="StateLock"/>.</para>
/// </remarks>
private TorControlClient? TorControlClient { get; set; }

/// <inheritdoc cref="StartAsync(int, CancellationToken)"/>
public Task<(CancellationToken, TorControlClient)> StartAsync(CancellationToken cancellationToken)
public Task<(CancellationToken, TorControlClient?)> StartAsync(CancellationToken cancellationToken)
{
return StartAsync(attempts: 1, cancellationToken);
}
Expand All @@ -68,7 +75,7 @@ internal TorProcessManager(TorSettings settings, TorTcpConnectionFactory tcpConn
/// <remarks>This method must be called exactly once.</remarks>
/// <exception cref="OperationCanceledException">When the operation is cancelled by the user.</exception>
/// <exception cref="InvalidOperationException">When all attempts are tried without success.</exception>
public async Task<(CancellationToken, TorControlClient)> StartAsync(int attempts, CancellationToken cancellationToken)
public async Task<(CancellationToken, TorControlClient?)> StartAsync(int attempts, CancellationToken cancellationToken)
{
LoopTask = RestartingLoopAsync(cancellationToken);

Expand All @@ -94,7 +101,7 @@ internal TorProcessManager(TorSettings settings, TorTcpConnectionFactory tcpConn
/// <summary>Waits until Tor process is fully started or until it is stopped for some reason.</summary>
/// <returns>Cancellation token which is canceled once Tor process terminates or once <paramref name="cancellationToken"/> is canceled.</returns>
/// <remarks>This is useful to set up Tor control monitors that need to be restarted once Tor process is started again.</remarks>
public Task<(CancellationToken, TorControlClient)> WaitForNextAttemptAsync(CancellationToken cancellationToken)
public Task<(CancellationToken, TorControlClient?)> WaitForNextAttemptAsync(CancellationToken cancellationToken)
{
return _tcs.Task.WaitAsync(cancellationToken);
}
Expand All @@ -103,7 +110,56 @@ internal TorProcessManager(TorSettings settings, TorTcpConnectionFactory tcpConn
/// <param name="globalCancellationToken">Application lifetime cancellation token.</param>
private async Task RestartingLoopAsync(CancellationToken globalCancellationToken)
{
await RestartingLoopForBundledTorAsync(globalCancellationToken).ConfigureAwait(false);
if (Settings.UseOnlyRunningTor)
{
await RestartingLoopForRunningTorAsync(globalCancellationToken).ConfigureAwait(false);
}
else
{
await RestartingLoopForBundledTorAsync(globalCancellationToken).ConfigureAwait(false);
}
}

private async Task RestartingLoopForRunningTorAsync(CancellationToken globalCancellationToken)
{
using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(globalCancellationToken, LoopCts.Token);
CancellationToken cancellationToken = linkedCts.Token;

bool detectedTorState = false;
CancellationTokenSource torStoppedCts = new();

while (!cancellationToken.IsCancellationRequested)
{
bool isTorRunning = await TcpConnectionFactory.IsTorRunningAsync(cancellationToken).ConfigureAwait(false);

if (detectedTorState && isTorRunning) // Case: Still running.
{
// No-op.
}
else if (!detectedTorState && isTorRunning) // Case: Started running.
{
_tcs.SetResult((torStoppedCts.Token, null));
}
else if (detectedTorState && !isTorRunning) // Case: Stopped running.
{
bool willWaitForTorRestart = !cancellationToken.IsCancellationRequested;
OnTorStop(willWaitForTorRestart, exception: null, torStoppedCts, globalCancellationToken);
torStoppedCts = new();
}

detectedTorState = isTorRunning;

// We don't use Tor Control client with an already running Tor, so we just use a simple sleep mechanism.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);

if (cancellationToken.IsCancellationRequested)
{
Logger.LogDebug("User canceled operation.");
break;
}
}

torStoppedCts.Dispose();
}

Check warning on line 163 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ New issue: Complex Method

RestartingLoopForRunningTorAsync has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 163 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ New issue: Bumpy Road Ahead

RestartingLoopForRunningTorAsync has 3 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

/// <summary>
Expand Down Expand Up @@ -266,27 +322,7 @@ private async Task RestartingLoopForBundledTorAsync(CancellationToken globalCanc
}
finally
{
TaskCompletionSource<(CancellationToken, TorControlClient)> originalTcs = _tcs;

if (setNewTcs)
{
// (1) and (2) must be in this order. Otherwise, there is a race condition risk of getting invalid CT by clients.
TaskCompletionSource<(CancellationToken, TorControlClient)> newTcs = new();
originalTcs = Interlocked.Exchange(ref _tcs, newTcs); // (1)
}

cts.Cancel(); // (2)

if (exception is not null)
{
originalTcs.TrySetException(exception);
}
else
{
originalTcs.TrySetCanceled(globalCancellationToken);
}

cts.Dispose();
OnTorStop(setNewTcs, exception, cts, globalCancellationToken);

Check notice on line 325 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

✅ No longer an issue: Complex Method

RestartingLoopAsync is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 325 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ New issue: Complex Method

RestartingLoopForBundledTorAsync has a cyclomatic complexity of 19, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check notice on line 325 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

✅ No longer an issue: Bumpy Road Ahead

RestartingLoopAsync is no longer above the threshold for logical blocks with deeply nested code. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

Check warning on line 325 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ New issue: Bumpy Road Ahead

RestartingLoopForBundledTorAsync has 3 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

Check notice on line 325 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

✅ No longer an issue: Deep, Nested Complexity

RestartingLoopAsync is no longer above the threshold for nested complexity depth. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.

Check notice on line 325 in WalletWasabi/Tor/TorProcessManager.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

ℹ New issue: Deep, Nested Complexity

RestartingLoopForBundledTorAsync has a nested complexity depth of 4, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.

if (controlClient is not null)
{
Expand All @@ -304,6 +340,31 @@ private async Task RestartingLoopForBundledTorAsync(CancellationToken globalCanc
}
}

private void OnTorStop(bool willWaitForTorRestart, Exception? exception, CancellationTokenSource torStoppedCts, CancellationToken globalCancellationToken)
{
TaskCompletionSource<(CancellationToken, TorControlClient?)> originalTcs = _tcs;

if (willWaitForTorRestart)
{
// (1) and (2) must be in this order. Otherwise, there is a race condition risk of getting invalid CT by clients.
TaskCompletionSource<(CancellationToken, TorControlClient?)> newTcs = new();
originalTcs = Interlocked.Exchange(ref _tcs, newTcs); // (1)
}

torStoppedCts.Cancel(); // (2)

if (exception is not null)
{
originalTcs.TrySetException(exception);
}
else
{
originalTcs.TrySetCanceled(globalCancellationToken);
}

torStoppedCts.Dispose();
}

internal virtual Process[] GetTorProcesses()
{
return Process.GetProcessesByName(TorSettings.TorBinaryFileName);
Expand Down
14 changes: 13 additions & 1 deletion WalletWasabi/Tor/TorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public TorSettings(
string dataDir,
string distributionFolderPath,
bool terminateOnExit,
bool useOnlyRunningTor = false,
int socksPort = DefaultSocksPort,
int controlPort = DefaultControlPort,
string? torFolder = null,
Expand Down Expand Up @@ -62,12 +63,23 @@ public TorSettings(
LogFilePath = Path.Combine(dataDir, "TorLogs.txt");
IoHelpers.EnsureContainingDirectoryExists(LogFilePath);
DistributionFolder = distributionFolderPath;
TerminateOnExit = terminateOnExit;

if (useOnlyRunningTor && terminateOnExit)
{
Logger.LogWarning("Wasabi is instructed to use a running Tor process. Terminate on exit was disabled.");
}

UseOnlyRunningTor = useOnlyRunningTor;
TerminateOnExit = useOnlyRunningTor ? false : terminateOnExit;

Check warning on line 74 in WalletWasabi/Tor/TorSettings.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ New issue: Complex Method

TorSettings has a cyclomatic complexity of 10, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check notice on line 74 in WalletWasabi/Tor/TorSettings.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

ℹ Getting worse: Constructor Over-Injection

TorSettings increases from 9 to 10 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.
Log = log;
GeoIpPath = Path.Combine(DistributionFolder, "Tor", "Geoip", "geoip");
GeoIp6Path = Path.Combine(DistributionFolder, "Tor", "Geoip", "geoip6");
}

/// <summary><c>true</c> if Wasabi should not attempt to run any Tor process (bundled) or specified via <c>--TorFolder</c> command line option.</summary>
public bool UseOnlyRunningTor { get; }

/// <summary><c>true</c> if user specified a custom Tor folder to run a (possibly) different Tor binary than the bundled Tor, <c>false</c> otherwise.</summary>
public bool IsCustomTorFolder { get; }

Expand Down
4 changes: 2 additions & 2 deletions WalletWasabi/WebClients/Wasabi/WasabiHttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class WasabiHttpClientFactory : IWasabiHttpClientFactory, IAsyncDisposabl
/// Creates a new instance of the object.
/// </summary>
/// <param name="torEndPoint">If <c>null</c> then clearnet (not over Tor) is used, otherwise HTTP requests are routed through provided Tor endpoint.</param>
public WasabiHttpClientFactory(EndPoint? torEndPoint, Func<Uri>? backendUriGetter)
public WasabiHttpClientFactory(EndPoint? torEndPoint, Func<Uri>? backendUriGetter, bool torControlAvailable = true)
{
HttpClient = CreateLongLivedHttpClient(automaticDecompression: DecompressionMethods.GZip | DecompressionMethods.Brotli);

Expand All @@ -27,7 +27,7 @@ public WasabiHttpClientFactory(EndPoint? torEndPoint, Func<Uri>? backendUriGette
// Connecting to loopback's URIs cannot be done via Tor.
if (TorEndpoint is { } && (BackendUriGetter is null || !BackendUriGetter().IsLoopback))
{
TorHttpPool = new(TorEndpoint);
TorHttpPool = new(TorEndpoint, torControlAvailable);
BackendHttpClient = new TorHttpClient(BackendUriGetter, TorHttpPool, Mode.DefaultCircuit);
}
else
Expand Down

0 comments on commit 2ecc6c3

Please sign in to comment.