Skip to content

fix: support IPv6 addresses in the local Appium service URL - #2495

Open
RaphaelFakhri wants to merge 1 commit into
appium:masterfrom
RaphaelFakhri:fix/local-service-ipv6-address
Open

RaphaelFakhri wants to merge 1 commit into
appium:masterfrom
RaphaelFakhri:fix/local-service-ipv6-address

Conversation

@RaphaelFakhri

Copy link
Copy Markdown

Change list

  • AppiumDriverLocalService encloses an IPv6 literal (for example :: or ::1) in square brackets when it builds the server URL. Before this change, build() failed with MalformedURLException.
  • The broadcast address check compares the host without brackets, so a service bound to BROADCAST_IP6_ADDRESS (::) is pinged on [::1] as intended.
  • Unit tests in AppiumServiceBuilderTest cover ::1 and BROADCAST_IP6_ADDRESS.

Types of changes

  • No changes in production code.
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Details

AppiumServiceBuilder exposes BROADCAST_IP6_ADDRESS, but passing it, or any other IPv6 address, to withIPAddress breaks build():

new AppiumServiceBuilder()
        .withIPAddress(AppiumServiceBuilder.BROADCAST_IP6_ADDRESS)
        .build();
// java.net.MalformedURLException: Error at index 0 in: "::4723"

The service formats the URL as http://%s:%d/, which produces http://:::4723/. With brackets the URL becomes http://[::]:4723/. The --address value sent to the server stays unbracketed, because the server expects a plain address.

fixBroadcastAddresses compared URL.getHost() with ::, but getHost() returns [::] for IPv6 hosts, so the IPv6 branch could never match. It now strips the brackets before the comparison.

@mykola-mokhnach mykola-mokhnach left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • AppiumDriverLocalService.java:129: replaceHost does a regex replaceFirst on the whole URL string. The new IPv6 path only works because "http://[::]:4777/".replaceFirst("::", "::1") happens to hit the host. oldHost isn't quoted, so the dots in 0.0.0.0 are regex wildcards, and newHost isn't passed through Matcher.quoteReplacement. Would building the URL from getProtocol()/getHost()/getPort() be safer?

this.startupTimeout = startupTimeout;
this.url = new URL(String.format(URL_MASK, ipAddress, nodeJSPort));
// IPv6 literals must be enclosed in square brackets to be a valid URL host
String host = ipAddress.contains(":") && !ipAddress.startsWith("[") ? "[" + ipAddress + "]" : ipAddress;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bracket-wrapping check (contains(":") and not starting with [) misses some IPv6 forms:

  • A zone id like fe80::1%en0 becomes [fe80::1%en0], but % must be encoded as %25 in a URL, so the host is mis-parsed or the connection fails.
  • If a user passes a bracketed [::1] to withIPAddress, the URL is correct, but createArgs passes the literal [::1] to Appium's --address, which Node rejects. The two code paths disagree. Should the address be normalized once and the unbracketed form used for --address?


private URL fixBroadcastAddresses(URL url) {
var host = url.getHost();
// IPv6 hosts are returned in square brackets

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the exact string :: is treated as the IPv6 any-address. 0:0:0:0:0:0:0:0 and ::0 are equivalent wildcard binds but won't be rewritten to loopback. ping then connects to the unspecified address, which some OSes block, so start() times out even though the server is up.


private URL fixBroadcastAddresses(URL url) {
var host = url.getHost();
// IPv6 hosts are returned in square brackets

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: "^\\[(.*)]$" is recompiled on every ping. host.startsWith("[") && host.endsWith("]") with substring does the same job, or use a precompiled Pattern. It could also sit in one helper next to the bracket-wrapping at line 87.

assertFalse(service.isRunning());
}

@Test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tests only check URL formatting. Nothing exercises fixBroadcastAddresses or the bracket-stripping in the ping path. If [::] stops being rewritten to ::1, no test fails. An already-bracketed [::1] input and a zone-id input are also untested.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants