-
Notifications
You must be signed in to change notification settings - Fork 69
fix: IPv6 host support #4367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3.x.x
Are you sure you want to change the base?
fix: IPv6 host support #4367
Changes from all commits
c702446
623960f
99e1065
93d577c
0ca2efa
b1307ad
170d4e5
a473483
cb213d6
a3279b3
f739842
2907323
7ffa42f
c01baff
a0dc139
2a5ef3b
f4ad343
83d1253
a378cb8
52580af
c253933
fe7c7b9
f480f23
a00a7df
98d6b2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,7 @@ | |
| import org.zowe.apiml.product.gateway.GatewayClient; | ||
| import org.zowe.apiml.product.instance.ServiceAddress; | ||
| import org.zowe.apiml.product.routing.RoutedService; | ||
| import org.zowe.apiml.util.UrlUtils; | ||
|
|
||
| import java.net.URI; | ||
| import java.util.Collections; | ||
|
|
@@ -106,7 +107,7 @@ private void updateServer(OpenAPI openAPI) { | |
| if (openAPI.getServers() != null) { | ||
| openAPI.getServers() | ||
| .forEach(server -> server.setUrl( | ||
| String.format("%s://%s/%s", scheme, getHostname(), server.getUrl()))); | ||
| UrlUtils.getUrl(scheme, UrlUtils.formatHostnameForUrl(getHostname())) + "/" + server.getUrl())); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getHostname() returns host+port, which contains ":" even if there is no IPv6 address. This will probably create an incorrect URL
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've considered the possibility that getHostname() might be returning the host along with the port, and I've added handling for that here. But, I suspect the issue might be related to the formatHostnameForUrl() method. I'll take a closer look to better understand what's going wrong. |
||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,4 +112,128 @@ public boolean isValidUrl(String urlString) { | |
| return false; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Determines if a given string is an IPv6 address. | ||
| * | ||
| * @param address The string to check | ||
| * @return true if the address is an IPv6 address, false otherwise | ||
| */ | ||
| private boolean isIPv6Address(String address) { | ||
| try { | ||
| return InetAddress.getByName(address) instanceof Inet6Address; | ||
| } catch (UnknownHostException e) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validates if a string represents a valid port number. | ||
| * | ||
| * @param port The string to validate as a port number | ||
| * @return true if the string represents a valid port, false otherwise | ||
| */ | ||
| private boolean isValidPort(String port) { | ||
|
|
||
| if (port == null || port.isEmpty()) { | ||
| return false; | ||
| } | ||
|
|
||
| try { | ||
| int portNum = Integer.parseInt(port); | ||
| return portNum >= 0 && portNum <= 65535; | ||
| } catch (NumberFormatException e) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Formats a hostname properly, ensuring IPv6 addresses are enclosed in square brackets. | ||
| * If the input is already a properly formatted IPv6 address (with brackets), it remains unchanged. | ||
| * Handles both IPv6 addresses and hostname:port combinations. | ||
| * | ||
| * @param hostname The hostname or IP address to format | ||
| * @return Properly formatted hostname, with IPv6 addresses enclosed in square brackets | ||
| */ | ||
| public String formatHostnameForUrl(String hostname) { | ||
| if (hostname == null || hostname.isEmpty()) { | ||
| return hostname; | ||
| } | ||
|
|
||
| // If already properly formatted with brackets, return as is | ||
| if (hostname.startsWith("[") && hostname.contains("]")) { | ||
| return hostname; | ||
| } | ||
|
|
||
| // Check for hostname:port format by looking at the last colon | ||
| // We check this BEFORE checking if the entire string is IPv6 because: | ||
| // 1. "2001:db8::1:8080" could be ambiguous - is 8080 part of IPv6 or a port? | ||
| // 2. If the last segment after colon is a valid port number, we should treat it as such | ||
| int lastColonIndex = hostname.lastIndexOf(':'); | ||
| if (lastColonIndex > -1) { | ||
| String possibleHost = hostname.substring(0, lastColonIndex); | ||
| String possiblePort = hostname.substring(lastColonIndex + 1); | ||
|
|
||
| // Check if what follows the last colon is a valid port number | ||
| if (isValidPort(possiblePort)) { | ||
| // If we have a valid port, check if the host part is IPv6 | ||
| if (isIPv6Address(possibleHost)) { | ||
| return "[" + possibleHost + "]:" + possiblePort; | ||
| } | ||
| // If the full string is NOT IPv6, return as-is (hostname:port or IPv4:port) | ||
| if (!isIPv6Address(hostname)) { | ||
| return hostname; | ||
| } | ||
| // Edge case: If full hostname IS IPv6 but possibleHost is not, | ||
| // fall through to check if it's a plain IPv6 address without port | ||
| } | ||
| } | ||
|
|
||
| // No valid port found, check if it's a plain IPv6 address | ||
| if (isIPv6Address(hostname)) { | ||
| return "[" + hostname + "]"; | ||
| } | ||
|
|
||
| return hostname; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a proper URL string with scheme, hostname, and port, | ||
| * handling IPv6 addresses correctly. | ||
| * | ||
| * @param scheme The URL scheme (http, https, etc.) | ||
| * @param hostname The hostname or IP address | ||
| * @param port The port number | ||
| * @return A properly formatted URL string with IPv6 address handling | ||
| */ | ||
| public String getUrl(String scheme, String hostname, int port) { | ||
| String formattedHostname = formatHostnameForUrl(hostname); | ||
| return String.format("%s://%s:%d", scheme, formattedHostname, port); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a proper URL string with scheme and host (which may include port), | ||
| * handling IPv6 addresses correctly. | ||
| * | ||
| * @param scheme The URL scheme (http, https, etc.) | ||
| * @param hostWithPort The hostname or IP address, possibly including a port | ||
| * @return A properly formatted URL string with IPv6 address handling | ||
| */ | ||
| public String getUrl(String scheme, String hostWithPort) { | ||
| if (scheme == null || scheme.isEmpty()) { | ||
| throw new IllegalArgumentException("Scheme cannot be null or empty"); | ||
| } | ||
|
|
||
| if (hostWithPort == null || hostWithPort.isEmpty()) { | ||
| throw new IllegalArgumentException("Host cannot be null or empty"); | ||
| } | ||
|
|
||
| // Remove any existing scheme if present | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it possible that there will be scheme if the parameter is called hostWithPort?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added this just to safeguard us if any caller passes value which may contain scheme, also as it's a single regex check hence, computationally cheap for us and makes sure correct host/host+port is sent. |
||
| String cleanHostWithPort = hostWithPort.replaceFirst("^[a-zA-Z][a-zA-Z0-9+.-]*://", ""); | ||
|
|
||
| // Format the hostname part properly | ||
| String formattedHost = formatHostnameForUrl(cleanHostWithPort); | ||
|
|
||
| return String.format("%s://%s", scheme, formattedHost); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.