Repository: influxdata/influxdb-client-java (java)

Issue:
IPv6 hosts are not supported
https://github.com/influxdata/influxdb-client-java/blob/886eb8840b4d72fdb9973801ba4cd6e1d6fa1306/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java#L697

The InfluxDBClientOptions.ParsedUrl constructor strips the square brackets off IPv6 addresses, rendering urls containing them invalid. E.g. `http://[2001:db8:3333:4444:5555:6666:7777:8888]:8086/` becomes `http://2001:db8:3333:4444:5555:6666:7777:8888:8086/`.

See how okhttp3 HttpUrl.Builder.toString() handles IPv6 addresses.

Pull request description:
fix: Issue/623
Closes #623

## Proposed Changes

Change the way in which `InfluxDBClientOptions` prepares a parsed URL, so that IPv6 addresses will be detected and preserved. 

## Checklist

<!-- Checkboxes below this note can be erased if not applicable to your Pull Request. -->

- [x] CHANGELOG.md updated
- [x] Rebased/mergeable
- [x] A test has been added if appropriate
- [x] `mvn test` completes successfully
- [x] Commit messages are [conventional](https://www.conventionalcommits.org/en/v1.0.0/)
- [x] Sign [CLA](https://www.influxdata.com/legal/cla/) (if not already signed)


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b379b0cd74..918bfef712 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,8 @@
 ### Bug Fixes
 
 1. [#684](https://github.com/influxdata/influxdb-client-java/issues/684): Fix checking for CSV end of table marker when parsing CSV stream to InfluxQLQueryResult, needed for example when parsing the results of a query like "SHOW SERIES".
-2. [#662](https://github.com/influxdata/influxdb-client-java/issues/662): Adds to FluxDsl support for the `|> elapsed(unit)` function. 
+2. [#662](https://github.com/influxdata/influxdb-client-java/issues/662): Adds to FluxDsl support for the `|> elapsed(unit)` function.
+3. [#623](https://github.com/influxdata/influxdb-client-java/issues/623): Enables the use of IPv6 addresses. 
 
 ### Dependencies
 
diff --git a/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java b/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java
index 291f0f85ee..528f5418dd 100644
--- a/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java
+++ b/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java
@@ -694,12 +694,13 @@ public final class InfluxDBClientOptions {
 
                 HttpUrl url = this.httpUrl.newBuilder().build();
 
-                String urlWithoutParams = url.scheme() + "://" + url.host() + ":" + url.port() + url.encodedPath();
-                if (!urlWithoutParams.endsWith("/")) {
-                    urlWithoutParams += "/";
-                }
+                //detect IPV6
+                String host = url.host().contains(":") ? "[" + url.host() + "]" : url.host();
+                String urlWithoutParams = url.scheme() + "://" + host + ":" + url.port() + url.encodedPath();
 
-                this.urlWithoutParams = urlWithoutParams;
+                this.urlWithoutParams = urlWithoutParams.endsWith("/")
+                  ? urlWithoutParams
+                  : urlWithoutParams + "/";
             }
         }
     }


Test patch:
diff --git a/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java b/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java
index 5c18f75c3e..c95ae134bd 100644
--- a/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java
+++ b/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java
@@ -21,11 +21,14 @@
  */
 package com.influxdb.client;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import com.influxdb.client.domain.WritePrecision;
 
+import com.influxdb.exceptions.InfluxException;
 import okhttp3.OkHttpClient;
 import okhttp3.Protocol;
 import org.assertj.core.api.Assertions;
@@ -156,4 +159,71 @@ class InfluxDBClientOptionsTest {
 
         Assertions.assertThat(options.getClientType()).isEqualTo("properties-service");
     }
+
+    @Test
+    public void ipv6Loopback(){
+        String[] loopbacks = {"[::1]", "[0000:0000:0000:0000:0000:0000:0000:0001]"};
+
+      for (String loopback : loopbacks) {
+        InfluxDBClientOptions options = InfluxDBClientOptions.builder()
+          .url(String.format("http://%s:9999/api/v2/", loopback))
+          .authenticateToken("xyz".toCharArray())
+          .org("my-org")
+          .build();
+
+        Assertions.assertThat(options.getUrl()).isEqualTo("http://[::1]:9999/api/v2/");
+        Assertions.assertThat(options.getAuthScheme()).isEqualTo(InfluxDBClientOptions.AuthScheme.TOKEN);
+        Assertions.assertThat(options.getOkHttpClient()).isNotNull();
+        Assertions.assertThat(options.getPrecision()).isEqualTo(WritePrecision.NS);
+        Assertions.assertThat(options.getOrg()).isEqualTo("my-org");
+      }
+    }
+
+    @Test
+    public void ipv6General(){
+        Map<String, String> ipv6Expected = Map.of(
+          "[2001:db80:0001:1000:1100:0011:1110:0111]", "[2001:db80:1:1000:1100:11:1110:111]",
+          "[2001:db8:1000:0000:0000:0000:0000:0001]", "[2001:db8:1000::1]",
+          "[2001:db8f:0ff0:00ee:0ddd:000c:bbbb:aaaa]", "[2001:db8f:ff0:ee:ddd:c:bbbb:aaaa]",
+          "[2001:0db8:0000:0000:0000:9876:0000:001f]", "[2001:db8::9876:0:1f]",
+          "[0000:0000:0000:0000:0000:0000:0000:0000]", "[::]",
+          "[2001:0db8:fedc:edcb:dcba:cba9:ba98:a987]", "[2001:db8:fedc:edcb:dcba:cba9:ba98:a987]"//,
+          //"[::1]", ""
+        );
+
+        for(String key : ipv6Expected.keySet()){
+            InfluxDBClientOptions options = InfluxDBClientOptions.builder()
+              .url(String.format("http://%s:9999/api/v2/query?orgID=my-org", key))
+              .authenticateToken("xyz".toCharArray())
+              .build();
+
+            System.out.println(key + ": " + options.getUrl());
+
+            Assertions.assertThat(options.getUrl())
+              .isEqualTo(String.format("http://%s:9999/api/v2/query/", ipv6Expected.get(key)));
+            Assertions.assertThat(options.getToken())
+              .isEqualTo("xyz".toCharArray());
+        }
+    }
+
+    @Test
+    public void ipv6Invalid(){
+        List<String> invalidIpv6 = Arrays.asList(
+          "[:1]",
+          "[:::1]",
+          "[2001:db8:0000:1]",
+          "[2001:db8:00000::1]",
+          "[2001:db8:0000:::1]",
+          "[:0000::1]",
+          "[:::0000::1]");
+        for(String ipv6 : invalidIpv6){
+            Assertions.assertThatThrownBy(() -> { InfluxDBClientOptions options2 = InfluxDBClientOptions.builder()
+              .url(String.format("http://%s:9999/api/v2/query?orgID=my-org", ipv6))
+              .authenticateToken("xyz".toCharArray())
+              .build();}).isInstanceOf(InfluxException.class)
+              .hasMessage(String.format("Unable to parse connection string http://%s:9999/api/v2/query?orgID=my-org", ipv6));
+        }
+
+    }
+
 }
\ No newline at end of file
diff --git a/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java b/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java
index ee163c12c9..af876ccf75 100644
--- a/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java
+++ b/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java
@@ -21,7 +21,9 @@
  */
 package com.influxdb.client;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.net.InetSocketAddress;
 import java.net.Proxy;
 import java.util.List;
@@ -31,6 +33,9 @@ import java.util.logging.LogRecord;
 import java.util.logging.Logger;
 import javax.annotation.Nonnull;
 
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpHandler;
 import com.influxdb.LogLevel;
 import com.influxdb.client.domain.Authorization;
 import com.influxdb.client.domain.Run;