Skip to content
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

[dotnet] Propagate service asynchronicity to the command executor. #15246

Open
wants to merge 23 commits into
base: trunk
Choose a base branch
from
Open
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8fc36d2
[dotnet] Make `DriverService.Start()` thread-safe
RenderMichael Feb 6, 2025
16024d7
Merge branch 'trunk' into concurrent-driver-service
RenderMichael Feb 6, 2025
046dd0e
Simplify `DriverService.IsInitialized`
RenderMichael Feb 6, 2025
aabd21c
Merge branch 'concurrent-driver-service' of https://github.com/Render…
RenderMichael Feb 6, 2025
5d46690
Merge branch 'trunk' into concurrent-driver-service
RenderMichael Feb 6, 2025
6769ec7
Remove thread-safety, implement asynchronicity
RenderMichael Feb 6, 2025
3461067
Merge branch 'concurrent-driver-service' of https://github.com/Render…
RenderMichael Feb 6, 2025
83013be
minimize unnecessary diffs
RenderMichael Feb 6, 2025
60b87b1
minimize diffs
RenderMichael Feb 6, 2025
71b0ab9
Further minimize diffs
RenderMichael Feb 6, 2025
714ca57
Fill in comments
RenderMichael Feb 6, 2025
9b20295
Poll for accessible driver service, even if process has begun
RenderMichael Feb 7, 2025
77b730e
Assign `driverServiceProcess` before polling for process running
RenderMichael Feb 7, 2025
23a7c47
Re-introduce thread-safety
RenderMichael Feb 7, 2025
97aab60
minimize diff
RenderMichael Feb 7, 2025
3df26d2
Wait for service initialization, even if process exists
RenderMichael Feb 7, 2025
8306d87
Simplify thread safety now that we have a wait in place
RenderMichael Feb 7, 2025
d4b91ff
remove unused field
RenderMichael Feb 7, 2025
9fe018d
Fix obsoletion messages
RenderMichael Feb 7, 2025
3d085be
minimize diffs
RenderMichael Feb 7, 2025
589aa45
Merge branch 'trunk' into concurrent-driver-service
RenderMichael Feb 9, 2025
bcb532f
Remove pubic-facing changes
RenderMichael Feb 9, 2025
f423837
Null out disposed value on throw
RenderMichael Feb 9, 2025
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
103 changes: 56 additions & 47 deletions dotnet/src/webdriver/DriverService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace OpenQA.Selenium
public abstract class DriverService : ICommandServer
{
private bool isDisposed;
private readonly object driverServiceProcessLock = new();
private Process? driverServiceProcess;

/// <summary>
Expand Down Expand Up @@ -176,32 +177,27 @@ protected virtual bool IsInitialized
{
get
{
bool isInitialized = false;

try
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.ConnectionClose = true;
httpClient.Timeout = TimeSpan.FromSeconds(5);
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.ConnectionClose = true;
httpClient.Timeout = TimeSpan.FromSeconds(5);

Uri serviceHealthUri = new Uri(this.ServiceUrl, new Uri(DriverCommand.Status, UriKind.Relative));
using (var response = Task.Run(async () => await httpClient.GetAsync(serviceHealthUri)).GetAwaiter().GetResult())
{
// Checking the response from the 'status' end point. Note that we are simply checking
// that the HTTP status returned is a 200 status, and that the resposne has the correct
// Content-Type header. A more sophisticated check would parse the JSON response and
// validate its values. At the moment we do not do this more sophisticated check.
isInitialized = response.StatusCode == HttpStatusCode.OK && response.Content.Headers.ContentType is { MediaType: string mediaType } && mediaType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase);
}
}
Uri serviceHealthUri = new Uri(this.ServiceUrl, new Uri(DriverCommand.Status, UriKind.Relative));
using var response = Task.Run(async () => await httpClient.GetAsync(serviceHealthUri)).GetAwaiter().GetResult();

// Checking the response from the 'status' end point. Note that we are simply checking
// that the HTTP status returned is a 200 status, and that the response has the correct
// Content-Type header. A more sophisticated check would parse the JSON response and
// validate its values. At the moment we do not do this more sophisticated check.
bool isInitialized = response.StatusCode == HttpStatusCode.OK && response.Content.Headers.ContentType is { MediaType: string mediaType } && mediaType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase);

return isInitialized;
}
catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException)
{
// Do nothing. The exception is expected, meaning driver service is not initialized.
return false;
}

return isInitialized;
}
}

Expand All @@ -220,42 +216,55 @@ public void Dispose()
[MemberNotNull(nameof(driverServiceProcess))]
public void Start()
{
if (this.driverServiceProcess != null)
if (this.driverServiceProcess is null)
{
return;
}
lock (this.driverServiceProcessLock)
{
if (this.driverServiceProcess is null)
{
var driverServiceProcess = new Process();

this.driverServiceProcess = new Process();
try
{
if (this.DriverServicePath != null)
{
if (this.DriverServiceExecutableName is null)
{
throw new InvalidOperationException("If the driver service path is specified, the driver service executable name must be as well");
}

if (this.DriverServicePath != null)
{
if (this.DriverServiceExecutableName is null)
{
throw new InvalidOperationException("If the driver service path is specified, the driver service executable name must be as well");
}
driverServiceProcess.StartInfo.FileName = Path.Combine(this.DriverServicePath, this.DriverServiceExecutableName);
}
else
{
driverServiceProcess.StartInfo.FileName = new DriverFinder(this.GetDefaultDriverOptions()).GetDriverPath();
}

this.driverServiceProcess.StartInfo.FileName = Path.Combine(this.DriverServicePath, this.DriverServiceExecutableName);
}
else
{
this.driverServiceProcess.StartInfo.FileName = new DriverFinder(this.GetDefaultDriverOptions()).GetDriverPath();
}
driverServiceProcess.StartInfo.Arguments = this.CommandLineArguments;
driverServiceProcess.StartInfo.UseShellExecute = false;
driverServiceProcess.StartInfo.CreateNoWindow = this.HideCommandPromptWindow;

this.driverServiceProcess.StartInfo.Arguments = this.CommandLineArguments;
this.driverServiceProcess.StartInfo.UseShellExecute = false;
this.driverServiceProcess.StartInfo.CreateNoWindow = this.HideCommandPromptWindow;
this.OnDriverProcessStarting(new DriverProcessStartingEventArgs(driverServiceProcess.StartInfo));

DriverProcessStartingEventArgs eventArgs = new DriverProcessStartingEventArgs(this.driverServiceProcess.StartInfo);
this.OnDriverProcessStarting(eventArgs);
driverServiceProcess.Start();
bool serviceAvailable = this.WaitForServiceInitialization();

this.driverServiceProcess.Start();
bool serviceAvailable = this.WaitForServiceInitialization();
DriverProcessStartedEventArgs processStartedEventArgs = new DriverProcessStartedEventArgs(this.driverServiceProcess);
this.OnDriverProcessStarted(processStartedEventArgs);
this.OnDriverProcessStarted(new DriverProcessStartedEventArgs(driverServiceProcess));

if (!serviceAvailable)
{
throw new WebDriverException($"Cannot start the driver service on {this.ServiceUrl}");
if (!serviceAvailable)
{
throw new WebDriverException($"Cannot start the driver service on {this.ServiceUrl}");
}
}
catch
{
driverServiceProcess.Dispose();
throw;
}

this.driverServiceProcess = driverServiceProcess;
}
}
}
}

Expand Down
Loading