TorHttpPool: Avoid using `AnyOneOffCircuit` as actual circuit
MODIFY
WalletWasabi.Tests/UnitTests/Tor/Socks5/Pool/TorHttpPoolTests.cs
WalletWasabi.Tests/UnitTests/Tor/Socks5/Pool/TorHttpPoolTests.cs
@@ -75,13 +75,13 @@ public class TorHttpPoolTests
using HttpRequestMessage request = new(HttpMethod.Get, "http://wasabi.backend");
- using HttpResponseMessage aliceResponse = await pool.SendAsync(request, aliceIdentity);
+ using HttpResponseMessage aliceResponse = await pool.SendAsync(request, aliceIdentity, timeoutCts.Token);
Assert.Equal("Alice circuit!", await aliceResponse.Content.ReadAsStringAsync(timeoutCts.Token));
- using HttpResponseMessage bobResponse = await pool.SendAsync(request, bobIdentity);
+ using HttpResponseMessage bobResponse = await pool.SendAsync(request, bobIdentity, timeoutCts.Token);
Assert.Equal("Bob circuit!", await bobResponse.Content.ReadAsStringAsync(timeoutCts.Token));
- using HttpResponseMessage defaultResponse = await pool.SendAsync(request, defaultIdentity);
+ using HttpResponseMessage defaultResponse = await pool.SendAsync(request, defaultIdentity, timeoutCts.Token);
Assert.Equal("Default circuit!", await defaultResponse.Content.ReadAsStringAsync(timeoutCts.Token));
mockTcpConnectionFactory.VerifyAll();
@@ -116,7 +116,7 @@ public class TorHttpPoolTests
Task sendTask = Task.Run(async () =>
{
Debug.WriteLine("[client] About send HTTP request.");
- using HttpResponseMessage httpResponseMessage = await pool.SendAsync(request, circuit).ConfigureAwait(false);
+ using HttpResponseMessage httpResponseMessage = await pool.SendAsync(request, circuit, timeoutCts.Token).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.OK, httpResponseMessage.StatusCode);
Debug.WriteLine("[client] Done sending HTTP request.");
});
@@ -215,14 +215,14 @@ public class TorHttpPoolTests
using HttpRequestMessage request = new(HttpMethod.Get, "http://wasabi.backend");
// Alice circuit is NOT yet disposed.
- using HttpResponseMessage aliceResponse = await pool.SendAsync(request, aliceCircuit);
+ using HttpResponseMessage aliceResponse = await pool.SendAsync(request, aliceCircuit, timeoutCts.Token);
Assert.True(aliceResponse.IsSuccessStatusCode);
// Dispose Alice circuit.
aliceCircuit.Dispose();
// Alice circuit is already disposed and thus it cannot be used.
- HttpRequestException httpRequestException = await Assert.ThrowsAsync<HttpRequestException>(async () => await pool.SendAsync(request, aliceCircuit).ConfigureAwait(false));
+ HttpRequestException httpRequestException = await Assert.ThrowsAsync<HttpRequestException>(async () => await pool.SendAsync(request, aliceCircuit, timeoutCts.Token).ConfigureAwait(false));
_ = Assert.IsType<TorCircuitExpiredException>(httpRequestException.InnerException);
mockTcpConnectionFactory.VerifyAll();

MODIFY
WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs
WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs
@@ -137,7 +137,7 @@ public class TorHttpPool : IAsyncDisposable
/// </summary>
/// <exception cref="HttpRequestException">When <paramref name="request"/> fails to be processed.</exception>
/// <exception cref="OperationCanceledException">When the operation was canceled.</exception>
- public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, ICircuit circuit, CancellationToken cancellationToken = default)
+ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, ICircuit circuit, CancellationToken cancellationToken)
{
int i = 0;
int attemptsNo = 3;

MODIFY
WalletWasabi/Tor/Socks5/TorTcpConnectionFactory.cs
WalletWasabi/Tor/Socks5/TorTcpConnectionFactory.cs
@@ -72,9 +72,16 @@ public class TorTcpConnectionFactory
{
TcpClient? tcpClient = null;
Stream? transportStream = null;
+ OneOffCircuit? oneOffCircuitToDispose = null;
try
{
+ if (circuit is AnyOneOffCircuit)
+ {
+ oneOffCircuitToDispose = new OneOffCircuit();
+ circuit = oneOffCircuitToDispose;
+ }
+
tcpClient = await TcpClientSocks5Connector.ConnectAsync(TorSocks5EndPoint, cancellationToken).ConfigureAwait(false);
transportStream = tcpClient.GetStream();
@@ -91,12 +98,15 @@ public class TorTcpConnectionFactory
transportStream = null;
tcpClient = null;
+ oneOffCircuitToDispose = null;
+
return result;
}
finally
{
transportStream?.Dispose();
tcpClient?.Dispose();
+ oneOffCircuitToDispose?.Dispose();
}
}
@@ -129,7 +139,7 @@ public class TorTcpConnectionFactory
/// <seealso href="https://gitweb.torproject.org/torspec.git/tree/socks-extensions.txt#n35"/>
/// <exception cref="NotSupportedException">When authentication fails due to unsupported authentication method.</exception>
/// <exception cref="InvalidOperationException">When authentication fails due to invalid credentials.</exception>
- private async Task HandshakeAsync(TcpClient tcpClient, ICircuit circuit, CancellationToken cancellationToken = default)
+ private async Task HandshakeAsync(TcpClient tcpClient, ICircuit circuit, CancellationToken cancellationToken)
{
VersionMethodRequest versionMethodRequest = circuit switch
{
@@ -208,7 +218,7 @@ public class TorTcpConnectionFactory
/// <exception cref="TorConnectCommandFailedException">When response to <see cref="CmdField.Connect"/> request is NOT <see cref="RepField.Succeeded"/>.</exception>
/// <exception cref="TorException">When sending of the HTTP(s) request fails for any reason.</exception>
/// <seealso href="https://tools.ietf.org/html/rfc1928">Section 3. Procedure for TCP-based clients</seealso>
- private async Task ConnectToDestinationAsync(TcpClient tcpClient, string host, int port, CancellationToken cancellationToken = default)
+ private async Task ConnectToDestinationAsync(TcpClient tcpClient, string host, int port, CancellationToken cancellationToken)
{
Logger.LogTrace($"> {nameof(host)}='{host}', {nameof(port)}={port}");

TorHttpPool: Avoid using `AnyOneOffCircuit` as actual circuit