Add support for HTTP proxying (#506)

* Add `--proxy` and `--no-proxy` CLI flags
* Add property `http` to `pkl:settings`
* Move `EvaluatorSettings` from `pkl:Project` to its own module and add property `http`
* Add support for proxying in server mode, and through Gradle
* Add `setProxy()` to `HttpClient`
* Add documentation
This commit is contained in:
Philip K.F. Hölzenspies
2024-06-12 19:54:22 +01:00
committed by GitHub
parent a520ae7d04
commit b03530ed1f
61 changed files with 1581 additions and 412 deletions
@@ -10,6 +10,7 @@ pkl:base
pkl:Benchmark
pkl:DocPackageInfo
pkl:DocsiteInfo
pkl:EvaluatorSettings
pkl:json
pkl:jsonnet
pkl:math
@@ -0,0 +1,146 @@
package org.pkl.core.http
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.net.URI
@Suppress("HttpUrlsUsage")
class NoProxyRuleTest {
@Test
fun wildcard() {
val noProxyRule = NoProxyRule("*")
assertTrue(noProxyRule.matches(URI("https://foo.com")))
assertTrue(noProxyRule.matches(URI("https://bar.com")))
assertTrue(noProxyRule.matches(URI("https://foo:5000")))
}
@Test
fun `hostname matching`() {
val noProxyRule = NoProxyRule("foo.com")
assertTrue(noProxyRule.matches(URI("https://foo.com")))
assertTrue(noProxyRule.matches(URI("http://foo.com")))
assertTrue(noProxyRule.matches(URI("https://foo.com:5000")))
assertTrue(noProxyRule.matches(URI("https://FOO.COM")))
assertTrue(noProxyRule.matches(URI("https://bar.foo.com")))
assertFalse(noProxyRule.matches(URI("https://bar.foo.com.bar")))
assertFalse(noProxyRule.matches(URI("https://bar.foocom")))
assertFalse(noProxyRule.matches(URI("https://fooo.com")))
assertFalse(noProxyRule.matches(URI("https://ooofoo.com")))
assertFalse(noProxyRule.matches(URI("pkl:foo.com")))
}
@Test
fun `hostname matching, leading dot`() {
val noProxyRule = NoProxyRule(".foo.com")
assertTrue(noProxyRule.matches(URI("https://foo.com")))
assertTrue(noProxyRule.matches(URI("http://foo.com")))
assertTrue(noProxyRule.matches(URI("https://foo.com:5000")))
assertTrue(noProxyRule.matches(URI("https://FOO.COM")))
assertTrue(noProxyRule.matches(URI("https://bar.foo.com")))
assertFalse(noProxyRule.matches(URI("https://bar.foo.com.bar")))
assertFalse(noProxyRule.matches(URI("https://bar.foocom")))
assertFalse(noProxyRule.matches(URI("https://fooo.com")))
assertFalse(noProxyRule.matches(URI("https://ooofoo.com")))
assertFalse(noProxyRule.matches(URI("pkl:foo.com")))
}
@Test
fun `hostname matching, with port`() {
val noProxyRule = NoProxyRule("foo.com:5000")
assertTrue(noProxyRule.matches(URI("https://foo.com:5000")))
assertFalse(noProxyRule.matches(URI("https://foo.com")))
assertFalse(noProxyRule.matches(URI("https://foo.com:3000")))
}
@Test
fun `ipv4 address literal matching`() {
val noProxyRule = NoProxyRule("192.168.1.1")
assertTrue(noProxyRule.matches(URI("http://192.168.1.1:5000")))
assertTrue(noProxyRule.matches(URI("http://192.168.1.1")))
assertTrue(noProxyRule.matches(URI("https://192.168.1.1")))
assertFalse(noProxyRule.matches(URI("https://192.168.1.0")))
assertFalse(noProxyRule.matches(URI("https://192.168.1.0:5000")))
}
@Test
fun `ipv4 address literal matching, with port`() {
val noProxyRule = NoProxyRule("192.168.1.1:5000")
assertTrue(noProxyRule.matches(URI("http://192.168.1.1:5000")))
assertTrue(noProxyRule.matches(URI("https://192.168.1.1:5000")))
assertFalse(noProxyRule.matches(URI("http://192.168.1.1")))
assertFalse(noProxyRule.matches(URI("https://192.168.1.1")))
assertFalse(noProxyRule.matches(URI("http://192.168.1.1:3000")))
assertFalse(noProxyRule.matches(URI("https://192.168.1.1:3000")))
}
@Test
fun `ipv6 address literal matching`() {
val noProxyRule = NoProxyRule("::1")
assertTrue(noProxyRule.matches(URI("http://[::1]")))
assertTrue(noProxyRule.matches(URI("http://[::1]:5000")))
assertTrue(noProxyRule.matches(URI("https://[::1]")))
assertTrue(noProxyRule.matches(URI("https://[0000:0000:0000:0000:0000:0000:0000:0001]")))
assertFalse(noProxyRule.matches(URI("https://[::2]")))
assertFalse(noProxyRule.matches(URI("https://[::2]:5000")))
}
@Test
fun `ipv6 address literal matching, with port`() {
val noProxyRule = NoProxyRule("[::1]:5000")
assertTrue(noProxyRule.matches(URI("http://[::1]:5000")))
assertTrue(noProxyRule.matches(URI("https://[0000:0000:0000:0000:0000:0000:0000:0001]:5000")))
assertFalse(noProxyRule.matches(URI("http://[::1]")))
assertFalse(noProxyRule.matches(URI("https://[::1]")))
assertFalse(noProxyRule.matches(URI("https://[::2]")))
assertFalse(noProxyRule.matches(URI("https://[::2]:5000")))
}
@Test
fun `ipv4 port from protocol`() {
val noProxyRuleHttp = NoProxyRule("192.168.1.1:80")
assertTrue(noProxyRuleHttp.matches(URI("http://192.168.1.1")))
assertTrue(noProxyRuleHttp.matches(URI("http://192.168.1.1:80")))
assertTrue(noProxyRuleHttp.matches(URI("https://192.168.1.1:80")))
assertFalse(noProxyRuleHttp.matches(URI("https://192.168.1.1")))
assertFalse(noProxyRuleHttp.matches(URI("https://192.168.1.1:5000")))
val noProxyRuleHttps = NoProxyRule("192.168.1.1:443")
assertTrue(noProxyRuleHttps.matches(URI("https://192.168.1.1")))
assertTrue(noProxyRuleHttps.matches(URI("http://192.168.1.1:443")))
assertFalse(noProxyRuleHttps.matches(URI("http://192.168.1.1")))
assertFalse(noProxyRuleHttps.matches(URI("https://192.168.1.1:80")))
}
@Test
fun `ipv4 cidr block matching`() {
val noProxyRule1 = NoProxyRule("10.0.0.0/16")
assertTrue(noProxyRule1.matches(URI("https://10.0.0.0")))
assertTrue(noProxyRule1.matches(URI("https://10.0.255.255")))
assertTrue(noProxyRule1.matches(URI("https://10.0.255.255:5000")))
assertFalse(noProxyRule1.matches(URI("https://10.1.0.0")))
assertFalse(noProxyRule1.matches(URI("https://11.0.0.0")))
assertFalse(noProxyRule1.matches(URI("https://9.255.255.255")))
assertFalse(noProxyRule1.matches(URI("https://9.255.255.255:5000")))
val noProxyRule2 = NoProxyRule("10.0.0.0/32")
assertTrue(noProxyRule2.matches(URI("https://10.0.0.0")))
assertTrue(noProxyRule2.matches(URI("https://10.0.0.0:5000")))
assertFalse(noProxyRule2.matches(URI("https://10.0.0.1")))
assertFalse(noProxyRule2.matches(URI("https://9.255.255.55:5000")))
}
@Test
fun `ipv6 cidr block matching`() {
val noProxyRule1 = NoProxyRule("1000::ff/32")
assertTrue(noProxyRule1.matches(URI("https://[1000::]")))
assertTrue(noProxyRule1.matches(URI("https://[1000:0:ffff:ffff:ffff:ffff:ffff:ffff]")))
assertFalse(noProxyRule1.matches(URI("https://[999::]")))
assertFalse(noProxyRule1.matches(URI("https://[1000:1::]")))
val noProxyRule2 = NoProxyRule("1000::ff/128")
assertTrue(noProxyRule2.matches(URI("https://[1000::ff]")))
assertFalse(noProxyRule2.matches(URI("https://[999::]")))
assertFalse(noProxyRule2.matches(URI("https://[1001::]")))
}
}
@@ -6,7 +6,7 @@ import java.net.http.HttpResponse
class RequestCapturingClient : HttpClient {
lateinit var request: HttpRequest
override fun <T : Any> send(
request: HttpRequest,
responseBodyHandler: HttpResponse.BodyHandler<T>
@@ -10,7 +10,7 @@ import org.pkl.commons.writeString
import org.pkl.core.*
import org.pkl.core.http.HttpClient
import org.pkl.core.packages.PackageUri
import org.pkl.core.project.Project.EvaluatorSettings
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
import java.net.URI
import java.nio.file.Path
import java.util.regex.Pattern
@@ -40,7 +40,7 @@ class ProjectTest {
listOf(Path.of("apiTest1.pkl"), Path.of("apiTest2.pkl")),
listOf("PklProject", "PklProject.deps.json", ".**", "*.exe")
)
val expectedSettings = EvaluatorSettings(
val expectedSettings = PklEvaluatorSettings(
mapOf("two" to "2"),
mapOf("one" to "1"),
listOf("foo:", "bar:").map(Pattern::compile),
@@ -52,7 +52,8 @@ class ProjectTest {
path.resolve("modulepath2/")
),
Duration.ofMinutes(5.0),
path
path,
null
)
projectPath.writeString("""
amends "pkl:Project"
@@ -116,7 +117,7 @@ class ProjectTest {
""".trimIndent())
val project = Project.loadFromPath(projectPath)
assertThat(project.`package`).isEqualTo(expectedPackage)
assertThat(project.settings).isEqualTo(expectedSettings)
assertThat(project.evaluatorSettings).isEqualTo(expectedSettings)
assertThat(project.tests).isEqualTo(listOf(path.resolve("test1.pkl"), path.resolve("test2.pkl")))
}
@@ -4,13 +4,18 @@ import java.nio.file.Path
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.createParentDirectories
import org.pkl.commons.writeString
import org.pkl.core.Evaluator
import org.pkl.core.ModuleSource
import org.pkl.core.PObject
import org.pkl.core.StackFrameTransformers
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
import org.pkl.core.runtime.VmException
import org.pkl.core.settings.PklSettings.Editor
import java.net.URI
class PklSettingsTest {
@Test
@@ -25,7 +30,61 @@ class PklSettingsTest {
)
val settings = PklSettings.loadFromPklHomeDir(tempDir)
assertThat(settings).isEqualTo(PklSettings(Editor.SUBLIME))
assertThat(settings).isEqualTo(PklSettings(Editor.SUBLIME, null))
}
@Test
fun `load user settings with http`(@TempDir tempDir: Path) {
val settingsPath = tempDir.resolve("settings.pkl")
settingsPath.createParentDirectories()
settingsPath.writeString(
"""
amends "pkl:settings"
http {
proxy {
address = "http://localhost:8080"
noProxy {
"example.com"
"pkg.pkl-lang.org"
}
}
}
""".trimIndent()
)
val settings = PklSettings.loadFromPklHomeDir(tempDir)
val expectedHttp = PklEvaluatorSettings.Http(
PklEvaluatorSettings.Proxy(
URI("http://localhost:8080"),
listOf("example.com", "pkg.pkl-lang.org")
)
)
assertThat(settings).isEqualTo(PklSettings(Editor.SYSTEM, expectedHttp))
}
@Test
fun `load user settings with http, but no noProxy`(@TempDir tempDir: Path) {
val settingsPath = tempDir.resolve("settings.pkl")
settingsPath.createParentDirectories()
settingsPath.writeString(
"""
amends "pkl:settings"
http {
proxy {
address = "http://localhost:8080"
}
}
""".trimIndent()
)
val settings = PklSettings.loadFromPklHomeDir(tempDir)
val expectedHttp = PklEvaluatorSettings.Http(
PklEvaluatorSettings.Proxy(
URI("http://localhost:8080"),
listOf(),
)
)
assertThat(settings).isEqualTo(PklSettings(Editor.SYSTEM, expectedHttp))
}
@Test
@@ -39,7 +98,7 @@ class PklSettingsTest {
)
val settings = PklSettings.load(ModuleSource.path(settingsPath))
assertThat(settings).isEqualTo(PklSettings(Editor.IDEA))
assertThat(settings).isEqualTo(PklSettings(Editor.IDEA, null))
}
@Test
@@ -76,6 +135,6 @@ class PklSettingsTest {
}
private fun checkEquals(expected: Editor, actual: PObject) {
assertThat(actual.getProperty("urlScheme") as String).isEqualTo(expected.urlScheme)
assertThat(actual.getProperty("urlScheme") as String).isEqualTo(expected.urlScheme())
}
}