Last active
June 10, 2025 03:08
-
-
Save unclebean/09cad2100e71b394b800ea70581cdbd6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import org.apache.hc.client5.http.classic.methods.HttpGet; | |
| import org.apache.hc.client5.http.classic.CloseableHttpClient; | |
| import org.apache.hc.client5.http.impl.classic.HttpClients; | |
| import org.apache.hc.core5.ssl.SSLContextBuilder; | |
| import org.apache.hc.core5.ssl.SSLContexts; | |
| import javax.net.ssl.SSLContext; | |
| import java.io.File; | |
| import java.security.KeyStore; | |
| public class P12HttpClientExample { | |
| public static void main(String[] args) throws Exception { | |
| String p12Path = "src/main/resources/client.p12"; | |
| String password = "changeit"; // same as when you exported the file | |
| // Load the PKCS12 keystore | |
| KeyStore keyStore = KeyStore.getInstance("PKCS12"); | |
| try (var is = new java.io.FileInputStream(new File(p12Path))) { | |
| keyStore.load(is, password.toCharArray()); | |
| } | |
| // Build SSL context from keystore | |
| SSLContext sslContext = SSLContexts.custom() | |
| .loadKeyMaterial(keyStore, password.toCharArray()) | |
| .build(); | |
| // Create HttpClient with SSL | |
| CloseableHttpClient client = HttpClients.custom() | |
| .setSSLContext(sslContext) | |
| .build(); | |
| HttpGet request = new HttpGet("https://your-api.example.com/secure-endpoint"); | |
| client.execute(request, response -> { | |
| System.out.println("Status: " + response.getCode()); | |
| return null; | |
| }); | |
| client.close(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import org.apache.hc.client5.http.classic.methods.HttpGet; | |
| import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; | |
| import org.apache.hc.client5.http.impl.classic.HttpClients; | |
| import org.apache.hc.core5.ssl.SSLContextBuilder; | |
| import javax.net.ssl.SSLContext; | |
| import java.io.FileInputStream; | |
| import java.nio.file.Files; | |
| import java.nio.file.Paths; | |
| import java.security.KeyFactory; | |
| import java.security.KeyStore; | |
| import java.security.PrivateKey; | |
| import java.security.cert.CertificateFactory; | |
| import java.security.cert.X509Certificate; | |
| import java.security.spec.PKCS8EncodedKeySpec; | |
| import java.util.Base64; | |
| public class ClientWithPEM { | |
| public static void main(String[] args) throws Exception { | |
| // Step 1: Load certificate | |
| CertificateFactory cf = CertificateFactory.getInstance("X.509"); | |
| X509Certificate certificate = (X509Certificate) cf.generateCertificate( | |
| new FileInputStream("client.crt") | |
| ); | |
| // Step 2: Load private key | |
| String keyPEM = new String(Files.readAllBytes(Paths.get("client.key"))); | |
| keyPEM = keyPEM | |
| .replace("-----BEGIN PRIVATE KEY-----", "") | |
| .replace("-----END PRIVATE KEY-----", "") | |
| .replaceAll("\\s", ""); | |
| byte[] keyBytes = Base64.getDecoder().decode(keyPEM); | |
| PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); | |
| PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec); | |
| // Step 3: Create in-memory KeyStore | |
| KeyStore keyStore = KeyStore.getInstance("PKCS12"); | |
| keyStore.load(null); | |
| keyStore.setKeyEntry("client", privateKey, new char[0], new java.security.cert.Certificate[]{certificate}); | |
| // Step 4: Build SSLContext | |
| SSLContext sslContext = SSLContextBuilder.create() | |
| .loadKeyMaterial(keyStore, new char[0]) // no password | |
| .build(); | |
| // Step 5: Create client | |
| CloseableHttpClient client = HttpClients.custom() | |
| .setSSLContext(sslContext) | |
| .build(); | |
| // Step 6: Make HTTPS request | |
| HttpGet request = new HttpGet("https://your-api"); | |
| client.execute(request, response -> { | |
| System.out.println("Status: " + response.getCode()); | |
| return null; | |
| }); | |
| client.close(); | |
| } | |
| } | |
| // | |
| dependencies { | |
| testImplementation 'com.intuit.karate:karate-junit5:1.4.1' // Use latest stable version | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; | |
| import org.apache.hc.client5.http.impl.classic.HttpClients; | |
| import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; | |
| import org.apache.hc.core5.http.io.SocketConfig; | |
| import org.apache.hc.core5.ssl.SSLContextBuilder; | |
| import org.apache.hc.core5.ssl.SSLConnectionSocketFactoryBuilder; | |
| import org.apache.hc.core5.ssl.TLS; | |
| import javax.net.ssl.SSLContext; | |
| import java.io.FileInputStream; | |
| import java.security.KeyStore; | |
| public class MtlsHttpClientFactory { | |
| public static CloseableHttpClient create() throws Exception { | |
| // Load client keystore (PKCS12 with client cert and key) | |
| KeyStore keyStore = KeyStore.getInstance("PKCS12"); | |
| try (FileInputStream ksStream = new FileInputStream("client-keystore.p12")) { | |
| keyStore.load(ksStream, "changeit".toCharArray()); | |
| } | |
| // Load truststore (JKS with server CA) | |
| KeyStore trustStore = KeyStore.getInstance("JKS"); | |
| try (FileInputStream tsStream = new FileInputStream("truststore.jks")) { | |
| trustStore.load(tsStream, "changeit".toCharArray()); | |
| } | |
| // Build SSL context | |
| SSLContext sslContext = SSLContextBuilder.create() | |
| .loadKeyMaterial(keyStore, "changeit".toCharArray()) | |
| .loadTrustMaterial(trustStore, null) | |
| .build(); | |
| // Use SSLConnectionSocketFactoryBuilder (not deprecated) | |
| var sslSocketFactory = SSLConnectionSocketFactoryBuilder.create() | |
| .setSslContext(sslContext) | |
| .setTlsVersions(TLS.V_1_3, TLS.V_1_2) | |
| .build(); | |
| var connectionManager = PoolingHttpClientConnectionManagerBuilder.create() | |
| .setSSLSocketFactory(sslSocketFactory) // ✅ This is correct for classic client | |
| .setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(30_000).build()) | |
| .build(); | |
| return HttpClients.custom() | |
| .setConnectionManager(connectionManager) | |
| .build(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import org.apache.hc.client5.http.async.methods.*; | |
| import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; | |
| import org.apache.hc.client5.http.impl.async.HttpAsyncClients; | |
| import org.apache.hc.core5.concurrent.FutureCallback; | |
| import java.util.List; | |
| import java.util.concurrent.*; | |
| import java.util.stream.Collectors; | |
| public class SafeHttp5MultiCaller { | |
| private final CloseableHttpAsyncClient httpClient; | |
| private final int maxRetries = 3; | |
| private final long backoffMillis = 300; | |
| public SafeHttp5MultiCaller() { | |
| this.httpClient = HttpAsyncClients.createDefault(); | |
| this.httpClient.start(); | |
| } | |
| public List<String> callApis(List<String> urls) throws Exception { | |
| List<CompletableFuture<String>> futures = urls.stream() | |
| .map(this::sendWithRetryAsync) | |
| .toList(); | |
| CompletableFuture<Void> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); | |
| all.join(); // Wait for all | |
| return futures.stream() | |
| .map(CompletableFuture::join) | |
| .collect(Collectors.toList()); | |
| } | |
| private CompletableFuture<String> sendWithRetryAsync(String url) { | |
| CompletableFuture<String> result = new CompletableFuture<>(); | |
| sendAttempt(url, result, 0); | |
| return result; | |
| } | |
| private void sendAttempt(String url, CompletableFuture<String> future, int attempt) { | |
| HttpGet request = new HttpGet(url); | |
| httpClient.execute(request, new FutureCallback<>() { | |
| @Override | |
| public void completed(SimpleHttpResponse response) { | |
| future.complete(response.getBodyText()); | |
| } | |
| @Override | |
| public void failed(Exception ex) { | |
| if (attempt < maxRetries) { | |
| try { | |
| Thread.sleep(backoffMillis * (1L << attempt)); // Exponential backoff | |
| } catch (InterruptedException ignored) {} | |
| sendAttempt(url, future, attempt + 1); | |
| } else { | |
| future.completeExceptionally(ex); | |
| } | |
| } | |
| @Override | |
| public void cancelled() { | |
| future.cancel(true); | |
| } | |
| }); | |
| } | |
| public static void main(String[] args) throws Exception { | |
| var caller = new SafeHttp5MultiCaller(); | |
| List<String> responses = caller.callApis(List.of( | |
| "https://httpbin.org/get?1", | |
| "https://httpbin.org/get?2", | |
| "https://httpbin.org/status/500" // fail on purpose | |
| )); | |
| responses.forEach(System.out::println); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment