Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion dotnet/src/webdriver/BiDi/Broker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public async ValueTask DisposeAsync()

_receiveMessagesCancellationTokenSource.Dispose();

_transport.Dispose();
await _transport.DisposeAsync().ConfigureAwait(false);

GC.SuppressFinalize(this);
}
Expand Down Expand Up @@ -269,6 +269,15 @@ private async Task ReceiveMessagesAsync(CancellationToken cancellationToken)
_logger.Error($"Unhandled error occurred while receiving remote messages: {ex}");
}

// Fail all pending commands, as the connection is likely broken if we failed to receive messages.
foreach (var id in _pendingCommands.Keys)
{
if (_pendingCommands.TryRemove(id, out var pendingCommand))
{
pendingCommand.TaskCompletionSource.TrySetException(ex);
}
}

throw;
}
}
Expand Down
2 changes: 1 addition & 1 deletion dotnet/src/webdriver/BiDi/ITransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

namespace OpenQA.Selenium.BiDi;

interface ITransport : IDisposable
interface ITransport : IAsyncDisposable
{
Task<byte[]> ReceiveAsync(CancellationToken cancellationToken);

Expand Down
43 changes: 28 additions & 15 deletions dotnet/src/webdriver/BiDi/WebSocketTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

namespace OpenQA.Selenium.BiDi;

sealed class WebSocketTransport(ClientWebSocket webSocket) : ITransport, IDisposable
sealed class WebSocketTransport(ClientWebSocket webSocket) : ITransport
{
private readonly static ILogger _logger = Internal.Logging.Log.GetLogger<WebSocketTransport>();

Expand Down Expand Up @@ -67,6 +67,14 @@ public async Task<byte[]> ReceiveAsync(CancellationToken cancellationToken)
{
result = await _webSocket.ReceiveAsync(segment, cancellationToken).ConfigureAwait(false);

if (result.MessageType == WebSocketMessageType.Close)
{
await _webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);

throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely,
$"The remote end closed the WebSocket connection. Status: {result.CloseStatus}, Description: {result.CloseStatusDescription}");
}
Comment thread
nvborisenko marked this conversation as resolved.

_sharedMemoryStream.Write(receiveBuffer, 0, result.Count);
}
while (!result.EndOfMessage);
Expand Down Expand Up @@ -107,26 +115,31 @@ public async Task SendAsync(byte[] data, CancellationToken cancellationToken)

private bool _disposed;

public void Dispose()
public async ValueTask DisposeAsync()
{
Dispose(true);
GC.SuppressFinalize(this);
}
if (_disposed) return;
Comment thread
nvborisenko marked this conversation as resolved.

private void Dispose(bool disposing)
{
if (_disposed)
if (_webSocket.State == WebSocketState.Open)
{
return;
try
{
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
if (_logger.IsEnabled(LogEventLevel.Warn))
{
_logger.Warn($"Error closing WebSocket gracefully: {ex.Message}");
}
}
}

if (disposing)
{
_webSocket.Dispose();
_sharedMemoryStream.Dispose();
_socketSendSemaphoreSlim.Dispose();
}
_webSocket.Dispose();
_sharedMemoryStream.Dispose();
_socketSendSemaphoreSlim.Dispose();

_disposed = true;

GC.SuppressFinalize(this);
}
}