Skip to content

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

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

RaphaelFakhri wants to merge 4 commits 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?

Strip square brackets once in withIPAddress, so --address always gets
the bare address. Treat any form of the unspecified IPv6 address as a
wildcard bind when pinging, and rebuild the ping URL from its parts
instead of a regex replacement.
Normalize the address once where the server arguments are built, so
toUrlHost no longer strips brackets that it then adds back.
var host = url.getHost();
static URL fixBroadcastAddresses(URL url) {
// IPv6 hosts are returned in square brackets
var host = stripBrackets(url.getHost());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why is it needed to strip brackets here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It isn't needed. InetAddress.getByName accepts the bracketed form ([::], [::0] and [0:0:0:0:0:0:0:0] all give isAnyLocalAddress() == true), and the 0.0.0.0 comparison never has brackets. Removed in 5d86594, fixBroadcastAddresses now passes url.getHost() through as is. The existing fixBroadcastAddresses tests still pass.

private static URL replaceHost(URL source, String oldHost, String newHost) {
return new URL(source.toString().replaceFirst(oldHost, newHost));
private static URL replaceHost(URL source, String newHost) {
return new URL(source.getProtocol(), toUrlHost(newHost), source.getPort(), source.getFile());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

does URL require IPv6 wrapped into brackets?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not when the host is passed separately. new URL(protocol, host, port, file) adds the brackets itself when the host contains : (new URL("http", "::1", 4723, "/") gives http://[::1]:4723/, and a zone ID like fe80::1%en0 also comes out as [fe80::1%en0]). Only the string form http://%s:%d/ needed them. In 5d86594 the constructor uses new URL("http", ipAddress, nodeJSPort, "/"), so toUrlHost and URL_MASK are gone. stripBrackets is now only used in createArgs(), so I moved it into AppiumServiceBuilder as a private method. Unit tests and checkstyle pass.

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.

3 participants