Skip to content

Commit 52e7103

Browse files
committed
fix(System.Net.Sockets): avoid errors for pending nonblocking connects
Non-blocking Socket.Connect can return WouldBlock on Windows or InProgress on Unix while the OS connect attempt remains pending. Treat those results as pending telemetry states instead of failures. Leave the emitted connect Activity status unset, omit error.type, and avoid emitting ConnectFailed for these pending results. Add a regression test for the real synchronous Connect path so the pending result is verified through the public socket API.
1 parent ad50b41 commit 52e7103

2 files changed

Lines changed: 40 additions & 2 deletions

File tree

src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,16 @@ public void AfterConnect(SocketError error, Activity? activity, string? exceptio
147147
long newCount = Interlocked.Decrement(ref _currentOutgoingConnectAttempts);
148148
Debug.Assert(newCount >= 0);
149149

150+
// _currentOutgoingConnectAttempts tracks managed Connect calls. A non-blocking Connect call
151+
// can return WouldBlock (Windows) or InProgress (Unix) while the OS connect attempt remains
152+
// pending after this method decrements that counter; its eventual outcome is not observable
153+
// here. Don't report these pending results as failures.
154+
// See https://github.com/dotnet/runtime/issues/129252
155+
bool connectPending = error is SocketError.WouldBlock or SocketError.InProgress;
156+
150157
if (activity is not null)
151158
{
152-
if (error != SocketError.Success)
159+
if (error != SocketError.Success && !connectPending)
153160
{
154161
activity.SetStatus(ActivityStatusCode.Error);
155162
activity.SetTag("error.type", GetErrorType(error));
@@ -163,7 +170,7 @@ public void AfterConnect(SocketError error, Activity? activity, string? exceptio
163170
Debug.Assert(exceptionMessage is null);
164171
Interlocked.Increment(ref _outgoingConnectionsEstablished);
165172
}
166-
else
173+
else if (!connectPending)
167174
{
168175
ConnectFailed(error, exceptionMessage);
169176
}

src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,37 @@ static void VerifyTcpConnectActivity(Activity activity, IPEndPoint remoteEndPoin
158158
ActivityAssert.HasTag(activity, "network.transport", "tcp");
159159
}
160160

161+
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
162+
public async Task Connect_NonBlockingPending_ActivityNotMarkedAsError()
163+
{
164+
await RemoteExecutor.Invoke(static () =>
165+
{
166+
using Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
167+
server.BindToAnonymousPort(IPAddress.Loopback);
168+
server.Listen();
169+
170+
using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
171+
{
172+
Blocking = false
173+
};
174+
175+
using ActivityRecorder recorder = new ActivityRecorder(ActivitySourceName, ActivityName);
176+
177+
// A non-blocking connect reports WouldBlock (Windows) or InProgress (Unix) by design while
178+
// the attempt continues in the background. The "socket connect" activity is started and
179+
// stopped synchronously inside Connect, so its final state can be asserted immediately.
180+
SocketException ex = Assert.Throws<SocketException>(() => client.Connect(server.LocalEndPoint));
181+
Assert.True(ex.SocketErrorCode is SocketError.WouldBlock or SocketError.InProgress,
182+
$"Unexpected SocketError: {ex.SocketErrorCode}");
183+
184+
recorder.VerifyActivityRecorded(1);
185+
Activity activity = recorder.LastFinishedActivity;
186+
VerifyTcpConnectActivity(activity, (IPEndPoint)server.LocalEndPoint, ipv6: false);
187+
Assert.Equal(ActivityStatusCode.Unset, activity.Status);
188+
Assert.Null(activity.GetTagItem("error.type"));
189+
}).DisposeAsync();
190+
}
191+
161192
[OuterLoop("Connection failure takes long on Windows.")]
162193
[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
163194
[MemberData(nameof(SocketMethods_WithBools_MemberData))]

0 commit comments

Comments
 (0)