You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2023/04/06 09:37:30 UTC
[httpcomponents-core] 01/03: Add support for Internationalized Domain Names (IDN)
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch 5.3.x
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git
commit 716f64c744d227bca80d8ee6a0a5cc951a5910d9
Author: Arturo Bernal <ar...@gmail.com>
AuthorDate: Fri Feb 24 22:18:55 2023 +0100
Add support for Internationalized Domain Names (IDN)
This change updates the URI parsing logic to correctly handle non-ASCII domain names using the Internationalized Domain Names (IDN) system. When parsing a URI, the code now checks if the domain name contains non-ASCII characters, and if so, it converts the domain name to its ASCII-compatible encoding using the IDN.toASCII() method.
This allows applications using this code to correctly handle URLs with non-ASCII domain names, which are increasingly common in today's globalized internet.
---
.../main/java/org/apache/hc/core5/net/Host.java | 23 +++++++++++++++++++---
.../java/org/apache/hc/core5/util/TextUtils.java | 18 +++++++++++++++++
.../http/examples/ClassicPostExecutionExample.java | 2 +-
.../http/support/TestBasicMessageBuilders.java | 8 ++++++++
.../java/org/apache/hc/core5/net/TestHost.java | 11 +++++++++++
5 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/net/Host.java b/httpcore5/src/main/java/org/apache/hc/core5/net/Host.java
index 3ef6192f1..008a4f9f2 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/net/Host.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/net/Host.java
@@ -27,6 +27,7 @@
package org.apache.hc.core5.net;
import java.io.Serializable;
+import java.net.IDN;
import java.net.URISyntaxException;
import org.apache.hc.core5.annotation.Contract;
@@ -50,10 +51,22 @@ public final class Host implements NamedEndpoint, Serializable {
private final String lcName;
private final int port;
+ static boolean isPunyCode(final CharSequence s) {
+ if (s == null || s.length() < 4) {
+ return false;
+ }
+ return ((s.charAt(0) == 'x' || s.charAt(0) == 'X') &&
+ (s.charAt(1) == 'n' || s.charAt(1) == 'N') &&
+ s.charAt(2) == '-' &&
+ s.charAt(3) == '-');
+ }
+
public Host(final String name, final int port) {
super();
- this.name = Args.notNull(name, "Host name");
- this.port = Ports.checkWithDefault(port);
+ Args.notNull(name, "Host name");
+ Ports.checkWithDefault(port);
+ this.name = isPunyCode(name) ? IDN.toUnicode(name) : name;
+ this.port = port;
this.lcName = TextUtils.toLowerCase(this.name);
}
@@ -105,7 +118,11 @@ public final class Host implements NamedEndpoint, Serializable {
if (InetAddressUtils.isIPv6Address(hostName)) {
buf.append('[').append(hostName).append(']');
} else {
- buf.append(hostName);
+ if (TextUtils.isAllASCII(hostName)) {
+ buf.append(hostName);
+ } else {
+ buf.append(IDN.toASCII(hostName));
+ }
}
if (endpoint.getPort() != -1) {
buf.append(":");
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java b/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java
index a8b6a6e28..867cb3786 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java
@@ -141,4 +141,22 @@ public final class TextUtils {
return s.toLowerCase(Locale.ROOT);
}
+
+ /**
+ * Determines whether the given {@link CharSequence} contains only ASCII characters.
+ *
+ * @param s the {@link CharSequence} to check
+ * @return true if the {@link CharSequence} contains only ASCII characters, false otherwise
+ * @throws IllegalArgumentException if the input {@link CharSequence} is null
+ * @since 5.3
+ */
+ public static boolean isAllASCII(final CharSequence s) {
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) > 0x7F) {
+ return false;
+ }
+ }
+ return true;
+ }
+
}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicPostExecutionExample.java b/httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicPostExecutionExample.java
index e574def45..fa79ae635 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicPostExecutionExample.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicPostExecutionExample.java
@@ -106,7 +106,7 @@ public class ClassicPostExecutionExample {
final String requestUri = "/post";
for (int i = 0; i < requestBodies.length; i++) {
- final ClassicHttpRequest request = ClassicRequestBuilder.post()
+ final ClassicHttpRequest request = ClassicRequestBuilder.get()
.setHttpHost(target)
.setPath(requestUri)
.build();
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java b/httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java
index 9c91c8ec6..a3c4705fb 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java
@@ -28,6 +28,7 @@
package org.apache.hc.core5.http.support;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.hc.core5.http.HeaderMatcher;
import org.apache.hc.core5.http.HeadersMatcher;
@@ -237,4 +238,11 @@ public class TestBasicMessageBuilders {
new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2")));
}
+ @Test
+ void testIDNIntegration() {
+ final String url = "http://müller.example.com:8080/path";
+ final HttpRequest request = new BasicHttpRequest(Method.GET, URI.create(url));
+ assertEquals(new URIAuthority("xn--mller-kva.example.com",8080), request.getAuthority());
+ }
+
}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/net/TestHost.java b/httpcore5/src/test/java/org/apache/hc/core5/net/TestHost.java
index d2090923a..1a9a4ddb3 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/net/TestHost.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/net/TestHost.java
@@ -50,6 +50,15 @@ public class TestHost {
final Host host2 = new Host("somehost", 0);
Assertions.assertEquals("somehost", host2.getHostName());
Assertions.assertEquals(0, host2.getPort());
+ final Host host3 = new Host("Яндекс.Ру", 0);
+ Assertions.assertEquals("Яндекс.Ру", host3.getHostName());
+ Assertions.assertEquals(0, host3.getPort());
+ final Host host4 = new Host("xn--d1acpjx3f.xn--p1ag", 0);
+ Assertions.assertEquals("яндекс.ру", host4.getHostName());
+ Assertions.assertEquals(0, host4.getPort());
+ final Host host5 = new Host("XN--D1Acpjx3f.xn--p1ag", 0);
+ Assertions.assertEquals("яндекс.ру", host5.getHostName());
+ Assertions.assertEquals(0, host5.getPort());
Assertions.assertThrows(NullPointerException.class, () -> new Host(null, 0));
}
@@ -104,6 +113,8 @@ public class TestHost {
Assertions.assertEquals(new Host("somehost", 8080), Host.create("somehost:8080"));
Assertions.assertEquals(new Host("somehost", 1234), Host.create("somehost:1234"));
Assertions.assertEquals(new Host("somehost", 0), Host.create("somehost:0"));
+ Assertions.assertEquals(new Host("яндекс.ру", -1), Host.create("xn--d1acpjx3f.xn--p1ag"));
+ Assertions.assertEquals(new Host("Яндекс.Ру", -1), Host.create("Яндекс.Ру"));
}
@Test