Clean up http-client changes (#295)

* pkl-excutor tests: symlink 0.25.0 distribution into pkl-executable/build
* Use `IoUtils.getPklHomeDir` in HttpClientBuilder
* Simplify Exceptions.java
* Enable testing for older pkl-executor distribution
This commit is contained in:
Daniel Chao
2024-03-07 10:43:04 -08:00
committed by GitHub
parent 3fb1f03780
commit 9defe868c0
7 changed files with 84 additions and 66 deletions
@@ -26,24 +26,26 @@ import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.pkl.core.Release; import org.pkl.core.Release;
import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.ErrorMessages;
import org.pkl.core.util.IoUtils;
final class HttpClientBuilder implements HttpClient.Builder { final class HttpClientBuilder implements HttpClient.Builder {
private final Path userHome;
private String userAgent; private String userAgent;
private Duration connectTimeout = Duration.ofSeconds(60); private Duration connectTimeout = Duration.ofSeconds(60);
private Duration requestTimeout = Duration.ofSeconds(60); private Duration requestTimeout = Duration.ofSeconds(60);
private final Path caCertsDir;
private final List<Path> certificateFiles = new ArrayList<>(); private final List<Path> certificateFiles = new ArrayList<>();
private final List<URI> certificateUris = new ArrayList<>(); private final List<URI> certificateUris = new ArrayList<>();
HttpClientBuilder() { HttpClientBuilder() {
this(Path.of(System.getProperty("user.home"))); this(IoUtils.getPklHomeDir().resolve("cacerts"));
} }
// only exists for testing // only exists for testing
HttpClientBuilder(Path userHome) { HttpClientBuilder(Path caCertsDir) {
this.userHome = userHome;
var release = Release.current(); var release = Release.current();
userAgent = "Pkl/" + release.version() + " (" + release.os() + "; " + release.flavor() + ")"; this.caCertsDir = caCertsDir;
this.userAgent =
"Pkl/" + release.version() + " (" + release.os() + "; " + release.flavor() + ")";
} }
public HttpClient.Builder setUserAgent(String userAgent) { public HttpClient.Builder setUserAgent(String userAgent) {
@@ -80,10 +82,9 @@ final class HttpClientBuilder implements HttpClient.Builder {
} }
public HttpClient.Builder addDefaultCliCertificates() { public HttpClient.Builder addDefaultCliCertificates() {
var directory = userHome.resolve(".pkl").resolve("cacerts");
var fileCount = certificateFiles.size(); var fileCount = certificateFiles.size();
if (Files.isDirectory(directory)) { if (Files.isDirectory(caCertsDir)) {
try (var files = Files.list(directory)) { try (var files = Files.list(caCertsDir)) {
files.filter(Files::isRegularFile).forEach(certificateFiles::add); files.filter(Files::isRegularFile).forEach(certificateFiles::add);
} catch (IOException e) { } catch (IOException e) {
throw new HttpClientInitException(e); throw new HttpClientInitException(e);
@@ -132,11 +132,18 @@ public class ExecutorSpiImpl implements ExecutorSpi {
private HttpClient getOrCreateHttpClient(ExecutorSpiOptions options) { private HttpClient getOrCreateHttpClient(ExecutorSpiOptions options) {
List<Path> certificateFiles; List<Path> certificateFiles;
List<URI> certificateUris; List<URI> certificateUris;
if (options instanceof ExecutorSpiOptions2) { try {
var options2 = (ExecutorSpiOptions2) options; if (options instanceof ExecutorSpiOptions2) {
certificateFiles = options2.getCertificateFiles(); var options2 = (ExecutorSpiOptions2) options;
certificateUris = options2.getCertificateUris(); certificateFiles = options2.getCertificateFiles();
} else { certificateUris = options2.getCertificateUris();
} else {
certificateFiles = List.of();
certificateUris = List.of();
}
// host pkl-executor does not have class ExecutorOptions2 defined.
// this will happen if the pkl-executor distribution is too old.
} catch (NoClassDefFoundError e) {
certificateFiles = List.of(); certificateFiles = List.of();
certificateUris = List.of(); certificateUris = List.of();
} }
@@ -20,10 +20,8 @@ public final class Exceptions {
public static Throwable getRootCause(Throwable t) { public static Throwable getRootCause(Throwable t) {
var result = t; var result = t;
var cause = result.getCause(); while (result.getCause() != null) {
while (cause != null) { result = result.getCause();
result = cause;
cause = cause.getCause();
} }
return result; return result;
} }
@@ -103,21 +103,22 @@ class HttpClientTest {
@Test @Test
fun `can load certificates from Pkl user home cacerts directory`(@TempDir tempDir: Path) { fun `can load certificates from Pkl user home cacerts directory`(@TempDir tempDir: Path) {
val certFile = tempDir.resolve(".pkl") val certsDir = tempDir.resolve(".pkl")
.resolve("cacerts") .resolve("cacerts")
.createDirectories() .createDirectories()
.resolve("certs.pem") .also { dir ->
FileTestUtils.selfSignedCertificate.copyTo(certFile) FileTestUtils.selfSignedCertificate.copyTo(dir.resolve("certs.pem"))
}
assertDoesNotThrow { assertDoesNotThrow {
HttpClientBuilder(tempDir).addDefaultCliCertificates().build() HttpClientBuilder(certsDir).addDefaultCliCertificates().build()
} }
} }
@Test @Test
fun `loading certificates from cacerts directory falls back to built-in certificates`(@TempDir userHome: Path) { fun `loading certificates from cacerts directory falls back to built-in certificates`(@TempDir certsDir: Path) {
assertDoesNotThrow { assertDoesNotThrow {
HttpClientBuilder(userHome).addDefaultCliCertificates().build() HttpClientBuilder(certsDir).addDefaultCliCertificates().build()
} }
} }
+24 -7
View File
@@ -1,3 +1,5 @@
import java.nio.file.Files
plugins { plugins {
pklAllProjects pklAllProjects
pklJavaLibrary pklJavaLibrary
@@ -6,7 +8,7 @@ plugins {
} }
val pklDistributionCurrent: Configuration by configurations.creating val pklDistributionCurrent: Configuration by configurations.creating
val pklDistribution025: Configuration by configurations.creating val pklHistoricalDistributions: Configuration by configurations.creating
// Because pkl-executor doesn't depend on other Pkl modules // Because pkl-executor doesn't depend on other Pkl modules
// (nor has overlapping dependencies that could cause a version conflict), // (nor has overlapping dependencies that could cause a version conflict),
@@ -15,7 +17,7 @@ val pklDistribution025: Configuration by configurations.creating
dependencies { dependencies {
pklDistributionCurrent(project(":pkl-config-java", "fatJar")) pklDistributionCurrent(project(":pkl-config-java", "fatJar"))
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
pklDistribution025(libs.pklConfigJavaAll025) pklHistoricalDistributions(libs.pklConfigJavaAll025)
implementation(libs.slf4jApi) implementation(libs.slf4jApi)
@@ -52,14 +54,29 @@ sourceSets {
} }
} }
// this task could be folded into tasks.test by switching to IntelliJ's Gradle test runner val prepareHistoricalDistributions by tasks.registering {
val outputDir = layout.buildDirectory.dir("pklHistoricalDistributions")
inputs.files(pklHistoricalDistributions.files())
outputs.dir(outputDir)
doLast {
val distributionDir = outputDir.get().asFile.toPath()
.also(Files::createDirectories)
for (file in pklHistoricalDistributions.files) {
val link = distributionDir.resolve(file.name)
if (!Files.isSymbolicLink(link)) {
if (Files.exists(link)) {
Files.delete(link)
}
Files.createSymbolicLink(link, file.toPath())
}
}
}
}
val prepareTest by tasks.registering { val prepareTest by tasks.registering {
// used by EmbeddedExecutorTest dependsOn(pklDistributionCurrent, prepareHistoricalDistributions)
dependsOn(pklDistributionCurrent, pklDistribution025)
} }
tasks.test { tasks.test {
dependsOn(prepareTest) dependsOn(prepareTest)
systemProperty("pklDistributionCurrent", pklDistributionCurrent.singleFile)
systemProperty("pklDistribution025", pklDistribution025.singleFile)
} }
@@ -39,7 +39,7 @@ class EmbeddedExecutorTest {
// This context has a pkl-executor version that is lower than the distribution version. // This context has a pkl-executor version that is lower than the distribution version.
// It can be enabled once there is a distribution that includes pkl-executor. // It can be enabled once there is a distribution that includes pkl-executor.
//ExecutionContext(executor1_2.value, ::convertToOptions1, "Options1, Executor1, Distribution2"), ExecutionContext(executor1_2.value, ::convertToOptions1, "Options1, Executor1, Distribution2"),
ExecutionContext(executor2_1.value, ::convertToOptions1, "Options1, Executor2, Distribution1"), ExecutionContext(executor2_1.value, ::convertToOptions1, "Options1, Executor2, Distribution1"),
ExecutionContext(executor2_1.value, ::convertToOptions2, "Options2, Executor2, Distribution1"), ExecutionContext(executor2_1.value, ::convertToOptions2, "Options2, Executor2, Distribution1"),
@@ -70,7 +70,7 @@ class EmbeddedExecutorTest {
} }
// A pkl-executor library that supports ExecutorSpiOptions up to v2 // A pkl-executor library that supports ExecutorSpiOptions up to v2
// and a Pkl distribution that supports ExecutorSpiOptions up to v. // and a Pkl distribution that supports ExecutorSpiOptions up to v2.
private val executor2_2: Lazy<Executor> = lazy { private val executor2_2: Lazy<Executor> = lazy {
EmbeddedExecutor(listOf(pklDistribution2), pklExecutorClassLoader2) EmbeddedExecutor(listOf(pklDistribution2), pklExecutorClassLoader2)
} }
@@ -101,25 +101,19 @@ class EmbeddedExecutorTest {
// a Pkl distribution that supports ExecutorSpiOptions up to v1 // a Pkl distribution that supports ExecutorSpiOptions up to v1
private val pklDistribution1: Path by lazy { private val pklDistribution1: Path by lazy {
val path = System.getProperty("pklDistribution025")?.toPath() ?: FileTestUtils.rootProjectDir.resolve("pkl-executor/build/pklHistoricalDistributions/pkl-config-java-all-0.25.0.jar").apply {
// can get rid of this path by switching to IntelliJ's Gradle test runner if (!exists()) {
System.getProperty("user.home").toPath() throw AssertionError("Missing test fixture. " +
.resolve(".gradle/caches/modules-2/files-2.1/org.pkl-lang/pkl-config-java-all/" +
"0.25.0/e9451dda554f1659e49ff5bdd30accd26be7bf0f/pkl-config-java-all-0.25.0.jar")
path.apply {
if (!exists()) throw AssertionError("Missing test fixture. " +
"To fix this problem, run `./gradlew :pkl-executor:prepareTest`.") "To fix this problem, run `./gradlew :pkl-executor:prepareTest`.")
} }
}
} }
// a Pkl distribution that supports ExecutorSpiOptions up to v2 // a Pkl distribution that supports ExecutorSpiOptions up to v2
private val pklDistribution2: Path by lazy { private val pklDistribution2: Path by lazy {
val path = System.getProperty("pklDistributionCurrent")?.toPath() ?: FileTestUtils.rootProjectDir
// can get rid of this path by switching to IntelliJ's Gradle test runner .resolve("pkl-config-java/build/libs/pkl-config-java-all-" +
FileTestUtils.rootProjectDir "${Release.current().version().withBuild(null).toString().replaceFirst("dev", "SNAPSHOT")}.jar").apply {
.resolve("pkl-config-java/build/libs/pkl-config-java-all-" +
"${Release.current().version().withBuild(null).toString().replaceFirst("dev", "SNAPSHOT")}.jar")
path.apply {
if (!exists()) throw AssertionError("Missing test fixture. " + if (!exists()) throw AssertionError("Missing test fixture. " +
"To fix this problem, run `./gradlew :pkl-executor:prepareTest`.") "To fix this problem, run `./gradlew :pkl-executor:prepareTest`.")
} }