From cb60d46dee78778b3f80daa8987969b358bc274d Mon Sep 17 00:00:00 2001 From: Matas Date: Tue, 30 Apr 2024 11:24:24 -0400 Subject: [PATCH] fix: support `http.nonProxyHost` JVM system property (#1083) --- .../040b1587-ec85-4601-9eec-a5158cb0edac.json | 8 +++++ .../http/engine/EnvironmentProxySelector.kt | 32 +++++++++++-------- .../http/engine/HttpClientEngineConfig.kt | 1 + .../engine/EnvironmentProxySelectorTest.kt | 11 +++++++ .../runtime/http/engine/NoProxyHostTest.kt | 2 +- 5 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 .changes/040b1587-ec85-4601-9eec-a5158cb0edac.json diff --git a/.changes/040b1587-ec85-4601-9eec-a5158cb0edac.json b/.changes/040b1587-ec85-4601-9eec-a5158cb0edac.json new file mode 100644 index 000000000..a8b83b1c6 --- /dev/null +++ b/.changes/040b1587-ec85-4601-9eec-a5158cb0edac.json @@ -0,0 +1,8 @@ +{ + "id": "040b1587-ec85-4601-9eec-a5158cb0edac", + "type": "bugfix", + "description": "Support `http.nonProxyHosts` JVM system property", + "issues": [ + "https://github.com/smithy-lang/smithy-kotlin/issues/1081" + ] +} \ No newline at end of file diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/engine/EnvironmentProxySelector.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/engine/EnvironmentProxySelector.kt index c3ad5d3bd..4d89d0146 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/engine/EnvironmentProxySelector.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/engine/EnvironmentProxySelector.kt @@ -22,6 +22,7 @@ import aws.smithy.kotlin.runtime.util.PropertyProvider * - `http.proxyPort` * - `https.proxyHost` * - `https.proxyPort` + * - `http.nonProxyHosts` * - `http.noProxyHosts` * * **Environment variables in the given order**: @@ -32,10 +33,10 @@ import aws.smithy.kotlin.runtime.util.PropertyProvider internal class EnvironmentProxySelector(provider: PlatformEnvironProvider = PlatformProvider.System) : ProxySelector { private val httpProxy by lazy { resolveProxyByProperty(provider, Scheme.HTTP) ?: resolveProxyByEnvironment(provider, Scheme.HTTP) } private val httpsProxy by lazy { resolveProxyByProperty(provider, Scheme.HTTPS) ?: resolveProxyByEnvironment(provider, Scheme.HTTPS) } - private val noProxyHosts by lazy { resolveNoProxyHosts(provider) } + private val nonProxyHosts by lazy { resolveNonProxyHosts(provider) } override fun select(url: Url): ProxyConfig { - if (httpProxy == null && httpsProxy == null || noProxy(url)) return ProxyConfig.Direct + if (httpProxy == null && httpsProxy == null || nonProxy(url)) return ProxyConfig.Direct val proxyConfig = when (url.scheme) { Scheme.HTTP -> httpProxy @@ -46,7 +47,7 @@ internal class EnvironmentProxySelector(provider: PlatformEnvironProvider = Plat return proxyConfig ?: ProxyConfig.Direct } - private fun noProxy(url: Url): Boolean = noProxyHosts.any { it.matches(url) } + private fun nonProxy(url: Url): Boolean = nonProxyHosts.any { it.matches(url) } } private fun resolveProxyByProperty(provider: PropertyProvider, scheme: Scheme): ProxyConfig? { @@ -94,7 +95,7 @@ private fun resolveProxyByEnvironment(provider: EnvironmentProvider, scheme: Sch } } -internal data class NoProxyHost(val hostMatch: String, val port: Int? = null) { +internal data class NonProxyHost(val hostMatch: String, val port: Int? = null) { fun matches(url: Url): Boolean { // any host if (hostMatch == "*") return true @@ -115,24 +116,29 @@ internal data class NoProxyHost(val hostMatch: String, val port: Int? = null) { } } -private fun parseNoProxyHost(raw: String): NoProxyHost { +private fun parseNonProxyHost(raw: String): NonProxyHost { val pair = raw.split(':', limit = 2) return when (pair.size) { - 1 -> NoProxyHost(pair[0]) - 2 -> NoProxyHost(pair[0], pair[1].toInt()) - else -> error("invalid no proxy host: $raw") + 1 -> NonProxyHost(pair[0]) + 2 -> NonProxyHost(pair[0], pair[1].toInt()) + else -> error("invalid non proxy host: $raw") } } -private fun resolveNoProxyHosts(provider: PlatformEnvironProvider): Set { +private fun resolveNonProxyHosts(provider: PlatformEnvironProvider): Set { // http.nonProxyHosts:a list of hosts that should be reached directly, bypassing the proxy. This is a list of // patterns separated by '|'. The patterns may start or end with a '*' for wildcards. Any host matching one of // these patterns will be reached through a direct connection instead of through a proxy. - val noProxyHostProps = provider.getProperty("http.noProxyHosts") + + // NOTE: Both http.nonProxyHosts (correct value according to the spec) AND http.noProxyHosts (legacy behavior) are checked + // https://github.com/smithy-lang/smithy-kotlin/issues/1081 + val nonProxyHostProperty = provider.getProperty("http.nonProxyHosts") ?: provider.getProperty("http.noProxyHosts") + + val nonProxyHostProps = nonProxyHostProperty ?.split('|') ?.map { it.trim() } ?.map { it.trimStart('.') } - ?.map(::parseNoProxyHost) + ?.map(::parseNonProxyHost) ?.toSet() ?: emptySet() // `no_proxy` is a comma or space-separated list of machine or domain names, with optional :port part. @@ -142,8 +148,8 @@ private fun resolveNoProxyHosts(provider: PlatformEnvironProvider): Set - val noProxyHost = NoProxyHost(testCase.noProxyHost, testCase.noProxyPort) + val noProxyHost = NonProxyHost(testCase.noProxyHost, testCase.noProxyPort) val url = Url.parse(testCase.url) assertEquals(testCase.shouldMatch, noProxyHost.matches(url), "[idx=$i] expected $noProxyHost to match $url")