Skip to content

Commit 2aed524

Browse files
committed
Add cancellation to initial socket connection
Part of the fix to #1420 Use a linked token source to combine external cancellation and timeout cancelltion for initial RMQ connection Fix API since we have to use the same for all target frameworks. Fix the order of operations when establishing a connection. Add missing ConfigureAwait Add some more missing ConfigureAwait(false) statements. A timeout of 1 second can result in OperationCanceledException Validate hostname in SocketFrameHandler to restore expectation that an exception will be thrown. Ugh. Increase connection timeout for CI Add helpful message to connection failure. Fix tests in a very ugly way. Much TODO Make endpoint resolver SelectOne async. Pass on CancellationToken Pass cancellation token into more connection establishment methods. Fix expected exception for net472 Use Async method otherwise GetAwaiter/GetResult locks up Change expected exception type Increase connection timeout for CI due to slow runners Try out idea to fix odd CI error in GHA Bump version Upgrade dependencies. Modify Windows GHA setup to explicitly create firewall rules for Erlang programs. Tweak setting up firewalls on Windows Use Get-Random to help ensure unique fw rule names
1 parent 52f494c commit 2aed524

39 files changed

+691
-409
lines changed

.ci/windows/gha-setup.ps1

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
$ProgressPreference = 'Continue'
2+
$VerbosePreference = 'Continue'
23
$ErrorActionPreference = 'Stop'
34
Set-StrictMode -Version 2.0
45

@@ -38,7 +39,7 @@ $erlang_install_dir = Join-Path -Path $HOME -ChildPath 'erlang'
3839

3940
Write-Host '[INFO] Downloading Erlang...'
4041

41-
if (-Not (Test-Path $erlang_installer_path))
42+
if (-Not (Test-Path -LiteralPath $erlang_installer_path))
4243
{
4344
Invoke-WebRequest -UseBasicParsing -Uri $erlang_download_url -OutFile $erlang_installer_path
4445
}
@@ -54,26 +55,76 @@ $rabbitmq_installer_download_url = "https://github.com/rabbitmq/rabbitmq-server/
5455
$rabbitmq_installer_path = Join-Path -Path $base_installers_dir -ChildPath "rabbitmq-server-$rabbitmq_ver.exe"
5556
Write-Host "[INFO] rabbitmq installer path $rabbitmq_installer_path"
5657

57-
$erlang_reg_path = 'HKLM:\SOFTWARE\Ericsson\Erlang'
58-
if (Test-Path 'HKLM:\SOFTWARE\WOW6432Node\')
58+
if (Test-Path -LiteralPath 'HKLM:\SOFTWARE\WOW6432Node\')
5959
{
60-
$erlang_reg_path = 'HKLM:\SOFTWARE\WOW6432Node\Ericsson\Erlang'
60+
New-Variable -Name erlangRegKeyPath -Option Constant `
61+
-Value 'HKLM:\SOFTWARE\WOW6432Node\Ericsson\Erlang'
62+
}
63+
else
64+
{
65+
New-Variable -Name erlangRegKeyPath -Option Constant `
66+
-Value 'HKLM:\SOFTWARE\Ericsson\Erlang'
67+
}
68+
69+
New-Variable -Name erlangRegKey -Option Constant `
70+
-Value (Get-ChildItem $erlangRegKeyPath)
71+
72+
if ($erlangRegKey -eq $null) {
73+
Write-Error "Could not find Erlang installation registry key at $erlangRegKeyPath"
74+
}
75+
76+
New-Variable -Name erlangErtsVersion -Option Constant `
77+
-Value (Select-Object -InputObject $erlangRegKey -Last 1).PSChildName
78+
Write-Verbose "erlangErtsVersion: $erlangErtsVersion"
79+
80+
New-Variable -Name erlangErtsRegKeyPath -Option Constant `
81+
-Value "HKLM:\SOFTWARE\WOW6432Node\Ericsson\Erlang\$erlangErtsVersion"
82+
83+
New-Variable -Name erlangErtsRegKey -Option Constant `
84+
-Value (Get-ItemProperty -LiteralPath HKLM:\SOFTWARE\WOW6432Node\Ericsson\Erlang\$erlangErtsVersion)
85+
86+
if ($erlangErtsRegKey -eq $null) {
87+
Write-Error "Could not find Erlang erts registry key at $erlangErtsRegKeyPath"
88+
}
89+
90+
New-Variable -Name erlangProgramFilesPath -Option Constant `
91+
-Value ($erlangErtsRegKey.'(default)')
92+
93+
if (Test-Path -LiteralPath $erlangProgramFilesPath) {
94+
Write-Verbose "Erlang installation directory: '$erlangProgramFilesPath'"
95+
}
96+
else {
97+
Write-Error 'Could not find Erlang installation directory!'
98+
}
99+
100+
New-Variable -Name allowedExes -Option Constant -Value @('erl.exe', 'epmd.exe', 'werl.exe')
101+
102+
New-Variable -Name exes -Option Constant -Value `
103+
$(Get-ChildItem -Filter '*.exe' -Recurse -LiteralPath $erlangProgramFilesPath | Where-Object { $_.Name -in $allowedExes })
104+
105+
foreach ($exe in $exes) {
106+
$fwRuleName = "rabbitmq-allow-$($exe.Name)-$(Get-Random)"
107+
Write-Verbose "Updating or creating firewall rule for '$exe' - fwRuleName: $fwRuleName"
108+
if (!(Get-NetFirewallRule -ErrorAction 'SilentlyContinue' -Name $fwRuleName)) {
109+
New-NetFirewallRule -Enabled True -Name $fwRuleName -DisplayName $fwRuleName -Direction In -Program $exe -Profile Any -Action Allow
110+
}
111+
else {
112+
Set-NetFirewallRule -Enabled True -Name $fwRuleName -DisplayName $fwRuleName -Direction In -Program $exe -Profile Any -Action Allow
113+
}
61114
}
62-
$erlang_erts_version = Get-ChildItem -Path $erlang_reg_path -Name
63-
$erlang_home = (Get-ItemProperty -LiteralPath $erlang_reg_path\$erlang_erts_version).'(default)'
64115

65-
Write-Host "[INFO] Setting ERLANG_HOME to '$erlang_home'..."
66-
$env:ERLANG_HOME = $erlang_home
67-
[Environment]::SetEnvironmentVariable('ERLANG_HOME', $erlang_home, 'Machine')
68-
Add-Content -Verbose -LiteralPath $env:GITHUB_ENV -Value "ERLANG_HOME=$erlang_home"
116+
Write-Host "[INFO] Setting ERLANG_HOME to '$erlangProgramFilesPath'..."
117+
$env:ERLANG_HOME = $erlangProgramFilesPath
118+
[Environment]::SetEnvironmentVariable('ERLANG_HOME', $erlangProgramFilesPath, 'Machine')
119+
Add-Content -Verbose -LiteralPath $env:GITHUB_ENV -Value "ERLANG_HOME=$erlangProgramFilesPath"
69120

70121
Write-Host "[INFO] Setting RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS..."
71122
$env:RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS = '-rabbitmq_stream advertised_host localhost'
72123
[Environment]::SetEnvironmentVariable('RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS', '-rabbitmq_stream advertised_host localhost', 'Machine')
73124

74125
Write-Host '[INFO] Downloading RabbitMQ...'
75126

76-
if (-Not (Test-Path $rabbitmq_installer_path))
127+
if (-Not (Test-Path -LiteralPath $rabbitmq_installer_path))
77128
{
78129
Invoke-WebRequest -UseBasicParsing -Uri $rabbitmq_installer_download_url -OutFile $rabbitmq_installer_path
79130
}
@@ -83,15 +134,15 @@ else
83134
}
84135

85136
Write-Host "[INFO] Installer dir '$base_installers_dir' contents:"
86-
Get-ChildItem -Verbose -Path $base_installers_dir
137+
Get-ChildItem -Verbose -LiteralPath $base_installers_dir
87138

88139
$rabbitmq_conf_in_file = Join-Path -Path $ci_windows_dir -ChildPath 'rabbitmq.conf.in'
89140
$rabbitmq_appdata_dir = Join-Path -Path $env:AppData -ChildPath 'RabbitMQ'
90141
New-Item -Path $rabbitmq_appdata_dir -ItemType Directory
91142
$rabbitmq_conf_file = Join-Path -Path $rabbitmq_appdata_dir -ChildPath 'rabbitmq.conf'
92143

93144
Write-Host "[INFO] Creating RabbitMQ configuration file in '$rabbitmq_appdata_dir'"
94-
Get-Content $rabbitmq_conf_in_file | %{ $_ -replace '@@CERTS_DIR@@', $certs_dir } | %{ $_ -replace '\\', '/' } | Set-Content -Path $rabbitmq_conf_file
145+
Get-Content $rabbitmq_conf_in_file | %{ $_ -replace '@@CERTS_DIR@@', $certs_dir } | %{ $_ -replace '\\', '/' } | Set-Content -LiteralPath $rabbitmq_conf_file
95146
Get-Content $rabbitmq_conf_file
96147

97148
Write-Host '[INFO] Creating Erlang cookie files...'
@@ -114,9 +165,9 @@ Write-Host '[INFO] Installing and starting RabbitMQ...'
114165
& $rabbitmq_installer_path '/S' | Out-Null
115166
(Get-Service -Name RabbitMQ).Status
116167

117-
$rabbitmq_base_path = (Get-ItemProperty -Name Install_Dir -Path 'HKLM:\SOFTWARE\WOW6432Node\VMware, Inc.\RabbitMQ Server').Install_Dir
168+
$rabbitmq_base_path = (Get-ItemProperty -Name Install_Dir -LiteralPath 'HKLM:\SOFTWARE\WOW6432Node\VMware, Inc.\RabbitMQ Server').Install_Dir
118169
$regPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\RabbitMQ'
119-
if (Test-Path 'HKLM:\SOFTWARE\WOW6432Node\')
170+
if (Test-Path -LiteralPath 'HKLM:\SOFTWARE\WOW6432Node\')
120171
{
121172
$regPath = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\RabbitMQ'
122173
}
@@ -138,7 +189,7 @@ $env:RABBITMQ_RABBITMQCTL_PATH = $rabbitmqctl_path
138189
$epmd_running = $false
139190
[int]$count = 1
140191

141-
$epmd_exe = Join-Path -Path $erlang_home -ChildPath "erts-$erlang_erts_version" | Join-Path -ChildPath 'bin' | Join-Path -ChildPath 'epmd.exe'
192+
$epmd_exe = Join-Path -Path $erlangProgramFilesPath -ChildPath "erts-$erlangErtsVersion" | Join-Path -ChildPath 'bin' | Join-Path -ChildPath 'epmd.exe'
142193

143194
Write-Host "[INFO] Waiting for epmd ($epmd_exe) to report that RabbitMQ has started..."
144195

.ci/windows/versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"erlang": "26.1.2",
3-
"rabbitmq": "3.12.6"
3+
"rabbitmq": "3.12.10"
44
}

projects/Benchmarks/Benchmarks.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18-
<PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
18+
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
1919
<PackageReference Include="Ductus.FluentDocker" Version="2.10.59" />
2020
</ItemGroup>
2121

projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
</PropertyGroup>
3939

4040
<ItemGroup Condition="'$(Configuration)' == 'Release' and '$(SourceRoot)' == ''">
41-
<SourceRoot Include="$(MSBuildThisFileDirectory)/"/>
41+
<SourceRoot Include="$(MSBuildThisFileDirectory)/" />
4242
</ItemGroup>
4343

4444
<ItemGroup>
@@ -54,10 +54,10 @@
5454
</ItemGroup>
5555

5656
<ItemGroup>
57-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
57+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
5858
<PackageReference Include="MinVer" Version="4.3.0" PrivateAssets="all" />
59-
<PackageReference Include="System.Net.Http.Json" Version="7.0.1" />
60-
<PackageReference Include="System.Text.Json" Version="7.0.3" />
59+
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
60+
<PackageReference Include="System.Text.Json" Version="8.0.0" />
6161
</ItemGroup>
6262

6363
<ItemGroup>

projects/RabbitMQ.Client/RabbitMQ.Client.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767

6868
<ItemGroup>
6969
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
70-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
70+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
7171
<PackageReference Include="MinVer" Version="4.3.0" PrivateAssets="all" />
7272
<PackageReference Include="System.Memory" Version="4.5.5" />
7373
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />

projects/RabbitMQ.Client/client/TaskExtensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,23 @@ internal static class TaskExtensions
4040
#if !NET6_0_OR_GREATER
4141
private static readonly TaskContinuationOptions s_tco = TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously;
4242
private static void IgnoreTaskContinuation(Task t, object s) => t.Exception.Handle(e => true);
43+
44+
public static async Task WithCancellation(this Task task, CancellationToken cancellationToken)
45+
{
46+
var tcs = new TaskCompletionSource<bool>();
47+
48+
// https://devblogs.microsoft.com/pfxteam/how-do-i-cancel-non-cancelable-async-operations/
49+
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
50+
{
51+
if (task != await Task.WhenAny(task, tcs.Task))
52+
{
53+
task.Ignore();
54+
throw new OperationCanceledException(cancellationToken);
55+
}
56+
}
57+
58+
await task.ConfigureAwait(false);
59+
}
4360
#endif
4461

4562
public static Task TimeoutAfter(this Task task, TimeSpan timeout)

projects/RabbitMQ.Client/client/api/ConnectionConfig.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
using System;
3333
using System.Collections.Generic;
34-
34+
using System.Threading;
35+
using System.Threading.Tasks;
3536
using RabbitMQ.Client.Impl;
3637

3738
namespace RabbitMQ.Client
@@ -142,7 +143,7 @@ public sealed class ConnectionConfig
142143
/// </summary>
143144
public readonly int DispatchConsumerConcurrency;
144145

145-
internal readonly Func<AmqpTcpEndpoint, IFrameHandler> FrameHandlerFactory;
146+
internal readonly Func<AmqpTcpEndpoint, CancellationToken, Task<IFrameHandler>> FrameHandlerFactoryAsync;
146147

147148
internal ConnectionConfig(string virtualHost, string userName, string password,
148149
ICredentialsProvider credentialsProvider, ICredentialsRefresher credentialsRefresher,
@@ -152,7 +153,7 @@ internal ConnectionConfig(string virtualHost, string userName, string password,
152153
TopologyRecoveryFilter topologyRecoveryFilter, TopologyRecoveryExceptionHandler topologyRecoveryExceptionHandler,
153154
TimeSpan networkRecoveryInterval, TimeSpan heartbeatInterval, TimeSpan continuationTimeout, TimeSpan handshakeContinuationTimeout, TimeSpan requestedConnectionTimeout,
154155
bool dispatchConsumersAsync, int dispatchConsumerConcurrency,
155-
Func<AmqpTcpEndpoint, IFrameHandler> frameHandlerFactory)
156+
Func<AmqpTcpEndpoint, CancellationToken, Task<IFrameHandler>> frameHandlerFactoryAsync)
156157
{
157158
VirtualHost = virtualHost;
158159
UserName = userName;
@@ -174,7 +175,7 @@ internal ConnectionConfig(string virtualHost, string userName, string password,
174175
RequestedConnectionTimeout = requestedConnectionTimeout;
175176
DispatchConsumersAsync = dispatchConsumersAsync;
176177
DispatchConsumerConcurrency = dispatchConsumerConcurrency;
177-
FrameHandlerFactory = frameHandlerFactory;
178+
FrameHandlerFactoryAsync = frameHandlerFactoryAsync;
178179
}
179180
}
180181
}

0 commit comments

Comments
 (0)