mirror of
https://github.com/apple/pkl.git
synced 2026-07-04 12:11:40 +02:00
Add support for HTTP rewrites (#1062)
This adds a new configuration option for the HTTP client to replace URI prefixes when making outbound calls. Follows the design of https://github.com/apple/pkl-evolution/pull/17
This commit is contained in:
-3
@@ -1,3 +0,0 @@
|
||||
import "pkl:analyze"
|
||||
|
||||
result = analyze.importGraph(Set("http://localhost:0/foo.pkl"))
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
–– Pkl Error ––
|
||||
HTTP/1.1 header parser received no bytes
|
||||
|
||||
x | result = analyze.importGraph(Set("http://localhost:0/foo.pkl"))
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at analyzeInvalidHttpModule#result (file:///$snippetsDir/input/errors/analyzeInvalidHttpModule.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | bytes = text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,14 @@ import org.junit.jupiter.api.Test
|
||||
|
||||
class RequestRewritingClientTest {
|
||||
private val captured = RequestCapturingClient()
|
||||
private val client = RequestRewritingClient("Pkl", Duration.ofSeconds(42), -1, captured)
|
||||
private val client =
|
||||
RequestRewritingClient(
|
||||
"Pkl",
|
||||
Duration.ofSeconds(42),
|
||||
-1,
|
||||
captured,
|
||||
mapOf(URI("https://foo/") to URI("https://bar/")),
|
||||
)
|
||||
private val exampleUri = URI("https://example.com/foo/bar.html")
|
||||
private val exampleRequest = HttpRequest.newBuilder(exampleUri).build()
|
||||
|
||||
@@ -114,7 +121,7 @@ class RequestRewritingClientTest {
|
||||
@Test
|
||||
fun `rewrites port 0 if test port is set`() {
|
||||
val captured = RequestCapturingClient()
|
||||
val client = RequestRewritingClient("Pkl", Duration.ofSeconds(42), 5000, captured)
|
||||
val client = RequestRewritingClient("Pkl", Duration.ofSeconds(42), 5000, captured, mapOf())
|
||||
val request = HttpRequest.newBuilder(URI("https://example.com:0")).build()
|
||||
|
||||
client.send(request, BodyHandlers.discarding())
|
||||
@@ -130,4 +137,175 @@ class RequestRewritingClientTest {
|
||||
|
||||
assertThat(captured.request.uri().port).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `matches rewrite rule`() {
|
||||
fun assertThatRewriteMatches(uri: String, rule: String) =
|
||||
assertThat(RequestRewritingClient.matchesRewriteRule(URI(uri), URI(rule)))
|
||||
.`as`("$uri matches $rule")
|
||||
|
||||
assertThatRewriteMatches("https://www.foo.com/path/to/qux.html", "https://www.foo.com/").isTrue
|
||||
assertThatRewriteMatches("HTTPS://www.foo.com/path/to/qux.html", "https://www.foo.com/").isTrue
|
||||
assertThatRewriteMatches("HTTPS://WWW.FOO.COM/path/to/qux.html", "https://www.foo.com/").isTrue
|
||||
|
||||
assertThatRewriteMatches("https://www.foo.com/path/to/qux.html", "https://www.foo.com/path/")
|
||||
.isTrue
|
||||
assertThatRewriteMatches("https://www.foo.com/path/to/qux.html", "https://www.foo.com/PATH/")
|
||||
.isFalse
|
||||
assertThatRewriteMatches("https://www.foo.com", "https://www.foo.com/").isFalse
|
||||
|
||||
assertThatRewriteMatches(
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar",
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar",
|
||||
)
|
||||
.isTrue
|
||||
assertThatRewriteMatches(
|
||||
"https://www.foo.com/path/to/qux.html?foo&baz",
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar",
|
||||
)
|
||||
.isFalse
|
||||
|
||||
assertThatRewriteMatches(
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar#qux",
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar#q",
|
||||
)
|
||||
.isTrue
|
||||
assertThatRewriteMatches(
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar#qux",
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar#w",
|
||||
)
|
||||
.isFalse
|
||||
assertThatRewriteMatches(
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar",
|
||||
"https://www.foo.com/path/to/qux.html?foo&bar#w",
|
||||
)
|
||||
.isFalse
|
||||
|
||||
assertThatRewriteMatches("https:///", "https:///").isTrue
|
||||
assertThatRewriteMatches("https:///", "http:///").isFalse
|
||||
|
||||
// userinfo
|
||||
assertThatRewriteMatches("https://foo@foo.com/", "http://foo.com/").isFalse
|
||||
assertThatRewriteMatches("https://foo@foo.com/", "http://foo@foo.com/").isFalse
|
||||
assertThatRewriteMatches("https://foo@foo.com/", "http://FOO@foo.com/").isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rewrites URIs`() {
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://foo.com/bar/baz",
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://FOO.COM/bar/baz",
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://foo.com/bar/baz",
|
||||
mapOf(URI("https://FOO.COM/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://foo.com/bar/baz",
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/qux/baz/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/qux/baz/bar/baz")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://foo.com/bar/baz",
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/qux/baz/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/qux/baz/bar/baz")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://foo.com/bar/baz?qux=foo#corge",
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz?qux=foo#corge")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://fooey@foo.com/bar/baz",
|
||||
mapOf(URI("https://fooey@foo.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rewrites URIs - longest rewrite wins`() {
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://foo.com/qux/bar/baz",
|
||||
mapOf(
|
||||
URI("https://foo.com/") to URI("https://bar.com/"),
|
||||
URI("https://foo.com/qux") to URI("https://corge.com/"),
|
||||
),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://corge.com/bar/baz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rewrites URIs - hostname is always lowercased`() {
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://foo.com/bar/baz",
|
||||
mapOf(URI("https://FOO.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://FOO.com/bar/baz",
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rewrites URIs - scheme is always lowercased`() {
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"HTTPS://foo.com/bar/baz",
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
|
||||
assertThat(
|
||||
rewrittenRequest(
|
||||
"https://FOO.com/bar/baz",
|
||||
mapOf(URI("HTTPS://foo.com/") to URI("HTTPS://bar.com/")),
|
||||
)
|
||||
)
|
||||
.isEqualTo("https://bar.com/bar/baz")
|
||||
}
|
||||
|
||||
private fun rewrittenRequest(uri: String, rules: Map<URI, URI>): String {
|
||||
val captured = RequestCapturingClient()
|
||||
val client = RequestRewritingClient("Pkl", Duration.ofSeconds(42), -1, captured, rules)
|
||||
val request = HttpRequest.newBuilder(URI(uri)).build()
|
||||
client.send(request, BodyHandlers.discarding())
|
||||
return captured.request.uri().toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ class PklSettingsTest {
|
||||
"pkg.pkl-lang.org"
|
||||
}
|
||||
}
|
||||
rewrites {
|
||||
["https://foo.com/"] = "https://bar.com/"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
@@ -72,7 +75,8 @@ class PklSettingsTest {
|
||||
PklEvaluatorSettings.Proxy(
|
||||
URI("http://localhost:8080"),
|
||||
listOf("example.com", "pkg.pkl-lang.org"),
|
||||
)
|
||||
),
|
||||
mapOf(URI("https://foo.com/") to URI("https://bar.com/")),
|
||||
)
|
||||
assertThat(settings).isEqualTo(PklSettings(Editor.SYSTEM, expectedHttp))
|
||||
}
|
||||
@@ -95,7 +99,10 @@ class PklSettingsTest {
|
||||
|
||||
val settings = PklSettings.loadFromPklHomeDir(tempDir)
|
||||
val expectedHttp =
|
||||
PklEvaluatorSettings.Http(PklEvaluatorSettings.Proxy(URI("http://localhost:8080"), listOf()))
|
||||
PklEvaluatorSettings.Http(
|
||||
PklEvaluatorSettings.Proxy(URI("http://localhost:8080"), listOf()),
|
||||
null,
|
||||
)
|
||||
assertThat(settings).isEqualTo(PklSettings(Editor.SYSTEM, expectedHttp))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user