Skip to content

Commit 3674f95

Browse files
authored
feat: Add support for Relax SSL validation (#922)
* Add RelaxSslValidation property and IsModified method Added a new property `RelaxSslValidation` to the `AppiumClientConfig` class to allow users to specify whether SSL validation should be relaxed. Introduced a new method `IsModified` that checks if the current configuration has been modified from the default configuration, specifically comparing the `DirectConnect` and `RelaxSslValidation` properties. * Add AppiumHttpCommandExecutor class with custom HttpClientHandler Introduce AppiumHttpCommandExecutor in OpenQA.Selenium.Appium.Service. This class inherits from HttpCommandExecutor and includes two constructors for initializing with a Uri and TimeSpan, with an optional AppiumClientConfig. Override CreateHttpClientHandler to customize HttpClientHandler with a callback for server certificate validation. Add ModifyHttpClientHandler method to return the customized handler. Include necessary using directives. * Switch to AppiumHttpCommandExecutor for better integration Replaced instances of HttpCommandExecutor with AppiumHttpCommandExecutor in AppiumCommandExecutor.cs. Updated CreateRealExecutor and GetNewExecutorWithDirectConnect methods to return AppiumHttpCommandExecutor. Modified ModifyNewSessionHttpRequestHeader to cast commandExecutor to AppiumHttpCommandExecutor and added a call to ModifyHttpClientHandler with ClientConfig. These changes enhance the command execution process by leveraging Appium-specific functionalities. * Refactor AppiumCommandExecutor and AppiumHttpCommandExecutor Updated CreateRealExecutor to accept AppiumClientConfig and pass it to AppiumHttpCommandExecutor. Modified constructors of AppiumCommandExecutor to include clientConfig. Reformatted Execute and HandleCommandException methods for consistency. Cleaned up ModifyNewSessionHttpRequestHeader method to remove commented-out code and ensure IdempotencyHeader is added. Updated AppiumHttpCommandExecutor constructor to optionally accept clientConfig and initialize _clientConfig. Adjusted CreateHttpClientHandler to set ServerCertificateCustomValidationCallback based on RelaxSslValidation property. Removed obsolete ModifyHttpClientHandler method. * Add test for RelaxSslValidation in AppiumClientConfig A new test method `SetAndGetRelaxSSLValidation` was added to the `AppiumClientConfigTest` class within the `Appium.Net.Integration.Tests.ServerTests` namespace. This test verifies the `RelaxSslValidation` property of the `AppiumClientConfig` class. It asserts that the default value of `RelaxSslValidation` is `False`, then sets it to `True` and asserts the updated value. * Remove `IsModified` method from `AppiumClientConfig` * Add Apache License header to AppiumHttpCommandExecutor.cs * Passing clientConfig to ensure consistent behavior in GetNewExecutorWithDirectConnect * Add RelaxSslValidationTest for Appium SSL validation * Refactor AppiumHttpCommandExecutor initialization Refactored the `AppiumHttpCommandExecutor` class to simplify initialization: - Moved constructor parameters directly into the class definition. - Initialized `_clientConfig` field at the point of declaration. - Removed redundant constructor definition and initialization of `_clientConfig` within the constructor body. * Make ClientConfig readonly in AppiumCommandExecutor * Add conditional compilation for .NET version-specific tests * Directly setting callback when enabling the relax SSL Validation. * Consolidate cleanup logic in RelaxSslValidationTest * Simplify exception handling in RelaxSslValidationTest
1 parent 05e2404 commit 3674f95

File tree

5 files changed

+141
-12
lines changed

5 files changed

+141
-12
lines changed

src/Appium.Net/Appium/Service/AppiumClientConfig.cs

+3
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,8 @@ public static AppiumClientConfig DefaultConfig()
3737
///
3838
/// </summary>
3939
public bool DirectConnect { get; set; }
40+
41+
public bool RelaxSslValidation { get; set; }
42+
4043
}
4144
}

src/Appium.Net/Appium/Service/AppiumCommandExecutor.cs

+12-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
//See the License for the specific language governing permissions and
1313
//limitations under the License.
1414

15-
using OpenQA.Selenium.Remote;
1615
using System;
1716
using System.Threading.Tasks;
1817

