diff --git a/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java b/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java
index 33195b7d..22204171 100644
--- a/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java
+++ b/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java
@@ -19,8 +19,10 @@
import org.ice4j.*;
-import java.io.*;
import java.net.*;
+import java.net.http.*;
+import java.time.*;
+import java.util.*;
import java.util.logging.*;
/**
@@ -39,6 +41,14 @@ public class AwsCandidateHarvester
private static final Logger logger
= Logger.getLogger(AwsCandidateHarvester.class.getName());
+ /**
+ * The HttpClient used by the AwsCandidateHarvester
+ * class and its instances for making requests to IMDS.
+ */
+ private static final HttpClient httpClient = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofMillis(500))
+ .build();
+
/**
* The URL where one obtains AWS public addresses.
*/
@@ -52,10 +62,28 @@ public class AwsCandidateHarvester
= "http://169.254.169.254/latest/meta-data/local-ipv4";
/**
- * The URL to use to test whether we are running on Amazon EC2.
+ * The URL to get IMDSv2 API token for further meta-data requests.
+ */
+ private static final String IMDS_API_TOKEN_URL
+ = "http://169.254.169.254/latest/api/token";
+
+ /**
+ * The HTTP header name to provide session token.
*/
- private static final String EC2_TEST_URL
- = "http://169.254.169.254/latest/meta-data/";
+ private static final String EC2_METADATA_TOKEN_HEADER
+ = "X-aws-ec2-metadata-token";
+
+ /**
+ * The HTTP header name to request session token TTL.
+ */
+ private static final String EC2_METADATA_TOKEN_TTL_HEADER
+ = "X-aws-ec2-metadata-token-ttl-seconds";
+
+ /**
+ * The default session token TTL value.
+ */
+ private static final String EC2_METADATA_TOKEN_DEFAULT_TTL
+ = "21600";
/**
* Whether we are running on Amazon EC2.
@@ -103,8 +131,11 @@ private static synchronized void obtainEC2Addresses()
try
{
- localIPStr = fetch(LOCAL_IP_URL);
- publicIPStr = fetch(PUBLIC_IP_URL);
+ String metaDataToken = fetch(IMDS_API_TOKEN_URL,
+ Collections.singletonMap(EC2_METADATA_TOKEN_TTL_HEADER, EC2_METADATA_TOKEN_DEFAULT_TTL), "PUT");
+ Map tokenHeader = Collections.singletonMap(EC2_METADATA_TOKEN_HEADER, metaDataToken);
+ localIPStr = fetch(LOCAL_IP_URL, tokenHeader);
+ publicIPStr = fetch(PUBLIC_IP_URL, tokenHeader);
//now let's cross our fingers and hope that what we got above are
//real IP addresses
@@ -183,11 +214,10 @@ private static boolean doTestEc2()
{
try
{
- URLConnection conn = new URL(EC2_TEST_URL).openConnection();
- conn.setConnectTimeout(500); //don't hang for too long
- conn.getContent();
+ String metaDataToken = fetch(IMDS_API_TOKEN_URL,
+ Collections.singletonMap(EC2_METADATA_TOKEN_TTL_HEADER, EC2_METADATA_TOKEN_DEFAULT_TTL), "PUT");
- return true;
+ return metaDataToken != null;
}
catch(Exception exc)
{
@@ -201,21 +231,40 @@ private static boolean doTestEc2()
*
* @param url the URL we'd like to open and query.
*
+ * @param headers the HTTP headers to put into the request.
+ *
+ * @throws Exception if anything goes wrong.
+ */
+ private static String fetch(String url, Map headers)
+ throws Exception
+ {
+ return fetch(url, headers, "GET");
+ }
+
+ /**
+ * Retrieves the content at the specified url. No more, no less.
+ *
+ * @param url the URL we'd like to open and query.
+ *
+ * @param headers the HTTP headers to put into the request.
+ *
+ * @param method the HTTP method we'd like to use.
+ *
* @return the String we retrieved from the URL.
*
* @throws Exception if anything goes wrong.
*/
- private static String fetch(String url)
+ private static String fetch(String url, Map headers, String method)
throws Exception
{
- URLConnection conn = new URL(url).openConnection();
- BufferedReader in = new BufferedReader(new InputStreamReader(
- conn.getInputStream(), "UTF-8"));
-
- String retString = in.readLine();
-
- in.close();
-
- return retString;
+ HttpRequest.Builder builder = HttpRequest.newBuilder()
+ .uri(new URI(url))
+ .method(method, HttpRequest.BodyPublishers.noBody());
+ for (Map.Entry header : headers.entrySet())
+ {
+ builder.setHeader(header.getKey(), header.getValue());
+ }
+ HttpResponse response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString());
+ return response.body();
}
}