From 2939a249995321dfe10de636c4cc8cf86c2d5f99 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 26 Feb 2026 21:22:21 +0300 Subject: [PATCH 1/5] [dotnet] [bidi] Configurable options pattern --- dotnet/src/webdriver/BiDi/BiDi.cs | 5 ++++- dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/BiDi.cs b/dotnet/src/webdriver/BiDi/BiDi.cs index b65b15078cbd1..18b6267c71ae4 100644 --- a/dotnet/src/webdriver/BiDi/BiDi.cs +++ b/dotnet/src/webdriver/BiDi/BiDi.cs @@ -52,8 +52,11 @@ private BiDi() { } public Emulation.IEmulationModule Emulation => AsModule(); - public static async Task ConnectAsync(string url, BiDiOptions? options = null, CancellationToken cancellationToken = default) + public static async Task ConnectAsync(string url, Action? configure = null, CancellationToken cancellationToken = default) { + BiDiOptions options = new(); + configure?.Invoke(options); + var transport = await WebSocketTransport.ConnectAsync(new Uri(url), cancellationToken).ConfigureAwait(false); BiDi bidi = new(); diff --git a/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs b/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs index 124d6bebd18b6..dce131dbfb074 100644 --- a/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs +++ b/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs @@ -21,7 +21,7 @@ namespace OpenQA.Selenium.BiDi; public static class WebDriverExtensions { - public static async Task AsBiDiAsync(this IWebDriver webDriver, BiDiOptions? options = null, CancellationToken cancellationToken = default) + public static async Task AsBiDiAsync(this IWebDriver webDriver, Action? configure = null, CancellationToken cancellationToken = default) { if (webDriver is null) throw new ArgumentNullException(nameof(webDriver)); @@ -34,7 +34,7 @@ public static async Task AsBiDiAsync(this IWebDriver webDriver, BiDiOptio if (webSocketUrl is null) throw new BiDiException("The driver is not compatible with bidirectional protocol or \"webSocketUrl\" not enabled in driver options."); - var bidi = await BiDi.ConnectAsync(webSocketUrl, options, cancellationToken).ConfigureAwait(false); + var bidi = await BiDi.ConnectAsync(webSocketUrl, configure, cancellationToken).ConfigureAwait(false); return bidi; } From 3a873674c0762da4d3a93eaebac442241097845e Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 26 Feb 2026 21:50:59 +0300 Subject: [PATCH 2/5] Builder --- dotnet/src/webdriver/BiDi/BiDi.cs | 6 +++--- .../BiDi/{BiDiOptions.cs => BiDiOptionsBuilder.cs} | 4 ++-- dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename dotnet/src/webdriver/BiDi/{BiDiOptions.cs => BiDiOptionsBuilder.cs} (88%) diff --git a/dotnet/src/webdriver/BiDi/BiDi.cs b/dotnet/src/webdriver/BiDi/BiDi.cs index 18b6267c71ae4..082b92cc9e601 100644 --- a/dotnet/src/webdriver/BiDi/BiDi.cs +++ b/dotnet/src/webdriver/BiDi/BiDi.cs @@ -52,10 +52,10 @@ private BiDi() { } public Emulation.IEmulationModule Emulation => AsModule(); - public static async Task ConnectAsync(string url, Action? configure = null, CancellationToken cancellationToken = default) + public static async Task ConnectAsync(string url, Action? configure = null, CancellationToken cancellationToken = default) { - BiDiOptions options = new(); - configure?.Invoke(options); + BiDiOptionsBuilder builder = new(); + configure?.Invoke(builder); var transport = await WebSocketTransport.ConnectAsync(new Uri(url), cancellationToken).ConfigureAwait(false); diff --git a/dotnet/src/webdriver/BiDi/BiDiOptions.cs b/dotnet/src/webdriver/BiDi/BiDiOptionsBuilder.cs similarity index 88% rename from dotnet/src/webdriver/BiDi/BiDiOptions.cs rename to dotnet/src/webdriver/BiDi/BiDiOptionsBuilder.cs index 7bcac32ca6c0d..ceaef0908b6e3 100644 --- a/dotnet/src/webdriver/BiDi/BiDiOptions.cs +++ b/dotnet/src/webdriver/BiDi/BiDiOptionsBuilder.cs @@ -1,4 +1,4 @@ -// +// // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information @@ -19,6 +19,6 @@ namespace OpenQA.Selenium.BiDi; -public sealed class BiDiOptions +public sealed class BiDiOptionsBuilder { } diff --git a/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs b/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs index dce131dbfb074..f3eed2d70a17d 100644 --- a/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs +++ b/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs @@ -21,7 +21,7 @@ namespace OpenQA.Selenium.BiDi; public static class WebDriverExtensions { - public static async Task AsBiDiAsync(this IWebDriver webDriver, Action? configure = null, CancellationToken cancellationToken = default) + public static async Task AsBiDiAsync(this IWebDriver webDriver, Action? configure = null, CancellationToken cancellationToken = default) { if (webDriver is null) throw new ArgumentNullException(nameof(webDriver)); From fe1fe83f26144dbc468703b9281081ad057543b4 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:18:04 +0300 Subject: [PATCH 3/5] Add websocket options example --- dotnet/src/webdriver/BiDi/BiDi.cs | 2 +- dotnet/src/webdriver/BiDi/BiDiOptionsBuilder.cs | 9 +++++++++ dotnet/src/webdriver/BiDi/WebSocketTransport.cs | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/BiDi.cs b/dotnet/src/webdriver/BiDi/BiDi.cs index 082b92cc9e601..b7d0fe910b304 100644 --- a/dotnet/src/webdriver/BiDi/BiDi.cs +++ b/dotnet/src/webdriver/BiDi/BiDi.cs @@ -57,7 +57,7 @@ public static async Task ConnectAsync(string url, Action +using System.Net.WebSockets; + namespace OpenQA.Selenium.BiDi; public sealed class BiDiOptionsBuilder { + internal Action? WebSocketConfigure { get; private set; } + + public BiDiOptionsBuilder UseWebSocket(Action configure) + { + WebSocketConfigure = configure; + return this; + } } diff --git a/dotnet/src/webdriver/BiDi/WebSocketTransport.cs b/dotnet/src/webdriver/BiDi/WebSocketTransport.cs index fcab07b9a7770..eec5f7324f405 100644 --- a/dotnet/src/webdriver/BiDi/WebSocketTransport.cs +++ b/dotnet/src/webdriver/BiDi/WebSocketTransport.cs @@ -32,10 +32,12 @@ sealed class WebSocketTransport(ClientWebSocket webSocket) : ITransport private readonly SemaphoreSlim _socketSendSemaphoreSlim = new(1, 1); private readonly MemoryStream _sharedMemoryStream = new(); - public static async Task ConnectAsync(Uri uri, CancellationToken cancellationToken) + public static async Task ConnectAsync(Uri uri, Action? configure, CancellationToken cancellationToken) { ClientWebSocket webSocket = new(); + configure?.Invoke(webSocket.Options); + try { await webSocket.ConnectAsync(uri, cancellationToken).ConfigureAwait(false); From 2a17e5ef1fc25ea73ff4262b9e82a78b21813e47 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:48:52 +0300 Subject: [PATCH 4/5] Useful builder --- dotnet/src/webdriver/BiDi/BiDi.cs | 2 +- .../src/webdriver/BiDi/BiDiOptionsBuilder.cs | 21 ++++++++++++++++--- .../src/webdriver/BiDi/WebSocketTransport.cs | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/BiDi.cs b/dotnet/src/webdriver/BiDi/BiDi.cs index b7d0fe910b304..e6933b933e660 100644 --- a/dotnet/src/webdriver/BiDi/BiDi.cs +++ b/dotnet/src/webdriver/BiDi/BiDi.cs @@ -57,7 +57,7 @@ public static async Task ConnectAsync(string url, Action +/// Provides a fluent API for configuring BiDi connection options, +/// such as the underlying transport mechanism. +/// public sealed class BiDiOptionsBuilder { - internal Action? WebSocketConfigure { get; private set; } + internal Func> TransportFactory { get; private set; } + = (uri, ct) => WebSocketTransport.ConnectAsync(uri, null, ct); - public BiDiOptionsBuilder UseWebSocket(Action configure) + /// + /// Configures the BiDi connection to use a WebSocket transport. + /// + /// + /// WebSocket is the default transport; calling this method is only necessary + /// when you need to customize the underlying + /// (e.g., to set headers, proxy, or certificates). + /// + /// An optional action to configure the before connecting. + /// The current instance for chaining. + public BiDiOptionsBuilder UseWebSocket(Action? configure = null) { - WebSocketConfigure = configure; + TransportFactory = (uri, ct) => WebSocketTransport.ConnectAsync(uri, configure, ct); return this; } } diff --git a/dotnet/src/webdriver/BiDi/WebSocketTransport.cs b/dotnet/src/webdriver/BiDi/WebSocketTransport.cs index eec5f7324f405..9fd13bce8ad6f 100644 --- a/dotnet/src/webdriver/BiDi/WebSocketTransport.cs +++ b/dotnet/src/webdriver/BiDi/WebSocketTransport.cs @@ -32,7 +32,7 @@ sealed class WebSocketTransport(ClientWebSocket webSocket) : ITransport private readonly SemaphoreSlim _socketSendSemaphoreSlim = new(1, 1); private readonly MemoryStream _sharedMemoryStream = new(); - public static async Task ConnectAsync(Uri uri, Action? configure, CancellationToken cancellationToken) + public static async Task ConnectAsync(Uri uri, Action? configure, CancellationToken cancellationToken) { ClientWebSocket webSocket = new(); From 95c1b5258b8eb525892110d81f909a0ddcceccf3 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:54:46 +0300 Subject: [PATCH 5/5] Dispose ws if failure --- dotnet/src/webdriver/BiDi/WebSocketTransport.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/WebSocketTransport.cs b/dotnet/src/webdriver/BiDi/WebSocketTransport.cs index 9fd13bce8ad6f..6b0920500b6d3 100644 --- a/dotnet/src/webdriver/BiDi/WebSocketTransport.cs +++ b/dotnet/src/webdriver/BiDi/WebSocketTransport.cs @@ -36,10 +36,10 @@ public static async Task ConnectAsync(Uri uri, Action