@@ -24,13 +23,13 @@ internal class AppiumCommandExecutor : ICommandExecutor
2423
private ICommandExecutor RealExecutor;
2524
private bool isDisposed;
2625
private const string IdempotencyHeader = "X-Idempotency-Key";
27-
private AppiumClientConfig ClientConfig;
26+
private readonly AppiumClientConfig ClientConfig;
2827

2928
private TimeSpan CommandTimeout;
3029

31-
private static ICommandExecutor CreateRealExecutor(Uri remoteAddress, TimeSpan commandTimeout)
30+
private static ICommandExecutor CreateRealExecutor(Uri remoteAddress, TimeSpan commandTimeout, AppiumClientConfig clientConfig)
3231
{
33-
return new HttpCommandExecutor(remoteAddress, commandTimeout);
32+
return new AppiumHttpCommandExecutor(remoteAddress, commandTimeout, clientConfig);
3433
}
3534

3635
private AppiumCommandExecutor(ICommandExecutor realExecutor)
@@ -39,25 +38,25 @@ private AppiumCommandExecutor(ICommandExecutor realExecutor)
3938
}
4039

4140
internal AppiumCommandExecutor(Uri url, TimeSpan timeForTheServerResponding, AppiumClientConfig clientConfig)
42-
: this(CreateRealExecutor(url, timeForTheServerResponding))
41+
: this(CreateRealExecutor(url, timeForTheServerResponding, clientConfig))
4342
{
4443
CommandTimeout = timeForTheServerResponding;
4544
Service = null;
4645
ClientConfig = clientConfig;
4746
}
4847

4948
internal AppiumCommandExecutor(AppiumLocalService service, TimeSpan timeForTheServerResponding, AppiumClientConfig clientConfig)
50-
: this(CreateRealExecutor(service.ServiceUrl, timeForTheServerResponding))
49+
: this(CreateRealExecutor(service.ServiceUrl, timeForTheServerResponding, clientConfig))
5150
{
5251
CommandTimeout = timeForTheServerResponding;
5352
Service = service;
5453
ClientConfig = clientConfig;
5554
}
5655

5756
public Response Execute(Command commandToExecute)
58-
{
59-
return Task.Run(() => ExecuteAsync(commandToExecute)).GetAwaiter().GetResult();
60-
}
57+
{
58+
return Task.Run(() => ExecuteAsync(commandToExecute)).GetAwaiter().GetResult();
59+
}
6160

6261
public async Task<Response> ExecuteAsync(Command commandToExecute)
6362
{
@@ -131,7 +130,7 @@ private void HandleCommandException(Command command)
131130
if (command.Name == DriverCommand.NewSession)
132131
{
133132
Service?.Dispose();
134-
}
133+
}
135134
}
136135

137136
/// <summary>
@@ -142,7 +141,8 @@ private void HandleCommandException(Command command)
142141
private ICommandExecutor ModifyNewSessionHttpRequestHeader(ICommandExecutor commandExecutor)
143142
{
144143
if (commandExecutor == null) throw new ArgumentNullException(nameof(commandExecutor));
145-
var modifiedCommandExecutor = commandExecutor as HttpCommandExecutor;
144+
145+
var modifiedCommandExecutor = commandExecutor as AppiumHttpCommandExecutor;
146146

147147
modifiedCommandExecutor.SendingRemoteHttpRequest += (sender, args) =>
148148
args.AddHeader(IdempotencyHeader, Guid.NewGuid().ToString());
@@ -183,7 +183,7 @@ private ICommandExecutor GetNewExecutorWithDirectConnect(Response response)
183183
var newUri = new DirectConnect(response).GetUri();
184184
if (newUri != null)
185185
{
186-
return new HttpCommandExecutor(newUri, CommandTimeout);
186+
return new AppiumHttpCommandExecutor(newUri, CommandTimeout, ClientConfig);
187187
}
188188

189189
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//Licensed under the Apache License, Version 2.0 (the "License");
2+
//you may not use this file except in compliance with the License.
3+
//See the NOTICE file distributed with this work for additional
4+
//information regarding copyright ownership.
5+
//You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
//Unless required by applicable law or agreed to in writing, software
10+
//distributed under the License is distributed on an "AS IS" BASIS,
11+
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
//See the License for the specific language governing permissions and
13+
//limitations under the License.
14+
15+
using OpenQA.Selenium.Remote;
16+
using System;
17+
using System.Net.Http;
18+
19+
namespace OpenQA.Selenium.Appium.Service
20+
{
21+
public class AppiumHttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, AppiumClientConfig clientConfig) : HttpCommandExecutor(addressOfRemoteServer, timeout, enableKeepAlive: true)
22+
{
23+
private readonly AppiumClientConfig _clientConfig = clientConfig;
24+
25+
protected override HttpClientHandler CreateHttpClientHandler()
26+
{
27+
var handler = base.CreateHttpClientHandler();
28+
29+
if (_clientConfig != null && _clientConfig.RelaxSslValidation)
30+
{
31+
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
32+
}
33+
return handler;
34+
}
35+
36+
}
37+
}

test/integration/ServerTests/AppiumClientConfigTest.cs

+11
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,16 @@ public void SetAndGetDirectConnect()
3030
clientConfig.DirectConnect = true;
3131
Assert.That(clientConfig.DirectConnect);
3232
}
33+
34+
[Test]
35+
public void SetAndGetRelaxSSLValidation()
36+
{
37+
var clientConfig = AppiumClientConfig.DefaultConfig();
38+
Assert.That(clientConfig.RelaxSslValidation, Is.False);
39+
40+
clientConfig.RelaxSslValidation = true;
41+
Assert.That(clientConfig.RelaxSslValidation);
42+
}
43+
3344
}
3445
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//Licensed under the Apache License, Version 2.0 (the "License");
2+
//you may not use this file except in compliance with the License.
3+
//See the NOTICE file distributed with this work for additional
4+
//information regarding copyright ownership.
5+
//You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
//Unless required by applicable law or agreed to in writing, software
10+
//distributed under the License is distributed on an "AS IS" BASIS,
11+
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
//See the License for the specific language governing permissions and
13+
//limitations under the License.
14+
15+
16+
using System;
17+
using Appium.Net.Integration.Tests.helpers;
18+
using NUnit.Framework;
19+
using OpenQA.Selenium.Appium;
20+
using OpenQA.Selenium.Appium.Android;
21+
using OpenQA.Selenium.Appium.Service;
22+
23+
namespace Appium.Net.Integration.Tests.ServerTests
24+
{
25+
[TestFixture]
26+
public class RelaxSslValidationTest
27+
{
28+
private AppiumDriver _driver;
29+
AppiumClientConfig appiumClient;
30+
Uri serverUri;
31+
AppiumOptions capabilities;
32+
33+
[OneTimeSetUp]
34+
public void BeforeAll()
35+
{
36+
capabilities = Caps.GetAndroidUIAutomatorCaps(Apps.Get("androidApiDemos"));
37+
// TODO: Create serverUri that will redirect to https://
38+
serverUri = Env.ServerIsRemote() ? AppiumServers.RemoteServerUri : AppiumServers.LocalServiceUri;
39+
appiumClient = AppiumClientConfig.DefaultConfig();
40+
}
41+
42+
[Test]
43+
public void TestRelaxSslValidationTrue()
44+
{
45+
appiumClient.RelaxSslValidation = true;
46+
_driver = new AndroidDriver(serverUri, capabilities, Env.InitTimeoutSec, appiumClient);
47+
_driver.Manage().Timeouts().ImplicitWait = Env.ImplicitTimeoutSec;
48+
Assert.That(_driver, Is.Not.Null, "Driver initialization failed.");
49+
Assert.That(_driver.SessionId, Is.Not.Null, "Driver session ID is null.");
50+
}
51+
52+
[Test]
53+
public void TestRelaxSslValidationFalse()
54+
{
55+
appiumClient.RelaxSslValidation = false;
56+
try
57+
{
58+
_driver = new AndroidDriver(serverUri, capabilities, Env.InitTimeoutSec, appiumClient);
59+
}
60+
catch (Exception e)
61+
{
62+
using (Assert.EnterMultipleScope())
63+
{
64+
Assert.That(e, Is.InstanceOf<OpenQA.Selenium.WebDriverException>());
65+
Assert.That(_driver, Is.Null, "Driver initialization failed as it should.");
66+
}
67+
}
68+
}
69+
70+
[TearDown]
71+
public void TearDown()
72+
{
73+
_driver?.Dispose();
74+
_driver = null;
75+
}
76+
77+
}
78+
}

0 commit comments

Comments
 (0)