|
101 | 101 | import software.amazon.awssdk.metrics.MetricCollector; |
102 | 102 | import software.amazon.awssdk.metrics.NoOpMetricCollector; |
103 | 103 | import software.amazon.awssdk.utils.AttributeMap; |
| 104 | +import software.amazon.awssdk.utils.ClassLoaderHelper; |
104 | 105 | import software.amazon.awssdk.utils.Logger; |
105 | 106 | import software.amazon.awssdk.utils.Validate; |
106 | 107 |
|
@@ -543,6 +544,12 @@ public interface Builder extends SdkHttpClient.Builder<Apache5HttpClient.Builder |
543 | 544 | } |
544 | 545 |
|
545 | 546 | private static final class DefaultBuilder implements Builder { |
| 547 | + private static final String[] REQUIRED_TCP_KEEPALIVE_PERMISSIONS = { |
| 548 | + "setOption.TCP_KEEPIDLE", |
| 549 | + "setOption.TCP_KEEPINTERVAL", |
| 550 | + "setOption.TCP_KEEPCOUNT" |
| 551 | + }; |
| 552 | + |
546 | 553 | private final AttributeMap.Builder standardOptions = AttributeMap.builder(); |
547 | 554 | private Registry<AuthSchemeFactory> authSchemeRegistry; |
548 | 555 | private ProxyConfiguration proxyConfiguration = ProxyConfiguration.builder().build(); |
@@ -744,8 +751,37 @@ public void setAuthSchemeProviderRegistry(Registry<AuthSchemeFactory> authScheme |
744 | 751 | public SdkHttpClient buildWithDefaults(AttributeMap serviceDefaults) { |
745 | 752 | AttributeMap resolvedOptions = standardOptions.build().merge(serviceDefaults).merge( |
746 | 753 | SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS); |
| 754 | + checkTcpKeepAlivePermissions(); |
747 | 755 | return new Apache5HttpClient(this, resolvedOptions); |
748 | 756 | } |
| 757 | + |
| 758 | + /** |
| 759 | + * Fails fast if a SecurityManager is active but denies the {@code jdk.net.NetworkPermission} entries |
| 760 | + * that Apache HC5 requires for its default TCP keepalive socket options. |
| 761 | + * No-op when no SecurityManager is installed (including Java 24+). |
| 762 | + */ |
| 763 | + private static void checkTcpKeepAlivePermissions() { |
| 764 | + SecurityManager sm = System.getSecurityManager(); |
| 765 | + if (sm == null) { |
| 766 | + return; |
| 767 | + } |
| 768 | + |
| 769 | + try { |
| 770 | + Class<?> permClass = ClassLoaderHelper.loadClass("jdk.net.NetworkPermission", Apache5HttpClient.class); |
| 771 | + for (String permName : REQUIRED_TCP_KEEPALIVE_PERMISSIONS) { |
| 772 | + java.security.Permission perm = |
| 773 | + (java.security.Permission) permClass.getConstructor(String.class).newInstance(permName); |
| 774 | + sm.checkPermission(perm); |
| 775 | + } |
| 776 | + } catch (SecurityException e) { |
| 777 | + throw new IllegalStateException( |
| 778 | + "Apache5HttpClient requires jdk.net.NetworkPermission for \"" |
| 779 | + + String.join("\", \"", REQUIRED_TCP_KEEPALIVE_PERMISSIONS) |
| 780 | + + "\" when a SecurityManager is active.", e); |
| 781 | + } catch (Exception e) { |
| 782 | + log.warn(() -> "Unable to verify TCP keepalive permissions: " + e.getMessage(), e); |
| 783 | + } |
| 784 | + } |
749 | 785 | } |
750 | 786 |
|
751 | 787 | private static class ApacheConnectionManagerFactory { |
|
0 commit comments