mirror of
https://github.com/apple/pkl.git
synced 2026-04-25 17:58:50 +02:00
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:
committed by
GitHub
parent
a520ae7d04
commit
b03530ed1f
@@ -2,12 +2,12 @@
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=jmh,jmhCompileClasspath,jmhImplementationDependenciesMetadata,jmhRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
net.sf.jopt-simple:jopt-simple:5.0.4=jmh,jmhCompileClasspath,jmhImplementationDependenciesMetadata,jmhRuntimeClasspath
|
||||
org.apache.commons:commons-math3:3.6.1=jmh,jmhCompileClasspath,jmhImplementationDependenciesMetadata,jmhRuntimeClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.compiler:compiler:23.0.2=graal
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=graal,jmh,jmhRuntimeClasspath,truffle
|
||||
org.graalvm.truffle:truffle-api:23.0.2=graal,jmh,jmhRuntimeClasspath,truffle
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
# This file is expected to be part of source control.
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
io.leangen.geantyref:geantyref:1.3.15=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -121,6 +121,9 @@ outputFormat: String?
|
||||
/// The project dependency settings.
|
||||
project: Project?
|
||||
|
||||
/// Configuration of outgoing HTTP requests.
|
||||
http: Http?
|
||||
|
||||
class ClientResourceReader {
|
||||
/// The URI scheme this reader is responsible for reading.
|
||||
scheme: String
|
||||
@@ -175,6 +178,54 @@ class Project {
|
||||
dependencies: Mapping<String, Project|RemoteDependency>
|
||||
}
|
||||
|
||||
/// Settings that control how Pkl talks to HTTP(S) servers.
|
||||
class Http {
|
||||
/// Configuration of the HTTP proxy to use.
|
||||
///
|
||||
/// If [null], uses the operating system's proxy configuration.
|
||||
proxy: Proxy?
|
||||
}
|
||||
|
||||
/// Settings that control how Pkl talks to HTTP proxies.
|
||||
class Proxy {
|
||||
/// The proxy to use for HTTP(S) connections.
|
||||
///
|
||||
/// At the moment, only HTTP proxies are supported.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// address = "http://my.proxy.example.com:5080"
|
||||
/// ```
|
||||
address: Uri(startsWith("http://"))?
|
||||
|
||||
/// Hosts to which all connections should bypass a proxy.
|
||||
///
|
||||
/// Values can be either hostnames, or IP addresses.
|
||||
/// IP addresses can optionally be provided using [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation).
|
||||
///
|
||||
/// The only wildcard is `"*"`, which disables all proxying.
|
||||
///
|
||||
/// A hostname matches all subdomains.
|
||||
/// For example, `example.com` matches `foo.example.com`, but not `fooexample.com`.
|
||||
/// A hostname that is prefixed with a dot matches the hostname itself,
|
||||
/// so `.example.com` matches `example.com`.
|
||||
///
|
||||
/// Optionally, a port can be specified.
|
||||
/// If a port is omitted, all ports are matched.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// noProxy {
|
||||
/// "127.0.0.1"
|
||||
/// "169.254.0.0/16"
|
||||
/// "example.com"
|
||||
/// "localhost:5050"
|
||||
/// }
|
||||
/// ```
|
||||
noProxy: Listing<String>(isDistinct)
|
||||
}
|
||||
|
||||
class RemoteDependency {
|
||||
type: "remote"
|
||||
|
||||
|
||||
@@ -829,3 +829,18 @@ These certificates can be overridden via either of the two options:
|
||||
Both these options will *replace* the default CA certificates bundled with Pkl. +
|
||||
The CLI option takes precedence over the certificates in `~/.pkl/cacerts/`. +
|
||||
Certificates need to be X.509 certificates in PEM format.
|
||||
|
||||
[[http-proxy]]
|
||||
== HTTP Proxy
|
||||
|
||||
When making HTTP(S) requests, Pkl can use a proxy.
|
||||
By default, no proxy is configured.
|
||||
A proxy can be configured as follows:
|
||||
|
||||
- Using `--proxy <uri>`.
|
||||
The URI must (currently) have scheme `http`, and may not contain anything other than a host and port.
|
||||
- Using `--no-proxy <hosts>`.
|
||||
The given (comma separated list of) hosts bypass the proxy.
|
||||
Hosts can be specified by domain name (in which case all subdomains also bypass the proxy), IP addresses, or IP ranges (using link:https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation[CIDR notation]).
|
||||
For individual hosts (not CIDRs), ports can be specified.
|
||||
When no port is specified for a given host, connections to all ports bypass the proxy.
|
||||
|
||||
@@ -122,3 +122,21 @@ Certificates need to be X.509 certificates in PEM format.
|
||||
|
||||
For other methods of configuring certificates, see xref:pkl-cli:index.adoc#ca-certs[CA Certificates].
|
||||
====
|
||||
|
||||
.--proxy
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (none) +
|
||||
Example: `http://proxy.example.com:1234` +
|
||||
Configures HTTP connections to connect to the provided proxy address.
|
||||
The URI must have scheme `http`, and may not contain anything other than a host and port.
|
||||
====
|
||||
|
||||
.--no-proxy
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (none) +
|
||||
Example: `example.com,169.254.0.0/16` +
|
||||
Comma separated list of hosts to which all connections should bypass the proxy.
|
||||
Hosts can be specified by name, IP address, or IP range using https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation[CIDR notation].
|
||||
====
|
||||
|
||||
@@ -83,3 +83,21 @@ Example: `modulePath.from files("dir1", "zip1.zip", "jar1.jar")` +
|
||||
The directories, ZIP archives, or JAR archives to search when resolving `modulepath:` URIs.
|
||||
Relative paths are resolved against the project directory.
|
||||
====
|
||||
|
||||
.proxy: Property<URI>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example: `http://proxy.example.com:1234` +
|
||||
Configures HTTP connections to connect to the provided proxy address.
|
||||
The URI must have scheme `http`, and may not contain anything other than a host and port.
|
||||
====
|
||||
|
||||
.noProxy: ListProperty<String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example: `example.com,169.254.0.0/16` +
|
||||
Hosts to which all connections should bypass the proxy.
|
||||
Hosts can be specified by name, IP address, or IP range using https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation[CIDR notation].
|
||||
====
|
||||
|
||||
@@ -47,6 +47,7 @@ slf4j = "1.+"
|
||||
# Breaking change in snakeYaml 2.6 (removing DumpSettingsBuilder::setScalarResolver), so pin to 2.5
|
||||
snakeYaml = "2.5"
|
||||
spotlessPlugin = "6.11.0"
|
||||
wiremock = "3.+"
|
||||
|
||||
[libraries] # ordered alphabetically
|
||||
antlr = { group = "com.tunnelvisionlabs", name = "antlr4", version.ref = "antlr" }
|
||||
@@ -94,6 +95,7 @@ spotlessPlugin = { group = "com.diffplug.spotless", name = "spotless-plugin-grad
|
||||
svm = { group = "org.graalvm.nativeimage", name = "svm", version.ref = "graalVm" }
|
||||
truffleApi = { group = "org.graalvm.truffle", name = "truffle-api", version.ref = "graalVm" }
|
||||
truffleDslProcessor = { group = "org.graalvm.truffle", name = "truffle-dsl-processor", version.ref = "graalVm" }
|
||||
wiremock = { group = "org.wiremock", name = "wiremock", version.ref = "wiremock" }
|
||||
|
||||
[plugins] # ordered alphabetically
|
||||
checksum = { id = "org.gradle.crypto.checksum", version.ref = "checksumPlugin" }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -1,13 +1,59 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.ethlo.time:itu:1.8.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-annotations:2.17.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-core:2.17.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-databind:2.17.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.fasterxml.jackson:jackson-bom:2.17.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.github.ajalt.clikt:clikt-jvm:3.5.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.ajalt.clikt:clikt:3.5.1=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.github.jknack:handlebars-helpers:4.3.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.github.jknack:handlebars:4.3.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.google.errorprone:error_prone_annotations:2.26.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.google.guava:failureaccess:1.0.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.google.guava:guava:33.2.0-jre=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.google.j2objc:j2objc-annotations:3.0.0=testCompileClasspath,testImplementationDependenciesMetadata
|
||||
com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.networknt:json-schema-validator:1.4.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
commons-fileupload:commons-fileupload:1.5=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
commons-io:commons-io:2.11.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
net.javacrumbs.json-unit:json-unit-core:2.38.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.minidev:accessors-smart:2.5.1=testRuntimeClasspath
|
||||
net.minidev:json-smart:2.5.1=testRuntimeClasspath
|
||||
net.sf.jopt-simple:jopt-simple:5.0.4=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.apache.httpcomponents.client5:httpclient5:5.3.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.apache.httpcomponents.core5:httpcore5-h2:5.2.4=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.apache.httpcomponents.core5:httpcore5:5.2.4=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.checkerframework:checker-qual:3.42.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty.http2:http2-common:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty.http2:http2-hpack:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty.http2:http2-server:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty.toolchain:jetty-jakarta-servlet-api:5.0.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-alpn-client:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-alpn-java-client:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-alpn-java-server:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-alpn-server:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-bom:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-client:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-http:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-io:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-proxy:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-security:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-server:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-servlet:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-servlets:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-util:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-webapp:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-xml:11.0.20=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.fusesource.jansi:jansi:2.4.0=default
|
||||
org.fusesource.jansi:jansi:2.4.1=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.compiler:compiler:23.0.2=compileClasspath,compileOnlyDependenciesMetadata
|
||||
@@ -17,6 +63,8 @@ org.graalvm.nativeimage:pointsto:23.0.2=compileClasspath,compileOnlyDependencies
|
||||
org.graalvm.nativeimage:svm:23.0.2=compileClasspath,compileOnlyDependenciesMetadata
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=compileClasspath,compileOnlyDependenciesMetadata,default,runtimeClasspath,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=compileClasspath,compileOnlyDependenciesMetadata,default,runtimeClasspath,testRuntimeClasspath
|
||||
org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.hamcrest:hamcrest:2.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
@@ -32,14 +80,10 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10=apiDependenciesMetadata,compileCl
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains:annotations:13.0=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-native:3.23.0=default
|
||||
org.jline:jline-native:3.23.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-reader:3.23.0=default
|
||||
org.jline:jline-reader:3.23.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-terminal-jansi:3.23.0=default
|
||||
org.jline:jline-terminal-jansi:3.23.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-terminal:3.23.0=default
|
||||
org.jline:jline-terminal:3.23.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-native:3.23.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-reader:3.23.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-terminal-jansi:3.23.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jline:jline-terminal:3.23.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.10.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.jupiter:junit-jupiter-engine:5.10.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.jupiter:junit-jupiter-params:5.10.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
@@ -49,5 +93,12 @@ org.junit:junit-bom:5.10.2=testCompileClasspath,testImplementationDependenciesMe
|
||||
org.msgpack:msgpack-core:0.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.organicdesign:Paguro:3.10.3=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.slf4j:slf4j-api:2.0.11=testRuntimeClasspath
|
||||
org.slf4j:slf4j-api:2.0.9=testCompileClasspath,testImplementationDependenciesMetadata
|
||||
org.snakeyaml:snakeyaml-engine:2.5=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.wiremock:wiremock:3.6.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.xmlunit:xmlunit-core:2.10.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.xmlunit:xmlunit-legacy:2.10.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.xmlunit:xmlunit-placeholders:2.10.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.yaml:snakeyaml:2.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
empty=annotationProcessor,archives,compile,intransitiveDependenciesMetadata,javaExecutable,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDef,kotlinScriptDefExtensions,runtime,runtimeOnlyDependenciesMetadata,shadow,signatures,sourcesJar,stagedAlpineLinuxAmd64Executable,stagedLinuxAarch64Executable,stagedLinuxAmd64Executable,stagedMacAarch64Executable,stagedMacAmd64Executable,testAnnotationProcessor,testApiDependenciesMetadata,testCompile,testCompileOnly,testCompileOnlyDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDef,testKotlinScriptDefExtensions,testRuntime
|
||||
|
||||
@@ -56,6 +56,7 @@ dependencies {
|
||||
}
|
||||
|
||||
testImplementation(projects.pklCommonsTest)
|
||||
testImplementation(libs.wiremock)
|
||||
|
||||
fun executableDir(name: String) = files(layout.buildDirectory.dir("executable/$name"))
|
||||
stagedMacAmd64Executable(executableDir("pkl-macos-amd64"))
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.pkl.server.Server
|
||||
class CliServer(options: CliBaseOptions) : CliCommand(options) {
|
||||
override fun doRun() =
|
||||
try {
|
||||
val server = Server(MessageTransports.stream(System.`in`, System.out), httpClient)
|
||||
val server = Server(MessageTransports.stream(System.`in`, System.out))
|
||||
server.use { it.start() }
|
||||
} catch (e: ProtocolException) {
|
||||
throw CliException(e.message!!)
|
||||
|
||||
@@ -15,17 +15,23 @@
|
||||
*/
|
||||
package org.pkl.cli
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.WireMock.*
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest
|
||||
import java.io.StringReader
|
||||
import java.io.StringWriter
|
||||
import java.net.ServerSocket
|
||||
import java.net.URI
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.time.Duration
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.io.path.*
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatCode
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.junit.jupiter.api.condition.DisabledOnOs
|
||||
@@ -40,8 +46,10 @@ import org.pkl.commons.cli.CliException
|
||||
import org.pkl.commons.test.FileTestUtils
|
||||
import org.pkl.commons.test.PackageServer
|
||||
import org.pkl.core.OutputFormat
|
||||
import org.pkl.core.SecurityManagers
|
||||
import org.pkl.core.util.IoUtils
|
||||
|
||||
@WireMockTest(httpsEnabled = true, proxyMode = true)
|
||||
class CliEvaluatorTest {
|
||||
companion object {
|
||||
private val defaultContents =
|
||||
@@ -1262,6 +1270,196 @@ result = someLib.x
|
||||
.hasMessageNotContainingAny("java.", "sun.") // class names have been filtered out
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `eval http module from proxy`(wwRuntimeInfo: WireMockRuntimeInfo) {
|
||||
stubFor(
|
||||
get(urlEqualTo("/bar.pkl")).withHost(equalTo("not.a.valid.host")).willReturn(ok("foo = 1"))
|
||||
)
|
||||
val options =
|
||||
CliEvaluatorOptions(
|
||||
CliBaseOptions(
|
||||
sourceModules = listOf(URI("http://not.a.valid.host/bar.pkl")),
|
||||
proxyAddress = URI("http://localhost:${wwRuntimeInfo.httpPort}"),
|
||||
allowedModules = SecurityManagers.defaultAllowedModules + Pattern.compile("http:"),
|
||||
),
|
||||
)
|
||||
val output = evalToConsole(options)
|
||||
assertThat(output).isEqualTo("foo = 1\n")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `eval https -- no proxy`(wwRuntimeInfo: WireMockRuntimeInfo) {
|
||||
// pick an address on the local machine so we can be sure this test is not making any outbound
|
||||
// connections.
|
||||
val openPort = ServerSocket(0).use { it.localPort }
|
||||
val targetAddress = "https://127.0.0.1:$openPort"
|
||||
val options =
|
||||
CliEvaluatorOptions(
|
||||
CliBaseOptions(
|
||||
// use loopback address to prevent test from making outbound http connection.
|
||||
sourceModules = listOf(URI("$targetAddress/foo.pkl")),
|
||||
proxyAddress = URI(wwRuntimeInfo.httpBaseUrl),
|
||||
noProxy = listOf("*"),
|
||||
allowedModules = SecurityManagers.defaultAllowedModules + Pattern.compile("http:"),
|
||||
)
|
||||
)
|
||||
assertThatCode { evalToConsole(options) }
|
||||
.hasMessageContaining("I/O error loading module `$targetAddress/foo.pkl`")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO: figure out why this is failing.
|
||||
fun `eval package from proxy`(wwRuntimeInfo: WireMockRuntimeInfo) {
|
||||
stubFor(
|
||||
any(anyUrl()).willReturn(aResponse().proxiedFrom("https://localhost:${packageServer.port}"))
|
||||
)
|
||||
val options =
|
||||
CliEvaluatorOptions(
|
||||
CliBaseOptions(
|
||||
sourceModules = listOf(URI("package://localhost:1/birds@0.5.0#/catalog/Ostritch.pkl")),
|
||||
noCache = true,
|
||||
proxyAddress = URI(wwRuntimeInfo.httpBaseUrl),
|
||||
caCertificates = listOf(FileTestUtils.selfSignedCertificate),
|
||||
allowedModules = SecurityManagers.defaultAllowedModules + Pattern.compile("http:")
|
||||
)
|
||||
)
|
||||
val output = evalToConsole(options)
|
||||
assertThat(output)
|
||||
.isEqualTo(
|
||||
"""
|
||||
name = "Ostritch"
|
||||
|
||||
favoriteFruit {
|
||||
name = "Orange"
|
||||
}
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
verify(getRequestedFor(urlEqualTo("birds@0.5.0")))
|
||||
verify(getRequestedFor(urlEqualTo("fruit@1.0.5")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `eval http module from proxy -- configured in settings`(
|
||||
@TempDir tempDir: Path,
|
||||
wwRuntimeInfo: WireMockRuntimeInfo
|
||||
) {
|
||||
val settingsModule =
|
||||
tempDir.writeFile(
|
||||
"settings.pkl",
|
||||
"""
|
||||
amends "pkl:settings"
|
||||
|
||||
http {
|
||||
proxy {
|
||||
address = "${wwRuntimeInfo.httpBaseUrl}"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
stubFor(
|
||||
get(urlEqualTo("/bar.pkl")).withHost(equalTo("not.a.valid.host")).willReturn(ok("foo = 1"))
|
||||
)
|
||||
val options =
|
||||
CliEvaluatorOptions(
|
||||
CliBaseOptions(
|
||||
sourceModules = listOf(URI("http://not.a.valid.host/bar.pkl")),
|
||||
settings = settingsModule.toUri(),
|
||||
allowedModules = SecurityManagers.defaultAllowedModules + Pattern.compile("http:"),
|
||||
),
|
||||
)
|
||||
val output = evalToConsole(options)
|
||||
assertThat(output).isEqualTo("foo = 1\n")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `eval http module from proxy -- configured in PklProject`(
|
||||
@TempDir tempDir: Path,
|
||||
wwRuntimeInfo: WireMockRuntimeInfo
|
||||
) {
|
||||
tempDir.writeFile(
|
||||
"PklProject",
|
||||
"""
|
||||
amends "pkl:Project"
|
||||
|
||||
evaluatorSettings {
|
||||
http {
|
||||
proxy {
|
||||
address = "${wwRuntimeInfo.httpBaseUrl}"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
stubFor(
|
||||
get(urlEqualTo("/bar.pkl")).withHost(equalTo("not.a.valid.host")).willReturn(ok("foo = 1"))
|
||||
)
|
||||
val options =
|
||||
CliEvaluatorOptions(
|
||||
CliBaseOptions(
|
||||
sourceModules = listOf(URI("http://not.a.valid.host/bar.pkl")),
|
||||
allowedModules = SecurityManagers.defaultAllowedModules + Pattern.compile("http:"),
|
||||
projectDir = tempDir
|
||||
),
|
||||
)
|
||||
val output = evalToConsole(options)
|
||||
assertThat(output).isEqualTo("foo = 1\n")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `eval http module from proxy -- PklProject beats user settings`(
|
||||
@TempDir tempDir: Path,
|
||||
wwRuntimeInfo: WireMockRuntimeInfo
|
||||
) {
|
||||
val projectDir = tempDir.resolve("my-project")
|
||||
projectDir.writeFile(
|
||||
"PklProject",
|
||||
"""
|
||||
amends "pkl:Project"
|
||||
|
||||
evaluatorSettings {
|
||||
http {
|
||||
proxy {
|
||||
address = "${wwRuntimeInfo.httpBaseUrl}"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
val homeDir = tempDir.resolve("my-home")
|
||||
homeDir.writeFile(
|
||||
"settings.pkl",
|
||||
"""
|
||||
amends "pkl:settings"
|
||||
|
||||
http {
|
||||
proxy {
|
||||
address = "http://invalid.proxy.address"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
val options =
|
||||
CliEvaluatorOptions(
|
||||
CliBaseOptions(
|
||||
sourceModules = listOf(URI("http://not.a.valid.host/bar.pkl")),
|
||||
allowedModules = SecurityManagers.defaultAllowedModules + Pattern.compile("http:"),
|
||||
projectDir = projectDir,
|
||||
settings = homeDir.resolve("settings.pkl").toUri()
|
||||
),
|
||||
)
|
||||
stubFor(get(anyUrl()).willReturn(ok("result = 1")))
|
||||
val output = evalToConsole(options)
|
||||
assertThat(output).isEqualTo("result = 1\n")
|
||||
}
|
||||
|
||||
private fun evalModuleThatImportsPackage(certsFile: Path, testPort: Int = -1) {
|
||||
val moduleUri =
|
||||
writePklFile(
|
||||
|
||||
@@ -6,10 +6,10 @@ com.github.ajalt.clikt:clikt:3.5.1=apiDependenciesMetadata,compileClasspath,defa
|
||||
com.squareup:javapoet:1.13.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
io.leangen.geantyref:geantyref:1.3.15=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -6,10 +6,10 @@ com.github.ajalt.clikt:clikt:3.5.1=apiDependenciesMetadata,compileClasspath,defa
|
||||
com.squareup:kotlinpoet:1.6.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
io.leangen.geantyref:geantyref:1.3.15=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,testRuntimeClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
com.github.ajalt.clikt:clikt-jvm:3.5.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.ajalt.clikt:clikt:3.5.1=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.time.Duration
|
||||
import java.util.regex.Pattern
|
||||
import org.pkl.core.http.HttpClient
|
||||
import org.pkl.core.module.ProjectDependenciesManager
|
||||
import org.pkl.core.util.IoUtils
|
||||
|
||||
@@ -129,6 +128,12 @@ data class CliBaseOptions(
|
||||
* `~/.pkl/cacerts/` does not exist or is empty, Pkl's built-in CA certificates are used.
|
||||
*/
|
||||
val caCertificates: List<Path> = listOf(),
|
||||
|
||||
/** The proxy to connect to. */
|
||||
val proxyAddress: URI? = null,
|
||||
|
||||
/** Hostnames, IP addresses, or CIDR blocks to not proxy. */
|
||||
val noProxy: List<String>? = null,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -177,24 +182,4 @@ data class CliBaseOptions(
|
||||
|
||||
/** [caCertificates] after normalization. */
|
||||
val normalizedCaCertificates: List<Path> = caCertificates.map(normalizedWorkingDir::resolve)
|
||||
|
||||
/**
|
||||
* The HTTP client shared between CLI commands created with this [CliBaseOptions] instance.
|
||||
*
|
||||
* To release the resources held by the HTTP client in a timely manner, call its `close()` method.
|
||||
*/
|
||||
val httpClient: HttpClient by lazy {
|
||||
with(HttpClient.builder()) {
|
||||
setTestPort(testPort)
|
||||
if (normalizedCaCertificates.isEmpty()) {
|
||||
addDefaultCliCertificates()
|
||||
} else {
|
||||
for (file in normalizedCaCertificates) addCertificates(file)
|
||||
}
|
||||
// Lazy building significantly reduces execution time of commands that do minimal work.
|
||||
// However, it means that HTTP client initialization errors won't surface until an HTTP
|
||||
// request is made.
|
||||
buildLazily()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.pkl.commons.cli
|
||||
import java.nio.file.Path
|
||||
import java.util.regex.Pattern
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
|
||||
import org.pkl.core.http.HttpClient
|
||||
import org.pkl.core.module.ModuleKeyFactories
|
||||
import org.pkl.core.module.ModuleKeyFactory
|
||||
@@ -35,6 +36,9 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
|
||||
if (cliOptions.testMode) {
|
||||
IoUtils.setTestMode()
|
||||
}
|
||||
|
||||
proxyAddress?.let(IoUtils::setSystemProxy)
|
||||
|
||||
try {
|
||||
doRun()
|
||||
} catch (e: PklException) {
|
||||
@@ -97,42 +101,44 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
|
||||
)
|
||||
}
|
||||
|
||||
private val projectSettings: Project.EvaluatorSettings? by lazy {
|
||||
if (cliOptions.omitProjectSettings) null else project?.settings
|
||||
private val evaluatorSettings: PklEvaluatorSettings? by lazy {
|
||||
if (cliOptions.omitProjectSettings) null else project?.evaluatorSettings
|
||||
}
|
||||
|
||||
protected val allowedModules: List<Pattern> by lazy {
|
||||
cliOptions.allowedModules
|
||||
?: projectSettings?.allowedModules ?: SecurityManagers.defaultAllowedModules
|
||||
?: evaluatorSettings?.allowedModules ?: SecurityManagers.defaultAllowedModules
|
||||
}
|
||||
|
||||
protected val allowedResources: List<Pattern> by lazy {
|
||||
cliOptions.allowedResources
|
||||
?: projectSettings?.allowedResources ?: SecurityManagers.defaultAllowedResources
|
||||
?: evaluatorSettings?.allowedResources ?: SecurityManagers.defaultAllowedResources
|
||||
}
|
||||
|
||||
protected val rootDir: Path? by lazy { cliOptions.normalizedRootDir ?: projectSettings?.rootDir }
|
||||
protected val rootDir: Path? by lazy {
|
||||
cliOptions.normalizedRootDir ?: evaluatorSettings?.rootDir
|
||||
}
|
||||
|
||||
protected val environmentVariables: Map<String, String> by lazy {
|
||||
cliOptions.environmentVariables ?: projectSettings?.env ?: System.getenv()
|
||||
cliOptions.environmentVariables ?: evaluatorSettings?.env ?: System.getenv()
|
||||
}
|
||||
|
||||
protected val externalProperties: Map<String, String> by lazy {
|
||||
cliOptions.externalProperties ?: projectSettings?.externalProperties ?: emptyMap()
|
||||
cliOptions.externalProperties ?: evaluatorSettings?.externalProperties ?: emptyMap()
|
||||
}
|
||||
|
||||
protected val moduleCacheDir: Path? by lazy {
|
||||
if (cliOptions.noCache) null
|
||||
else
|
||||
cliOptions.normalizedModuleCacheDir
|
||||
?: projectSettings?.let { settings ->
|
||||
if (settings.isNoCache == true) null else settings.moduleCacheDir
|
||||
?: evaluatorSettings?.let { settings ->
|
||||
if (settings.noCache == true) null else settings.moduleCacheDir
|
||||
}
|
||||
?: IoUtils.getDefaultModuleCacheDir()
|
||||
}
|
||||
|
||||
protected val modulePath: List<Path> by lazy {
|
||||
cliOptions.normalizedModulePath ?: projectSettings?.modulePath ?: emptyList()
|
||||
cliOptions.normalizedModulePath ?: evaluatorSettings?.modulePath ?: emptyList()
|
||||
}
|
||||
|
||||
protected val stackFrameTransformer: StackFrameTransformer by lazy {
|
||||
@@ -152,9 +158,36 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
|
||||
)
|
||||
}
|
||||
|
||||
// share HTTP client with other commands with the same cliOptions
|
||||
protected val httpClient: HttpClient
|
||||
get() = cliOptions.httpClient
|
||||
private val proxyAddress =
|
||||
cliOptions.proxyAddress
|
||||
?: project?.evaluatorSettings?.http?.proxy?.address ?: settings.http?.proxy?.address
|
||||
|
||||
private val noProxy =
|
||||
cliOptions.noProxy
|
||||
?: project?.evaluatorSettings?.http?.proxy?.noProxy ?: settings.http?.proxy?.noProxy
|
||||
|
||||
/**
|
||||
* The HTTP client used for this command.
|
||||
*
|
||||
* To release resources held by the HTTP client in a timely manner, call [HttpClient.close].
|
||||
*/
|
||||
val httpClient: HttpClient by lazy {
|
||||
with(HttpClient.builder()) {
|
||||
setTestPort(cliOptions.testPort)
|
||||
if (cliOptions.normalizedCaCertificates.isEmpty()) {
|
||||
addDefaultCliCertificates()
|
||||
} else {
|
||||
for (file in cliOptions.normalizedCaCertificates) addCertificates(file)
|
||||
}
|
||||
if ((proxyAddress ?: noProxy) != null) {
|
||||
setProxy(proxyAddress, noProxy ?: listOf())
|
||||
}
|
||||
// Lazy building significantly reduces execution time of commands that do minimal work.
|
||||
// However, it means that HTTP client initialization errors won't surface until an HTTP
|
||||
// request is made.
|
||||
buildLazily()
|
||||
}
|
||||
}
|
||||
|
||||
protected fun moduleKeyFactories(modulePathResolver: ModulePathResolver): List<ModuleKeyFactory> {
|
||||
return buildList {
|
||||
|
||||
@@ -27,6 +27,9 @@ fun cliMain(block: () -> Unit) {
|
||||
if (!message.endsWith('\n')) stream.println()
|
||||
}
|
||||
|
||||
// Force `native-image` to use system proxies (which does not happen with `-D`).
|
||||
System.setProperty("java.net.useSystemProxies", "true")
|
||||
|
||||
try {
|
||||
block()
|
||||
} catch (e: CliTestException) {
|
||||
|
||||
@@ -172,6 +172,32 @@ class BaseOptions : OptionGroup() {
|
||||
.path()
|
||||
.multiple()
|
||||
|
||||
@Suppress("HttpUrlsUsage")
|
||||
val proxy: URI? by
|
||||
option(
|
||||
names = arrayOf("--proxy"),
|
||||
metavar = "<address>",
|
||||
help = "Proxy to use for HTTP(S) connections."
|
||||
)
|
||||
.single()
|
||||
.convert { URI(it) }
|
||||
.validate { uri ->
|
||||
require(
|
||||
uri.scheme == "http" && uri.host != null && uri.path.isEmpty() && uri.userInfo == null
|
||||
) {
|
||||
"Malformed proxy URI (expecting `http://<host>[:<port>]`)"
|
||||
}
|
||||
}
|
||||
|
||||
val noProxy: List<String>? by
|
||||
option(
|
||||
names = arrayOf("--no-proxy"),
|
||||
metavar = "<pattern1,pattern2>",
|
||||
help = "Hostnames that should not be connected to via a proxy."
|
||||
)
|
||||
.single()
|
||||
.split(",")
|
||||
|
||||
// hidden option used by native tests
|
||||
private val testPort: Int by
|
||||
option(names = arrayOf("--test-port"), help = "Internal test option", hidden = true)
|
||||
@@ -202,7 +228,9 @@ class BaseOptions : OptionGroup() {
|
||||
testPort = testPort,
|
||||
omitProjectSettings = projectOptions?.omitProjectSettings ?: false,
|
||||
noProject = projectOptions?.noProject ?: false,
|
||||
caCertificates = caCertificates
|
||||
caCertificates = caCertificates,
|
||||
proxyAddress = proxy,
|
||||
noProxy = noProxy ?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
net.bytebuddy:byte-buddy:1.12.21=default
|
||||
net.bytebuddy:byte-buddy:1.14.11=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=apiDependenciesMetadata,compileClasspath,implementationDependenciesMetadata,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.24.2=default
|
||||
org.assertj:assertj-core:3.25.3=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -8,10 +8,10 @@ com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,pklCodegenJava,runtimeClasspat
|
||||
io.leangen.geantyref:geantyref:1.3.14=default
|
||||
io.leangen.geantyref:geantyref:1.3.15=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
javax.inject:javax.inject:1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=default,pklCodegenJava,runtimeClasspath,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=default,pklCodegenJava,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -7,10 +7,10 @@ com.squareup:kotlinpoet:1.6.0=pklCodegenKotlin
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,pklCodegenKotlin,pklConfigJava,runtimeClasspath,testRuntimeClasspath
|
||||
io.leangen.geantyref:geantyref:1.3.14=default
|
||||
io.leangen.geantyref:geantyref:1.3.15=pklConfigJava,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=default,pklCodegenKotlin,pklConfigJava,runtimeClasspath,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=default,pklCodegenKotlin,pklConfigJava,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -6,13 +6,13 @@ com.squareup:javapoet:1.13.0=generatorCompileClasspath,generatorImplementationDe
|
||||
com.tunnelvisionlabs:antlr4-annotations:4.9.0=antlr
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=antlr,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.tunnelvisionlabs:antlr4:4.9.0=antlr
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.abego.treelayout:org.abego.treelayout.core:1.0.1=antlr
|
||||
org.antlr:ST4:4.3=antlr
|
||||
org.antlr:antlr-runtime:3.5.2=antlr
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=compileClasspath,default,generatorCompileClasspath,generatorImplementationDependenciesMetadata,generatorRuntimeClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=compileClasspath,default,generatorCompileClasspath,generatorImplementationDependenciesMetadata,generatorRuntimeClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-dsl-processor:23.0.2=annotationProcessor
|
||||
|
||||
@@ -440,39 +440,39 @@ public final class EvaluatorBuilder {
|
||||
*/
|
||||
public EvaluatorBuilder applyFromProject(Project project) {
|
||||
this.dependencies = project.getDependencies();
|
||||
var settings = project.getSettings();
|
||||
var settings = project.getEvaluatorSettings();
|
||||
if (securityManager != null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot call both `setSecurityManager` and `setProject`, because both define security manager settings. Call `setProjectOnly` if the security manager is desired.");
|
||||
}
|
||||
if (settings.getAllowedModules() != null) {
|
||||
setAllowedModules(settings.getAllowedModules());
|
||||
if (settings.allowedModules() != null) {
|
||||
setAllowedModules(settings.allowedModules());
|
||||
}
|
||||
if (settings.getAllowedResources() != null) {
|
||||
setAllowedResources(settings.getAllowedResources());
|
||||
if (settings.allowedResources() != null) {
|
||||
setAllowedResources(settings.allowedResources());
|
||||
}
|
||||
if (settings.getExternalProperties() != null) {
|
||||
setExternalProperties(settings.getExternalProperties());
|
||||
if (settings.externalProperties() != null) {
|
||||
setExternalProperties(settings.externalProperties());
|
||||
}
|
||||
if (settings.getEnv() != null) {
|
||||
setEnvironmentVariables(settings.getEnv());
|
||||
if (settings.env() != null) {
|
||||
setEnvironmentVariables(settings.env());
|
||||
}
|
||||
if (settings.getTimeout() != null) {
|
||||
setTimeout(settings.getTimeout().toJavaDuration());
|
||||
if (settings.timeout() != null) {
|
||||
setTimeout(settings.timeout().toJavaDuration());
|
||||
}
|
||||
if (settings.getModulePath() != null) {
|
||||
if (settings.modulePath() != null) {
|
||||
// indirectly closed by `ModuleKeyFactories.closeQuietly(builder.moduleKeyFactories)`
|
||||
var modulePathResolver = new ModulePathResolver(settings.getModulePath());
|
||||
var modulePathResolver = new ModulePathResolver(settings.modulePath());
|
||||
addResourceReader(ResourceReaders.modulePath(modulePathResolver));
|
||||
addModuleKeyFactory(ModuleKeyFactories.modulePath(modulePathResolver));
|
||||
}
|
||||
if (settings.getRootDir() != null) {
|
||||
setRootDir(settings.getRootDir());
|
||||
if (settings.rootDir() != null) {
|
||||
setRootDir(settings.rootDir());
|
||||
}
|
||||
if (Boolean.TRUE.equals(settings.isNoCache())) {
|
||||
if (Boolean.TRUE.equals(settings.noCache())) {
|
||||
setModuleCacheDir(null);
|
||||
} else if (settings.getModuleCacheDir() != null) {
|
||||
setModuleCacheDir(settings.getModuleCacheDir());
|
||||
} else if (settings.moduleCacheDir() != null) {
|
||||
setModuleCacheDir(settings.moduleCacheDir());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public final class StackFrameTransformers {
|
||||
public static StackFrameTransformer createDefault(PklSettings settings) {
|
||||
return defaultTransformer
|
||||
// order is relevant
|
||||
.andThen(convertFilePathToUriScheme(settings.getEditor().getUrlScheme()));
|
||||
.andThen(convertFilePathToUriScheme(settings.editor().urlScheme()));
|
||||
}
|
||||
|
||||
private static StackFrameTransformer loadFromServiceProviders() {
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Copyright © 2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.evaluatorSettings;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Pattern;
|
||||
import org.pkl.core.Duration;
|
||||
import org.pkl.core.PNull;
|
||||
import org.pkl.core.PObject;
|
||||
import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.PklException;
|
||||
import org.pkl.core.Value;
|
||||
import org.pkl.core.util.ErrorMessages;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/** Java version of {@code pkl.EvaluatorSettings}. */
|
||||
public record PklEvaluatorSettings(
|
||||
@Nullable Map<String, String> externalProperties,
|
||||
@Nullable Map<String, String> env,
|
||||
@Nullable List<Pattern> allowedModules,
|
||||
@Nullable List<Pattern> allowedResources,
|
||||
@Nullable Boolean noCache,
|
||||
@Nullable Path moduleCacheDir,
|
||||
@Nullable List<Path> modulePath,
|
||||
@Nullable Duration timeout,
|
||||
@Nullable Path rootDir,
|
||||
@Nullable Http http) {
|
||||
|
||||
/** Initializes a {@link PklEvaluatorSettings} from a raw object representation. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public static PklEvaluatorSettings parse(
|
||||
Value input, BiFunction<? super String, ? super String, Path> pathNormalizer) {
|
||||
if (!(input instanceof PObject pSettings)) {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
|
||||
var moduleCacheDirStr = (String) pSettings.get("moduleCacheDir");
|
||||
var moduleCacheDir =
|
||||
moduleCacheDirStr == null
|
||||
? null
|
||||
: pathNormalizer.apply(moduleCacheDirStr, "moduleCacheDir");
|
||||
|
||||
var allowedModulesStrs = (List<String>) pSettings.get("allowedModules");
|
||||
var allowedModules =
|
||||
allowedModulesStrs == null
|
||||
? null
|
||||
: allowedModulesStrs.stream().map(Pattern::compile).toList();
|
||||
|
||||
var allowedResourcesStrs = (List<String>) pSettings.get("allowedResources");
|
||||
var allowedResources =
|
||||
allowedResourcesStrs == null
|
||||
? null
|
||||
: allowedResourcesStrs.stream().map(Pattern::compile).toList();
|
||||
|
||||
var modulePathStrs = (List<String>) pSettings.get("modulePath");
|
||||
var modulePath =
|
||||
modulePathStrs == null
|
||||
? null
|
||||
: modulePathStrs.stream().map(it -> pathNormalizer.apply(it, "modulePath")).toList();
|
||||
|
||||
var rootDirStr = (String) pSettings.get("rootDir");
|
||||
var rootDir = rootDirStr == null ? null : pathNormalizer.apply(rootDirStr, "rootDir");
|
||||
|
||||
return new PklEvaluatorSettings(
|
||||
(Map<String, String>) pSettings.get("externalProperties"),
|
||||
(Map<String, String>) pSettings.get("env"),
|
||||
allowedModules,
|
||||
allowedResources,
|
||||
(Boolean) pSettings.get("noCache"),
|
||||
moduleCacheDir,
|
||||
modulePath,
|
||||
(Duration) pSettings.get("timeout"),
|
||||
rootDir,
|
||||
Http.parse((Value) pSettings.get("http")));
|
||||
}
|
||||
|
||||
public record Http(@Nullable Proxy proxy) {
|
||||
public static final Http DEFAULT = new Http(null);
|
||||
|
||||
public static @Nullable Http parse(@Nullable Value input) {
|
||||
if (input == null || input instanceof PNull) {
|
||||
return null;
|
||||
} else if (input instanceof PObject http) {
|
||||
var proxy = Proxy.parse((Value) http.getProperty("proxy"));
|
||||
return proxy == null ? DEFAULT : new Http(proxy);
|
||||
} else {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record Proxy(@Nullable URI address, @Nullable List<String> noProxy) {
|
||||
public static Proxy create(@Nullable String address, @Nullable List<String> noProxy) {
|
||||
URI addressUri;
|
||||
try {
|
||||
addressUri = address == null ? null : new URI(address);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new PklException(ErrorMessages.create("invalidUri", address));
|
||||
}
|
||||
return new Proxy(addressUri, noProxy);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static @Nullable Proxy parse(Value input) {
|
||||
if (input instanceof PNull) {
|
||||
return null;
|
||||
} else if (input instanceof PObject proxy) {
|
||||
var address = (String) proxy.get("address");
|
||||
var noProxy = (List<String>) proxy.get("noProxy");
|
||||
return create(address, noProxy);
|
||||
} else {
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean arePatternsEqual(
|
||||
@Nullable List<Pattern> thesePatterns, @Nullable List<Pattern> thosePatterns) {
|
||||
if (thesePatterns == null) {
|
||||
return thosePatterns == null;
|
||||
}
|
||||
if (thosePatterns == null || thesePatterns.size() != thosePatterns.size()) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < thesePatterns.size(); i++) {
|
||||
if (!thesePatterns.get(i).pattern().equals(thosePatterns.get(i).pattern())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof PklEvaluatorSettings that)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(externalProperties, that.externalProperties)
|
||||
&& Objects.equals(env, that.env)
|
||||
&& arePatternsEqual(allowedModules, that.allowedModules)
|
||||
&& arePatternsEqual(allowedResources, that.allowedResources)
|
||||
&& Objects.equals(noCache, that.noCache)
|
||||
&& Objects.equals(moduleCacheDir, that.moduleCacheDir)
|
||||
&& Objects.equals(timeout, that.timeout)
|
||||
&& Objects.equals(rootDir, that.rootDir)
|
||||
&& Objects.equals(http, that.http);
|
||||
}
|
||||
|
||||
private int hashPatterns(@Nullable List<Pattern> patterns) {
|
||||
if (patterns == null) {
|
||||
return 0;
|
||||
}
|
||||
var ret = 1;
|
||||
for (var pattern : patterns) {
|
||||
ret = 31 * ret + pattern.pattern().hashCode();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
var result =
|
||||
Objects.hash(externalProperties, env, noCache, moduleCacheDir, timeout, rootDir, http);
|
||||
result = 31 * result + hashPatterns(allowedModules);
|
||||
result = 31 * result + hashPatterns(allowedResources);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NonnullByDefault
|
||||
package org.pkl.core.evaluatorSettings;
|
||||
|
||||
import org.pkl.core.util.NonnullByDefault;
|
||||
@@ -21,7 +21,9 @@ import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.net.http.HttpTimeoutException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/**
|
||||
* An HTTP client.
|
||||
@@ -36,6 +38,7 @@ import javax.net.ssl.SSLContext;
|
||||
public interface HttpClient extends AutoCloseable {
|
||||
|
||||
/** A builder of {@linkplain HttpClient HTTP clients}. */
|
||||
@SuppressWarnings("unused")
|
||||
interface Builder {
|
||||
/**
|
||||
* Sets the {@code User-Agent} header.
|
||||
@@ -116,6 +119,32 @@ public interface HttpClient extends AutoCloseable {
|
||||
*/
|
||||
Builder setTestPort(int port);
|
||||
|
||||
/**
|
||||
* Sets the proxy selector to use when establishing connections.
|
||||
*
|
||||
* <p>Defaults to: {@link java.net.ProxySelector#getDefault()}.
|
||||
*/
|
||||
Builder setProxySelector(java.net.ProxySelector proxySelector);
|
||||
|
||||
/**
|
||||
* Configures HTTP connections to connect to the provided proxy address.
|
||||
*
|
||||
* <p>The provided {@code proxyAddress} must have scheme http, not contain userInfo, and not
|
||||
* have a path segment.
|
||||
*
|
||||
* <p>If {@code proxyAddress} is {@code null}, uses the proxy address provided by {@link
|
||||
* java.net.ProxySelector#getDefault()}.
|
||||
*
|
||||
* <p>NOTE: Due to a <a href="https://bugs.openjdk.org/browse/JDK-8256409">limitation in the
|
||||
* JDK</a>, this does not configure the proxy server used for certificate revocation checking.
|
||||
* To configure the certificate revocation checker, the result of {@link
|
||||
* java.net.ProxySelector#getDefault} needs to be changed either by setting system properties,
|
||||
* or via {@link java.net.ProxySelector#setDefault}.
|
||||
*
|
||||
* @throws IllegalArgumentException if `proxyAddress` is invalid.
|
||||
*/
|
||||
Builder setProxy(@Nullable URI proxyAddress, List<String> noProxy);
|
||||
|
||||
/**
|
||||
* Creates a new {@code HttpClient} from the current state of this builder.
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.pkl.core.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
@@ -25,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import org.pkl.core.Release;
|
||||
import org.pkl.core.http.HttpClient.Builder;
|
||||
import org.pkl.core.util.ErrorMessages;
|
||||
import org.pkl.core.util.IoUtils;
|
||||
|
||||
@@ -36,6 +38,7 @@ final class HttpClientBuilder implements HttpClient.Builder {
|
||||
private final List<Path> certificateFiles = new ArrayList<>();
|
||||
private final List<URI> certificateUris = new ArrayList<>();
|
||||
private int testPort = -1;
|
||||
private ProxySelector proxySelector;
|
||||
|
||||
HttpClientBuilder() {
|
||||
this(IoUtils.getPklHomeDir().resolve("cacerts"));
|
||||
@@ -109,6 +112,17 @@ final class HttpClientBuilder implements HttpClient.Builder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClient.Builder setProxySelector(ProxySelector proxySelector) {
|
||||
this.proxySelector = proxySelector;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setProxy(URI proxyAddress, List<String> noProxy) {
|
||||
this.proxySelector = new org.pkl.core.http.ProxySelector(proxyAddress, noProxy);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient build() {
|
||||
return doBuild().get();
|
||||
@@ -123,8 +137,11 @@ final class HttpClientBuilder implements HttpClient.Builder {
|
||||
// make defensive copies because Supplier may get called after builder was mutated
|
||||
var certificateFiles = List.copyOf(this.certificateFiles);
|
||||
var certificateUris = List.copyOf(this.certificateUris);
|
||||
var proxySelector =
|
||||
this.proxySelector != null ? this.proxySelector : java.net.ProxySelector.getDefault();
|
||||
return () -> {
|
||||
var jdkClient = new JdkHttpClient(certificateFiles, certificateUris, connectTimeout);
|
||||
var jdkClient =
|
||||
new JdkHttpClient(certificateFiles, certificateUris, connectTimeout, proxySelector);
|
||||
return new RequestRewritingClient(userAgent, requestTimeout, testPort, jdkClient);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -77,11 +77,16 @@ final class JdkHttpClient implements HttpClient {
|
||||
closeMethod = result;
|
||||
}
|
||||
|
||||
JdkHttpClient(List<Path> certificateFiles, List<URI> certificateUris, Duration connectTimeout) {
|
||||
JdkHttpClient(
|
||||
List<Path> certificateFiles,
|
||||
List<URI> certificateUris,
|
||||
Duration connectTimeout,
|
||||
java.net.ProxySelector proxySelector) {
|
||||
underlying =
|
||||
java.net.http.HttpClient.newBuilder()
|
||||
.sslContext(createSslContext(certificateFiles, certificateUris))
|
||||
.connectTimeout(connectTimeout)
|
||||
.proxy(proxySelector)
|
||||
.followRedirects(Redirect.NORMAL)
|
||||
.build();
|
||||
}
|
||||
|
||||
215
pkl-core/src/main/java/org/pkl/core/http/NoProxyRule.java
Normal file
215
pkl-core/src/main/java/org/pkl/core/http/NoProxyRule.java
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Copyright © 2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.http;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.regex.Pattern;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a noproxy entry.
|
||||
*
|
||||
* <p>Follows the rules described in <a
|
||||
* href="https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/#standardizing-no_proxy">Standardizing
|
||||
* {@code no_proxy}</a>
|
||||
*/
|
||||
final class NoProxyRule {
|
||||
private static final String portString = "(?::(?<port>\\d{1,5}))?";
|
||||
private static final String cidrString = "(?:/(?<cidr>\\d{1,3}))?";
|
||||
private static final String ipv4AddressString =
|
||||
"(?<host>[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})";
|
||||
private static final Pattern ipv4Address = Pattern.compile("^" + ipv4AddressString + "$");
|
||||
private static final Pattern ipv4AddressOrCidr =
|
||||
Pattern.compile("^" + ipv4AddressString + cidrString + portString + "$");
|
||||
private static final String ipv6AddressString =
|
||||
"(?<host>(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(:0{1,4})?:)?(?:(?:25[0-5]|(2[0-4]|1?[0-9])?[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9]))";
|
||||
private static final Pattern ipv6AddressOrCidr =
|
||||
Pattern.compile(
|
||||
"^(?<open>\\[)?" + ipv6AddressString + cidrString + "(?<close>])?" + portString + "$");
|
||||
private static final Pattern hostnamePattern =
|
||||
Pattern.compile("^\\.?(?<host>[^:]+)" + portString + "$");
|
||||
|
||||
private @Nullable Integer ipv4 = null;
|
||||
private @Nullable Integer ipv4Mask = null;
|
||||
private @Nullable BigInteger ipv6 = null;
|
||||
private @Nullable BigInteger ipv6Mask = null;
|
||||
private @Nullable String hostname = null;
|
||||
private int port = 0;
|
||||
private boolean allNoProxy = false;
|
||||
|
||||
public NoProxyRule(String repr) {
|
||||
if (repr.equals("*")) {
|
||||
allNoProxy = true;
|
||||
return;
|
||||
}
|
||||
var ipv4Matcher = ipv4AddressOrCidr.matcher(repr);
|
||||
if (ipv4Matcher.matches()) {
|
||||
var ipAddress = ipv4Matcher.group("host");
|
||||
ipv4 = parseIpv4(ipAddress);
|
||||
if (ipv4Matcher.group("cidr") != null) {
|
||||
var prefixLength = Integer.parseInt(ipv4Matcher.group("cidr"));
|
||||
if (prefixLength > 32) {
|
||||
// best-effort (don't fail on invalid cidrs).
|
||||
hostname = repr;
|
||||
}
|
||||
ipv4Mask = 0xffffffff << (32 - prefixLength);
|
||||
}
|
||||
if (ipv4Matcher.group("port") != null) {
|
||||
port = Integer.parseInt(ipv4Matcher.group("port"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
var ipv6Matcher = ipv6AddressOrCidr.matcher(repr);
|
||||
if (ipv6Matcher.matches()) {
|
||||
var ipAddress = ipv6Matcher.group("host");
|
||||
ipv6 = parseIpv6(ipAddress);
|
||||
if (ipv6Matcher.group("cidr") != null) {
|
||||
var maskBuffer = ByteBuffer.allocate(16).putLong(-1L).putLong(-1L);
|
||||
var prefixLength = Integer.parseInt(ipv6Matcher.group("cidr"));
|
||||
if (prefixLength > 128) {
|
||||
// best-effort (don't fail on invalid cidrs).
|
||||
hostname = repr;
|
||||
return;
|
||||
}
|
||||
ipv6Mask = new BigInteger(1, maskBuffer.array()).not().shiftRight(prefixLength);
|
||||
}
|
||||
if (ipv6Matcher.group("port") != null) {
|
||||
port = Integer.parseInt(ipv6Matcher.group("port"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
var hostnameMatcher = hostnamePattern.matcher(repr);
|
||||
if (hostnameMatcher.matches()) {
|
||||
hostname = hostnameMatcher.group("host");
|
||||
if (hostnameMatcher.group("port") != null) {
|
||||
port = Integer.parseInt(hostnameMatcher.group("port"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Failed to parse hostname in no-proxy rule: " + repr);
|
||||
}
|
||||
|
||||
public boolean matches(URI uri) {
|
||||
if (allNoProxy) {
|
||||
return true;
|
||||
}
|
||||
if (!hostMatches(uri)) {
|
||||
return false;
|
||||
}
|
||||
if (port == 0) {
|
||||
return true;
|
||||
}
|
||||
var thatPort = uri.getPort();
|
||||
if (thatPort == -1) {
|
||||
thatPort =
|
||||
switch (uri.getScheme()) {
|
||||
case "http" -> 80;
|
||||
case "https" -> 443;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
return port == thatPort;
|
||||
}
|
||||
|
||||
/** Tells if the provided URI should not be proxied according to the rules described. */
|
||||
public boolean hostMatches(URI uri) {
|
||||
if (allNoProxy) {
|
||||
return true;
|
||||
}
|
||||
var host = uri.getHost();
|
||||
if (host == null) {
|
||||
return false;
|
||||
}
|
||||
if (host.equalsIgnoreCase(hostname)) {
|
||||
return true;
|
||||
}
|
||||
if (hostname != null && endsWithIgnoreCase(host, "." + hostname)) {
|
||||
return true;
|
||||
}
|
||||
return ipV6Matches(uri.getHost()) || ipV4Matches(uri.getHost());
|
||||
}
|
||||
|
||||
private boolean endsWithIgnoreCase(String str, String suffix) {
|
||||
var len = suffix.length();
|
||||
return str.regionMatches(true, str.length() - len, suffix, 0, len);
|
||||
}
|
||||
|
||||
private boolean ipV4Matches(String hostname) {
|
||||
if (ipv4 == null) {
|
||||
return false;
|
||||
}
|
||||
if (!ipv4Address.matcher(hostname).matches()) {
|
||||
return false;
|
||||
}
|
||||
var address = parseIpv4(hostname);
|
||||
if (ipv4.equals(address)) {
|
||||
return true;
|
||||
}
|
||||
if (ipv4Mask != null) {
|
||||
return (ipv4 & ipv4Mask) == (address & ipv4Mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean ipV6Matches(String hostname) {
|
||||
if (ipv6 == null) {
|
||||
return false;
|
||||
}
|
||||
if (!hostname.startsWith("[") && !hostname.endsWith("]")) {
|
||||
return false;
|
||||
}
|
||||
var ipv6Repr = hostname.substring(1, hostname.length() - 1);
|
||||
// According to RFC3986, square brackets can _only_ surround IPV6 addresses, so it should be
|
||||
// safe to straight up parse it.
|
||||
// <https://www.ietf.org/rfc/rfc3986.txt>
|
||||
var address = parseIpv6(ipv6Repr);
|
||||
if (ipv6.equals(address)) {
|
||||
return true;
|
||||
}
|
||||
if (ipv6Mask != null) {
|
||||
return ipv6.and(ipv6Mask).equals(address.and(ipv6Mask));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private BigInteger parseIpv6(String repr) {
|
||||
try {
|
||||
var inet = Inet6Address.getByName(repr);
|
||||
var byteArr = inet.getAddress();
|
||||
return new BigInteger(1, byteArr);
|
||||
} catch (UnknownHostException e) {
|
||||
// should never happen; `repr` is an IPV6 literal.
|
||||
throw new RuntimeException(
|
||||
"Received unexpected UnknownHostException during parsing IPV6 literal", e);
|
||||
}
|
||||
}
|
||||
|
||||
private int parseIpv4(String repr) {
|
||||
try {
|
||||
var inet = Inet4Address.getByName(repr);
|
||||
return ByteBuffer.wrap(inet.getAddress()).getInt();
|
||||
} catch (UnknownHostException e) {
|
||||
// should never happen; `repr` is an IPV4 literal.
|
||||
throw new RuntimeException(
|
||||
"Received unexpected UnknownHostException during parsing IPV4 literal", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
pkl-core/src/main/java/org/pkl/core/http/ProxySelector.java
Normal file
78
pkl-core/src/main/java/org/pkl/core/http/ProxySelector.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright © 2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.http;
|
||||
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.pkl.core.util.ErrorMessages;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
final class ProxySelector extends java.net.ProxySelector {
|
||||
|
||||
public static final List<Proxy> NO_PROXY = List.of(Proxy.NO_PROXY);
|
||||
|
||||
private final @Nullable List<Proxy> myProxy;
|
||||
private final List<NoProxyRule> noProxyRules;
|
||||
private final @Nullable java.net.ProxySelector delegate;
|
||||
|
||||
ProxySelector(@Nullable URI proxyAddress, List<String> noProxyRules) {
|
||||
this.noProxyRules = noProxyRules.stream().map(NoProxyRule::new).toList();
|
||||
if (proxyAddress == null) {
|
||||
this.delegate = java.net.ProxySelector.getDefault();
|
||||
this.myProxy = null;
|
||||
} else {
|
||||
if (!proxyAddress.getScheme().equalsIgnoreCase("http")
|
||||
|| proxyAddress.getHost() == null
|
||||
|| !proxyAddress.getPath().isEmpty()
|
||||
|| proxyAddress.getUserInfo() != null) {
|
||||
throw new IllegalArgumentException(
|
||||
ErrorMessages.create("malformedProxyAddress", proxyAddress));
|
||||
}
|
||||
this.delegate = null;
|
||||
var port = proxyAddress.getPort();
|
||||
if (port == -1) {
|
||||
port = 80;
|
||||
}
|
||||
this.myProxy =
|
||||
List.of(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress.getHost(), port)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ExplodeLoop
|
||||
public List<Proxy> select(URI uri) {
|
||||
for (var proxyRule : noProxyRules) {
|
||||
if (proxyRule.matches(uri)) {
|
||||
return NO_PROXY;
|
||||
}
|
||||
}
|
||||
if (delegate != null) {
|
||||
return delegate.select(uri);
|
||||
}
|
||||
assert myProxy != null;
|
||||
return myProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,9 @@ import org.pkl.core.SecurityManager;
|
||||
import org.pkl.core.SecurityManagers;
|
||||
import org.pkl.core.StackFrameTransformer;
|
||||
import org.pkl.core.StackFrameTransformers;
|
||||
import org.pkl.core.Value;
|
||||
import org.pkl.core.Version;
|
||||
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings;
|
||||
import org.pkl.core.module.ModuleKeyFactories;
|
||||
import org.pkl.core.packages.Checksums;
|
||||
import org.pkl.core.packages.Dependency.RemoteDependency;
|
||||
@@ -54,7 +56,7 @@ import org.pkl.core.util.Nullable;
|
||||
public final class Project {
|
||||
private final @Nullable Package pkg;
|
||||
private final DeclaredDependencies dependencies;
|
||||
private final EvaluatorSettings evaluatorSettings;
|
||||
private final PklEvaluatorSettings evaluatorSettings;
|
||||
private final URI projectFileUri;
|
||||
private final URI projectBaseUri;
|
||||
private final List<URI> tests;
|
||||
@@ -178,7 +180,9 @@ public final class Project {
|
||||
getProperty(
|
||||
module,
|
||||
"evaluatorSettings",
|
||||
(settings) -> parseEvaluatorSettings(settings, projectBaseUri));
|
||||
(settings) ->
|
||||
PklEvaluatorSettings.parse(
|
||||
(Value) settings, (it, name) -> resolveNullablePath(it, projectBaseUri, name)));
|
||||
@SuppressWarnings("unchecked")
|
||||
var testPathStrs = (List<String>) getProperty(module, "tests");
|
||||
var tests =
|
||||
@@ -210,51 +214,6 @@ public final class Project {
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static EvaluatorSettings parseEvaluatorSettings(Object settings, URI projectBaseUri) {
|
||||
var pSettings = (PObject) settings;
|
||||
var externalProperties = getNullableProperty(pSettings, "externalProperties", Project::asMap);
|
||||
var env = getNullableProperty(pSettings, "env", Project::asMap);
|
||||
var allowedModules = getNullableProperty(pSettings, "allowedModules", Project::asPatternList);
|
||||
var allowedResources =
|
||||
getNullableProperty(pSettings, "allowedResources", Project::asPatternList);
|
||||
var noCache = (Boolean) getNullableProperty(pSettings, "noCache");
|
||||
var modulePathStrs = (List<String>) getNullableProperty(pSettings, "modulePath");
|
||||
var timeout = (Duration) getNullableProperty(pSettings, "timeout");
|
||||
|
||||
List<Path> modulePath = null;
|
||||
if (modulePathStrs != null) {
|
||||
modulePath =
|
||||
modulePathStrs.stream()
|
||||
.map((it) -> resolveNullablePath(it, projectBaseUri, "modulePath"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
var moduleCacheDir = getNullablePath(pSettings, "moduleCacheDir", projectBaseUri);
|
||||
var rootDir = getNullablePath(pSettings, "rootDir", projectBaseUri);
|
||||
return new EvaluatorSettings(
|
||||
externalProperties,
|
||||
env,
|
||||
allowedModules,
|
||||
allowedResources,
|
||||
noCache,
|
||||
moduleCacheDir,
|
||||
modulePath,
|
||||
timeout,
|
||||
rootDir);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, String> asMap(Object t) {
|
||||
assert t instanceof Map;
|
||||
return (Map<String, String>) t;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<Pattern> asPatternList(Object t) {
|
||||
return ((List<String>) t).stream().map(Pattern::compile).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static Object getProperty(PObject settings, String propertyName) {
|
||||
return settings.getProperty(propertyName);
|
||||
}
|
||||
@@ -307,12 +266,6 @@ public final class Project {
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Path getNullablePath(
|
||||
Composite object, String propertyName, URI projectBaseUri) {
|
||||
return resolveNullablePath(
|
||||
(String) getNullableProperty(object, propertyName), projectBaseUri, propertyName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Package parsePackage(PObject pObj) throws URISyntaxException {
|
||||
var name = (String) pObj.getProperty("name");
|
||||
@@ -353,7 +306,7 @@ public final class Project {
|
||||
private Project(
|
||||
@Nullable Package pkg,
|
||||
DeclaredDependencies dependencies,
|
||||
EvaluatorSettings evaluatorSettings,
|
||||
PklEvaluatorSettings evaluatorSettings,
|
||||
URI projectFileUri,
|
||||
URI projectBaseUri,
|
||||
List<URI> tests,
|
||||
@@ -371,7 +324,13 @@ public final class Project {
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/** Use {@link org.pkl.core.project.Project#getEvaluatorSettings()} instead. */
|
||||
@Deprecated(forRemoval = true)
|
||||
public EvaluatorSettings getSettings() {
|
||||
return new EvaluatorSettings(evaluatorSettings);
|
||||
}
|
||||
|
||||
public PklEvaluatorSettings getEvaluatorSettings() {
|
||||
return evaluatorSettings;
|
||||
}
|
||||
|
||||
@@ -430,17 +389,13 @@ public final class Project {
|
||||
return Path.of(projectBaseUri);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static class EvaluatorSettings {
|
||||
private final PklEvaluatorSettings delegate;
|
||||
|
||||
private final @Nullable Map<String, String> externalProperties;
|
||||
private final @Nullable Map<String, String> env;
|
||||
private final @Nullable List<Pattern> allowedModules;
|
||||
private final @Nullable List<Pattern> allowedResources;
|
||||
private final @Nullable Boolean noCache;
|
||||
private final @Nullable Path moduleCacheDir;
|
||||
private final @Nullable List<Path> modulePath;
|
||||
private final @Nullable Duration timeout;
|
||||
private final @Nullable Path rootDir;
|
||||
public EvaluatorSettings(PklEvaluatorSettings delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public EvaluatorSettings(
|
||||
@Nullable Map<String, String> externalProperties,
|
||||
@@ -452,81 +407,63 @@ public final class Project {
|
||||
@Nullable List<Path> modulePath,
|
||||
@Nullable Duration timeout,
|
||||
@Nullable Path rootDir) {
|
||||
this.externalProperties = externalProperties;
|
||||
this.env = env;
|
||||
this.allowedModules = allowedModules;
|
||||
this.allowedResources = allowedResources;
|
||||
this.noCache = noCache;
|
||||
this.moduleCacheDir = moduleCacheDir;
|
||||
this.modulePath = modulePath;
|
||||
this.timeout = timeout;
|
||||
this.rootDir = rootDir;
|
||||
this.delegate =
|
||||
new PklEvaluatorSettings(
|
||||
externalProperties,
|
||||
env,
|
||||
allowedModules,
|
||||
allowedResources,
|
||||
noCache,
|
||||
moduleCacheDir,
|
||||
modulePath,
|
||||
timeout,
|
||||
rootDir,
|
||||
null);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable Map<String, String> getExternalProperties() {
|
||||
return externalProperties;
|
||||
return delegate.externalProperties();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable Map<String, String> getEnv() {
|
||||
return env;
|
||||
return delegate.env();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable List<Pattern> getAllowedModules() {
|
||||
return allowedModules;
|
||||
return delegate.allowedModules();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable List<Pattern> getAllowedResources() {
|
||||
return allowedResources;
|
||||
return delegate.allowedResources();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable Boolean isNoCache() {
|
||||
return noCache;
|
||||
return delegate.noCache();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable List<Path> getModulePath() {
|
||||
return modulePath;
|
||||
return delegate.modulePath();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable Duration getTimeout() {
|
||||
return timeout;
|
||||
return delegate.timeout();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable Path getModuleCacheDir() {
|
||||
return moduleCacheDir;
|
||||
return delegate.moduleCacheDir();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public @Nullable Path getRootDir() {
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
private boolean arePatternsEqual(
|
||||
@Nullable List<Pattern> myPattern, @Nullable List<Pattern> thatPattern) {
|
||||
if (myPattern == null) {
|
||||
return thatPattern == null;
|
||||
}
|
||||
if (thatPattern == null) {
|
||||
return false;
|
||||
}
|
||||
if (myPattern.size() != thatPattern.size()) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < myPattern.size(); i++) {
|
||||
if (!myPattern.get(i).pattern().equals(thatPattern.get(i).pattern())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int hashPatterns(@Nullable List<Pattern> patterns) {
|
||||
if (patterns == null) {
|
||||
return 0;
|
||||
}
|
||||
var ret = 1;
|
||||
for (var pattern : patterns) {
|
||||
ret = 31 * ret + pattern.pattern().hashCode();
|
||||
}
|
||||
return ret;
|
||||
return delegate.rootDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -534,52 +471,37 @@ public final class Project {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EvaluatorSettings that = (EvaluatorSettings) o;
|
||||
return Objects.equals(externalProperties, that.externalProperties)
|
||||
&& Objects.equals(env, that.env)
|
||||
&& arePatternsEqual(allowedModules, that.allowedModules)
|
||||
&& arePatternsEqual(allowedResources, that.allowedResources)
|
||||
&& Objects.equals(noCache, that.noCache)
|
||||
&& Objects.equals(moduleCacheDir, that.moduleCacheDir)
|
||||
&& Objects.equals(modulePath, that.modulePath)
|
||||
&& Objects.equals(timeout, that.timeout)
|
||||
&& Objects.equals(rootDir, that.rootDir);
|
||||
return o != null
|
||||
&& getClass() == o.getClass()
|
||||
&& Objects.equals(delegate, ((EvaluatorSettings) o).delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
var result =
|
||||
Objects.hash(
|
||||
externalProperties, env, noCache, moduleCacheDir, modulePath, timeout, rootDir);
|
||||
result = 31 * result + hashPatterns(allowedModules);
|
||||
result = 31 * result + hashPatterns(allowedResources);
|
||||
return result;
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EvaluatorSettings{"
|
||||
+ "externalProperties="
|
||||
+ externalProperties
|
||||
+ delegate.externalProperties()
|
||||
+ ", env="
|
||||
+ env
|
||||
+ delegate.env()
|
||||
+ ", allowedModules="
|
||||
+ allowedModules
|
||||
+ delegate.allowedModules()
|
||||
+ ", allowedResources="
|
||||
+ allowedResources
|
||||
+ delegate.allowedResources()
|
||||
+ ", noCache="
|
||||
+ noCache
|
||||
+ delegate.noCache()
|
||||
+ ", moduleCacheDir="
|
||||
+ moduleCacheDir
|
||||
+ delegate.moduleCacheDir()
|
||||
+ ", modulePath="
|
||||
+ modulePath
|
||||
+ delegate.modulePath()
|
||||
+ ", timeout="
|
||||
+ timeout
|
||||
+ delegate.timeout()
|
||||
+ ", rootDir="
|
||||
+ rootDir
|
||||
+ delegate.rootDir()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,11 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import org.pkl.core.*;
|
||||
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings;
|
||||
import org.pkl.core.module.ModuleKeyFactories;
|
||||
import org.pkl.core.resource.ResourceReaders;
|
||||
import org.pkl.core.runtime.VmEvalException;
|
||||
import org.pkl.core.runtime.VmExceptionBuilder;
|
||||
import org.pkl.core.util.IoUtils;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
@@ -32,19 +34,13 @@ import org.pkl.core.util.Nullable;
|
||||
* {@code load} methods.
|
||||
*/
|
||||
// keep in sync with stdlib/settings.pkl
|
||||
public final class PklSettings {
|
||||
public record PklSettings(Editor editor, @Nullable PklEvaluatorSettings.Http http) {
|
||||
private static final List<Pattern> ALLOWED_MODULES =
|
||||
List.of(Pattern.compile("pkl:"), Pattern.compile("file:"));
|
||||
|
||||
private static final List<Pattern> ALLOWED_RESOURCES =
|
||||
List.of(Pattern.compile("env:"), Pattern.compile("file:"));
|
||||
|
||||
private final Editor editor;
|
||||
|
||||
public PklSettings(Editor editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the user settings file ({@literal ~/.pkl/settings.pkl}). If this file does not exist,
|
||||
* returns default settings defined by module {@literal pkl.settings}.
|
||||
@@ -56,7 +52,9 @@ public final class PklSettings {
|
||||
/** For testing only. */
|
||||
static PklSettings loadFromPklHomeDir(Path pklHomeDir) throws VmEvalException {
|
||||
var path = pklHomeDir.resolve("settings.pkl");
|
||||
return Files.exists(path) ? load(ModuleSource.path(path)) : new PklSettings(Editor.SYSTEM);
|
||||
return Files.exists(path)
|
||||
? load(ModuleSource.path(path))
|
||||
: new PklSettings(Editor.SYSTEM, null);
|
||||
}
|
||||
|
||||
/** Loads a settings file from the given path. */
|
||||
@@ -73,46 +71,34 @@ public final class PklSettings {
|
||||
.addEnvironmentVariables(System.getenv())
|
||||
.build()) {
|
||||
var module = evaluator.evaluateOutputValueAs(moduleSource, PClassInfo.Settings);
|
||||
return parseSettings(module);
|
||||
return parseSettings(module, moduleSource);
|
||||
}
|
||||
}
|
||||
|
||||
private static PklSettings parseSettings(PObject module) throws VmEvalException {
|
||||
// can't use object mapping in pkl-core, so map manually
|
||||
var editor = (PObject) module.getProperty("editor");
|
||||
var urlScheme = (String) editor.getProperty("urlScheme");
|
||||
return new PklSettings(new Editor(urlScheme));
|
||||
private static PklSettings parseSettings(PObject module, ModuleSource location)
|
||||
throws VmEvalException {
|
||||
|
||||
if (!(module.getPropertyOrNull("editor") instanceof PObject pObject)
|
||||
|| !(pObject.getPropertyOrNull("urlScheme") instanceof String str)) {
|
||||
throw new VmExceptionBuilder().evalError("invalidSettingsFile", location.getUri()).build();
|
||||
}
|
||||
var editor = new Editor(str);
|
||||
var httpSettings = PklEvaluatorSettings.Http.parse((Value) module.getProperty("http"));
|
||||
return new PklSettings(editor, httpSettings);
|
||||
}
|
||||
|
||||
/** Returns the editor for viewing and editing Pkl files. */
|
||||
/**
|
||||
* Returns the editor for viewing and editing Pkl files.
|
||||
*
|
||||
* <p>This method is deprecated, use {@link #editor()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public Editor getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
var that = (PklSettings) o;
|
||||
|
||||
return editor.equals(that.editor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return editor.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PklSettings{" + "editor=" + editor + '}';
|
||||
}
|
||||
|
||||
/** An editor for viewing and editing Pkl files. */
|
||||
public static final class Editor {
|
||||
private final String urlScheme;
|
||||
|
||||
public record Editor(String urlScheme) {
|
||||
/** The editor associated with {@code file:} URLs ending in {@code .pkl}. */
|
||||
public static final Editor SYSTEM = new Editor("%{url}, line %{line}");
|
||||
|
||||
@@ -134,37 +120,15 @@ public final class PklSettings {
|
||||
/** The <a href="https://code.visualstudio.com">Visual Studio Code</a> editor. */
|
||||
public static final Editor VS_CODE = new Editor("vscode://file/%{path}:%{line}:%{column}");
|
||||
|
||||
/** Constructs an editor. */
|
||||
public Editor(String urlScheme) {
|
||||
this.urlScheme = urlScheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL scheme for opening files in this editor. The following placeholders are
|
||||
* supported: {@code %{url}}, {@code %{path}}, {@code %{line}}, {@code %{column}}.
|
||||
*
|
||||
* <p>This method is deprecated; use {@link #urlScheme()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public String getUrlScheme() {
|
||||
return urlScheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
var editor = (Editor) o;
|
||||
|
||||
return urlScheme.equals(editor.urlScheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return urlScheme.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Editor{" + "urlScheme='" + urlScheme + '\'' + '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,6 +540,18 @@ public final class IoUtils {
|
||||
System.setProperty("org.pkl.testMode", "true");
|
||||
}
|
||||
|
||||
public static void setSystemProxy(URI proxyAddress) {
|
||||
// Set HTTP proxy settings to configure the certificate revocation checker, because
|
||||
// there is no other way to configure it. (see https://bugs.openjdk.org/browse/JDK-8256409)
|
||||
//
|
||||
// This only influences the behavior of the revocation checker.
|
||||
// Otherwise, proxying is handled by [ProxySelector].
|
||||
System.setProperty("http.proxyHost", proxyAddress.getHost());
|
||||
System.setProperty(
|
||||
"http.proxyPort",
|
||||
proxyAddress.getPort() == -1 ? "80" : String.valueOf(proxyAddress.getPort()));
|
||||
}
|
||||
|
||||
public static @Nullable String parseTripleDotPath(URI importUri) throws URISyntaxException {
|
||||
var importScheme = importUri.getScheme();
|
||||
if (importScheme != null) return null;
|
||||
|
||||
@@ -1053,3 +1053,7 @@ Certificates can only be loaded from `jar:` or `file:` URLs, but got:\n\
|
||||
cannotFindBuiltInCertificates=\
|
||||
Cannot find Pkl's trusted CA certificates on the class path.\n\
|
||||
To fix this problem, add dependendy `org.pkl:pkl-certs`.
|
||||
|
||||
# suppress inspection "HttpUrlsUsage"
|
||||
malformedProxyAddress=\
|
||||
Malformed proxy URI (expecting `http://<host>[:<port>]`): `{0}`.
|
||||
|
||||
@@ -10,6 +10,7 @@ pkl:base
|
||||
pkl:Benchmark
|
||||
pkl:DocPackageInfo
|
||||
pkl:DocsiteInfo
|
||||
pkl:EvaluatorSettings
|
||||
pkl:json
|
||||
pkl:jsonnet
|
||||
pkl:math
|
||||
|
||||
146
pkl-core/src/test/kotlin/org/pkl/core/http/NoProxyRuleTest.kt
Normal file
146
pkl-core/src/test/kotlin/org/pkl/core/http/NoProxyRuleTest.kt
Normal file
@@ -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::]")))
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@ com.google.guava:guava:32.1.1-jre=testCompileClasspath,testImplementationDepende
|
||||
com.google.j2objc:j2objc-annotations:2.8=testCompileClasspath,testImplementationDependenciesMetadata
|
||||
com.google.jimfs:jimfs:1.3.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.ibm.icu:icu4j:58.2=validator
|
||||
com.ibm.icu:icu4j:71.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.ibm.icu:icu4j:72.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.shapesecurity:salvation:2.7.2=validator
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
commons-codec:commons-codec:1.10=validator
|
||||
commons-io:commons-io:2.4=validator
|
||||
commons-logging:commons-logging:1.2=validator
|
||||
isorelax:isorelax:20030108=validator
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
net.sf.saxon:Saxon-HE:9.6.0-4=validator
|
||||
nu.validator:cssvalidator:1.0.8=validator
|
||||
@@ -32,7 +32,7 @@ org.apache.httpcomponents:httpcore:4.4=validator
|
||||
org.apache.logging.log4j:log4j-1.2-api:2.17.1=validator
|
||||
org.apache.logging.log4j:log4j-api:2.17.1=validator
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.checkerframework:checker-qual:3.33.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.commonmark:commonmark-ext-gfm-tables:0.21.0=default
|
||||
org.commonmark:commonmark-ext-gfm-tables:0.22.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ajalt.clikt:clikt-jvm:3.5.1=compileClasspath
|
||||
com.github.ajalt.clikt:clikt:3.5.1=compileClasspath,compileOnlyDependenciesMetadata
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -281,6 +281,8 @@ public class PklPlugin implements Plugin<Project> {
|
||||
spec.getNoCache().convention(false);
|
||||
|
||||
spec.getTestPort().convention(-1);
|
||||
|
||||
spec.getNoProxy().convention(List.of());
|
||||
}
|
||||
|
||||
private void configureCodeGenSpec(CodeGenSpec spec) {
|
||||
@@ -424,6 +426,8 @@ public class PklPlugin implements Plugin<Project> {
|
||||
task.getModuleCacheDir().set(spec.getModuleCacheDir());
|
||||
task.getEvalTimeout().set(spec.getEvalTimeout());
|
||||
task.getTestPort().set(spec.getTestPort());
|
||||
task.getProxyAddress().set(spec.getProxyAddress());
|
||||
task.getNoProxy().set(spec.getNoProxy());
|
||||
}
|
||||
|
||||
private <T extends ModulesTask, S extends ModulesSpec> void configureModulesTask(T task, S spec) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.pkl.gradle.spec;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
@@ -50,4 +51,8 @@ public interface BasePklSpec {
|
||||
Property<Duration> getEvalTimeout();
|
||||
|
||||
Property<Integer> getTestPort();
|
||||
|
||||
Property<URI> getProxyAddress();
|
||||
|
||||
ListProperty<String> getNoProxy();
|
||||
}
|
||||
|
||||
@@ -125,6 +125,14 @@ public abstract class BasePklTask extends DefaultTask {
|
||||
@Optional
|
||||
public abstract Property<Integer> getTestPort();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
public abstract Property<URI> getProxyAddress();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
public abstract ListProperty<String> getNoProxy();
|
||||
|
||||
@TaskAction
|
||||
public void runTask() {
|
||||
doRunTask();
|
||||
@@ -156,7 +164,9 @@ public abstract class BasePklTask extends DefaultTask {
|
||||
false,
|
||||
false,
|
||||
getTestPort().getOrElse(-1),
|
||||
Collections.emptyList());
|
||||
Collections.emptyList(),
|
||||
getProxyAddress().getOrNull(),
|
||||
getNoProxy().getOrElse(List.of()));
|
||||
}
|
||||
return cachedOptions;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,9 @@ public abstract class ModulesTask extends BasePklTask {
|
||||
getNoProject().getOrElse(false),
|
||||
false,
|
||||
getTestPort().getOrElse(-1),
|
||||
Collections.emptyList());
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
List.of());
|
||||
}
|
||||
return cachedOptions;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.11=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.25.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:23.0.2=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:23.0.2=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.nio.file.Path
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings.*
|
||||
import org.pkl.core.module.PathElement
|
||||
import org.pkl.core.packages.Checksums
|
||||
|
||||
@@ -123,6 +124,7 @@ data class CreateEvaluatorRequest(
|
||||
val cacheDir: Path?,
|
||||
val outputFormat: String?,
|
||||
val project: Project?,
|
||||
val http: Http?,
|
||||
) : ClientRequestMessage() {
|
||||
override val type = MessageType.CREATE_EVALUATOR_REQUEST
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.msgpack.core.MessageTypeException
|
||||
import org.msgpack.core.MessageUnpacker
|
||||
import org.msgpack.value.Value
|
||||
import org.msgpack.value.impl.ImmutableStringValueImpl
|
||||
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
|
||||
import org.pkl.core.module.PathElement
|
||||
import org.pkl.core.packages.Checksums
|
||||
|
||||
@@ -50,8 +51,8 @@ internal class MessagePackDecoder(private val unpacker: MessageUnpacker) : Messa
|
||||
allowedModules = map.unpackStringListOrNull("allowedModules")?.map(Pattern::compile),
|
||||
allowedResources =
|
||||
map.unpackStringListOrNull("allowedResources")?.map(Pattern::compile),
|
||||
clientModuleReaders = map.unpackModuleReaderSpec("clientModuleReaders"),
|
||||
clientResourceReaders = map.unpackResourceReaderSpec("clientResourceReaders"),
|
||||
clientModuleReaders = map.unpackModuleReaderSpec(),
|
||||
clientResourceReaders = map.unpackResourceReaderSpec(),
|
||||
modulePaths = map.unpackStringListOrNull("modulePaths")?.map(Path::of),
|
||||
env = map.unpackStringMapOrNull("env"),
|
||||
properties = map.unpackStringMapOrNull("properties"),
|
||||
@@ -59,7 +60,8 @@ internal class MessagePackDecoder(private val unpacker: MessageUnpacker) : Messa
|
||||
rootDir = map.unpackStringOrNull("rootDir")?.let(Path::of),
|
||||
cacheDir = map.unpackStringOrNull("cacheDir")?.let(Path::of),
|
||||
outputFormat = map.unpackStringOrNull("outputFormat"),
|
||||
project = map.unpackProject("project")
|
||||
project = map.unpackProject(),
|
||||
http = map.unpackHttp(),
|
||||
)
|
||||
}
|
||||
MessageType.CREATE_EVALUATOR_RESPONSE.code -> {
|
||||
@@ -221,8 +223,8 @@ internal class MessagePackDecoder(private val unpacker: MessageUnpacker) : Messa
|
||||
PathElement(map.unpackString("name"), map.unpackBoolean("isDirectory"))
|
||||
}
|
||||
|
||||
private fun Map<Value, Value>.unpackModuleReaderSpec(name: String): List<ModuleReaderSpec>? {
|
||||
val keys = getNullable(name) ?: return null
|
||||
private fun Map<Value, Value>.unpackModuleReaderSpec(): List<ModuleReaderSpec>? {
|
||||
val keys = getNullable("clientModuleReaders") ?: return null
|
||||
return keys.asArrayValue().toList().map { value ->
|
||||
val readerMap = value.asMapValue().map()
|
||||
ModuleReaderSpec(
|
||||
@@ -234,8 +236,8 @@ internal class MessagePackDecoder(private val unpacker: MessageUnpacker) : Messa
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<Value, Value>.unpackResourceReaderSpec(name: String): List<ResourceReaderSpec> {
|
||||
val keys = getNullable(name) ?: return emptyList()
|
||||
private fun Map<Value, Value>.unpackResourceReaderSpec(): List<ResourceReaderSpec> {
|
||||
val keys = getNullable("clientResourceReaders") ?: return emptyList()
|
||||
return keys.asArrayValue().toList().map { value ->
|
||||
val readerMap = value.asMapValue().map()
|
||||
ResourceReaderSpec(
|
||||
@@ -246,13 +248,26 @@ internal class MessagePackDecoder(private val unpacker: MessageUnpacker) : Messa
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<Value, Value>.unpackProject(name: String): Project? {
|
||||
val projMap = getNullable(name)?.asMapValue()?.map() ?: return null
|
||||
private fun Map<Value, Value>.unpackProject(): Project? {
|
||||
val projMap = getNullable("project")?.asMapValue()?.map() ?: return null
|
||||
val projectFileUri = URI(projMap.unpackString("projectFileUri"))
|
||||
val dependencies = projMap.unpackDependencies("dependencies")
|
||||
return Project(projectFileUri, null, dependencies)
|
||||
}
|
||||
|
||||
private fun Map<Value, Value>.unpackHttp(): PklEvaluatorSettings.Http? {
|
||||
val httpMap = getNullable("http")?.asMapValue()?.map() ?: return null
|
||||
val proxy = httpMap.unpackProxy()
|
||||
return PklEvaluatorSettings.Http(proxy)
|
||||
}
|
||||
|
||||
private fun Map<Value, Value>.unpackProxy(): PklEvaluatorSettings.Proxy? {
|
||||
val proxyMap = getNullable("proxy")?.asMapValue()?.map() ?: return null
|
||||
val address = proxyMap.unpackString("address")
|
||||
val noProxy = proxyMap.unpackStringListOrNull("noProxy")
|
||||
return PklEvaluatorSettings.Proxy.create(address, noProxy)
|
||||
}
|
||||
|
||||
private fun Map<Value, Value>.unpackDependencies(name: String): Map<String, Dependency> {
|
||||
val mapValue = get(name).asMapValue().map()
|
||||
return mapValue.entries.associate { (key, value) ->
|
||||
|
||||
@@ -29,9 +29,9 @@ import org.pkl.core.packages.PackageUri
|
||||
import org.pkl.core.project.DeclaredDependencies
|
||||
import org.pkl.core.resource.ResourceReader
|
||||
import org.pkl.core.resource.ResourceReaders
|
||||
import org.pkl.core.util.IoUtils
|
||||
|
||||
class Server(private val transport: MessageTransport, private val httpClient: HttpClient) :
|
||||
AutoCloseable {
|
||||
class Server(private val transport: MessageTransport) : AutoCloseable {
|
||||
private val evaluators: MutableMap<Long, BinaryEvaluator> = ConcurrentHashMap()
|
||||
|
||||
// https://github.com/jano7/executor would be the perfect executor here
|
||||
@@ -162,6 +162,14 @@ class Server(private val transport: MessageTransport, private val httpClient: Ht
|
||||
val properties = message.properties ?: emptyMap()
|
||||
val timeout = message.timeout
|
||||
val cacheDir = message.cacheDir
|
||||
val http =
|
||||
with(HttpClient.builder()) {
|
||||
message.http?.proxy?.let { proxy ->
|
||||
setProxy(proxy.address, message.http.proxy?.noProxy ?: listOf())
|
||||
proxy.address?.let(IoUtils::setSystemProxy)
|
||||
}
|
||||
buildLazily()
|
||||
}
|
||||
val dependencies =
|
||||
message.project?.let { proj ->
|
||||
buildDeclaredDependencies(proj.projectFileUri, proj.dependencies, null)
|
||||
@@ -175,7 +183,7 @@ class Server(private val transport: MessageTransport, private val httpClient: Ht
|
||||
SecurityManagers.defaultTrustLevels,
|
||||
rootDir
|
||||
),
|
||||
httpClient,
|
||||
http,
|
||||
ClientLogger(evaluatorId, transport),
|
||||
createModuleKeyFactories(message, evaluatorId, resolver),
|
||||
createResourceReaders(message, evaluatorId, resolver),
|
||||
|
||||
@@ -1029,7 +1029,8 @@ abstract class AbstractServerTest {
|
||||
rootDir = null,
|
||||
cacheDir = cacheDir,
|
||||
outputFormat = null,
|
||||
project = project
|
||||
project = project,
|
||||
http = null,
|
||||
)
|
||||
|
||||
send(message)
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.io.PipedInputStream
|
||||
import java.io.PipedOutputStream
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.pkl.core.http.HttpClient
|
||||
|
||||
class JvmServerTest : AbstractServerTest() {
|
||||
private val transports: Pair<MessageTransport, MessageTransport> = run {
|
||||
@@ -35,7 +34,7 @@ class JvmServerTest : AbstractServerTest() {
|
||||
}
|
||||
|
||||
override val client: TestTransport = TestTransport(transports.first)
|
||||
private val server: Server = Server(transports.second, HttpClient.dummyClient())
|
||||
private val server: Server = Server(transports.second)
|
||||
|
||||
@BeforeEach
|
||||
fun beforeEach() {
|
||||
|
||||
@@ -112,7 +112,8 @@ class MessagePackCodecTest {
|
||||
"baz" to
|
||||
RemoteDependency(URI("package://localhost:0/baz@1.1.0"), Checksums("abc123"))
|
||||
)
|
||||
)
|
||||
),
|
||||
http = null,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,14 +28,10 @@ org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1=default,runtimeClasspath,
|
||||
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.5.1=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains:annotations:13.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jline:jline-native:3.23.0=default
|
||||
org.jline:jline-native:3.23.0=runtimeClasspath,testRuntimeClasspath
|
||||
org.jline:jline-reader:3.23.0=default
|
||||
org.jline:jline-reader:3.23.0=runtimeClasspath,testRuntimeClasspath
|
||||
org.jline:jline-terminal-jansi:3.23.0=default
|
||||
org.jline:jline-terminal-jansi:3.23.0=runtimeClasspath,testRuntimeClasspath
|
||||
org.jline:jline-terminal:3.23.0=default
|
||||
org.jline:jline-terminal:3.23.0=runtimeClasspath,testRuntimeClasspath
|
||||
org.jline:jline-native:3.23.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jline:jline-reader:3.23.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jline:jline-terminal-jansi:3.23.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jline:jline-terminal:3.23.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.msgpack:msgpack-core:0.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.organicdesign:Paguro:3.10.3=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.5=default,runtimeClasspath,testRuntimeClasspath
|
||||
|
||||
140
stdlib/EvaluatorSettings.pkl
Normal file
140
stdlib/EvaluatorSettings.pkl
Normal file
@@ -0,0 +1,140 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2024 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Common settings for Pkl's own evaluator.
|
||||
@ModuleInfo { minPklVersion = "0.26.0" }
|
||||
@Since { version = "0.26.0" }
|
||||
module pkl.EvaluatorSettings
|
||||
|
||||
/// The external properties available to Pkl, read using the `prop:` scheme.
|
||||
externalProperties: Mapping<String, String>?
|
||||
|
||||
/// The environment variables available to Pkl, read using the `env:` scheme.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// env {
|
||||
/// ["IS_PROD"] = "true"
|
||||
/// }
|
||||
/// ```
|
||||
env: Mapping<String, String>?
|
||||
|
||||
/// The set of module URI patterns that can be imported.
|
||||
///
|
||||
/// Each element is a regular expression pattern that is tested against a module import.
|
||||
///
|
||||
/// Modules are imported either through an amends or extends clause, an import clause, or an
|
||||
/// import expression.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// allowedModules {
|
||||
/// "https:"
|
||||
/// "file:"
|
||||
/// "package:"
|
||||
/// "projectpackage:"
|
||||
/// }
|
||||
/// ```
|
||||
allowedModules: Listing<String(isRegex)>?
|
||||
|
||||
/// The set of resource URI patterns that can be imported.
|
||||
///
|
||||
/// Each element is a regular expression pattern that is tested against a resource read.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// allowedResources {
|
||||
/// "https:"
|
||||
/// "file:"
|
||||
/// "package:"
|
||||
/// "projectpackage:"
|
||||
/// "env:"
|
||||
/// "prop:"
|
||||
/// }
|
||||
/// ```
|
||||
allowedResources: Listing<String(isRegex)>?
|
||||
|
||||
/// Disables the file system cache for `package:` modules.
|
||||
///
|
||||
/// When caching is disabled, packages are loaded over the network and stored in memory.
|
||||
noCache: Boolean?
|
||||
|
||||
/// A collection of jars, zips, or directories to be placed into the module path.
|
||||
///
|
||||
/// Module path modules and resources may be read and imported using the `modulepath:` scheme.
|
||||
modulePath: Listing<String>?
|
||||
|
||||
/// The duration after which evaluation of a source module will be timed out.
|
||||
///
|
||||
/// Note that a timeout is treated the same as a program error in that any subsequent source modules will not be evaluated.
|
||||
timeout: Duration?
|
||||
|
||||
/// The directory where `package:` modules are cached.
|
||||
moduleCacheDir: String?
|
||||
|
||||
/// Restricts access to file-based modules and resources to those located under this directory.
|
||||
rootDir: String?
|
||||
|
||||
/// Configuration of outgoing HTTP requests.
|
||||
http: Http?
|
||||
|
||||
/// Settings that control how Pkl talks to HTTP(S) servers.
|
||||
class Http {
|
||||
/// Configuration of the HTTP proxy to use.
|
||||
///
|
||||
/// If [null], uses the operating system's proxy configuration.
|
||||
proxy: Proxy?
|
||||
}
|
||||
|
||||
/// Settings that control how Pkl talks to HTTP proxies.
|
||||
class Proxy {
|
||||
/// The proxy to use for HTTP(S) connections.
|
||||
///
|
||||
/// At the moment, only HTTP proxies are supported.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// address = "http://my.proxy.example.com:5080"
|
||||
/// ```
|
||||
address: Uri(startsWith("http://"))?
|
||||
|
||||
/// Hosts to which all connections should bypass a proxy.
|
||||
///
|
||||
/// Values can be either hostnames, or IP addresses.
|
||||
/// IP addresses can optionally be provided using [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation).
|
||||
///
|
||||
/// The only wildcard is `"*"`, which disables all proxying.
|
||||
///
|
||||
/// A hostname matches all subdomains.
|
||||
/// For example, `example.com` matches `foo.example.com`, but not `fooexample.com`.
|
||||
/// A hostname that is prefixed with a dot matches the hostname itself,
|
||||
/// so `.example.com` matches `example.com`.
|
||||
///
|
||||
/// Optionally, a port can be specified.
|
||||
/// If a port is omitted, all ports are matched.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// noProxy {
|
||||
/// "127.0.0.1"
|
||||
/// "169.254.0.0/16"
|
||||
/// "example.com"
|
||||
/// "localhost:5050"
|
||||
/// }
|
||||
/// ```
|
||||
noProxy: Listing<String>(isDistinct)
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
@ModuleInfo { minPklVersion = "0.26.0" }
|
||||
module pkl.Project
|
||||
|
||||
import "pkl:EvaluatorSettings" as EvaluatorSettingsModule
|
||||
import "pkl:Project"
|
||||
import "pkl:reflect"
|
||||
import "pkl:semver"
|
||||
@@ -195,7 +196,9 @@ local isFileBasedProject = projectFileUri.startsWith("file:")
|
||||
/// - [modulePath][EvaluatorSettings.modulePath]
|
||||
/// - [rootDir][EvaluatorSettings.rootDir]
|
||||
/// - [moduleCacheDir][EvaluatorSettings.moduleCacheDir]
|
||||
evaluatorSettings: EvaluatorSettings(
|
||||
///
|
||||
/// For each of these, relative paths are resolved against the project's enclosing directory.
|
||||
evaluatorSettings: EvaluatorSettingsModule(
|
||||
(modulePath != null).implies(isFileBasedProject),
|
||||
(rootDir != null).implies(isFileBasedProject),
|
||||
(moduleCacheDir != null).implies(isFileBasedProject)
|
||||
@@ -403,82 +406,8 @@ class Package {
|
||||
fixed uri: PackageUri = "\(baseUri)@\(version)"
|
||||
}
|
||||
|
||||
class EvaluatorSettings {
|
||||
/// The external properties available to Pkl, read using the `prop:` scheme.
|
||||
externalProperties: Mapping<String, String>?
|
||||
|
||||
/// The environment variables available to Pkl, read using the `env:` scheme.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// env {
|
||||
/// ["IS_PROD"] = "true"
|
||||
/// }
|
||||
/// ```
|
||||
env: Mapping<String, String>?
|
||||
|
||||
/// The set of module URI patterns that can be imported.
|
||||
///
|
||||
/// Each element is a regular expression pattern that is tested against a module import.
|
||||
///
|
||||
/// Modules are imported either through an amends or extends clause, an import clause, or an
|
||||
/// import expression.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// allowedModules {
|
||||
/// "https:"
|
||||
/// "file:"
|
||||
/// "package:"
|
||||
/// "projectpackage:"
|
||||
/// }
|
||||
/// ```
|
||||
allowedModules: Listing<String(isRegex)>?
|
||||
|
||||
/// The set of resource URI patterns that can be imported.
|
||||
///
|
||||
/// Each element is a regular expression pattern that is tested against a resource read.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// allowedResources {
|
||||
/// "https:"
|
||||
/// "file:"
|
||||
/// "package:"
|
||||
/// "projectpackage:"
|
||||
/// "env:"
|
||||
/// "prop:"
|
||||
/// }
|
||||
/// ```
|
||||
allowedResources: Listing<String(isRegex)>?
|
||||
|
||||
/// Disables the file system cache for `package:` modules.
|
||||
///
|
||||
/// When caching is disabled, packages are loaded over the network and stored in memory.
|
||||
noCache: Boolean?
|
||||
|
||||
/// A collection of jars, zips, or directories to be placed into the module path.
|
||||
///
|
||||
/// Module path modules and resources may be read and imported using the `modulepath:` scheme.
|
||||
///
|
||||
/// Relative paths are resolved against PklProject's enclosing directory.
|
||||
modulePath: Listing<String>?
|
||||
|
||||
/// The duration after which evaluation of a source module will be timed out.
|
||||
///
|
||||
/// Note that a timeout is treated the same as a program error in that any subsequent source modules will not be evaluated.
|
||||
timeout: Duration?
|
||||
|
||||
/// The directory where `package:` modules are cached.
|
||||
///
|
||||
/// Relative paths are resolved against PklProject's enclosing directory.
|
||||
moduleCacheDir: String?
|
||||
|
||||
/// Restricts access to file-based modules and resources to those located under this directory.
|
||||
///
|
||||
/// Relative paths are resolved against PklProject's enclosing directory.
|
||||
rootDir: String?
|
||||
}
|
||||
@Deprecated { since = "0.26.0"; replaceWith = "EvaluatorSettingsModule" }
|
||||
typealias EvaluatorSettings = EvaluatorSettingsModule
|
||||
|
||||
/// Common software licenses in the [SPDX License List](https://spdx.org/licenses/).
|
||||
typealias CommonSpdxLicenseIdentifier =
|
||||
|
||||
@@ -22,9 +22,15 @@
|
||||
@ModuleInfo { minPklVersion = "0.26.0" }
|
||||
module pkl.settings
|
||||
|
||||
import "pkl:EvaluatorSettings"
|
||||
|
||||
/// The editor for viewing and editing Pkl files.
|
||||
editor: Editor = System
|
||||
|
||||
/// Settings for controlling how Pkl makes HTTP(S) requests.
|
||||
@Since { version = "0.26.0" }
|
||||
http: EvaluatorSettings.Http?
|
||||
|
||||
/// The editor associated with `file:` URLs ending in `.pkl`.
|
||||
hidden System: Editor = new {
|
||||
urlScheme = "%{url}, line %{line}"
|
||||
|
||||
Reference in New Issue
Block a user