diff --git a/dotnet/src/webdriver/CommandInfo.cs b/dotnet/src/webdriver/CommandInfo.cs
index 0e679e221dcc7..a1bde2f141a20 100644
--- a/dotnet/src/webdriver/CommandInfo.cs
+++ b/dotnet/src/webdriver/CommandInfo.cs
@@ -19,6 +19,8 @@
using System;
+#nullable enable
+
namespace OpenQA.Selenium
{
///
@@ -45,7 +47,7 @@ public override int GetHashCode()
///
/// The to compare to this instance.
/// if is a and its value is the same as this instance; otherwise, . If is , the method returns .
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return this.Equals(obj as CommandInfo);
}
@@ -55,7 +57,7 @@ public override bool Equals(object obj)
///
/// The to compare to this instance.
/// if the value of the parameter is the same as this instance; otherwise, . If is , the method returns .
- public bool Equals(CommandInfo other)
+ public bool Equals(CommandInfo? other)
{
if (other is null)
{
@@ -63,7 +65,7 @@ public bool Equals(CommandInfo other)
}
// Optimization for a common success case.
- if (Object.ReferenceEquals(this, other))
+ if (object.ReferenceEquals(this, other))
{
return true;
}
@@ -86,7 +88,7 @@ public bool Equals(CommandInfo other)
/// The first object to compare.
/// The second object to compare.
/// if the value of is the same as the value of ; otherwise, .
- public static bool operator ==(CommandInfo left, CommandInfo right)
+ public static bool operator ==(CommandInfo? left, CommandInfo? right)
{
if (left is null)
{
@@ -107,7 +109,7 @@ public bool Equals(CommandInfo other)
/// The first object to compare.
/// The second object to compare.
/// if the value of is different from the value of ; otherwise, .
- public static bool operator !=(CommandInfo left, CommandInfo right)
+ public static bool operator !=(CommandInfo? left, CommandInfo? right)
{
return !(left == right);
}
diff --git a/dotnet/src/webdriver/HttpCommandInfo.cs b/dotnet/src/webdriver/HttpCommandInfo.cs
index 0841ffcde7524..36965d875cd5b 100644
--- a/dotnet/src/webdriver/HttpCommandInfo.cs
+++ b/dotnet/src/webdriver/HttpCommandInfo.cs
@@ -20,6 +20,8 @@
using System;
using System.Globalization;
+#nullable enable
+
namespace OpenQA.Selenium
{
///
@@ -44,9 +46,6 @@ public class HttpCommandInfo : CommandInfo
private const string SessionIdPropertyName = "sessionId";
- private string resourcePath;
- private string method;
-
///
/// Initializes a new instance of the class
///
@@ -54,32 +53,26 @@ public class HttpCommandInfo : CommandInfo
/// Relative URL path to the resource used to execute the command
public HttpCommandInfo(string method, string resourcePath)
{
- this.resourcePath = resourcePath;
- this.method = method;
+ this.ResourcePath = resourcePath;
+ this.Method = method;
}
///
/// Gets the URL representing the path to the resource.
///
- public string ResourcePath
- {
- get { return this.resourcePath; }
- }
+ public string ResourcePath { get; }
///
/// Gets the HTTP method associated with the command.
///
- public string Method
- {
- get { return this.method; }
- }
+ public string Method { get; }
///
/// Gets the unique identifier for this command within the scope of its protocol definition
///
public override string CommandIdentifier
{
- get { return string.Format(CultureInfo.InvariantCulture, "{0} {1}", this.method, this.resourcePath); }
+ get { return string.Format(CultureInfo.InvariantCulture, "{0} {1}", this.Method, this.ResourcePath); }
}
///
@@ -93,7 +86,7 @@ public override string CommandIdentifier
/// substituted for the tokens in the template.
public Uri CreateCommandUri(Uri baseUri, Command commandToExecute)
{
- string[] urlParts = this.resourcePath.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
+ string[] urlParts = this.ResourcePath.Split(["/"], StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < urlParts.Length; i++)
{
string urlPart = urlParts[i];
@@ -103,13 +96,11 @@ public Uri CreateCommandUri(Uri baseUri, Command commandToExecute)
}
}
- Uri fullUri;
string relativeUrlString = string.Join("/", urlParts);
Uri relativeUri = new Uri(relativeUrlString, UriKind.Relative);
- bool uriCreateSucceeded = Uri.TryCreate(baseUri, relativeUri, out fullUri);
- if (!uriCreateSucceeded)
+ if (!Uri.TryCreate(baseUri, relativeUri, out Uri? fullUri))
{
- throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to create URI from base {0} and relative path {1}", baseUri == null ? string.Empty : baseUri.ToString(), relativeUrlString));
+ throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to create URI from base {0} and relative path {1}", baseUri?.ToString(), relativeUrlString));
}
return fullUri;
@@ -133,11 +124,11 @@ private static string GetCommandPropertyValue(string propertyName, Command comma
{
// Extract the URL parameter, and remove it from the parameters dictionary
// so it doesn't get transmitted as a JSON parameter.
- if (commandToExecute.Parameters.ContainsKey(propertyName))
+ if (commandToExecute.Parameters.TryGetValue(propertyName, out var propertyValueObject))
{
- if (commandToExecute.Parameters[propertyName] != null)
+ if (propertyValueObject != null)
{
- propertyValue = commandToExecute.Parameters[propertyName].ToString();
+ propertyValue = propertyValueObject.ToString()!;
commandToExecute.Parameters.Remove(propertyName);
}
}
diff --git a/dotnet/src/webdriver/ICommandExecutor.cs b/dotnet/src/webdriver/ICommandExecutor.cs
index e80d7ea63b10e..6fef4d9bf3ef1 100644
--- a/dotnet/src/webdriver/ICommandExecutor.cs
+++ b/dotnet/src/webdriver/ICommandExecutor.cs
@@ -18,8 +18,11 @@
//
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
+#nullable enable
+
namespace OpenQA.Selenium
{
///
@@ -31,15 +34,16 @@ public interface ICommandExecutor : IDisposable
/// Attempts to add a command to the repository of commands known to this executor.
///
/// The name of the command to attempt to add.
- /// The describing the commnd to add.
+ /// The describing the command to add.
/// if the new command has been added successfully; otherwise, .
- bool TryAddCommand(string commandName, CommandInfo info);
+ bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info);
///
/// Executes a command
///
/// The command you wish to execute
/// A response from the browser
+ /// If is .
Response Execute(Command commandToExecute);
@@ -48,6 +52,7 @@ public interface ICommandExecutor : IDisposable
///
/// The command you wish to execute
/// A task object representing the asynchronous operation
+ /// If is .
Task ExecuteAsync(Command commandToExecute);
}
}
diff --git a/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs b/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs
index 07f2169490391..145bb8c66013d 100644
--- a/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs
+++ b/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs
@@ -18,6 +18,9 @@
//
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+#nullable enable
namespace OpenQA.Selenium
{
@@ -32,7 +35,8 @@ public interface ICustomDriverCommandExecutor
/// The name of the command to execute. The command name must be registered with the command executor, and must not be a command name known to this driver type.
/// A containing the names and values of the parameters of the command.
/// An object that contains the value returned by the command, if any.
- object ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters);
+ /// The command returned an exceptional value.
+ object? ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters);
///
/// Registers a set of commands to be executed with this driver instance.
@@ -46,6 +50,6 @@ public interface ICustomDriverCommandExecutor
/// The unique name of the command to register.
/// The object describing the command.
/// if the command was registered; otherwise, .
- bool RegisterCustomDriverCommand(string commandName, CommandInfo commandInfo);
+ bool RegisterCustomDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo);
}
}
diff --git a/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs b/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs
index b9bb4efcf0084..0f1d341c8c353 100644
--- a/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs
+++ b/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs
@@ -18,6 +18,7 @@
//
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
#nullable enable
@@ -78,7 +79,7 @@ public DriverServiceCommandExecutor(DriverService service, HttpCommandExecutor c
// get { return this.HttpExecutor.CommandInfoRepository; }
//}
- public bool TryAddCommand(string commandName, CommandInfo info)
+ public bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info)
{
return this.HttpExecutor.TryAddCommand(commandName, info);
}
@@ -94,6 +95,7 @@ public bool TryAddCommand(string commandName, CommandInfo info)
///
/// The command you wish to execute
/// A response from the browser
+ /// If is .
public Response Execute(Command commandToExecute)
{
return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult();
@@ -104,6 +106,7 @@ public Response Execute(Command commandToExecute)
///
/// The command you wish to execute
/// A task object representing the asynchronous operation
+ /// If is .
public async Task ExecuteAsync(Command commandToExecute)
{
if (commandToExecute == null)
diff --git a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs
index 9ab474b8ec235..2bcee9c7673ad 100644
--- a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs
+++ b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs
@@ -21,6 +21,7 @@
using OpenQA.Selenium.Internal.Logging;
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Net;
@@ -30,6 +31,8 @@
using System.Threading;
using System.Threading.Tasks;
+#nullable enable
+
namespace OpenQA.Selenium.Remote
{
///
@@ -47,7 +50,7 @@ public class HttpCommandExecutor : ICommandExecutor
private readonly TimeSpan serverResponseTimeout;
private bool isDisposed;
private CommandInfoRepository commandInfoRepository = new W3CWireProtocolCommandInfoRepository();
- private HttpClient client;
+ private readonly Lazy client;
private static readonly ILogger _logger = Log.GetLogger();
@@ -56,6 +59,7 @@ public class HttpCommandExecutor : ICommandExecutor
///
/// Address of the WebDriver Server
/// The timeout within which the server must respond.
+ /// If is .
public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout)
: this(addressOfRemoteServer, timeout, true)
{
@@ -85,20 +89,21 @@ public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, bool ena
this.remoteServerUri = addressOfRemoteServer;
this.serverResponseTimeout = timeout;
this.IsKeepAliveEnabled = enableKeepAlive;
+ this.client = new Lazy(CreateHttpClient);
}
///
/// Occurs when the is sending an HTTP
/// request to the remote end WebDriver implementation.
///
- public event EventHandler SendingRemoteHttpRequest;
+ public event EventHandler? SendingRemoteHttpRequest;
///
/// Gets or sets an object to be used to proxy requests
/// between this and the remote end WebDriver
/// implementation.
///
- public IWebProxy Proxy { get; set; }
+ public IWebProxy? Proxy { get; set; }
///
/// Gets or sets a value indicating whether keep-alive is enabled for HTTP
@@ -109,7 +114,7 @@ public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, bool ena
///
/// Gets or sets the user agent string used for HTTP communication
- /// batween this and the remote end
+ /// between this and the remote end
/// WebDriver implementation
///
public string UserAgent { get; set; }
@@ -128,9 +133,9 @@ protected CommandInfoRepository CommandInfoRepository
/// Attempts to add a command to the repository of commands known to this executor.
///
/// The name of the command to attempt to add.
- /// The describing the commnd to add.
+ /// The describing the command to add.
/// if the new command has been added successfully; otherwise, .
- public bool TryAddCommand(string commandName, CommandInfo info)
+ public bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info)
{
if (info is not HttpCommandInfo commandInfo)
{
@@ -145,6 +150,7 @@ public bool TryAddCommand(string commandName, CommandInfo info)
///
/// The command you wish to execute.
/// A response from the browser.
+ /// If is .
public virtual Response Execute(Command commandToExecute)
{
return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult();
@@ -155,6 +161,7 @@ public virtual Response Execute(Command commandToExecute)
///
/// The command you wish to execute.
/// A task object representing the asynchronous operation.
+ /// If is .
public virtual async Task ExecuteAsync(Command commandToExecute)
{
if (commandToExecute == null)
@@ -164,20 +171,15 @@ public virtual async Task ExecuteAsync(Command commandToExecute)
if (_logger.IsEnabled(LogEventLevel.Debug))
{
- _logger.Debug($"Executing command: {commandToExecute}");
+ _logger.Debug($"Executing command: [{commandToExecute.SessionId}]: {commandToExecute.Name}");
}
- HttpCommandInfo info = this.commandInfoRepository.GetCommandInfo(commandToExecute.Name);
+ HttpCommandInfo? info = this.commandInfoRepository.GetCommandInfo(commandToExecute.Name);
if (info == null)
{
throw new NotImplementedException(string.Format("The command you are attempting to execute, {0}, does not exist in the protocol dialect used by the remote end.", commandToExecute.Name));
}
- if (this.client == null)
- {
- this.CreateHttpClient();
- }
-
HttpRequestInfo requestInfo = new HttpRequestInfo(this.remoteServerUri, commandToExecute, info);
HttpResponseInfo responseInfo;
try
@@ -216,13 +218,10 @@ protected virtual void OnSendingRemoteHttpRequest(SendingRemoteHttpRequestEventA
throw new ArgumentNullException(nameof(eventArgs), "eventArgs must not be null");
}
- if (this.SendingRemoteHttpRequest != null)
- {
- this.SendingRemoteHttpRequest(this, eventArgs);
- }
+ this.SendingRemoteHttpRequest?.Invoke(this, eventArgs);
}
- private void CreateHttpClient()
+ private HttpClient CreateHttpClient()
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
string userInfo = this.remoteServerUri.UserInfo;
@@ -242,16 +241,17 @@ private void CreateHttpClient()
handler = new DiagnosticsHttpHandler(httpClientHandler, _logger);
}
- this.client = new HttpClient(handler);
- this.client.DefaultRequestHeaders.UserAgent.ParseAdd(this.UserAgent);
- this.client.DefaultRequestHeaders.Accept.ParseAdd(RequestAcceptHeader);
- this.client.DefaultRequestHeaders.ExpectContinue = false;
+ var client = new HttpClient(handler);
+ client.DefaultRequestHeaders.UserAgent.ParseAdd(this.UserAgent);
+ client.DefaultRequestHeaders.Accept.ParseAdd(RequestAcceptHeader);
+ client.DefaultRequestHeaders.ExpectContinue = false;
if (!this.IsKeepAliveEnabled)
{
- this.client.DefaultRequestHeaders.Connection.ParseAdd("close");
+ client.DefaultRequestHeaders.Connection.ParseAdd("close");
}
- this.client.Timeout = this.serverResponseTimeout;
+ client.Timeout = this.serverResponseTimeout;
+ return client;
}
private async Task MakeHttpRequest(HttpRequestInfo requestInfo)
@@ -288,7 +288,7 @@ private async Task MakeHttpRequest(HttpRequestInfo requestInfo
requestMessage.Content.Headers.ContentType = contentTypeHeader;
}
- using (HttpResponseMessage responseMessage = await this.client.SendAsync(requestMessage).ConfigureAwait(false))
+ using (HttpResponseMessage responseMessage = await this.client.Value.SendAsync(requestMessage).ConfigureAwait(false))
{
var responseBody = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
var responseContentType = responseMessage.Content.Headers.ContentType?.ToString();
@@ -332,8 +332,6 @@ private Response CreateResponse(HttpResponseInfo responseInfo)
return response;
}
-#nullable enable
-
///
/// Releases all resources used by the .
///
@@ -353,7 +351,10 @@ protected virtual void Dispose(bool disposing)
{
if (!this.isDisposed)
{
- this.client?.Dispose();
+ if (this.client.IsValueCreated)
+ {
+ this.client.Value.Dispose();
+ }
this.isDisposed = true;
}
diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs
index 974cbf608fcba..b5e8d5b6437b5 100644
--- a/dotnet/src/webdriver/WebDriver.cs
+++ b/dotnet/src/webdriver/WebDriver.cs
@@ -463,13 +463,16 @@ public INavigation Navigate()
return new Navigator(this);
}
+#nullable enable
+
///
/// Executes a command with this driver.
///
/// The name of the command to execute. The command name must be registered with the command executor, and must not be a command name known to this driver type.
/// A containing the names and values of the parameters of the command.
/// A containing information about the success or failure of the command and any data returned by the command.
- public object ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters)
+ /// The command returned an exceptional value.
+ public object? ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters)
{
if (this.registeredCommands.Contains(driverCommandToExecute))
{
@@ -497,7 +500,7 @@ public void RegisterCustomDriverCommands(IReadOnlyDictionaryThe unique name of the command to register.
/// The object describing the command.
/// if the command was registered; otherwise, .
- public bool RegisterCustomDriverCommand(string commandName, CommandInfo commandInfo)
+ public bool RegisterCustomDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo)
{
return this.RegisterDriverCommand(commandName, commandInfo, false);
}
@@ -509,17 +512,23 @@ public bool RegisterCustomDriverCommand(string commandName, CommandInfo commandI
/// The object describing the command.
/// if the registered command is internal to the driver; otherwise .
/// if the command was registered; otherwise, .
- internal bool RegisterDriverCommand(string commandName, CommandInfo commandInfo, bool isInternalCommand)
+ internal bool RegisterDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo, bool isInternalCommand)
{
- bool commandAdded = this.CommandExecutor.TryAddCommand(commandName, commandInfo);
- if (commandAdded && isInternalCommand)
+ if (this.CommandExecutor.TryAddCommand(commandName, commandInfo))
{
- this.registeredCommands.Add(commandName);
+ if (isInternalCommand)
+ {
+ this.registeredCommands.Add(commandName);
+ }
+
+ return true;
}
- return commandAdded;
+ return false;
}
+#nullable restore
+
///
/// Find the element in the response
///
@@ -692,17 +701,21 @@ protected virtual Dictionary GetCapabilitiesDictionary(ICapabili
return capabilitiesDictionary;
}
+#nullable enable
+
///
/// Registers a command to be executed with this driver instance as an internally known driver command.
///
/// The unique name of the command to register.
/// The object describing the command.
/// if the command was registered; otherwise, .
- protected bool RegisterInternalDriverCommand(string commandName, CommandInfo commandInfo)
+ protected bool RegisterInternalDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo)
{
return this.RegisterDriverCommand(commandName, commandInfo, true);
}
+#nullable restore
+
///
/// Stops the client from running
///