diff --git a/build.gradle b/build.gradle index 88070b21e..c020e4b3a 100644 --- a/build.gradle +++ b/build.gradle @@ -51,6 +51,7 @@ dependencies { } } implementation 'com.google.code.gson:gson:2.10.1' + implementation 'com.google.guava:guava:31.1-jre' implementation 'cglib:cglib:3.3.0' implementation 'commons-validator:commons-validator:1.7' implementation 'org.apache.commons:commons-lang3:3.12.0' diff --git a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java index d2b4d37fc..d2947e17e 100644 --- a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java +++ b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java @@ -52,7 +52,9 @@ public final class AppiumDriverLocalService extends DriverService { - private static final String URL_MASK = "http://%s:%d/"; + private static final String URL_MASK = "%s://%s:%d/"; + private static final String HTTP_PROTOCOL = "http"; + private static final String HTTPS_PROTOCOL = "https"; private static final Logger LOG = LoggerFactory.getLogger(AppiumDriverLocalService.class); private static final Pattern LOGGER_CONTEXT_PATTERN = Pattern.compile("^(\\[debug\\] )?\\[(.+?)\\]"); private static final String APPIUM_SERVICE_SLF4J_LOGGER_PREFIX = "appium.service"; @@ -66,10 +68,11 @@ public final class AppiumDriverLocalService extends DriverService { private final ListOutputStream stream = new ListOutputStream().add(System.out); private final URL url; private String basePath; + private boolean isSecureConnection; private CommandLine process = null; - AppiumDriverLocalService(String ipAddress, File nodeJSExec, + AppiumDriverLocalService(String ipAddress, String domainName, boolean isSecureConnection, File nodeJSExec, int nodeJSPort, Duration startupTimeout, List nodeJSArgs, Map nodeJSEnvironment ) throws IOException { @@ -78,7 +81,10 @@ public final class AppiumDriverLocalService extends DriverService { this.nodeJSArgs = nodeJSArgs; this.nodeJSEnvironment = nodeJSEnvironment; this.startupTimeout = startupTimeout; - this.url = new URL(String.format(URL_MASK, ipAddress, nodeJSPort)); + this.isSecureConnection = isSecureConnection; + String protocol = this.isSecureConnection ? HTTPS_PROTOCOL : HTTP_PROTOCOL; + String address = StringUtils.isNoneBlank(domainName) ? domainName : ipAddress; + this.url = new URL(String.format(URL_MASK, protocol, address, nodeJSPort)); } public static AppiumDriverLocalService buildDefaultService() { @@ -98,6 +104,15 @@ public String getBasePath() { return this.basePath; } + public AppiumDriverLocalService withSecureConnection(boolean secureConnection) { + this.isSecureConnection = secureConnection; + return this; + } + + public boolean isSecureConnection() { + return this.isSecureConnection; + } + @SneakyThrows private static URL addSuffix(URL url, String suffix) { return url.toURI().resolve("." + (suffix.startsWith("/") ? suffix : "/" + suffix)).toURL(); diff --git a/src/main/java/io/appium/java_client/service/local/AppiumServiceBuilder.java b/src/main/java/io/appium/java_client/service/local/AppiumServiceBuilder.java index 0b795872d..940d94438 100644 --- a/src/main/java/io/appium/java_client/service/local/AppiumServiceBuilder.java +++ b/src/main/java/io/appium/java_client/service/local/AppiumServiceBuilder.java @@ -17,6 +17,7 @@ package io.appium.java_client.service.local; import com.google.common.collect.ImmutableList; +import com.google.common.net.InternetDomainName; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import io.appium.java_client.internal.ReflectionHelpers; @@ -81,6 +82,8 @@ public final class AppiumServiceBuilder private String ipAddress = BROADCAST_IP_ADDRESS; private Capabilities capabilities; private boolean autoQuoteCapabilitiesOnWindows = false; + private boolean secure = false; + private String domainName; private static final Function APPIUM_JS_NOT_EXIST_ERROR = (fullPath) -> String.format( "The main Appium script does not exist at '%s'", fullPath.getAbsolutePath()); private static final Function NODE_JS_NOT_EXIST_ERROR = (fullPath) -> @@ -279,6 +282,11 @@ public AppiumServiceBuilder withIPAddress(String ipAddress) { return this; } + public AppiumServiceBuilder withDomainName(String domainName){ + this.domainName = domainName; + return this; + } + @Nullable private static File loadPathFromEnv(String envVarName) { String fullPath = System.getProperty(envVarName); @@ -400,6 +408,13 @@ protected ImmutableList createArgs() { argList.add(capabilitiesToCmdlineArg()); } + if(domainName != null) { + if(!InternetDomainName.isValid(domainName)){ + throw new IllegalArgumentException( + String.format("The invalid domainName '%s' is defined", domainName)); + } + } + return new ImmutableList.Builder().addAll(argList).build(); } @@ -447,6 +462,11 @@ public AppiumServiceBuilder usingAnyFreePort() { return super.usingAnyFreePort(); } + public AppiumServiceBuilder usingSecureConnection(boolean secure) { + this.secure = secure; + return this; + } + /** * Defines the environment for the launched appium server. * @@ -478,7 +498,7 @@ protected AppiumDriverLocalService createDriverService(File nodeJSExecutable, in Map nodeEnvironment) { String basePath = serverArguments.getOrDefault( GeneralServerFlag.BASEPATH.getArgument(), serverArguments.get("-pa")); - return new AppiumDriverLocalService(ipAddress, nodeJSExecutable, nodeJSPort, startupTimeout, nodeArguments, - nodeEnvironment).withBasePath(basePath); + return new AppiumDriverLocalService(ipAddress, domainName, secure, nodeJSExecutable, nodeJSPort, startupTimeout, + nodeArguments, nodeEnvironment).withBasePath(basePath); } } \ No newline at end of file diff --git a/src/test/java/io/appium/java_client/service/local/ServerBuilderTest.java b/src/test/java/io/appium/java_client/service/local/ServerBuilderTest.java index 5d4fd5715..3bbe8ea90 100644 --- a/src/test/java/io/appium/java_client/service/local/ServerBuilderTest.java +++ b/src/test/java/io/appium/java_client/service/local/ServerBuilderTest.java @@ -34,6 +34,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -132,6 +133,12 @@ void checkAbilityToStartServiceOnAFreePort() { assertTrue(service.isRunning()); } + @Test + void checkSecureConnectionFlagStartsServerCorrectly() { + service = new AppiumServiceBuilder().usingSecureConnection(true).build(); + assertTrue(service.getUrl().toString().startsWith("https")); + } + @Test void checkAbilityToStartServiceUsingNonLocalhostIP() { service = new AppiumServiceBuilder().withIPAddress(testIP).build(); @@ -351,4 +358,74 @@ void checkAbilityToValidateBasePathForBlankBasePath() { void checkAbilityToValidateBasePathForNullBasePath() { assertThrows(NullPointerException.class, () -> new AppiumServiceBuilder().withArgument(BASEPATH, null)); } + + @Test + void checkAbilityToValidateInvalidSpecialCharDomainName() { + assertThrows(IllegalArgumentException.class, () -> new AppiumServiceBuilder().usingAnyFreePort() + .withDomainName("*").build()); + } + + @Test + void checkAbilityToValidateInvalidEmptyDomainName() { + assertThrows(IllegalArgumentException.class, () -> new AppiumServiceBuilder().usingAnyFreePort() + .withDomainName("").build()); + } + + @Test + void checkAbilityToValidateInvalidDomainNameWithProtocol() { + assertThrows(IllegalArgumentException.class, () -> new AppiumServiceBuilder().usingAnyFreePort() + .withDomainName("https://one.two.com/").build()); + } + + @Test + void checkAbilityToValidateInvalidDomainNameWithIpAddress() { + assertThrows(IllegalArgumentException.class, () -> new AppiumServiceBuilder().usingAnyFreePort() + .withDomainName("0.0.0.0").build()); + } + + @Test + void checkAbilityToValidateValidDomainName() { + assertDoesNotThrow(() -> new AppiumServiceBuilder().usingAnyFreePort() + .withDomainName("onetwo.com").build()); + } + + @Test + void checkSecureUrlIsBuiltCorrectly() { + AppiumDriverLocalService builder = new AppiumServiceBuilder().usingAnyFreePort() + .usingSecureConnection(true) + .withDomainName("onetwo.com") + .usingPort(1234) + .build(); + assertEquals("https://onetwo.com:1234/", builder.getUrl().toString()); + } + + @Test + void checkNoneSecureUrlIsBuiltCorrectly() { + AppiumDriverLocalService builder = new AppiumServiceBuilder().usingAnyFreePort() + .usingSecureConnection(false) + .withDomainName("onetwo.com") + .usingPort(1234) + .build(); + assertEquals("http://onetwo.com:1234/", builder.getUrl().toString()); + } + + @Test + void checkNoneSecureIpAddressIsBuiltCorrectly() { + AppiumDriverLocalService builder = new AppiumServiceBuilder().usingAnyFreePort() + .usingSecureConnection(false) + .withIPAddress("1.1.1.1") + .usingPort(1234) + .build(); + assertEquals("http://1.1.1.1:1234/", builder.getUrl().toString()); + } + + @Test + void checkSecureIpAddressIsBuiltCorrectly() { + AppiumDriverLocalService builder = new AppiumServiceBuilder().usingAnyFreePort() + .usingSecureConnection(true) + .withIPAddress("1.1.1.1") + .usingPort(1234) + .build(); + assertEquals("https://1.1.1.1:1234/", builder.getUrl().toString()); + } }