blob: 10a77ddf48cf84d5f19a73cb24a5b32032ff6bff [file] [log] [blame] [edit]
// <copyright file="SafariDriverService.cs" company="WebDriver Committers">
// 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
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium.Internal;
using OpenQA.Selenium.Internal.Logging;
namespace OpenQA.Selenium.Safari
{
/// <summary>
/// Exposes the service provided by the native SafariDriver executable.
/// </summary>
public sealed class SafariDriverService : DriverService
{
private const string DefaultSafariDriverServiceExecutableName = "safaridriver";
private readonly static ILogger logger = Log.GetLogger<SafariDriverService>();
/// <summary>
/// Initializes a new instance of the <see cref="SafariDriverService"/> class.
/// </summary>
/// <param name="executablePath">The full path to the SafariDriver executable.</param>
/// <param name="executableFileName">The file name of the SafariDriver executable.</param>
/// <param name="port">The port on which the SafariDriver executable should listen.</param>
private SafariDriverService(string executablePath, string executableFileName, int port)
: base(executablePath, port, executableFileName)
{
}
/// <inheritdoc />
protected override DriverOptions GetDefaultDriverOptions()
{
return new SafariOptions();
}
/// <summary>
/// Gets the command-line arguments for the driver service.
/// </summary>
protected override string CommandLineArguments
{
get
{
StringBuilder argsBuilder = new StringBuilder(base.CommandLineArguments);
return argsBuilder.ToString();
}
}
/// <summary>
/// Gets a value indicating the time to wait for the service to terminate before forcing it to terminate.
/// For the Safari driver, there is no time for termination
/// </summary>
protected override TimeSpan TerminationTimeout
{
// Use a very small timeout for terminating the Safari driver,
// because the executable does not have a clean shutdown command,
// which means we have to kill the process. Using a short timeout
// gets us to the termination point much faster.
get { return TimeSpan.FromMilliseconds(100); }
}
/// <summary>
/// Gets a value indicating whether the service has a shutdown API that can be called to terminate
/// it gracefully before forcing a termination.
/// </summary>
protected override bool HasShutdown
{
// The Safari driver executable does not have a clean shutdown command,
// which means we have to kill the process.
get { return false; }
}
/// <summary>
/// Gets a value indicating whether the service is responding to HTTP requests.
/// </summary>
protected override bool IsInitialized
{
get
{
bool isInitialized = false;
Uri serviceHealthUri = new Uri(this.ServiceUrl, new Uri("/session/FakeSessionIdForPollingPurposes", UriKind.Relative));
// Since Firefox driver won't implement the /session end point (because
// the W3C spec working group stupidly decided that it isn't necessary),
// we'll attempt to poll for a different URL which has no side effects.
// We've chosen to poll on the "quit" URL, passing in a nonexistent
// session id.
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.ConnectionClose = true;
httpClient.Timeout = TimeSpan.FromSeconds(5);
using (var httpRequest = new HttpRequestMessage(HttpMethod.Delete, serviceHealthUri))
{
try
{
using (var httpResponse = Task.Run(async () => await httpClient.SendAsync(httpRequest)).GetAwaiter().GetResult())
{
isInitialized = (httpResponse.StatusCode == HttpStatusCode.OK
|| httpResponse.StatusCode == HttpStatusCode.InternalServerError
|| httpResponse.StatusCode == HttpStatusCode.NotFound)
&& httpResponse.Content.Headers.ContentType.MediaType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase);
}
}
// Checking the response from deleting a nonexistent session. 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.
catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException)
{
if (logger.IsEnabled(LogEventLevel.Trace))
{
logger.Trace(ex.ToString());
}
}
}
}
return isInitialized;
}
}
/// <summary>
/// Creates a default instance of the SafariDriverService.
/// </summary>
/// <returns>A SafariDriverService that implements default settings.</returns>
public static SafariDriverService CreateDefaultService()
{
return new SafariDriverService(null, null, PortUtilities.FindFreePort());
}
/// <summary>
/// Creates a default instance of the SafariDriverService using a specified path to the SafariDriver executable.
/// </summary>
/// <param name="driverPath">The path to the executable or the directory containing the SafariDriver executable.</param>
/// <returns>A SafariDriverService using a random port.</returns>
public static SafariDriverService CreateDefaultService(string driverPath)
{
string fileName;
if (File.Exists(driverPath))
{
fileName = Path.GetFileName(driverPath);
driverPath = Path.GetDirectoryName(driverPath);
}
else
{
fileName = DefaultSafariDriverServiceExecutableName;
}
return CreateDefaultService(driverPath, fileName);
}
/// <summary>
/// Creates a default instance of the SafariDriverService using a specified path to the SafariDriver executable with the given name.
/// </summary>
/// <param name="driverPath">The directory containing the SafariDriver executable.</param>
/// <param name="driverExecutableFileName">The name of the SafariDriver executable file.</param>
/// <returns>A SafariDriverService using a random port.</returns>
public static SafariDriverService CreateDefaultService(string driverPath, string driverExecutableFileName)
{
return new SafariDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort());
}
}
}