-
-
Notifications
You must be signed in to change notification settings - Fork 8.7k
[dotnet] [bidi] Hide url required for websocket only #17208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,12 +53,14 @@ private BiDi() { } | |
|
|
||
| public Emulation.IEmulationModule Emulation => AsModule<Emulation.EmulationModule>(); | ||
|
|
||
| public static async Task<IBiDi> ConnectAsync(string url, Action<BiDiOptionsBuilder>? configure = null, CancellationToken cancellationToken = default) | ||
| public static async Task<IBiDi> ConnectAsync(Action<BiDiOptionsBuilder> configure, CancellationToken cancellationToken = default) | ||
| { | ||
| if (configure is null) throw new ArgumentNullException(nameof(configure)); | ||
|
|
||
| BiDiOptionsBuilder builder = new(); | ||
| configure?.Invoke(builder); | ||
| configure(builder); | ||
|
|
||
| var transport = await builder.TransportFactory(new Uri(url), cancellationToken).ConfigureAwait(false); | ||
| var transport = await builder.TransportFactory(cancellationToken).ConfigureAwait(false); | ||
|
Comment on lines
+56
to
+63
|
||
|
|
||
| BiDi bidi = new(); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,22 +27,21 @@ namespace OpenQA.Selenium.BiDi; | |
| /// </summary> | ||
| public sealed class BiDiOptionsBuilder | ||
| { | ||
| internal Func<Uri, CancellationToken, Task<ITransport>> TransportFactory { get; private set; } | ||
| = (uri, ct) => WebSocketTransport.ConnectAsync(uri, null, ct); | ||
| private Func<CancellationToken, Task<ITransport>>? _transportFactory; | ||
|
|
||
| /// <summary> | ||
| /// Configures the BiDi connection to use a WebSocket transport. | ||
| /// Configures the BiDi connection to use a WebSocket transport with the specified URL. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// WebSocket is the default transport; calling this method is only necessary | ||
| /// when you need to customize the underlying <see cref="ClientWebSocketOptions"/> | ||
| /// (e.g., to set headers, proxy, or certificates). | ||
| /// </remarks> | ||
| /// <param name="url">The WebSocket URL to connect to.</param> | ||
| /// <param name="configure">An optional action to configure the <see cref="ClientWebSocketOptions"/> before connecting.</param> | ||
| /// <returns>The current <see cref="BiDiOptionsBuilder"/> instance for chaining.</returns> | ||
| public BiDiOptionsBuilder UseWebSocket(Action<ClientWebSocketOptions>? configure = null) | ||
| public BiDiOptionsBuilder UseWebSocket(string url, Action<ClientWebSocketOptions>? configure = null) | ||
| { | ||
| TransportFactory = (uri, ct) => WebSocketTransport.ConnectAsync(uri, configure, ct); | ||
| var uri = new Uri(url); | ||
| _transportFactory = ct => WebSocketTransport.ConnectAsync(uri, configure, ct); | ||
| return this; | ||
| } | ||
|
Comment on lines
32
to
43
|
||
|
|
||
| internal Func<CancellationToken, Task<ITransport>> TransportFactory | ||
| => _transportFactory ?? throw new BiDiException("Transport is not configured. Call UseWebSocket(url) on BiDiOptionsBuilder."); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -34,7 +34,11 @@ public static async Task<IBiDi> AsBiDiAsync(this IWebDriver webDriver, Action<Bi | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| 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, configure, cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||||||
| var bidi = await BiDi.ConnectAsync(options => | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| configure?.Invoke(options); | ||||||||||||||||||||||||||||||
| options.UseWebSocket(webSocketUrl); | ||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+40
|
||||||||||||||||||||||||||||||
| configure?.Invoke(options); | |
| options.UseWebSocket(webSocketUrl); | |
| options.UseWebSocket(webSocketUrl); | |
| configure?.Invoke(options); |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
webSocketUrl is declared as string? and then captured into a lambda; nullable flow analysis generally won’t consider it non-null inside the delegate, which can produce nullable warnings (and potentially fail builds if warnings are treated as errors). Assign it to a non-null local (or use the null-forgiving operator after the null-check) before passing it into the lambda.
| var bidi = await BiDi.ConnectAsync(options => | |
| { | |
| configure?.Invoke(options); | |
| options.UseWebSocket(webSocketUrl); | |
| var nonNullWebSocketUrl = webSocketUrl; | |
| var bidi = await BiDi.ConnectAsync(options => | |
| { | |
| configure?.Invoke(options); | |
| options.UseWebSocket(nonNullWebSocketUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2. Transport config overwritten 🐞 Bug ✓ Correctness
WebDriverExtensions.AsBiDiAsync() always calls UseWebSocket(webSocketUrl) after running the user-supplied configure callback, overwriting any transport configuration performed inside configure. This makes caller transport/WebSocket configuration silently ineffective because the last UseWebSocket call replaces the builder’s transport factory.
Agent Prompt
### Issue description
`WebDriverExtensions.AsBiDiAsync()` currently runs the caller-provided `configure` callback and then unconditionally calls `options.UseWebSocket(webSocketUrl)`, which overwrites any transport configuration performed inside `configure`.
### Issue Context
`BiDiOptionsBuilder.UseWebSocket(...)` assigns `_transportFactory`, and only the last assignment is used. Therefore the unconditional `UseWebSocket(webSocketUrl)` call in `AsBiDiAsync` discards prior transport configuration.
### Fix Focus Areas
- dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs[37-41]
- dotnet/src/webdriver/BiDi/BiDiOptionsBuilder.cs[38-46]
### Suggested implementation direction
- Option A (non-breaking): after `configure?.Invoke(options)`, only call `UseWebSocket(webSocketUrl)` if transport was not configured.
- This likely requires adding an `internal bool IsTransportConfigured => _transportFactory != null;` to `BiDiOptionsBuilder`.
- Option B: call `options.UseWebSocket(webSocketUrl)` first, then `configure?.Invoke(options)` to allow callers to override the transport (if they choose to).
- If you need to preserve the pre-refactor ergonomics (customizing `ClientWebSocketOptions` without needing to supply the URL), add a new overload of `AsBiDiAsync` that accepts `Action<ClientWebSocketOptions>?` and uses it in `UseWebSocket(webSocketUrl, configureWs)`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. connectasync signature changed
📘 Rule violation✓ CorrectnessAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools